This is a combination of tutorial and reference guide for writing new device
layers for libwmf.
Since no two graphics interfaces are identical, the task of the IPA
(the interface between the interpreter and the device layer) is to simplify the
task of translating the metafile drawing commands. (It cannot be denied that
there is still considerable room for improvement.)
When writing a new device layer you will need to include the following headers:
#include <libwmf/ipa.h>
#include <libwmf/defs.h>
The former includes the API declarations as well as the IPA
declarations, while the latter defines metafile constants.
Container for a bitmap, of dimensions width and height.
If non-zero, data is a pointer to an internal representation of the
bitmap. The interpreter does not reference data and will not attempt
to draw a bitmap if data is zero; however, a bitmap brush pattern
may be set even though data is zero. (data=0 indicates
either that the device layer has no support for bitmaps or that the bitmap
is corrupt or unreadable.)
If lbStyle=BS_HATCHED or lbStyle=BS_SOLID then the foreground
color of the brush is lbColor. Otherwise, if the brush is a bitmap,
ensure that bmp.data is non-zero.
The following macros should be used to retrieve the above info.:
#include
/* WMF_BRUSH_STYLE(wmfBrush* B) -> (U16) brush style
* WMF_BRUSH_HATCH(wmfBrush* B) -> (U16) brush hatch style
*
* WMF_BRUSH_COLOR(wmfBrush* B) -> (wmfRGB*) brush color
*
* WMF_BRUSH_BITMAP(wmfBrush* B) -> (wmfBMP*) brush bitmap
*/
#define WMF_BRUSH_STYLE(B) ((B)->lbStyle)
#define WMF_BRUSH_HATCH(B) ((B)->lbHatch)
#define WMF_BRUSH_COLOR(B) (&((B)->lbColor))
#define WMF_BRUSH_BITMAP(B) (&((B)->bmp))
wmfPen is one element of the current drawing context. The nib has
dimensions width and height and color lopnColor.
lopnStyle contains style information on the line itself, on the ends,
on the joins, and on the type:
wmfFont is one element of the current drawing context. Of all of this,
the most important elements are ps_name (the postscript font name) and
ft_face, the freetype (2) font face for which bold and
italics have already been determined.
The following macros should be used to retrieve the above info.:
#include
/* WMF_TEXT_ANGLE(wmfFont* F) -> (double) text angle in radians
*
* WMF_TEXT_UNDERLINE(wmfFont* F) -> (U8) ?? whether to underline (?? how thick)
* WMF_TEXT_STRIKEOUT(wmfFont* F) -> (U8) ?? whether to strikeout (?? how thick)
*
* WMF_FONT_NAME(wmfFont* F) -> (char*) font name supplied by metafile
* WMF_FONT_PSNAME(wmfFont* F) -> (char*) font name to use in postscript output
* WMF_FONT_FTFACE(wmfFont* F) -> (FT_Face) freetype(2) font face
*/
#define WMF_TEXT_ANGLE(F) ((((double) (F)->lfEscapement) / 10) * M_PI / 180)
#define WMF_TEXT_UNDERLINE(F) ((F)->lfUnderline)
#define WMF_TEXT_STRIKEOUT(F) ((F)->lfStrikeOut)
#define WMF_FONT_NAME(F) ((F)->lfFaceName)
#define WMF_FONT_PSNAME(F) ((F)->ps_name)
#define WMF_FONT_FTFACE(F) ((F)->ft_face)
Still very much a work in progress, especially with regard to the drawing
context wmfDC; there is much in this structure that is (or should
be) irrelevant to the IPA.
See wmfBrush, wmfPen and wmfFont. textcolor is
the color to draw text, bgcolor is the background color (generally, as
well as with text) unless bgmode=TRANSPARENT, in which case there is
no background. bgmode is one of:
The following macros should be used to retrieve the above info.:
#include
/* WMF_DC_BRUSH(wmfDC* C) -> (wmfBrush*) current brush
* WMF_DC_PEN(wmfDC* C) -> (wmfPen*) current pen
* WMF_DC_FONT(wmfDC* C) -> (wmfFont*) current font
*
* WMF_DC_TEXTCOLOR(wmfDC* C) -> (wmfRGB*) text color
* WMF_DC_BACKGROUND(wmfDC* C) -> (wmfRGB*) background color
*
* WMF_DC_OPAQUE(wmfDC* C) -> (U16) whether to fill opaque (non-zero if true)
*
* WMF_DC_POLYFILL(wmfDC* C) -> (U16) how to fill polygons
*
* WMF_DC_ROP(wmfDC* C) -> (U16) ROP mode
*/
#define WMF_DC_BRUSH(C) ((C)->brush)
#define WMF_DC_PEN(C) ((C)->pen)
#define WMF_DC_FONT(C) ((C)->font)
#define WMF_DC_TEXTCOLOR(C) (&((C)->textcolor))
#define WMF_DC_BACKGROUND(C) (&((C)->bgcolor))
#define WMF_DC_OPAQUE(C) ((C)->bgmode - 1)
#define WMF_DC_POLYFILL(C) ((C)->polyfillmode)
#define WMF_DC_ROP(C) ((C)->ROPmode)
Flood-fill region with color color, starting at point pt.
Since this is almost certainly not a vector operation, the pixel width and
height are given (and are probably best ignored...). type is one
of:
Draw a complete or partial ellipse. TL and BR are the
coordinates of the top left and bottom right of the ellipse's bounding box
respectively. If incomplete, then start and end give the
start and end coordinates of the arc.
typedef struct _wmfPolyRectangle_t wmfPolyRectangle_t;
struct _wmfPolyRectangle_t
{ wmfDC* dc;
wmfD_Coord* TL; /* region_frame & region_paint: TL[count],BR[count] give the */
wmfD_Coord* BR; /* final `extents'... */
unsigned int count;
float width; /* region_frame: border thickness; zero otherwise */
float height;
};
Used by region and clip calls, wmfPolyRectangle_t is used to describe
multiple rectangles, whose top left and bottom right corners are given by the
TL and BR arrays respectively. These arrays are of length
count in the case of clip calls, and of length count + 1 in
the case of the region calls, the extra element containing the overall bounding
box (the region extents). In the case of region_frame, width
and height give the thickness of the frame.
typedef struct _wmfBMP_Read_t wmfBMP_Read_t;
struct _wmfBMP_Read_t /* Two means available for accessing BMP image: */
{ long offset; /* (1) position in source file of start of BMP; *
* use API->bbuf.seek to set pos(ition), etc. */
long length; /* (2) buffer of length length containing image of BMP */
unsigned char* buffer;
U16 width; /* WMF player may preset these values; zero otherwise. */
U16 height; /* Use caution - may be buggy... ?? [TODO] */
wmfBMP bmp;
};
There are two ways to read a bitmap presented by libwmf: the first is
to read it directly from the input stream using the internal stream functions:
/* Macro-wrappers for stream functions:
* (int) WMF_READ ((wmfAPI*) API) - returns unsigned char cast to int, or EOF
* (int) WMF_SEEK ((wmfAPI*) API,(long) position) - returns (-1) on error, else 0
* (long) WMF_TELL ((wmfAPI*) API) - returns (-1) on error, else current position
*/
where the bitmap starts at offset offset; the second (which is
preferred) is to use the buffer buffer of length length.
To complicate matters, the bitmap's data may be truncated so that the bitmap's
header is incorrect - width and height give the real
dimensions of the bitmap.
The bitmap's width and height should be entered into bmp (see
wmfBMP above) as well as a pointer to the bitmap data (which
can be in any format you choose; the library does not need to know).
In contrast to earlier versions, libwmf no longer makes any attempt to
crop or scale the bitmap. Instead the crop data are presented to the device
layer to use as it sees fit, and the pixel width and height as well. The bitmap
referenced by bmp (see wmfBMP above) is to be drawn
at point pt.
Fonts and text are triiicky! Fortunately libwmf tries to do
most of the work. As such text justification is taken care of by the
interpreter, and fonts (bold, italic, etc.) are selected
elsewhere. The emphasis with wmfDrawText_t is therefore the rendering
of text.
The text can be assumed to be left-justified, starting at point pt.
TL and BR are the corners of a clip rectangle. bbox
gives four corners of rectangle - an estimate of the background zone.
str is the text to draw; font_height is the height of the
font and font_ratio is how much to stretch the font width-wise. The
values for flags are:
typedef struct _wmfUserData_t wmfUserData_t;
struct _wmfUserData_t /* TODO: Need to be careful with usage here; not all these are set by the player! */
{ wmfDC* dc; /* dc is guaranteed */
void* data; /* data also, except for init */
};
The initialization function has two purposes, the first being to establish the
links that create the IPA by pointing the function reference variables
to your own drawing functions:
The second purpose of the initialization function is to allocate the device's
data structure, set the device parameters to default values if necessary, and
then to attach the data to the API->device_data hook.
The IPA functions are called by wmf_play (), the only exception
being device_close which is called by wmf_api_destroy () -
and then only if device_open has been called by wmf_play ().
device_open is called the first time (and only the first time)
wmf_play () is called, and is the very first IPA function to be
called, just as device_close will be the very last.
At the beginning of each play cycle (i.e., each call to
wmf_play ()) device_begin is called (after
device_open if it is the first cycle), and at the end of each cycle
device_end is called. The metafile graphics use other IPA
functions.
device_open, device_begin, device_end and
device_closeshould be written so that wmf_play () can
be called repeatedly.
The names of the functions are not important. The generic names are used below:
Note: Unless you're feeling masochistic and want to write, or re-write,
your own suite of functions for the reading, writing and general manipulation
of bitmaps, you should defer this to libwmf's built-in support which
is adapted from ImageMagick's excellent BMP coder.
Note: Unless you're feeling masochistic and want to write, or re-write,
your own suite of functions for the reading, writing and general manipulation
of bitmaps, you should defer this to libwmf's built-in support which
is adapted from ImageMagick's excellent BMP coder.
Note: Unless you're feeling masochistic and want to write, or re-write,
your own suite of functions for the reading, writing and general manipulation
of bitmaps, you should defer this to libwmf's built-in support which
is adapted from ImageMagick's excellent BMP coder.
The section applies only if you are working on the libwmf sources.
The build system uses automake and autoconf. If any changes are
made to any of the various Makefile.am files, or to
configure.in or libwmf.m4, then the build system will need to
be updated. Change to the top source directory (containing
configure.in and libwmf.m4) and:
When adding a new device layer, include/libwmf/Makefile.am and
src/ipa/Makefile.am will need to be modifiled. Source files
(src/ipa/new.c) and installing header files
(include/libwmf/new.h) do not need to be added to the distribution,
but other headers (src/ipa/new.h & src/ipa/new/*.h) will.