pyemf3 (3.3)
Installation
pip install --index-url https://git.helu.ca/api/packages/r/pypi/simple/ --extra-index-url https://pypi.org/simple pyemf3About this package
Pure Python Enhanced Metafile Library
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-234http://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),(widthdpi,heightdpi)]) emf.Polyline([(0,heightdpi),(widthdpi,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{widthdensity}, and vertically (from the top down) C{0} to C{heightdensity}. 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).