GiovineItalia / Gadfly.jl

Crafty statistical graphics for Julia.
http://gadflyjl.org/stable/
Other
1.9k stars 250 forks source link

Overlapping labels for the top and bottom legend when drawing to PNG/PDF #688

Closed mortenpi closed 8 years ago

mortenpi commented 9 years ago

The labels for the top/bottom legend sometimes overlap when drawing to the PNG and the PDF. For example: bug

The following code reproduces the issue:

using Gadfly
p = plot(exp, 0, 1, Guide.manual_color_key("Legend", ["foo45678", "bar"], ["red", "blue"]), Theme(key_position = :bottom))
draw(PDF("bug.pdf", 8cm, 8cm), p)
#draw(SVG(8cm, 8cm), p)

Same issue with top and bottom legends and to both PNG and PDF. SVG output is fine.

Versions:

dcjones commented 9 years ago

Could you try Pkg.add("Fontconfig") and run this again? Overlaps are generally because it's not accurately computing the size of the text when doing the layout. Having Fontconfig (and Cairo) installed can improve this.

mortenpi commented 9 years ago

Yep, adding Fontconfig fixes it!

Any chance of making Fontconfig install automatically? If I recall correctly then Gadfly doesn't install Cairo automatically and when one tries to do PNG/PDF plots, it says that Cairo is missing. Doing Pkg.add("Cairo") then is easy but it's not obvious that one should add Fontconfig as well.

dcjones commented 9 years ago

I'd like to, but there's not really a good mechanism now for optional or suggested dependencies. The best I could do is print a warning somewhere. That may be a good idea, because I get the impression almost no one has the Fontconfig package installed.

mortenpi commented 9 years ago

I decided to remind myself what the process of installing all these packages looked like (on v0.4). So, when you have Gadfly and you do something like draw(PNG("file.png", 20cm, 10cm), p) then you get this

LoadError: Install Cairo to use the PNG backend. You may need to delete your cache files (usually in ~/.julia/lib/v0.4) afterwards.
while loading In[17], in expression starting on line 3

 in error at ./error.jl:21
 in PNG at /home/morten/.julia/v0.4/Compose/src/Compose.jl:142

which originates from Compose.jl#L146 (I guess it's more of a Compose issue really).

I would propose adding the reference to Fontconfig there. Perhaps one could even make the error message more helpful by saying exactly what one should do:

LoadError: Cairo and Fontconfig packages are required by the PNG backend. Run:
Pkg.add("Cairo")
Pkg.add("Fontconfig")
You also have to clear your precompilation cache afterwards (usually in ~/.julia/lib/v0.4) and restart the REPL session.

Maybe too verbose but having these lines makes it easy and convenient to quickly copy-paste them to get everything installed.

Alternatively, perhaps it would make sense to have the Cairo.jl package itself depend on Fontconfig.jl? At least on Ubuntu, libcairo anyway depends libfontconfig so that should not be an issue. However, I am not 100% sure how these packages relate to each other.

Balinus commented 8 years ago

I get the same error, even after installing package Fontconfig. (tried the code provided in original post and my own code produce this kind of overlap error). I am also using "Guide.manual_color_key". So perhaps it's from there.

The only workaround is to set font size for legend smaller (which is not a solution right now).

mortenpi commented 8 years ago

@Balinus, assuming you are running Julia v0.4, did you remove the precompilation cache for Compose (should be in ~/.julia/lib/v0.4) and restart the REPL session?

Balinus commented 8 years ago

@mortenpi : Thanks! That did work.

mortenpi commented 8 years ago

I think dcjones/Compose.jl#168 is the best solution we can have for this issue for now and since that's merged this can be closed.

CiaranOMara commented 7 years ago

Overlapping of legend keys still seems to be a persistent issue after adding both Cairo and Fontconfig then removing Compose.ji.

screen shot 2017-08-23 at 11 08 21 am
bjarthur commented 7 years ago

the example in the original post works for me. could you please distill your problem to a minimal test case and post the code here? thanks.

CiaranOMara commented 7 years ago

Below is the minimum code for my case. The output has been included at the bottom.

using Gadfly

Gadfly.push_theme(:default)
Gadfly.push_theme(Theme(key_position= :bottom))

srand(1)

x1= [7,13,16]
x2= [3,5,9,20]
x3 = 1:20

y = rand(20)

layer1 = layer(
    xintercept=x1,
    Geom.vline(color="red"))

layer2 = layer(
    xintercept=x2,
    Geom.vline(color="lightblue"))

layer3 = layer(
    x=x3,
    y=y,
    Geom.bar,
    Theme(default_color="grey"))

plot = Gadfly.plot(
  Guide.Title("plot title"),
  Guide.manual_color_key("legend title", ["Binding Sites", "GATC Sites", "Methylated Fragment"], ["red", "lightblue", "grey"]),
  layer1, layer2, layer3)

draw(PDF("ouput.pdf",1920px, 1080px), plot)

