Kevin P. Welch
Kevin P. Welch is an MSJ contributing editor and president of Eikon Systems, Inc., a software development company specializing in the research and development of applications for Windows and OS/2 Presentation Manager.
{ewc navigate.dll, ewbutton, /Bcodeview /T"Click to open or copy the code samples from this article." /C"samples_1}
Aldus pioneered graphics import libraries for the MicrosoftÒ WindowsÔ graphical environment. This concept was developed for use with PageMakerÒ, an electronic page design and layout application. There are versions of PageMaker for the AppleÒ MacintoshÒ and Windows1.
When PageMaker was first designed, three general types of graphics files were common: object-oriented drawing files such as PICT or metafiles, raster image (bitmap) files such as TIFF, and Encapsulated PostScriptÒ (EPS) files. While raster and EPS file formats were readily transportable between the Macintosh and Windows, object-oriented drawing files presented a problem since they usually use a platform-specific format. The result was that Aldus integrated raster and EPS file support directly into PageMaker and separated the support of object-oriented drawing files into a collection of external dynamic-link libraries (DLLs). Thus was the concept of graphics import filter libraries born.
Since that time many other software vendors, including Microsoft and Lotus, have adopted graphics import filter libraries for use in their own applications. Although the basic import filter standard has remained relatively constant over the years, it has been expanded to support raster and EPS image files as well.
The primary job of a Windows graphics import filter is to convert a disk-based graphics image from some specific format to a memory-based Windows metafile. If you develop an application that generates a new, unique format of graphical output, writing a graphics import filter that supports that format enables any other application that supports graphical import filters to use that data.
As you may already know, a metafile is an ordered sequence of GDI commands that specify an image. This metafile can later be used to recreate the image on a variety of devices. It can also be copied to the Clipboard for the exchange of device-independent graphical images among applications.
Metafiles are created using the CreateMetaFile function, which returns a handle to a display context. This display context is unusual in that it does not have any default display attributes of its own, instead deriving them from the default attributes of the display context used to output the metafile. Figure 1 lists the GDI functions that can be used in a metafile. Once these GDI commands have been sent to a metafile, it can be closed using the CloseMetaFile function. This function returns a metafile handle that can subsequently be used in other metafile operations.
Figure 1 GDI Metafile Functions
AnimatePalette | ResizePalette |
Arc | RestoreDC |
BitBlt | RoundRect |
Chord | SaveDC |
CreateBrushIndirect | ScaleViewportExt |
CreateDIBPatternBrush | ScaleWindowExt |
CreateFontIndirect | SelectClipRegion |
CreatePatternBrush | SelectObject |
CreatePenIndirect | SelectPalette |
CreateRegion | SetBkColor |
DrawText | SetBkMode |
Ellipse | SetDIBitsToDevice |
Escape | SetMapMode |
ExcludeClipRect | SetMapperFlags |
ExtTextOut | SetPixel |
FloodFill | SetPolyFillMode |
IntersectClipRect | SetROP2 |
LineTo | SetStretchBltMode |
MoveTo | SetTextAlign |
OffsetClipRgn | SetTextCharExtra |
OffsetViewportOrg | SetTextColor |
OffsetWindowOrg | SetTextJustification |
PatBlt | SetViewportExt |
Pie | SetViewportOrg |
Polygon | SetWindowExt |
Polyline | SetWindowOrg |
PolyPolygon | StretchBlt |
RealizePalette | StretchDIBits |
Rectangle | TextOut |
Although theoretically all of the GDI functions listed in Figure 1 can be used when creating a metafile, a number of factors must be addressed to ensure true device-independence. These include mapping modes, BitBlt functions, escape functions, device-dependent objects, display context states, measurement systems, and text handling.
Mapping Modes First of all, the application managing the metafile returned by the filter may want to display the image at different resolutions, sizes, and aspect ratios. This is impossible if the mapping mode, viewport, or window was set when the metafile was created. As a result, you should avoid using the following functions:
SetMapMode
SetViewportOrg
SetViewportExt
SetWindowOrg
SetWindowExt
BitBlt Functions The BitBlt function is designed to display a fixed-size bitmap given a set of raster data. As a result, using it in a metafile introduces a device-dependent component. Although using BitBlt may be appropriate in some cases, in general you should use the StretchBlt function which does resize images.
Escape Functions These GDI calls enable you to perform device-specific actions. Using them in a metafile makes it device-dependent. Because of this you should avoid it.
Device-Dependent Objects Great care must be taken when selecting and using device-dependent objects. Several GDI functions and operations use pixel-oriented device-dependent objects such as bitmaps or pattern brushes. Because of this the resulting metafile may be adversely affected by the resolution of the target device. Device-independent bitmaps (DIBs), introduced in Windows 3.0, do alleviate some of the trouble.
Display Context States Several GDI functions modify the state of the metafile display context. When using these functions inside a metafile, you should be careful to save the current display context state and restore it when completed, using the SaveDC and RestoreDC functions.
Measurement Systems Ideally the measurement system used in a metafile should be the same as the source image measurement system and it should preserve the physical size of the original image. If the resolution of the measurement system is too fine it may need to be appropriately scaled to stay within range while minimizing granularity.
Text Handling The last consideration is text handling. In general, metafiles that contain text cannot be scaled and are difficult to transport between devices. This is because Windows version 3.0 fonts are device-dependent system resources that may or may not be available on all devices. Until Windows supports a device-independent, transportable font handling mechanism, you should carefully limit the use of text inside metafiles.
Now that a few issues relating to the creation of device-independent metafiles have been reviewed, the actual workings of a graphics import filter can be discussed. As mentioned, most graphics import filters consist of DLLs that import disk-based images into specific applications.
Although various applications may use slightly different mechanisms for referencing these libraries, most use a special graphics import filter section in the Windows initialization file WIN.INI. Each library is typically specified as follows:
<File Format> = <Filter Name>, <Extension>, <Optional Data>
For example, when you install Microsoft Word for Windows on your system, you are given the option to load a collection of AldusÒ-compatible graphics import filters. If you do so, depending on the actual filters you select, the installation program will create a section in your WIN.INI file much like the following:
[WWFilters]
CGM Graphics Import=CGMMPORT.FLT,CGM
HPGL Filter=HPGLIMP.FLT,HGL
Lotus Graphics PIC=LOTUSPIC.FLT,PIC
Micrografx Draw!=DRAW.FLT,PIC
Windows Metafile=WMF.FLT,WMF
PCX Filter=PCXIMP.FLT,PCX
The File Format field specifies the long name of the file format supported by the filter. This string often appears in some sort of Insert Picture or Place dialog box provided by the host application (the dialog that is invoked when the user wishes to place the foreign graphic somewhere in the current document). This field may contain spaces and is usually limited to 32 characters.
The Filter Name field specifies the name of the actual graphics import filter library. In most cases the filter is required to be in the current system search path, eliminating the need for directory information.
The Extension field specifies the file extension supported by the filter. When the user imports a graphics file the contents of this field determine which filter to use. When more than one filter is capable of handling a particular format the host application is responsible for deciding which filter will get the task. Ideally a dialog box should be displayed and the user given the chance to choose from a list of filters.
The Optional Data field (unused in the WWFilters list above) specifies additional information that is passed to the filter when it is initially loaded. This string can be used to put the filter into a specific mode or specify how a particular file type is to be handled.
Once a graphics import filter has been configured for a particular application, it is dynamically loaded whenever the user makes a request to import a graphic image. The host application determines which filter to use by checking for an appropriate entry in the WIN.INI file.
If the filter has not been previously loaded, the application loads the library and calls the GetFilterInfo function. The application passes GetFilterInfo its version number and a pointer to a copy of the optional configuration data from the filter’s WIN.INI entry. The GetFilterInfo function then checks the version number and uses the optional configuration data to define conversion parameters. If the filter displays a dialog box during the import process, the GetFilterInfo function can allocate global memory for later use. When initialization is complete, the GetFilterInfo function returns either a code indicating the filter type or an error code. The actual parameters to the GetFilterInfo function are shown in Figure 2.
Figure 2 The GetFilterInfo Function
WORD FAR PASCAL GetFilterInfo(idVersion, lpszIni,
lphPrefMem, lphFileTypes)
Parameter | Type | Description |
idVersion | WORD | Specifies the version number of the host application. Using this version number, the filter can configure itself to the capabilities of the application. |
lpszIni | LPSTR | Specifies a far pointer to a string that contains the optional part of the WIN.INI filter specification line. |
lphPrefMem | HANDLE FAR * | Specifies a far pointer to a variable that can be used to store a handle to a global memory block allocated by this function. This handle is passed to the library in all subsequent calls. |
lphFileTypes | HANDLE FAR * | Specifies a far pointer to a handle referencing a data structure that contains a list of the file types the filter can import. This parameter is currently unimplemented. |
If 0 is returned, the filter is unable to continue; if 1 is returned, the filter is a text filter (outside the scope of this article); and if 2 is returned, the filter is a graphics import filter.
In certain situations the user may want to specify additional conversion options. In Aldus PageMaker this is indicated by holding down the Shift key while pressing the OK button in the Place... dialog box. In situations such as this, the host application may call the GetFilterPref function (see Figure 3). This function is responsible for displaying another dialog box that enables the user to input specific conversion options. This dialog box could allow the user to specify how the background color for an image is handled by the filter or how certain graphic elements are converted.
Figure 3 The GetFilterPref Function
VOID FAR PASCAL GetFilterPref( hinst, hwndParent,
hPrefMem, wFlags )
Parameter | Type | Description |
hinst | HANDLE | Specifies the host application instance handle. This handle can be used to extract resources from the host application if desired. |
hwndParent | HWND | Specifies the host application window handle. If a dialog box is displayed, it should be created using this window handle as the parent. |
hPrefMem | HANDLE | Specifies handle to the preferences memory block allocated during the call to the GetFilterInfo function. |
wFlags | WORD | Specifies a set of flags defining the current import context. This parameter is currently unimplemented. |
The last but most important function called by the host application in the import process is the ImportGR function. This function opens the file, verifies the file contents, and generates the metafile using any conversion preferences specified by the user. When the conversion process is complete, this function releases any system resources used and closes the file. If an error occurs, this function is also responsible for returning an appropriate error code that accurately describes the situation encountered. The parameters to ImportGR are shown in Figure 4.
Figure 4 The ImportGR Function
WORD FAR PASCAL ImportGR( hdcPrint, lpfs, lppi, hPrefMem)
Parameter | Type | Description |
hdcPrint | HDC | Specifies a handle to a display context for the currently selected output device. This display context handle is to be used for determining various output device capabilities such as font metrics and colors. No attempt should be made to draw into this display context. |
lpfs | LPFILESPEC | Specifies a long pointer to a FILESPEC data structure. This structure contains complete file reference information, including the full filename, type, and various state information. |
typedef DWORD FILETYPE #define IBMFNSIZE 124 typedef struct _FILESPEC { unsigned slippery : 1; /* TRUE if file may disappear. */ unsigned write : 1; /* TRUE if open for write. */ unsigned unnamed : 1; /* TRUE if unnamed. */ unsigned linked : 1; /* Linked to an FS FCB. */ unsigned mark : 1; /* Generic mark bit. */ FILETYPE fType; /* The file type. */ short handle; /* MS-DOS open file handle. */ char fullName[IBMFNSIZE]; /* Device, path, file names. */ DWORD filePos; /* Our current file posn. */ } FILESPEC; |
||
lppi | LPPICTINFO | Specifies a long pointer to a PICTINFO data structure. This data structure provides additional information not available from the metafile itself. The metafile handle in the PICTINFO structure should be a global memory handle obtained from the GetMetaFileBits function. The bbox field specifies a rectangle that tightly bounds the metafile, and the inch field specifies the number of metafile units per inch. This value is typically used by the host application to compute the initial size of the image when imported. |
typedef struct _PICTINFO { HANDLE hmf; /* Global memory handle to metafile */ RECT bbox; /* bounding rect in metafile units */ DC inch; /* metafile units per inch */ } PICTINFO; |
||
hPrefMem | HANDLE | Specifies handle to the preferences memory block allocated during the call to the GetFilterInfo function. |
The value returned by this function should be one of the codes listed in Figure 5. In the case of failure, it is important that this function return as specific an error code as possible so that the user can correct the problem if possible.
Figure 5 ImportGR Return Codes
#define SUCCESS 0
#defineIE_BASE5300/* base value for IE_ error codes */
#define IE_NOT_MY_FILE(IE_BASE+1)/* generic "not my file" error */
#define IE_TOO_BIG(IE_BASE+2)/* bitmap or pict too big error */
#define IE_DUMB_BITMAP(IE_BASE+3)/* bitmap all white */
#define IE_BAD_VCHAR(IE_BASE+4)/* bad vchar in ImportString */
#define IE_BAD_TOKEN(IE_BASE+5)/* illegal wp token */
#define IE_NO_VERIFY(IE_BASE+6)/* failed to verify imported story */
#define IE_UNKNOWN_TYPE(IE_BASE+7)/* unknown file type */
#define IE_NOT_WP_FILE(IE_BASE+8)/* Not a %s file */
#define IE_BAD_FILE_DATA (IE_BASE+9)/* current file data is bad */
#define IE_IMPORT_ABORT (IE_BASE+10)/* import abort alert */
#define IE_MEM_FULL(IE_BASE+11)/* ran out of memory during import */
#define IE_MSNG_FONTS(IE_BASE+12)/* missing system fonts */
#define IE_METAFILE_TOO_BIG(IE_BASE+13)/* metafile too big */
#define IE_INVALID_LOTUS_PIC(IE_BASE+14)/* bad lotus .pic */
#define IE_MEM_FAIL(IE_BASE+15)/* couldn't lock memory */
#define IE_PAINT_BASE(IE_BASE+40)
#define IE_UNSUPP_COMPR(IE_PAINT_BASE+1)/* unsupp'd compress. style */
#define IE_UNSUPP_VERSION(IE_PAINT_BASE+2)/* unsupp'd file version */
#define IE_UNSUPP_COLOR(IE_PAINT_BASE+3)/* can't handle this style */
WMF.FLT is a sample graphics import filter, available on any MSJ bulletin board, that has been created to demonstrate how an Aldus placeable metafile can be imported into a Windows application.
Aldus placeable metafiles are similar to standard Windows metafiles, except that they are prefixed by a short header with a signature key, suggested sizing information, and a checksum (see Figure 6).
Figure 6 Placeable Metafile Header
#defineWMFKEY0x9A6CDD7L
typedef struct _WMFHEADER {
DWORDidkey;/* metafile keyword */
HANDLEhMF;/* handle to metafile - zero */
RECTbbox;/* tight bounding box */
WORDcInch;/* metafile units per inch */
DWORDreserved;/* reserved */
WORDidChecksum;/* header checksum */
} WMFHEADER;
Almost all metafiles that use the WMF file extension are based on this placeable metafile format. The WMF import filter is capable of reading the Aldus header and processing the actual metafile data that follows. Because the WMF file format contains an embedded metafile, the filter does not need to perform any additional processing to translate the data. The "filter" presented here doesn’t actually filter. It only loads the metafile data into memory from disk.
Unfortunately, this is not the case when creating an import filter for more complex file formats such as CGM (Computer Graphics Metafile), CDR (Corel Draw), or DRW (MicrografxÒ Designer). To create an import filter for one of these formats, you need to process the actual data and translate it into an equivalent series of GDI calls. Before you can do this, you need to obtain a detailed description of the file format you intend to create for the filter. You can usually obtain this information directly from the vendor who originally defined the filter format. In the case of metafiles, the binary data format is defined in the documentation provided with the Microsoft Windows Software Development Kit (SDK).
The sample WMF import filter is constructed from the files listed below.
WMF | Make file |
WMF.DEF | Module definition file |
WMF.H | Header file |
WMF.RC | Resource file (empty) |
WMF0.ASM | Library entry point module |
WMF1.C | Library main module |
WMF2.C | Library metafile module |
WMF3.C | Library utility function module |
The make file, WMF (see Figure 7), contains a standard set of make instructions to build the library. As is the case with most DLLs, each C source code module is compiled with SS != DS and with both the _WINDOWS and _WINDLL compile flags activated.
The module definition file, WMF.DEF, is a little more interesting. Four functions are exported--GetFilterInfo, ImportGr, GetFilterPref, and the WEP function. The WEP function, for those unfamiliar with writing DLLs, is a special function called by Windows whenever a library is discarded. This enables the library to perform any necessary cleanup.
Next is the library header file, WMF.H. This header file specifies the various data structures and definitions mentioned above, in addition to other miscellaneous local definitions. Following this is the filter resource template, WMF.RC. This file’s only purpose is to give the resource compiler something to attach to the resulting library (and hence become a Windows 3.0 program).
The assembly-language library module, WMF0.ASM (see Figure 7), contains code that simply calls the LibMain function. Hopefully the need for such assembly-language library entry points will go away in future versions of Windows, but for now you need one to load the filter correctly.
The most important module is the main library module, WMF1.C. It defines all the top-level functions exported by the library. The first of these is the LibMain function. This function is called by the assembly-language entry point to the library; it saves the library instance handle for future reference.
The second function is the GetFilterInfo function. Since the WMF import filter is relatively simple, no initialization is required. The only action performed by this function is to return a value of 2 indicating that it is a graphics import filter.
The third function is the GetFilterPref function. As discussed, this function enables the user to define various file conversion options. In this case there are no options to specify so it simply does nothing (unfortunately most Windows programming is not this easy).
The fourth and most substantial function is ImportGr. This function validates all the parameters, then proceeds to open the metafile. After the file is opened successfully, the cursor is changed to an hourglass and file processing begins. The first task is to allocate global memory for the metafile header, then to determine the actual size of the metafile data. The size of the metafile data is determined by seeking to the end of the file, then subtracting the number of bytes used for the header.
Next, the header is read into memory and validated using the checksum provided. If the file is actually a metafile, the bounding box and metafile data is retrieved. When all this is complete the original cursor is restored, the file closed, and all unwanted memory released. Note the careful attention to error checking and recovery evident in this function. When writing graphics import filters it is extremely important to provide thorough, robust error checking as you can never be certain when and where the filter will be used.
In a more complex import filter, the ImportGr function would be a great deal more involved. It would contain a considerable amount of code that converts the image on disk into a memory metafile.
The last function in the module is the WEP function. As mentioned, this function performs any cleanup that might be necessary when the library is unloaded. In this case nothing needs to be done so a normal result value is returned.
The third library module is WMF2.C. This module defines three utility functions used by the ImportGr function. The ValidateWMF function checks to see if the metafile is valid by computing a checksum of the header, using the ComputeWMFChecksum function.
The GetWMFBits function reads the metafile bits from the file beginning at the current file position. Although in most cases you can get by with a simpler version of this function, the library needs to be prepared to handle an extremely large metafile. In this case our own read function must be substituted to load the memory block by using a series of consecutive reads.
The fourth and last module is WMF3.C. This module defines two utility read functions, hread and MyRead. These functions enable a large read (greater than 64KB) to be performed while ignoring 64KB segment boundaries.
When you have all the source code in place you can create the filter by entering the following command from a DOS prompt:
Make WMF
This make file was created for use with version 6.0 of Microsoft C and version 3.0 of the Windows SDK. A complete and working WMF import filter should be created that you can start experimenting with. For example, if you have Microsoft Word for Windows, you can use the filter by copying it into your system path and entering the following line to the [WWFilters] section of your WIN.INI file. Be sure to delete the WMF filter that comes with Word for Windows first!
Windows Metafile=WMF.FLT,WMF
When you have done this, you can experiment by inserting metafiles into a Word document using the Insert Picture command (see Figure 8). If you don’t have Word, it is quite likely that you have some other program on your system that supports this graphics import filter standard.
Figure 8 The sample graphics import filter WMF.FLT with Word for Windows 2.0.
Although the graphics import filter concept was originally designed to import object-oriented images, several software vendors have recently expanded its use to include raster and EPS images as well.
When importing raster images, a device-independent bitmap (DIB) is extracted from the disk file and embedded in the target metafile using a StretchDIBits function call. This has the distinct advantage of enabling the host application to support many kinds of graphical data using only one import filter interface. On the downside, however, this approach has several shortcomings that make it less than desirable when extremely large images are in use or more advanced image control features are needed (such as those provided by PageMaker).
Using a graphics import filter to import EPS images is even more complicated. EPS images generally consist of a short header, a vector or raster screen representation of the image, followed by actual PostScript text. Although in theory a well-formed EPS image should contain all three parts, there are several popular (but unfortunately ill-behaved) applications on the market that output EPS images without headers, screen representations, and incredible as it sounds, sometimes even PostScript text!
Ideally, an EPS import filter would parse the EPS file, creating the metafile using the vector or raster screen representation and embedding the actual PostScript text into the metafile as carefully formatted comments. When the resulting metafile is output to a non-PostScript device, the screen representation is displayed as one would expect. When the metafile is output to a PostScript device, the screen representation is ignored and the PostScript text is automatically extracted from the metafile comments by the device driver--resulting in extremely high-quality, scalable output.
A truly robust EPS import filter must be capable of handling even poorly formed EPS files. Herein lies most of the effort.
One of the more sophisticated graphics import filters available is the ART file import filter. This Aldus-compatible graphics import filter was developed by Eikon Systems to enable end users to extract data from Scrapbook+ ART files. When you insert a picture from an ART file using a program such as Word for Windows, a dialog box is automatically displayed (see Figure 9). Inside this dialog box are several rows of thumbnail images, each of which represents a page in the ART file. By selecting a particular page (and format if desired) you can bring the image directly into the host application. You can do this without using Scrapbook+ and gain access to almost any kind of data--TIFF, EPS, bitmaps, text, and so on. Each format is automatically converted into a metafile (with associated comments when PostScript text is available) and brought in using the standard graphics import filter mechanisms presented in this article.
Figure 9 The ART filter converts many graphical formats into Windows metafiles.
One final word on graphics import filters--there are now two general types of graphics import filters in use. The one described in this article is that defined by the PageMaker 3.0 Developer’s Toolkit. With the release of PageMaker 4.0, Aldus extended this interface while retaining backward compatibility. Although these extensions greatly enhance the applicability of the graphics import filter concept, they require the use of proprietary Aldus DLLs. Because of this the latest graphics import filter interface used by PageMaker 4.0 has not received the same degree of acceptance as the original. The graphics import filter defined by the PageMaker 3.0 Developer’s Toolkit is the format used by most major Windows applications today.
1For ease of reading, "Windows" refers to the Microsoft Windows graphical environment. "Windows" is a trademark that refers to this product and does not refer to such products generally.