-
Notifications
You must be signed in to change notification settings - Fork 11
/
DESCRIPTION
228 lines (172 loc) · 8.86 KB
/
DESCRIPTION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
pyemf3 is a pure python module that provides a cross-platform ability
to generate enhanced metafiles (.emf files), a vector graphics format
defined by the ECMA-234 standard. Enhanced metafiles are a natively
supported image and scalable clip-art format in the OpenOffice suite
of tools and in Windows applications.
U{ECMA-234<http://www.ecma-international.org/publications/standards/Ecma-234.htm>}
is the published interface for enhanced metafiles, which is also a
file-based representation of the Windows graphics device interface.
This API follows most of the naming conventions of ECMA-234, and most
of the parameter lists of methods are the same as their ECMA-234
equivalents. The primary difference is that pyemf3 has extended the
API to be object-oriented based on the class L{EMF}. So, while in
ECMA-234 the first argument is generally the device context, here in
pyemf3 it is implicit in the class instance.
ECMA-234 defines a lot of constants (mostly integers that are used as
flags to various functions) that pyemf3 defines as module level
variables. So, rather than pollute your global namespace, it is
recommended that you use C{import pyemf3} rather than C{from pyemf3
import *}.
Introduction
============
To use pyemf3 in your programs, you L{instantiate<EMF.__init__>} an
L{EMF} object, draw some stuff using its methods, and save the file.
An example::
#!/usr/bin/env python
import pyemf3
width=8.0
height=6.0
dpi=300
emf=pyemf3.EMF(width,height,dpi)
thin=emf.CreatePen(pyemf3.PS_SOLID,1,(0x01,0x02,0x03))
emf.SelectObject(thin)
emf.Polyline([(0,0),(width*dpi,height*dpi)])
emf.Polyline([(0,height*dpi),(width*dpi,0)])
emf.save("test-1.emf")
This small program creates a 8in x 6in EMF at 300 dots per inch, and
draws two lines connecting the opposite corners. This simple test is
available as C{test-1.py} in the C{examples} directory of the pyemf3
distribution. There are many other small test programs to demonstrate
other features of the EMF class.
Naming Conventions in pyemf3
============================
Methods that belong to ECMA-234 are C{CamelCased} starting with a
capital letter. Methods that apply to the operation of the L{EMF}
class itself (i.e. L{load<EMF.load>} and L{save<EMF.save>}) are
C{lower} cased. Constants described in L{pyemf3} that are used as
parameters are C{ALL_UPPER_CASE}.
Coordinate System
=================
Coordinates are addressed a coordinate system called B{page space} by
integer pixels in a horizontal range (increasing to the right) from C{0}
to C{width*density}, and vertically (from the top down) C{0} to
C{height*density}. Density is either dots per inch if working in
english units, or dots per millimeter if working in metric.
World and Page Space
--------------------
Note that there are four coordinate spaces used by GDI: world, page,
device, and physical device. World and page are the same, unless a
world transform (L{SetWorldTransform<EMF.SetWorldTransform>},
L{ModifyWorldTransform<EMF.ModifyWorldTransform>}) is used. In that
case, you operate in world space (that is transformed into page space
by multiplying by the transformation matrix), and it could be scaled
differently.
Experimental Coordinate System
------------------------------
Experimental support for device coordinates is available through
L{SetMapMode<EMF.SetMapMode>} and the various Window and Viewport
methods. Device coordinates are referenced by physical dimensions
corresponding to the mapping mode currently used. [The methods work
correctly (in the sense that they generate the correct records in the
metafile) and the API won't change, but it's not clear to me what the
parameters should do.]
Drawing Characteristics
=======================
GDI has a concept of the B{current object} for the each of the three
drawing characteristics: line style, fill style, and font. Once a
characteristic is made current using
L{SelectObject<EMF.SelectObject>}, it remains current until it is
replaced by another call to SelectObject. Note that a call to
SelectObject only affects that characteristic, and not the other two,
so changing the line style doesn't effect the fill style or the font.
Additionally, there is a set of B{stock objects} retrievable with
L{GetStockObject<EMF.GetStockObject>} that should be available on any
system capable of rendering an EMF.
Colors
------
A quick note about color. Colors in pyemf3 are specified one of three
ways:
- (r,g,b) tuple, where each component is a integer between 0 and 255 inclusive.
- (r,g,b) tuple, where each component is a float between 0.0 and 1.0 inclusive.
- packed integer created by a call to L{RGB}
Line Styles
-----------
Line styles are created by L{CreatePen<EMF.CreatePen>} and specify the
style, width, and color.
Note that there is a NULL_PEN stock object if you don't actually want
to draw a line with a drawing primitive.
Fill Styles
-----------
Polygon fill styles are created by
L{CreateSolidBrush<EMF.CreateSolidBrush>} and theoretically
L{CreateHatchBrush<EMF.CreateHatchBrush>}, although the latter doesn't
seem to be supported currently in OpenOffice. So, reliably we can only
use CreateSolidBrush and thus can only specify a fill color and not a
fill pattern.
Note that there is a stock object NULL_BRUSH that doesn't fill, useful
if you want to only draw an outline of a primitive that is normally
filled.
An interesting side-note is that there is no direct support for
gradients in EMF. Examining some .emfs that do have gradients shows
that Windows produces them using clipping regions and subdividing the
object into areas of a single color an drawing slices of the
individual color. Better support for clipping regions is the subject
of a future release of pyemf3, but they also don't seem to work well in
OpenOffice, so it hasn't been a high priority.
Fonts
-----
L{CreateFont<EMF.CreateFont>} requires a large number of parameters,
the most important being the height, the rotation, and the name. Note
that the height can either be specified as a positive or negative
integer, where negative means use that value as the average I{glyph}
height and positive means use the value as the average I{cell} height.
Since a glyph is contained within a cell, the negative value will
yield a slightly larger font when rendered on screen.
Note that the two rotation values must specify the same angle.
Also note that font color is not part of a
L{SelectObject<EMF.SelectObject>} characteristic. It is specified
using the separate method L{SetTextColor<EMF.SetTextColor>}.
L{SetBkMode<EMF.SetBkMode>} and L{SetBkColor<EMF.SetBkColor>} are
supposed to work with text, but in my testing with OpenOffice it hasn't been
consistent. I tend to just C{SetBkMode(pyemf3.TRANSPARENT)} and leave
it at that.
Drawing
=======
The methods listed under B{Drawing Primitives} below use either the
current line style or the current fill style (or both). Any primitive
that creates a closed figure (L{Polygon<EMF.Polygon>},
L{PolyPolygon<EMF.PolyPolygon>}, L{Rectangle<EMF.Rectangle>},
L{RoundRect<EMF.RoundRect>}, L{Ellipse<EMF.Ellipse>},
L{Chord<EMF.Chord>}, and L{Pie<EMF.Pie>}) will use both the line and
fill style. Others (L{Polyline<EMF.Polyline>},
L{PolyPolyline<EMF.PolyPolyline>} and L{Arc<EMF.Arc>}) will only use
the line style, excepting L{SetPixel<EMF.SetPixel>} which doesn't use either.
Paths
=====
To create more complicated shapes, the B{Path Primitives} are used. A
path is started with a call to L{BeginPath<EMF.BeginPath>} and the
initial point should be set with L{MoveTo<EMF.MoveTo>}. Calls to
L{LineTo<EMF.LineTo>}, L{PolylineTo<EMF.PolylineTo>},
L{ArcTo<EMF.ArcTo>}, and L{PolyBezierTo<EMF.PolyBezierTo>} extend the
path. L{CloseFigure<EMF.CloseFigure>} should be used to connect the
final point to the starting point, otherwise the path may be filled
incorrectly. L{EndPath<EMF.EndPath>} then completes the path, and it
may be outlined with L{StrokePath<EMF.StrokePath>}, filled with
L{FillPath<EMF.FillPath>} or both with
L{StrokeAndFillPath<EMF.StrokeAndFillPath>}.
Note that OpenOffice ignores L{ArcTo<EMF.ArcTo>} in terms of path
continuity -- the arc is drawn, but it is not connected to the path.
Note that L{SelectClipPath<EMF.SelectClipPath>} is broken in OpenOffice.
Coordinate System Transformation
================================
You might have noticed that methods like L{Ellipse<EMF.Ellipse>} and
L{Rectangle<EMF.Rectangle>} can only create objects that are aligned
with the X-Y axis. This would be a real limitation without some way
to rotate the figures. L{SetWorldTransform<EMF.SetWorldTransform>}
and L{ModifyWorldTransform<EMF.ModifyWorldTransform>} provide this.
These methods provide a generalized linear transformation that can
translate, rotate, scale and shear subsequent graphics operations.
These methods aren't required by the ECMA-234 spec, which may explain
why their support in OpenOffice is mixed. Drawing primitives and
paths seem to be supported and are transformed, but text is not
(though it should be).