ouput.pdf

bjarthur commented 7 years ago

interesting that the SVG is fine; only the Cairo backends seem to not estimate the text extents properly. the colored boxes appear in exactly the same spot, and Cairo is using a slightly different font.

until this is fixed, the hack is just to add trailing whitespace to your labels: "Binding Sites ", etc.

dcjones commented 7 years ago

There are different things that can cause this, but the two most common in my experience are:

  1. Fontconfig not being installed or not working. You can double check this just by trying using Fontconfig.
  2. Different fonts getting selected. Estimating text extents, rendering through cairo, and drawing the SVG (e.g. in a browser) all separately involve selecting a font. It's intended to use the same font, but can end up choosing different ones resulting in errors like this. Try setting the font using Theme to something you know is installed on your computer.
CiaranOMara commented 7 years ago

It looks like I have Fontconfig setup and working.

@bjarthur, adding extra spaces does provide a workaround -- thank you.

@dcjones, on point 2, I don't have an awareness of having altered/changed any of the fonts on my Operating System. Does or should Gadfly choose safe default fonts based on the operating system it detects? Hopefully, I'll come back with the result of setting a font by the end of my day.

Last login: Wed Aug 30 23:11:53 on ttys006
➜  ~ exec '/Users/comara/Applications/Julia-0.6.app/Contents/Resources/julia/bi/julia'
               _
   _       _ _(_)_     |  A fresh approach to technical computing
  (_)     | (_) (_)    |  Documentation: https://docs.julialang.org
   _ _   _| |_  __ _   |  Type "?help" for help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 0.6.0 (2017-06-19 13:05 UTC)
 _/ |\__'_|_|_|\__'_|  |  Official http://julialang.org/ release
|__/                   |  x86_64-apple-darwin13.4.0

julia> using Fontconfig

julia> list()
679-element Array{Fontconfig.Pattern,1}:
 Fontconfig.Pattern(".Apple Color Emoji UI:style=Regular:file=/System/Library/Fonts/Apple Color Emoji.ttc")                                                                                                                                                           
 Fontconfig.Pattern("Cochin:style=Italic,斜體,Kursiv,Kursiivi,Italique,Corsivo,イタリック,이탤릭체,Cursief,Itálico,Курсивный,斜体,Cursiva:file=/Library/Fonts/Cochin.ttc")                                                                                                         
 Fontconfig.Pattern("Raanana:style=Negreta,粗體,Bold,Fed,Fett,Έντονα,Lihava,Gras,עבה,Félkövér,Grassetto,ボールド,볼드체,Vet,Fet,Negrito,Aldin,Жирный,Podebljani,หนา,Kalın,Tebal,Жирний,Đậm,बोल्ड,粗体,عريض,Negrita:file=/Library/Fonts/Raanana.ttc")                             
 Fontconfig.Pattern("SignPainter,SignPainter\-HouseScript:style=HouseScript,Regular:file=/Library/Fonts/SignPainter.ttc")                                                                                                                                             
 Fontconfig.Pattern("Iowan Old Style:style=Titling:file=/Library/Fonts/Iowan Old Style.ttc")                                                                                                                                                                          
 Fontconfig.Pattern("Avenir:style=Oblique:file=/System/Library/Fonts/Avenir.ttc")                                                                                                                                                                                     
 Fontconfig.Pattern(".SF Compact Text:style=Medium Italic:file=/System/Library/Fonts/SFCompactText-MediumItalic.otf")                                                                                                                                                 
 Fontconfig.Pattern("Al Bayan,البيان:style=Plain,平體,Almindelig,Standard,Normaali,Simple,Piano,プレーン,보통체,Normaal,Vanlig,Plano,Прямой,Normal,普通体,بسيط:file=/Library/Fonts/AlBayan.ttc")                                                                                  
 Fontconfig.Pattern("Apple SD Gothic Neo,Apple SD 산돌고딕 Neo:style=Heavy,무거운:file=/System/Library/Fonts/AppleSDGothicNeo.ttc")                                                                                                                                          
 Fontconfig.Pattern("Damascus,دمشق:style=Semi Bold,شبه عريض:file=/Library/Fonts/Damascus.ttc")                                                                                                                                                                        
 ⋮                                                                                                                                                                                                                                                                    
 Fontconfig.Pattern("Snell Roundhand:style=Regular:file=/Library/Fonts/SnellRoundhand.ttc")                                                                                                                                                                           
 Fontconfig.Pattern("Bodoni 72:style=Book:file=/Library/Fonts/Bodoni 72.ttc")                                                                                                                                                                                         
 Fontconfig.Pattern("Avenir Next:style=Italic:file=/System/Library/Fonts/Avenir Next.ttc")                                                                                                                                                                            
 Fontconfig.Pattern("Seravek,Seravek Light:style=Light Italic,Italic:file=/Library/Fonts/Seravek.ttc")                                                                                                                                                                
 Fontconfig.Pattern(".SF NS Display Condensed:style=Bold:file=/System/Library/Fonts/SFNSDisplayCondensed-Bold.otf")                                                                                                                                                   
 Fontconfig.Pattern("Helvetica Neue:style=Light,細體,Mager,Fein,Ohut,Fin,Leggero,ライト,가는체,Licht,Tynn,Leve,Светлый,细体,Fina:file=/System/Library/Fonts/HelveticaNeue.dfont")                                                                                               
 Fontconfig.Pattern("Skia:style=Condensed,Condensée,Komprimiert,Condensato,Smal,Condensada,Condensado,Kapea,S›k›fl›k,Usko:file=/Library/Fonts/Skia.ttf")                                                                                                               
 Fontconfig.Pattern("Optima:style=Bold Italic,粗斜體,Fed kursiv,Fett\, Kursiv,Puolilihava kursiivi,Gras italique,Grassetto corsivo,ボールド・イタリック,볼드 이탤릭체,Vet Cursief,Fet kursiv,Negrito Itálico,Жирный курсивный,粗斜体,Negrita cursiva:file=/System/Library/Fonts/Optima.ttc")
 Fontconfig.Pattern("Helvetica Neue:style=UltraLight,極細體,Ultramager,Extrafein,Erikoisohut,Ultrafin,Ultraleggero,ウルトラ・ライト,아주 가는체,UltraLicht,Ultratynn,Ultra Leve,Сверхсветлый,Extra mager,超细体,Ultrafina:file=/System/Library/Fonts/HelveticaNeue.dfont")               

