Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Graphics Import Filters for Windows Applications


JUL92: GRAPHICS IMPORT FILTERS FOR WINDOWS APPLICATIONS

This article contains the following executables: TWAIN.ZIP TWAIN.MAC VIEWER.ARC

Evangelo Prodromou is a programmer and writer from San Francisco, CA and has been programming Windows applications for two years. He works for Access Softek, a graphics filter vendor, located at 2550 9th Street #206, Berkeley, CA 94710. He can also be reached via CompuServe at 70661,3174.


Graphics support has always been the hallmark of leading-edge software--and usually the most difficult part to implement. With Windows 3, an integral part of graphical software development is the ability to import and manipulate a graphics file created in another application. Table 1 shows the many (some say, too many) graphics file formats that a user may want to import into a document, database, or other application, each format with its own method of storing graphics file information. Some formats, such as the CGM standard, encompass several sub-formats created by different drawing programs.

Table 1: Graphics file formats.

  Name                         Ext  Source              Comment
  -------------------------------------------------------------------------

  Adobe Illustrator file      .AI  Adobe Illustrator
  OS/2 Bitmap                 .BMP OS/2                 OS/2 standard
  Windows Bitmap              .BMP Microsoft Windows    Windows standard
  Corel Draw! file            .CDR Corel Draw!
  Computer Graphics Metafile  .CGM ANSI/ISO standard
   (CGM)
  Freelance file              .DRW Lotus Freelance
  Micrografx Drawing file     .DRW Micrografx Designer
  AutoCAD Drawing Exchange    .DXF Autodesk's AutoCAD
  Encapsulated PostScript     .EPS Adobe                Standard for
                                                        PostScript
                                                        printers
  Graphic Environment         .GEM Digital Research's
   Manager (GEM) Metafile       GEM
  General Image File (GIF)    .GIF CompuServe           Highly compressed
  OS/2 Metafile               .MET OS/2                 OS/2 standard
  QuickDraw Picture file
   (PICT)                     .PCT Macintosh QuickDraw
  PC Paintbrush               .PCX ZSoft PC Paintbrush
  Lotus 1-2-3 Graphics file   .PIC Lotus 1-2-3
  HP Graphics Language
   (HPGL)                     .PLT Hewlett-Packard      Developed for HP
                                                         plotters
  Tagged Image File Format
     (TIFF)                   .TIF Microsoft/Aldus
  Windows Metafile            .WMF Windows              Windows standard
  WordPerfect Graphics file   .WPG DrawPerfect

It's virtually impossible for an individual developer to account for all possible formats in an application. Just finding a format's specifications is difficult and time-consuming enough. After that, writing and testing the code to parse and display a particular file takes months of development time.

Alternatively, many Windows developers simply license file-import filters from developers who specialize in the nitty-gritty mechanics of file conversion. These filters can be used as software components to support a broad range of file formats without a corresponding increase in development time.

In this article, I describe an easy way to include graphics support in Windows applications, and then provide a sample application that quickly displays graphics files.

The Windows Environment

Windows has two features that make modular graphics support feasible: metafiles and Dynamic Link Libraries (DLLs).

The Windows metafile has two standard graphics formats: the metafile and the bitmap. The libraries included in the Windows Software Development Kit (SDK) contain functions to manipulate and store both formats. Graphics in these forms are consequently easy to incorporate into any Windows program.

Although each format has its advantages, the metafile is easier to display. Unlike a bitmap, which stores a bit-by-bit copy of the image, the metafile stores only the commands n cessary to create the image. This limits the device dependence of graphics display. The Windows Graphic Device Interface (GDI) assumes the responsibility for display options. The GDI will execute the graphics commands stored in a metafile only to the extent of the graphics device's capability. This makes the metafile a powerful tool for graphics computing.

Windows DLL files can contain code not included in the main executable file. If an application needs the code in the DLL, the file can be loaded into memory, used, and then removed. DLLs can be used as program "modules" that work with the central executable to implement all the application's features.

Fusing the concepts of the DLL and the metafile produces a powerful graphics tool--the graphics import filter. Filters are DLLs that can translate a foreign-format graphics file into a Windows metafile. Using the metafile ensures that the resulting image is independent of the capabilities of the display device. Using DLLs, on the other hand, means that import functionality can be developed independently of the main executable, and that it can be loaded by the application only when necessary.

The Aldus Interface

