1 """wxFancyText -- methods for rendering XML specified text
3 This module has four main methods:
5 def getExtent(str, dc=None, enclose=1):
6 def renderToBitmap(str, background=None, enclose=1)
7 def renderToDC(str, dc, x, y, enclose=1)
9 In all cases, 'str' is an XML string. The tags in the string can
10 currently specify the font, subscripts, superscripts, and the angle
11 sign. The allowable properties of font are size, family, style, weght,
12 encoding, and color. See the example on the bottom for a better idea
15 Note that start and end tags for the string are provided if enclose is
16 True, so for instance, renderToBitmap("X<sub>1</sub>") will work.
19 # Copyright 2001 Timothy Hochberg
20 # Use as you see fit. No warantees, I cannot be held responsible, etc.
24 # TODO: Make a wxFancyTextCtrl class that derives from wxControl.
25 # Add support for line breaks
31 from wxPython
.wx
import *
32 import xml
.parsers
.expat
, copy
34 _families
= {"default" : wxDEFAULT
, "decorative" : wxDECORATIVE
, "roman" : wxROMAN
,
35 "swiss" : wxSWISS
, "modern" : wxMODERN
}
36 _styles
= {"normal" : wxNORMAL, "slant" : wxSLANT, "italic" : wxITALIC}
37 _weights
= {"normal" : wxNORMAL, "light" : wxLIGHT, "bold" : wxBOLD}
39 # The next three classes: Renderer, SizeRenderer and DCRenderer are
40 # what you will need to override to extend the XML language. All of
41 # the font stuff as well as the subscript and superscript stuff are in
46 defaultSize
= wxNORMAL_FONT
.GetPointSize()
47 defaultFamily
= wxDEFAULT
48 defaultStyle
= wxNORMAL
49 defaultWeight
= wxNORMAL
50 defaultEncoding
= wxFont_GetDefaultEncoding()
51 defaultColor
= "black"
53 def __init__(self
, dc
=None):
60 def startElement(self
, name
, attrs
):
61 method
= "start_" + name
62 if not hasattr(self
, method
):
63 raise ValueError("XML tag '%s' not supported" % name
)
64 getattr(self
, method
)(attrs
)
66 def endElement(self
, name
):
67 method
= "end_" + name
68 if not hasattr(self
, method
):
69 raise ValueError("XML tag '%s' not supported" % name
)
70 getattr(self
, method
)()
72 def start_wxFancyString(self
, attrs
):
75 def end_wxFancyString(self
):
78 def start_font(self
, attrs
):
79 for key
, value
in attrs
.items():
83 value
= _families
[value
]
85 value
= _styles
[value
]
87 value
= _weights
[value
]
88 elif key
== "encoding":
93 raise ValueError("unknown font attribute '%s'" % key
)
95 font
= copy
.copy(self
.fonts
[-1])
97 self
.fonts
.append(font
)
102 def start_sub(self
, attrs
):
104 raise ValueError("<sub> does not take attributes")
105 font
= self
.getCurrentFont()
106 self
.offsets
.append(self
.offsets
[-1] + self
.dc
.GetFullTextExtent("M", font
)[1]*0.5)
107 self
.start_font({"size" : font.GetPointSize() * 0.8}
)
113 def start_sup(self
, attrs
):
115 raise ValueError("<sup> does not take attributes")
116 font
= self
.getCurrentFont()
117 self
.offsets
.append(self
.offsets
[-1] - self
.dc
.GetFullTextExtent("M", font
)[1]*0.3)
118 self
.start_font({"size" : font.GetPointSize() * 0.8}
)
124 def getCurrentFont(self
):
125 font
= self
.fonts
[-1]
126 return wxFont(font
.get("size", self
.defaultSize
),
127 font
.get("family", self
.defaultFamily
),
128 font
.get("style", self
.defaultStyle
),
129 font
.get("weight", self
.defaultWeight
),
130 encoding
= font
.get("encoding", self
.defaultEncoding
))
132 def getCurrentColor(self
):
133 font
= self
.fonts
[-1]
134 return wxNamedColour(font
.get("color", self
.defaultColor
))
137 class SizeRenderer(Renderer
):
139 def __init__(self
, dc
=None):
140 Renderer
.__init
__(self
, dc
)
141 self
.width
= self
.height
= 0
142 self
.minY
= self
.maxY
= 0
144 def characterData(self
, data
):
145 self
.dc
.SetFont(self
.getCurrentFont())
146 width
, height
= self
.dc
.GetTextExtent(data
)
147 self
.width
= self
.width
+ width
148 self
.minY
= min(self
.minY
, self
.offsets
[-1])
149 self
.maxY
= max(self
.maxY
, self
.offsets
[-1] + height
)
150 self
.height
= self
.maxY
- self
.minY
152 def start_angle(self
, attrs
):
153 self
.characterData("M")
158 class DCRenderer(Renderer
):
160 def __init__(self
, dc
=None, x
=0, y
=0):
161 Renderer
.__init
__(self
, dc
)
165 def characterData(self
, data
):
166 self
.dc
.SetFont(self
.getCurrentFont())
167 self
.dc
.SetTextForeground(self
.getCurrentColor())
168 width
, height
= self
.dc
.GetTextExtent(data
)
169 self
.dc
.DrawText(data
, self
.x
, self
.y
+ self
.offsets
[-1])
170 self
.x
= self
.x
+ width
172 def start_angle(self
, attrs
):
173 self
.dc
.SetFont(self
.getCurrentFont())
174 self
.dc
.SetPen(wxPen(self
.getCurrentColor(), 1))
175 width
, height
, descent
, leading
= self
.dc
.GetFullTextExtent("M")
176 y
= self
.y
+ self
.offsets
[-1] + height
- descent
177 self
.dc
.DrawLine(self
.x
, y
, self
.x
+width
, y
)
178 self
.dc
.DrawLine(self
.x
, y
, self
.x
+width
, y
-width
)
179 self
.x
= self
.x
+ width
184 # This is a rendering function that is primarily used internally,
185 # although it could be used externally if one had overridden the
188 def renderToRenderer(str, renderer
, enclose
=1):
190 str = '<?xml version="1.0"?><wxFancyString>%s</wxFancyString>' % str
191 p
= xml
.parsers
.expat
.ParserCreate()
192 p
.returns_unicode
= 0
193 p
.StartElementHandler
= renderer
.startElement
194 p
.EndElementHandler
= renderer
.endElement
195 p
.CharacterDataHandler
= renderer
.characterData
199 def getExtent(str, dc
=None, enclose
=1):
200 "Return the extent of str"
201 renderer
= SizeRenderer(dc
)
202 renderToRenderer(str, renderer
, enclose
)
203 return wxSize(renderer
.width
, renderer
.height
)
205 # This should probably only be used internally....
207 def getFullExtent(str, dc
=None, enclose
=1):
208 renderer
= SizeRenderer(dc
)
209 renderToRenderer(str, renderer
, enclose
)
210 return renderer
.width
, renderer
.height
, -renderer
.minY
212 def renderToBitmap(str, background
=None, enclose
=1):
213 "Return str rendered on a minumum size bitmap"
215 width
, height
, dy
= getFullExtent(str, dc
)
216 bmp
= wxEmptyBitmap(width
, height
)
218 if background
is not None:
219 dc
.SetBackground(background
)
221 renderer
= DCRenderer(dc
, y
=dy
)
223 renderToRenderer(str, renderer
, enclose
)
225 dc
.SelectObject(wxNullBitmap
)
228 def renderToDC(str, dc
, x
, y
, enclose
=1):
229 "Render str onto a wxDC at (x,y)"
230 width
, height
, dy
= getFullExtent(str, dc
)
231 renderer
= DCRenderer(dc
, x
, y
+dy
)
232 renderToRenderer(str, renderer
, enclose
)
235 if __name__
== "__main__":
236 str = ('<font style="italic" family="swiss" color="red" weight="bold" >some |<sup>23</sup> <angle/>text<sub>with <angle/> subscript</sub> </font> some other text'
237 '<font family="swiss" color="green" size="40">big green text</font>')
243 frame
= wxFrame(NULL
, -1, "wxFancyText demo", wxDefaultPosition
)
244 frame
.SetClientSize(getExtent(str))
245 bmp
= renderToBitmap(str, wxCYAN_BRUSH
)
246 sb
= wxStaticBitmap(frame
, -1, bmp
)
247 EVT_MENU(frame
, ID_EXIT
, frame
.Destroy
)