julia> 
CiaranOMara commented 7 years ago

In short, setting key_label_font to a single listed font can allow for an output without overlap.

I wrote the following to see the default key_label_font value and how it was different when manually set.

using Gadfly

# Initial theme
initial_theme = Gadfly.pop_theme()

open("initial_theme.txt", "w") do f
    write(f, string(initial_theme)*"\n")
end

# Default theme
Gadfly.push_theme(:default)
default_theme = Gadfly.pop_theme()

open("default_theme.txt", "w") do f
    write(f, string(default_theme)*"\n")
end

# Altered defualt theme
Gadfly.push_theme(:default)
font="PT Sans"
Gadfly.push_theme(Theme(key_position= :bottom, key_label_font=font))
altered_theme = Gadfly.pop_theme()

open("altered_theme.txt", "w") do f
    write(f, string(altered_theme)*"\n")
end

Looking at the difference we can see the value "'PT Sans','Helvetica Neue','Helvetica',sans-serif" reduced to "PT Sans".

screen shot 2017-09-02 at 11 21 09 am

I assume the value specifies preferential font selection, however, sans-serif is not wrapped in quotes, which later turns out not to be the issue.

I then used the following to check the fonts listed in the default key_label_font value. As far as I'm aware, I do not have sans-serif, I have Microsoft sans-serif.

using Gadfly

Gadfly.push_theme(:default)
# Gadfly.push_theme(Theme(key_position= :bottom))

font="PT Sans"
# font="Helvetica Neue"
# font="Helvetica"
# font="sans-serif"
# font = "'PT Sans','Helvetica Neue','Helvetica','sans-serif'" #note: quotes.
# font = "'PT Sans','Helvetica Neue','Helvetica'"

Gadfly.push_theme(Theme(key_position= :bottom, key_label_font=font))

srand(1)

x1= [7,13,16]
x2= [3,5,9,20]
x3 = 1:20

y = rand(20)

layer1 = layer(
    xintercept=x1,
    Geom.vline(color="red"))

layer2 = layer(
    xintercept=x2,
    Geom.vline(color="lightblue"))

layer3 = layer(
    x=x3,
    y=y,
    Geom.bar,
    Theme(default_color="grey"))

plot = Gadfly.plot(
  Guide.Title("plot title"),
  Guide.manual_color_key("legend title", ["Binding Sites", "GATC Sites", "Methylated Fragment"], ["red", "lightblue", "grey"]),
  layer1, layer2, layer3)

filename = "ouput-$(replace(font, ' ', '_')).pdf"

draw(PDF(filename,1920px, 1080px), plot)

The results for this code are as follows:

font="PT Sans"

screen shot 2017-09-02 at 12 27 21 pm

font="Helvetica Neue"

screen shot 2017-09-02 at 12 08 05 pm

font="Helvetica"

screen shot 2017-09-02 at 12 08 32 pm

font="sans-serif"

screen shot 2017-09-02 at 12 09 17 pm

font = "'PT Sans','Helvetica Neue','Helvetica','sans-serif'"

screen shot 2017-09-02 at 12 09 55 pm

font = "'PT Sans','Helvetica Neue','Helvetica'"

screen shot 2017-09-02 at 12 10 17 pm

@bjarthur and @dcjones, do you think it would be reasonable to add check that reduces the list to the first available font and only pass that font to the other libraries involved?