Aldus Corp. recognized the importance of graphics import filters when the company ported PageMaker to Windows. For PageMaker 3.0 for Windows, Aldus required each filter (DLLs licensed from third-parties) to have a uniform functional interface to process import requests from the main application. In other words, Aldus built an import "slot" into its application and required that all filter modules fit perfectly into that slot. This approach eased Aldus's graphics-support development load and made it possible for different Aldus products with the same interface to use the same filters.

The Aldus interface is now a de facto standard, having been adopted by many Windows developers, including Microsoft in Word for Windows and PowerPoint, Lotus in the Ami Pro 2.0 word processor, and Asymetrix in Tool-Book.

Fortunately, the Aldus interface is easy to program. Minimal changes in an application's source code can take full advantage of the graphics power behind import filters.

As an example, I've included a graphics file viewer I designed for use with the Windows File Manager. By associating a graphics file extension with this viewer application, VIEWER.EXE, I can quickly look at a particular file just by double-clicking on its filename. (The complete system is available electronically.)

Data Types

The Aldus standard includes two data types not typically found in Windows applications. These are defined in my application's header file VIEWER.H (see Listing One , page 108), which also has several defined constants and function declarations.

A FILESPEC structure represents the graphics file that the application wants the filter to translate. The structure contains the file's full pathname as well as other important file information: whether it's open for writing, where the current file pointer is, what its DOS file handle is, and so on. Applications need to fill in these fields if they operate on the file before passing it to the filter. (I used the default values in my code.)

The PICTINFO structure describes the resulting GDI metafile returned from a filter after translation. It includes a memory handle to the metafile and a RECT structure that describes the tightest-fitting rectangle this file can fit into. This makes it easier for an application to place or update an imported picture, because the filter that actually created the metafile returns its optimal size and shape.

Functionality

VIEWER.C (Listing Two, page 108) is the C source file for the viewer application. Its heart is the function ImportFile(), which handles the actual translation of the file to a Windows metafile.

ImportFile() uses the "anonymous call" method to access the functionality of a given filter. To do this, it uses the standard Windows LoadLibrary() function to load the filter DLL into memory. LoadLibrary() just needs the full pathname of the filter file (szFilter in my application) to load it. It returns a memory handle hLibrary to the library.

Then ImportFile() calls a second Windows function, GetProcAddress(), to return a pointer to a named procedure in that library. GetProcAddress() needs a handle to the loaded library and the name of the function. You can see that I've included literal strings as the names of the functions; all Aldus-standard filters must have exactly these names for their functions, so I can use these literal strings with impunity.

This is why a stock set of interface functions is so important. These calls to GetProcAddress() are the "slot" into which all files must fit.

