I’ve been working on a hiking app where I draw the names of the trails on the map with the trace of the trails themselves. My text on path method is working reasonably well as shown in the attached graphic. You can see that I am filling a rectangle behind each letter with a partially transparent color to make the letters more readable. However, the standard way of doing this in mapping apps is with outline text – not the old fashioned type that one gets with g.Outline, but where each letter is filled with the appropriate text color and then a white or black outline is drawn around each letter (look at any place name in Google Maps or Apple maps for an example).
I had thought this was impossible in Xojo, but I just found the following the in the documentation under Graphics.DrawText:
In specific cases where you need to draw the outline of the text on top of already rendered text, we’ve added the ability to opt into this specific way of rendering text by setting up the XOJO_GRAPHICS_DRAWTEXT_MODE environment variable. A value of “1” will enable Graphics.DrawText to render the text with similar metrics, regardless of whether Outline is enabled or not. This environment variable must be setup before the Graphics object is instantiated.
Will this do what I am trying to do and, if so, how does one use it? How is the “XOJO_GRAPHICS_DRAWTEXT_MODE” set and where?
Public Sub DrawTextAgainstBackground(extends g as Graphics, bg as Color, s as string, x as double, y as double, width as double = 0.0)
dim c as Color = g.DrawingColor
g.DrawingColor = bg
for i as integer = -1 to 1
for j as integer = -1 to 1
if i<>0 or j<>0 then g.DrawText s, x+i, y+j, width
next
next
g.DrawingColor = c
g.DrawText s,x,y,width
End Sub
Windows: In specific cases where you need to draw the outline of the text on top of already rendered text, we’ve added the ability to opt into this specific way of rendering text by setting up the XOJO_GRAPHICS_DRAWTEXT_MODE environment variable. A value of 1 will enable Graphics.DrawText to render the text with similar metrics, regardless of whether Outline is enabled or not. This environment variable must be setup before the Graphics object is instantiated. (73748)
Sorry, this is the only place I found it (in the local (I THINK) Documentation.
Thanks one and all for your suggestions. I should have mentioned that this is an iOS project and it appears that System.EnvironmentVariable is not supported in iOS. I haven’t tried it in a desktop app.
Paul, your solution works a charm! I was a bit concerned about the overhead of drawing each character 8 extra times but it seems to be fast enough for my purposes. Because the rotation is done using g.SaveState, g.Rotate, g.DrawText, g.RestoreState, your method works fine. The complete code is below in case it’s of any use to anyone.
g.SaveState
g.Translate(charX, charY)
g.Rotate(angle)
g.DrawingColor = BGKolor
for i as integer = -1 to 1
for j as integer = -1 to 1
if i<>0 or j<>0 then g.DrawText char, i, j
next
next
g.DrawingColor = kolor
g.DrawText(char, 0, 0)
g.RestoreState
Still, it would be nice if there were a single command to draw outline text rather than having to draw the character 8 times!
The Graphics object has an Outline property as Boolean that controls whether text is drawn as outline. Try this code, which draws the outline first and then overlays a solid rendering of the text:
This font is constructed using multiple simple paths that overlap, instead of using the more sophisticated compound path method. This works great for flat rendering but malfunctions when the font is used for outlining. For best results, it is probably best to render the outline behind a solid rendering of the text.
And now I’m curious why you didn’t follow the advice given to you on the bug report you filed a year ago, which exactly mirrors what I’ve just suggested:
That’s partially because his algorithm needs refinement. For better legibility, the baseline of his text should more closely mirror the displayed path (in this case, road/trail/river, etc). Mild discordance between the path baseline and a visible line is very disorienting to the reading eye.
Using g.outline yielded too many artifacts maybe because of the iOS system font that I’m using. Here’s a screenshot using g.outline as you suggested (and I had tried before with some of the same issues in your second screenshot)
// Now draw the character, itself
g.SaveState
g.Translate(charX, charY)
g.Rotate(angle)
g.DrawingColor = outKolor // &c000000
'for i as integer = -1 to 1
'for j as integer = -1 to 1
'if i<>0 or j<>0 then g.DrawText char, i, j
'next
'next
g.Outline = True
g.DrawText(char, 0, 0)
g.Outline = False
g.DrawingColor = kolor // &cFFFFFF
g.DrawText(char, 0, 0)
g.RestoreState
Second figure was generated with this code:
// Now draw the character, itself
g.SaveState
g.Translate(charX, charY)
g.Rotate(angle)
g.DrawingColor = outKolor // &c000000
for i as integer = -1 to 1
for j as integer = -1 to 1
if i<>0 or j<>0 then g.DrawText char, i, j
next
next
'g.Outline = True
g.DrawText(char, 0, 0)
'g.Outline = False
g.DrawingColor = kolor // &cFFFFFF
g.DrawText(char, 0, 0)
g.RestoreState
changed nothing else. Now you see why I don’t like using g.outline…
Run that code and inspect the kolor variable. It looks like you have it set with partial transparency. That’s going to cause some of the artifacting I’m seeing.
That’s the source of the darker areas in between the letters. You’re drawing the text character-by-character, so each drawing action that overlaps is going to produce a darkening effect.
Probably something else. Here is the g.outline option with zero transparency for either background or foreground. I’m guessing it has something to do with how rotation affects the rendering of the outline
Thanks for the explanation. I don’t get insulted easily. I’m a retired scientist not a programmer and always learn a lot from you guys.
I’ve tried semitransparent rectangles behind each letter (as in the screen shot in my first post but (a) it looks weird when the boxes and their letters have different degrees of rotation producing little pie shaped slices inbetween, and (b) the outlined letters – I assume that’s what you mean when you say contouring (I was confused at first because the background map as topo contours on it) – are the default on pretty much all maps everywhere so that’s what people expect. The screen also looks busier if I used rectangles for background rather than outlined letters.