ImportFile() first tries to locate the function GetFilterVersion() within the DLL. Aldus has defined a basic interface, version 1.0, and an enhanced, more powerful interface, version 2.0. (Version 2.0 requires the filter to use Aldus's proprietary DLLs for memory management. Consequently, Aldus is the only developer to implement this interface, as in PageMaker 4.0.) The purpose of GetFilterVersion(), therefore, is to tell the viewer which set of functions to use. At this writing, only version 2.0 filters contain GetFilterVersion(). If the filter doesn't have this function, GetProcAddress() will return a NULL pointer. This means that the filter uses the basic version 1.0 interface, and ImportFile() needs to access those simpler functions.

Version 1.0 filters have three functions: GetFilterInfo(), GetFilterPref(), and ImportGR(). GetFilterInfo (nPageMakerVersion, lpIni, lphPrefMem, lphFileTypes) initializes the filter. The argument nPageMakerVersion is an artifact from the time when only PageMaker used Aldus-standard filters; because it's no longer necessary, I set the argument to 0 in my code.

lpIni is a string of information stored in the WIN.INI file about the filter, and lphFileTypes defines the file types the filter is expected to support. Some applications may want to dynamically create a table of information on all available filters, but my viewer is a one-shot importer, so I set these two arguments to NULL also.

If the filter needs to get specific information from the user (for example, whether to use color or grayscale in the graphics image), GetFilterInfo() dynamically allocates memory in which to store the user's preferences. It then copies a handle to that memory to lphPrefMem.

GetFilterPref(hInst, hWnd, hPrefMem, wFlags) displays a dialog box in the window hWnd to get the user's import preferences. It stores the options in the memory in hPrefMem--the format varies from filter to filter--to be used during the import process.

More Details.

To actually translate the file, the application calls hPrintIC, ImportGR(hPrintIC, lpFileSpec, lpPictInfo, hPrefMem). The application passes the filter an information context hPrintIC (which describes the capabilities of an output device, such as a printer, a plotter, or a video display) for the supported printer, to determine supported fonts and other system-specific parameters. It also passes the memory block hPrefMem and a FILESPEC structure pointed to by lpFileSpec representing the file to import. ImportGR() will translate the file and fill the PICTINFO structure pointed to by lpPictInfo with the important information about the resultant metafile.

Filters supporting the version 2.0 interface have two additional functions: GetFilterVersion() (described earlier) and IsThisMyFile(). The 2.0 interface standard also replaces ImportGR() with the more versatile OutputGR().

IsThisMyFile(lpFileSpec) determines the format of the file defined by the FILESPEC structure in lpFileSpec. This function makes it easier for the application to dynamically match files to filters that can support them.

After checking the file, ImportFile() calls GetFilterPref(), as it would for the version 1.0 function set. In this case, however, GetFilterPref() allocates its own preference memory. Just as in the version 1.0 interface, this function will display a dialog box to retrieve the user's import preferences.

Finally, ImportFile() calls OutputGR (hOutputDC, hPrintIC, lpFileSpec, lpMetafileName, lpPictInfo, hPrefMem, lpInfoProc, bComplete) to translate the file lpFileSpec to a GDI metafile in lpPictInfo. Much more powerful than the version 1.0 ImportGR(), OutputGR() can also display or print the metafile to the device context (which defines a particular output device--a printer, a plotter, or a window on the video display. Windows prohibits an application from writing to a device directly; instead, it can send commands to the device context, which the GDI processes and implements on the physical device). hOutputDC. Alternately, it can save the metafile to disk as a file named lpMetafileName. For simplicity, I chose not to use either of these options; viewer.exe just requests a metafile and takes responsibility for displaying it.

If OutputGR() is passed a valid pointer to an information procedure lpInfoProc, it will call that procedure for more information about the current color palette, available printers, and so on. This usually isn't necessary unless the calling application is going to place the returned image into an existing graphics file. I pass a NULL pointer to tell OutputGR() just to use its default settings.

Once the graphics file has been translated by either ImportGR() or OutputGR(), ImportFile() frees the memory taken up by the filter DLL hfilter and the user-preference options, hPrefMem. The metafile is now ready to be displayed.

Implementing File Import

VIEWER.C includes a number of functions necessary to implement the application. WinMain(), the entrance point, creates the main window and handles the message loop; MainWndProc() handles the messages for the main window; and AboutDlgProc() handles the About dialog box. Although a full discussion of these functions is beyond the scope of this article, I'd like to explain how each of them supports ImportFile()'s file-import capability.

With the Windows File Manager, a user can "associate" file types by extension--such as "PCX"--with a particular application; VIEWER.EXE, for instance. If the user double-clicks on a filename with that extension, the associated application is launched and the file's full pathname is passed to it as the lpCmdLine argument in WinMain(). I copy that filename to the filename field in the FILESPEC structure so it can be used later by ImportFile().

WinMain() registers and creates the main window. When CreateWindow() is called, MainWndPro() receives the message WM_CREATE. Here, I check to see if a filename has been specified. Then, using the local function GetExtFilter(), I fill the global variable szFilter with the correct filter's full filename.

Available filter DLLs must be listed in the WIN.INI file in the form file-format=filter file, ext under the heading [GraphicViewer]. For example:

  [GraphicViewer]   PCX files=C:\VIEWER\PCXFILT.FLT,PCX

Here, file-format is the file format supported filter file is the full path and filename of the DLL to use for importing, and ext is the typical extension of the file type this filter supports. (If you use commercially available applications such as Word for Windows or PageMaker, you'll probably see similar headings in your WIN.INI file.)

These entries should give you a good idea of where you can find existing filter DLLs on your disk. You may have to do some detective work to discover which ones work for which file formats.

GetExtFilter() checks each entry under [GraphicViewer] using the standard Windows function GetProfileString(). It fills szFilter with the name of the filter that matches the file's extension. I then call ImportFile() to translate the file to a GDI metafile, which finishes the WM_CREATE processing in MainWndProc(). Flow then returns to WinMain(), which calls ShowWindow() to show the main window, then UpdateWindow(), which sends the WM_PAINT message to MainWndProc().

When the main window gets the WM_PAINT message, it prepares its client area to display the translated image. It then calls PlayMetafile(), another standard Windows function, to display the image on the screen. (The client area is the area of the window on which it can draw, that is, the whole window except title bar, scroll bars, borders, and menu.)

After updating the main window, WinMain() creates a message loop to process messages for the window. If the user maximizes or resizes the main window, MainWndProc() will receive the WM_SIZE message. Clicking the mouse on the "About Viewer... " menu item will cause MainWndProc() to show my copyright information with the AboutDlgProc().

Besides VIEWER.C and VIEWER.H, other files necessary to construct this application (the definition file to be used with Microsoft C; the resource file containing the viewer's menu and the "About" dialog box; and make file for the nmake utility in Microsoft C) are available electronically, as described on page 3.

For a more complete explanation of the aforementioned functions, I recommend the Windows SDK's Guide to Programming or Graphics Programming Under Windows by Meyers and Doner (Sybex, 1987).

Conclusion

Import and export filters are the first widely used examples of a standardized component approach to software development. A modular software system lets users adapt applications to their own needs, however specialized. In the future, both small and large developers will need to rely on modularity to confront the wide range of possibilities PCs will provide.

Standards the Twain Shall Meet

The Aldus graphics-filter interface constitutes an important part in the oncoming standardization of the graphics arena. However, the recently proposed Twain API specification, devised to provide a uniform interface between graphics-supporting software and image-capturing hardware, may ultimately play a leading role as well.

Typically, when users want to include an image derived from hardware such as hand-scanners, flat-bed scanners, slide scanners, or digital cameras, they must use a dedicated software package to acquire the image and then save it to a file on disk. To include the image in another document, the user must then import the file using a graphics filter or other graphics-support software.

The Twain API aims to circumvent these extra stages. Software that supports Twain can bring graphics images directly into a document, without the intermediate step of creating an additional file. Hardware vendors, on the other side, need only develop Twain-compatible drivers to make their image-source hardware accessible to many leading graphics-mode software packages.

Between the application and the source lies an intermediate step, the Twain Source Manager, implemented as a DLL under Windows and a code resource for the Macintosh. Applications that use the API can call on the Source Manager to acquire the images using a user-defined source. The process is similar to choosing and setting up a printer under Windows or on the Mac. Image files are returned to the application in device-independent bitmap (DIB) format for Windows or Picture format for the Macintosh.

Twain was developed by a working group from five players in the image processing and acquisition field: Aldus, Caere, Eastman Kodak, Hewlett Packard, and Logitech. Hardware vendors such as Canon and Ricoh and software developers including Lotus and Micrografx are also currently including support of the Twain API in their products. Corel, Adobe, and Ventura have also endorsed the interface.

Additional information on the Twain API is available in the HP Peripherals Forum on CompuServe, where you'll find the Twain Developer's Disk 1.0 for Windows (which uses Borland C++ or Microsoft C6 and includes Source Manager, DC.H file, application "glue" code, sample application, and sample Twain source). Likewise available is the Twain Developer's Disk 1.0 for Macintosh (for System 6 or 7 using Think C5 and including the Source Manager, DC.h file, application "glue" code, sample app, and sample Twain source). You can also contact the Twain Working Group at 1-800-722-0379 for a technical paper describing the Twain API (ask for document #9155) and the Twain Toolkit order form (document #9154).

--E.P.



_GRAPHICS IMPORT FILTERS FOR WINDOWS APPLICATIONS_
by Evangelo Prodromou


[LISTING ONE]
<a name="0198_000e">

/**************************************************************************

    FILE   : Viewer.h

    PURPOSE: header file for graphic viewer

) 1992, Evangelo Prodromou.  All rights reserved.
**************************************************************************/

/* Viewer Menu item definitions */

#define IDM_ABOUT       100
#define IDC_STATIC      -1

/*  general-use string size. */

#define STRINGSIZE 511

/* The following definitions are data types defined by the Aldus
** Interface. */

typedef DWORD FILETYPE;

typedef struct {
   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. */
#define IBMFNSIZE 124
   short       handle;              /* MS-DOS open file handle. */
   char        fullName[IBMFNSIZE]; /* Device, path, file names. */
   DWORD       filePos;          /* Our current file posn. */
} FILESPEC, FAR *LPFILESPEC;

typedef short DC;

typedef struct {        /* --- PICTINFO for Windows --- */
  HANDLE        hmf;    /* Global memory handle to the metafile */
  RECT          bbox;   /* Tightly bounding rectangle in metafile units */
  DC            inch;   /* Length of an inch in metafile units */
} PICTINFO, FAR* LPPICTINFO;


/* The following types are pointers to functions with the same arguments
** as those found in an Aldus-standard function.  They are necessary to
** make anonymous function calls. */

/* Version 1.0 filter functions */

typedef WORD (FAR PASCAL *PFN_INFO)   (short, LPSTR, HANDLE FAR*, HANDLE FAR*);
typedef WORD (FAR PASCAL *PFN_IMPORT) (HDC, LPFILESPEC, LPPICTINFO, HANDLE);
typedef void (FAR PASCAL *PFN_PREF)   (HANDLE, HWND, HANDLE, WORD);

/* Version 2.0 filter functions */

typedef WORD (FAR PASCAL *PFN_VER)    (DWORD, BOOL FAR *, WORD FAR *, WORD FAR *);
typedef WORD (FAR PASCAL *PFN_ISMY)   (LPFILESPEC);
typedef WORD (FAR PASCAL *PFN_PREF2)  (HANDLE, HANDLE, HANDLE FAR *, DWORD, FARPROC, LPFILESPEC);
typedef WORD (FAR PASCAL *PFN_OUTPUT) (HDC, HDC, LPFILESPEC, LPSTR, LPPICTINFO, HANDLE, FARPROC, BOOL);

/* The following are function declarations for functions local to
** this application. */

int PASCAL WinMain( HANDLE, HANDLE, LPSTR, int );
long FAR PASCAL MainWndProc( HWND, unsigned, WORD, LONG );
BOOL FAR PASCAL AboutDlgProc( HWND, unsigned, WORD, LONG );
BOOL NEAR ImportFile( HWND );
BOOL NEAR GetExtFilter( void );
HDC NEAR GetPrinterIC( void );





<a name="0198_000f">
<a name="0198_0010">
[LISTING TWO]
<a name="0198_0010">

/**************************************************************************

    FILE   : Viewer.c

    PURPOSE: Graphics file viewer

    FUNCTIONS:

   WinMain() - calls initialization function, processes message loop
   MainWndProc() - processes messages
   ImportFile() - Converts graphics file to Windows Metafile
   GetExtFilter() - Determines correct filter for chosen file
   GetPrinterIC() - Determines current printer
   AboutDlgProc() - processes messages for "About" dialog box

    COMMENTS:

) 1992, Evangelo Prodromou.  All rights reserved.
**************************************************************************/

/* include the general Windows header and the header for this application. */

#include "windows.h"
#include "viewer.h"

/* These strings are used repeatedly. */

char    szAppName[ ]            = "GraphicViewer";
char    szClassName[ ]          = "ViewerWClass";
char    szMenuName[ ]           = "ViewerMenu";

char    szString[ STRINGSIZE ];          /* General use string. */
char    szFilter[ IBMFNSIZE ];  /* full path name of import filter. */

/* Info struct of the imported metafile. */

PICTINFO PictInfo = { NULL,0,0,0,0,0 };

/* Spec struct for file to import. */

FILESPEC FileSpec = { 0,0,0,0,0,0L,NULL };

HANDLE  hInst;                    /* Handle for this instance. */

/*************************************************************************

FUNCTION: WinMain
PURPOSE:  Entrance point of application

ARGS:     hInstance     : handle to this instance.
     hPrevInstance : handle to previous instance of the application.
     lpCmdLine     : command line string
     nCmdShow      : show full or iconic? Passed to ShowWindow()

COMMENTS: If no previous instance, registers the window class.
     Saves this instance handle as a global variable.
     Parses command line for a file name to view.
     Creates window for this instance.
     Shows the window.
     Takes and translates messages from the message loop and passes
       them on to MainWndProc().

*************************************************************************/
int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance,
         LPSTR lpCmdLine, int nCmdShow)
{
   MSG             msg;    /* Windows message structure */
   WNDCLASS        wc;     /* Window class structure. */
   HWND            hMain;  /* handle to main window.  */

/* If there is no previous instance of the application, fill in the
** window class structure and register the class. */

   if (!hPrevInstance)
   {
      wc.style =              NULL;
      wc.lpfnWndProc =        MainWndProc;
      wc.cbClsExtra =         0;
      wc.cbWndExtra =         0;
      wc.hInstance =          hInstance;
      wc.hIcon =              LoadIcon(NULL, IDI_APPLICATION);
      wc.hCursor =            LoadCursor(NULL, IDC_ARROW);
      wc.hbrBackground =      GetStockObject(WHITE_BRUSH);
      wc.lpszMenuName =       (LPSTR) szMenuName;
      wc.lpszClassName =      (LPSTR) szClassName;

      if (!RegisterClass(&wc))
         return FALSE;
   }

/* Save the instance handle as a global variable. */

   hInst = hInstance;

/* Copy the command line argument to the filename field of FileSpec. */

   lstrcpy( FileSpec.fullName,  lpCmdLine );

/* Create the main window (will send WM_CREATE to the MainWndProc). */

   hMain = CreateWindow( szClassName, szAppName,
         WS_OVERLAPPEDWINDOW,
         CW_USEDEFAULT, CW_USEDEFAULT,
         CW_USEDEFAULT, CW_USEDEFAULT,
         NULL, NULL, hInstance, NULL );

   if (!hMain)
      return (FALSE);

/* Show the window and update it ( sends WM_PAINT to MainWndProc ). */

   ShowWindow(hMain, nCmdShow);
   UpdateWindow(hMain);

/* Get window messages for this instance and send them to the MainWndProc.
** Loops until WM_QUIT message is received. */

   while ( GetMessage( &msg,NULL,NULL,NULL )  )
   {
      TranslateMessage( &msg );
      DispatchMessage( &msg );
   }
   return ( msg.wParam );
}

/*************************************************************************

FUNCTION: MainWndProc
PURPOSE:  Processes messages for main window

ARGS:     hWnd          : handle to main window
     message       : windows message
     wParam        : extra message info
     lParam        : extra message info

MESSAGES: WM_CREATE     : If a file was specified on the command line, gets
           the correct filter by extension, and tries to
           import it.  If no file specified, or if no matching
           filter is found, asks for a new file.

     WM_PAINT      : If a file has been imported, displays the
           resulting metafile.  Otherwise, passes the message
           to default.

     WM_SIZE       : Invalidate whole client area, and repaint.

     WM_DESTROY    : Terminates program.

     WM_COMMAND    : IDM_ABOUT     : Displays About dialog box.

*************************************************************************/

long FAR PASCAL MainWndProc(HWND hWnd, unsigned message,
            WORD wParam, LONG lParam)
{
    FARPROC lpProc;
    BOOL    bError;
    RECT        rc;

    switch (message)
    {
   case WM_CREATE:

       /* Assume an error to begin with. */

       bError = TRUE;

       if ( FileSpec.fullName[0] == '\0' ) /* No file name on command line */
       {
      lstrcpy( (LPSTR) szString, "No file name specified." );
       }
       else if ( !GetExtFilter( ) ) /* Unable to find filter */
       {
      lstrcpy( (LPSTR) szString,
         "No filter specified in WIN.INI for file extension." );
       }
       else if ( !ImportFile( hWnd ) ) /* Unable to import */
       {
      lstrcpy( (LPSTR) szString, "Unable to import file." );
       }
       else bError = FALSE;  /* Import was a success */

       if (bError)
       {
      MessageBox( hWnd, (LPSTR) szString,
                 (LPSTR) szAppName,
                 MB_OK | MB_ICONHAND );
      DestroyWindow( hWnd );
       }
       break;


   case WM_PAINT:     /* Paint window by playing metafile. */

      if ( PictInfo.hmf ) /* A file has been imported. */
      {
         PAINTSTRUCT     ps;
         HDC             hDC = BeginPaint( hWnd, &ps );

         SetMapMode( hDC, MM_ANISOTROPIC );
         SetWindowExt( hDC, PictInfo.bbox.right - PictInfo.bbox.left,
                  PictInfo.bbox.bottom - PictInfo.bbox.top );
         GetClientRect( hWnd, &rc );
         SetViewportExt( hDC, rc.right - rc.left, rc.bottom - rc.top );

         SetWindowOrg( hDC, PictInfo.bbox.left, PictInfo.bbox.top );
         PlayMetaFile( hDC, PictInfo.hmf );

         EndPaint( hWnd, &ps );
      }
      break;

   case WM_SIZE:  /* Invalidate, and paint full window. */

      GetClientRect( hWnd, &rc );
      InvalidateRect( hWnd, &rc, TRUE);
      UpdateWindow( hWnd );
      break;

   case WM_DESTROY:  /* Send WM_QUIT message to message loop to end. */

      PostQuitMessage( hWnd );
      break;

   case WM_COMMAND:  /* Creates About dialog box. */

      if ( wParam == IDM_ABOUT )
      {
         lpProc = MakeProcInstance( AboutDlgProc, hInst );

         DialogBox( hInst, "AboutBox", hWnd, lpProc );

         FreeProcInstance( lpProc );

         break;
      }  /* Otherwise, fall through to default. */

   default:

       return ( DefWindowProc( hWnd, message, wParam, lParam ) );
    }
    return (NULL);
}

/**************************************************************************
*                                                                         *
*      FUNCTION: ImportFile                                               *
*      PURPOSE : Translate a graphic file to a metafile using a filter    *
*                                                                         *
*      ARGS    : HWND hWnd - handle to main window.                       *
*      RETURN  : TRUE if file import is successful, otherwise FALSE.      *
*                                                                         *
*      COMMENTS: Loads a filter DLL into memory, and uses it's import     *
*                functionality to translate a graphic file to a metafile. *
*                Uses Aldus-standard interface functions, version 1 or 2. *
*                                                                         *
**************************************************************************/

BOOL NEAR ImportFile(HWND hWnd)
{
   HANDLE          hFilter = NULL, hPrefMem = NULL;
   WORD            wFilterResult = -1;
   HDC             hPrintIC = NULL;

/* Version 1.0 Filter functions */

   PFN_INFO        lpfnGetFilterInfo = NULL;
   PFN_IMPORT      lpfnImportGR = NULL;
   PFN_PREF        lpfnGetFilterPref = NULL;

/* Version 2.0 Filter functions */

   PFN_VER         lpfnGetFilterVersion = NULL;
   PFN_ISMY        lpfnIsThisMyFile = NULL;
   PFN_PREF2       lpfnGetFilterPref2 = NULL;
   PFN_OUTPUT      lpfnOutputGR = NULL;

/* Load appropriate filter. */

   hFilter = LoadLibrary( (LPSTR) szFilter );

/* Try to find "GetFilterVersion." */

   lpfnGetFilterVersion = GetProcAddress( hFilter,
                "GetFilterVersion" );

   if ( !lpfnGetFilterVersion ) /* This is a v 1.0 filter. */
   {

      lpfnGetFilterInfo = GetProcAddress( hFilter,
                  "GetFilterInfo" );

      if ( lpfnGetFilterInfo )
      {
         wFilterResult = (*lpfnGetFilterInfo)( 2,
                  NULL,
                  &hPrefMem,
                  NULL );
      }

/* Call filter's GetFilterPref function, which creates a "filter preference"
** dialog box to set import options. */

      lpfnGetFilterPref = GetProcAddress( hFilter,
                  "GetFilterPref" );

      if ( lpfnGetFilterPref )
      {
          (*lpfnGetFilterPref)( hInst, hWnd, hPrefMem, 1 );
      }

/* Call filter's ImportGR function to convert the file to a Windows
** metafile (information is stored in FileSpec). */


      lpfnImportGR = GetProcAddress( hFilter, "ImportGR" );
      if ( lpfnImportGR )
      {
          hPrintIC = GetPrinterIC();

          wFilterResult = (*lpfnImportGR)( hPrintIC,
                       &FileSpec,
                       &PictInfo,
                       hPrefMem );
      }
   }

   else /* This is a v. 2.0 or higher filter. */
   {

/* Ensure that current file is compatible with current filter. */

      lpfnIsThisMyFile = GetProcAddress( hFilter,
                  "IsThisMyFile" );

      if ( lpfnIsThisMyFile )
      {

         (*lpfnIsThisMyFile) ( &FileSpec );

      }

/* Get user's import preferences. */

      lpfnGetFilterPref2 = GetProcAddress( hFilter, "GetFilterPref" );
      if (lpfnGetFilterPref2)
      {
         wFilterResult = (*lpfnGetFilterPref2) ( hInst,
                         hWnd,
                         &hPrefMem,
                         FileSpec.fType,
                         NULL,
                         &FileSpec);
      }
/* Convert chosen file to a metafile. */

      lpfnOutputGR = GetProcAddress( hFilter, "OutputGR" );
      if (lpfnOutputGR)
      {
         hPrintIC = GetPrinterIC();

         wFilterResult = (*lpfnOutputGR) ( NULL,
                     hPrintIC,
                     &FileSpec,
                     NULL,
                     &PictInfo,
                     hPrefMem,
                     NULL,
                     FALSE);
      }
   }

/* Free the memory allocated to the filter DLL and the preference memory.*/

   FreeLibrary( hFilter );
   GlobalFree( hPrefMem );

/* Set the window title to the file name, or return false. */

   if ( wFilterResult == 0 )
   {
      SetWindowText( hWnd, FileSpec.fullName );
      return TRUE;
   }
   else
   {
      return FALSE;
   }
}

/**************************************************************************
*                                                                         *
*      FUNCTION: GetExtFilter                                             *
*      PURPOSE : Get filename of filter appropriate for current file.     *
*                                                                         *
*      ARGS    : none                                                     *
*      RETURN  : TRUE if able to find filter, otherwise FALSE.            *
*                                                                         *
*      COMMENTS: Gets all filter names listed under [GraphicViewer]       *
*                heading in WIN.INI.  Checks which one entry supports     *
*                files with the same extension as FileSpec.fullName.      *
*                                                                         *
**************************************************************************/

BOOL NEAR GetExtFilter( void )
{
   PSTR pDesc, pExt, pSupExt;
   int  nLen = lstrlen( FileSpec.fullName );
   char szItem[ IBMFNSIZE + 4 ];

   /* Set pExt to last char in FileSpec.fullName. */

   if (!nLen) return FALSE;

   pExt = FileSpec.fullName + nLen - 1;

   while ( *(pExt - 1) != '.' )
   {
       pExt--;
       if (pExt == FileSpec.fullName) return FALSE;
   }

   /* get all profile string entries (description of filter). */

   nLen = GetProfileString( szAppName, NULL, NULL, szString, STRINGSIZE );

   /* start with the first description. */

   pDesc = szString;

   /* while we still have a string to check... */

   while ( pDesc < szString + nLen )
   {
      /* get the entry for this filter
      ("[filter file name],[ext]") */

      GetProfileString( (LPSTR) szAppName, (LPSTR) pDesc,
              NULL, (LPSTR) szItem, IBMFNSIZE + 4 );

      /* if one exists, and its extension matches the file
      ** extension... */

      strcpy( szFilter, strtok( szItem, "," ) );
      pSupExt = strtok( NULL, ", " );

      if( lstrcmpi( (LPSTR) pExt, (LPSTR) pSupExt ) == 0 )
      {
         *(pSupExt-1) = '\0';
         return TRUE;
      }
      else
      {
             /* move on to next filter. */
             pDesc += lstrlen( (LPSTR) pDesc ) + 1;
      }
   }

   /* if we get here, we couldn't find one. Make sure szFilter is blank.*/

   szFilter[0] = '\0';

   /* Report failure. */

   return FALSE;
}

/**************************************************************************
*                                                                         *
*      FUNCTION: GetPrinterIC                                             *
*      PURPOSE : Get information context for active print device.         *
*                                                                         *
*      ARGS    : none                                                     *
*      RETURN  : handle to printer's information context or NULL if fail. *
*                                                                         *
*      COMMENTS: Gets device description from [windows] heading in        *
*                WIN.INI.  Breaks down the description into device name,  *
*                driver name, and output port.  Uses CreateIC() to get    *
*                an information context.                                  *
*                                                                         *
**************************************************************************/

HDC GetPrinterIC( void )
{
   PSTR pDevice, pDriver, pOutput;
   HDC  hReturn = NULL;

/* Get the information on the current printer, listed under "windows" in
** WIN.INI as "device=[device name],[driver],[output port]". */

   GetProfileString("windows", "device", NULL,
               (LPSTR) szString, STRINGSIZE);

   if ( ( pDevice = strtok( szString, "," ) ) &&
        ( pDriver = strtok( NULL,     ", " ) ) &&
        ( pOutput = strtok( NULL,     ", " ) ) )
   {
       hReturn = CreateIC( (LPSTR) pDriver, (LPSTR) pDevice,
               (LPSTR) pOutput, NULL );
   }

   return ( hReturn );
}

/**************************************************************************
*                                                                         *
*      FUNCTION: AboutDlgProc                                             *
*      PURPOSE : Handles messages for AboutBox dialog box.                *
*                                                                         *
*      ARGS    : Standard callback arguments.                             *
*      RETURN  : N/A.                                                     *
*                                                                         *
*      COMMENTS: Closes AboutBox when OK button, Enter or ESC are pressed.*
*                                                                         *
**************************************************************************/


BOOL FAR PASCAL AboutDlgProc(HWND hDlg, unsigned message,
            WORD wParam, LONG lParam)
{
    switch (message)
    {
   case WM_INITDIALOG:  /* Beginning. No functionality. */

      return (TRUE);

   case WM_COMMAND:

      switch (wParam)
      {
         case IDOK:  /* OK or Enter were pressed. */
         case IDCANCEL: /* Esc was pressed. */

            EndDialog(hDlg, TRUE);
            return (TRUE);
      }
      break;
    }
    return (FALSE);
}


Copyright © 1992, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.