home *** CD-ROM | disk | FTP | other *** search
/ CICA 1992 November / CICA_MS_Windows_CD-ROM_Walnut_Creek_November_1992.iso / win3 / programr / meta2 / metaplac.txt
Text File  |  1992-03-10  |  20KB  |  388 lines

  1.  
  2. The following is a discussion of the many ways applications read and write metafiles and how your application can handle them correctly.
  3.  
  4. Metafile Overview
  5. Metafile Headers
  6. Placeable Metafiles
  7. Metafile Applications
  8. Creating Metafiles
  9. Displaying Metafiles
  10. Reading Metafiles
  11. Passing Metafiles via DDE
  12. Passing Metafiles to the Clipboard
  13.  
  14. Metafile Overview
  15.  
  16. A metafile is a way to store a series of GDI records, or objects, used for drawing so that they can be played back at a later time, or when the application gets a WM_PAINT message.
  17.  
  18. Metafiles are used to store images on disk or to be transferred between applications via the clipboard as a "Picture" or via DDE with a CF_METAFILEPICT format.  One of the many advantages of metafiles is that they can be resized inside your application or other applications without loosing a lot of the picture quality.  Also, since the metafile consists of a bunch of records, the metafile can be considerably smaller than a bitmap, especially a large bitmap.
  19.  
  20. A metafile consists of a collection of graphics device interface (GDI) functions that describe an 
  21. image.  Because metafiles take up less space and are more device independent than 
  22. bitmaps, they provide convenient storage for images that appear repeatedly in an application 
  23. or need to be moved from one application to another. 
  24.  
  25. To generate a metafile, a Windows application creates a special device context that sends 
  26. GDI commands to a file or memory for storage. The application can later play back the 
  27. metafile and display the image. 
  28.  
  29. A series of records follows the metafile header. GDI stores most of the GDI functions that an 
  30. application can use to create metafiles in typical records. The remainder of the functions are 
  31. stored in function-specific records. 
  32.  
  33. In processing Windows Metafiles, it is important to know that there are three kinds of headers  that exist and two file formats - both of which use the same .WMF file extension.
  34.  
  35. The following information will discuss to use the different headers for a memory and disk metafile, what they are for and when to use them.
  36.  
  37. Metafile Headers
  38.  
  39. In order to understand how to display a metafile, you need to know certain attributes that effect the displaying of them.
  40.  
  41. METAFILEPICT
  42. The two attributes that effect the displaying of the metafile are the "mapping mode", and how big to display/scale the metafile in the X and Y "extents" or directions the way the author intended it.  The structure that contains these two attributes is known as the METAFILEPICT structure and is used when passing information about the memory metafile to the clipboard or to another application via DDE.  However, we will see in the next structure that this information is not preserved when storing a metafile to disk.
  43.  
  44. typedef struct tagMETAFILEPICT {
  45.     int    mm;
  46.     int    xExt, yExt;
  47.     HANDLE    hMF;
  48. } METAFILEPICT;
  49.  
  50. How you use each field's value is discussed in the Displaying Metafiles section.
  51.  
  52. Normal disk metafile header
  53. The metafile that is stored in memory is the same as the "normal" metafile file stored on disk.  This metafile consists of two parts: a header followed by a list of GDI records.  This header contains a description of the size, in words, of the metafile and the number of drawing objects it uses.  This information is useful in allocating memory for the size of the disk metafile.
  54.  
  55. The metafile header has the following format: 
  56.  
  57. struct {
  58.     WORD    mtType;
  59.     WORD    mtHeaderSize;
  60.     WORD    mtVersion;
  61.     DWORD    mtSize;
  62.     WORD    mtNoObjects;
  63.     DWORD    mtMaxRecord;
  64.     WORD    mtNoParameters;
  65. }
  66.  
  67. The value of each field and the specific information on how the GDI records are stored on disk can be found in the Microsoft Windows Programmer's Reference, Volume 2, File Formats.
  68.  
  69. Placeable disk metafile header
  70. The other metafile file format is known as a "Placeable" metafile.  The Placeable metafile has the same information as normal metafiles except that there is an additional 22 byte header at the beginning of the file and is defined as follows:
  71.  
  72. struct {
  73.     DWORD    key;
  74.     HANDLE    hmf;
  75.     RECT    bbox;
  76.     WORD    inch;
  77.     DWORD    reserved;
  78.     WORD    checksum;
  79. } PLACEABLEHEADER;
  80.  
  81. The purpose of this extra 22 byte header is to preserve the original aspect ratio and size of the metafile the author intended the metafile to be when first "placed" on the screen - since the user may have the picture they are viewing in different ZOOMED modes.  However, this extra header makes it so the standard GetMetaFile function will not work correctly.  The format for these "placeable metafiles" has been standardized for a long time and is maintained by Aldus Corporation.  See the Metafile Applications for a list of the applications that support this format.
  82.  
  83. The value of each field is discussed in the Placeable Metafiles section.
  84.  
  85. Placeable Metafiles
  86. The Placeable header is 22 bytes in length and is defined as follows:
  87.  
  88. typedef struct
  89.   {
  90.   DWORD    key;
  91.   HANDLE    hmf;
  92.   RECT    bbox;
  93.   WORD    inch;
  94.   DWORD    reserved;
  95.   WORD    checksum;
  96.   } METAFILEHEADER;
  97.  
  98. These fields have the following  meanings:
  99.  
  100. Field    Definition
  101. key     Binary key that uniquely identifies this file type.  This must be 0x9AC6CDD7L.
  102. hmf    Unused; must be zero.
  103. bbox*    The coordinates of a rectangle that tightly bounds the picture.  These coordinates are in metafile units as defined below.
  104. inch    The number of metafile units to the inch.  To avoid numeric overflow, this value should be less than 1440.  Most applications use 576 or 1000.
  105. reserved    A reserved double word; must be zero.
  106. checksum**    A checksum of the 10 words that precede it, calculated by XORing zero with these 10 words and putting the result in the checksum field (see example below).
  107. metafileData    The actual content of the Windows metafile retrieved by copying the data returned by GetMetafileBits to the file.  The number of bytes should be equal to the MS-DOS file length minus 22.
  108.  
  109. * To determine this value, you need to provide the lowest point (leftmost and topmost) an object is displayed, and the largest point (rightmost and bottommost) an object is displayed.
  110.  
  111. **Example of checksum calculation code:
  112.  
  113. void ComputeMetafileHeaderChecksum(pMFHead)
  114. METAFILEHEADER *pMFHead;
  115. {
  116. WORD *p;
  117.  
  118. for (p =(WORD *)pMFHead,pMFHead -> checksum = 0;
  119.     p < (WORD *)&pMFHead ->checksum; ++p)
  120.        pMFHead ->checksum ^= *p;
  121. }
  122.  
  123. Metafile applications
  124. The following Microsoft applications REQUIRE "placeable" metafiles when reading from disk:
  125.     Power Point*
  126.     WinWord 2.0
  127.     MS Publisher
  128.     MS Draw
  129.     MS Graph
  130.     MS WordArt
  131. Non- MS applications that support "placeable" metafiles:
  132.     Aldus PageMaker
  133.     Micrografx Designer
  134.     Coral Draw
  135.  
  136. There is a sample application in the Win 3.1 SDK called WMFDCODE that demonstrates how to read and display "placeable" metafiles.
  137.  
  138. * PowerPoint will only read in metafiles that are in MM_ANISOTROPIC mode.
  139.  
  140. Creating Metafiles
  141. There are certain rules that every metafile should follow so that they can be accepted and easily manipulated in other applications.  Here are the rules.
  142. 1.    Set a Mapping Mode as one of the first records. Use MM_ANISOTROPIC whenever possible.  Be aware that some applications will only read in metafiles that are in MM_ANISOTROPIC mode.  Object Linking and Embedding (OLE) requires that the presentation format is in MM_ANISOTROPIC mode.
  143. 2.    Set the SetWindowOrg and SetWindowExt functions next.
  144. 3.    Use the Escape Comment function for internal use.
  145. 4.    Do not use SetViewportExt or SetViewportOrg IF you want the user to have the ability to resize or change the aspect ratio the object.
  146. 5.    Do not use any of the functions that start with Get or functions that returns data.
  147. 6.    Do not use any of the Region functions since they are not device independent.
  148. 7.    Use only the functions listed in Windows Reference Manual in the File formats section.  Microsoft Windows 3.0 Programmer's Reference, File Formats section.
  149. 8.    Use StretchBlt or StretchDIB instead of BitBlt.
  150.  
  151. Following these rules will allow you to make metafiles that all other applications can use easily.
  152.  
  153. Displaying Metafiles
  154.  
  155. To display a metafile in your application will vary depending on the source and kind of metafile.  Here are the different kinds, and the order in which we will discuss them.
  156. 1]    Internal - to be used only within the application that created it
  157. 2]    Clipboard & DDE - from another application
  158. 3]    Normal disk based metafile 
  159. 4]    Placeable disk based metafile 
  160.  
  161. Internal metafiles
  162. For internal based metafiles, you use the mapping mode you want and the size in the x and y direction you want.  To create and playback your own internal metafile could be as simple as the following code sample.
  163.  
  164.     HDC    hDC, hMetaDC;
  165.  
  166.     hMetaDC = CreateMetaFile(NULL); // NULL is for a memory metafile
  167.     Rectangle(hMetaDC, 10, 10, 20, 20);
  168.     CloseMetaFile(hMetaDC);
  169.     hDC = GetDC(hWnd);
  170.     PlayMetafile(hDC, hMetaDC);
  171.     ReleaseDC(hWnd, hDC);
  172.     DeleteMetaFile(hMetaDC);
  173.  
  174. If you wanted to get fancy and have the metafile displayed only within a rectangle you set by click and dragging the mouse, you would use the SetViewPortOrg and SetViewPortExt functions.
  175. Here is what the code would look like.
  176.  
  177. case WM_LBUTTONDOWN:
  178.     xStart=LOWORD(lParam);
  179.     yStart=HIWORD(lParam);
  180.     break;
  181.  
  182. case WM_LBUTTONUP:
  183.     xEnd=LOWORD(lParam);
  184.     yEnd=HIWORD(lParam);
  185.     SetViewPortOrg (hDC, xStart, yStart);
  186.     SetViewPortExt (hDC, xEnd - xStart, yEnd - yStart);
  187.     PlayMetaFile(hDC, hMetaDC);
  188.     break;
  189.  
  190. If you wanted the metafile to always be displayed so that the x and y size was always equal, you would set the mapping mode to MM_ISOTROPIC and then play the metafile.
  191.  
  192.  
  193. Clipboard Metafiles
  194. When displaying a metafile obtained from the clipboard, you have to set the display for the metafile based on the values in the METAFILEPICT structure.
  195.  
  196. typedef struct tagMETAFILEPICT {
  197.     int     mm;
  198.     int     xExt, yExt;
  199.     HANDLE  hMF;
  200. } METAFILEPICT;
  201.  
  202. The METAFILEPICT structure has the following fields:
  203.  
  204. mm    Specifies the mapping mode in which the picture is drawn. 
  205. xExt    Specifies the size of the metafile picture for all modes except the MM_ISOTROPIC and MM_ANISOTROPIC modes. The x-extent specifies the width of the rectangle within which the picture is drawn. The coordinates are in units that correspond to the mapping mode.
  206. yExt    Specifies the size of the metafile picture for all modes except the MM_ISOTROPIC and MM_ANISOTROPIC modes. The y-extent specifies the height of the rectangle within which the picture is drawn. The coordinates are in units that correspond to the mapping mode. 
  207. hMF    Identifies a memory metafile. 
  208.  
  209. For MM_ISOTROPIC and MM_ANISOTROPIC modes, which can be scaled, the xExt and yExt fields contain an optional suggested size in MM_HIMETRIC units.  For MM_ANISOTROPIC pictures, xExt and yExt can be zero when no suggested size is supplied.  For MM_ISOTROPIC pictures, an aspect ratio must be supplied even when no suggested size is given. (If a suggested size is given, the aspect ratio is implied by the size.)  To give an aspect ratio without implying a suggested size, set xExt and yExt to negative values whose ratio is the appropriate aspect ratio. The magnitude of the negative xExt and yExt values will be ignored; only the ratio will be used. 
  210.  
  211. Here is a sample function that you would want to use to prepare the display area.
  212.  
  213. void PrepareDisplay (hDC, lpmfp, xClient, yClient)
  214. HDC             hDC ;
  215. LPMETAFILEPICT  lpmfp ;
  216. short           xClient, yClient ;
  217. {
  218.      long       xlScale, ylScale, lScale ;
  219.  
  220.      SetMapMode (hDC, lpmfp->mm) ;
  221.      // The ViewPortOrg is defaulted to 0,0
  222.  
  223.      if (lpmfp->mm == MM_ISOTROPIC || lpmfp->mm == MM_ANISOTROPIC)
  224.      {
  225.         if (lpmfp->xExt == 0)
  226.           SetViewportExt (hDC, xClient, yClient) ;
  227.  
  228.         else if (lpmfp->xExt > 0)  // use MM_HIMETRIC so divide by 100
  229.              SetViewportExt (hDC,
  230.                     (short) ((long) lpmfp->xExt *
  231.                               GetDeviceCaps (hDC, HORZRES) /
  232.                               GetDeviceCaps (hDC, HORZSIZE) / 100),
  233.                     (short) ((long) lpmfp->yExt *
  234.                               GetDeviceCaps (hDC, VERTRES) /
  235.                               GetDeviceCaps (hDC, VERTSIZE) / 100)) ;
  236.  
  237.         else if (lpmfp->xExt < 0)  // preserve the ratio
  238.              {
  239.                xlScale = 100L * (long) xClient *
  240.                          GetDeviceCaps (hDC, HORZSIZE) /
  241.                          GetDeviceCaps (hDC, HORZRES) / -lpmfp->xExt ;
  242.                ylScale = 100L * (long) yClient *
  243.                          GetDeviceCaps (hDC, VERTSIZE) /
  244.                          GetDeviceCaps (hDC, VERTRES) / -lpmfp->yExt ;
  245.                lScale = min (xlScale, ylScale) ;
  246.  
  247.                SetViewportExt (hDC,
  248.                          (short) ((long) -lpmfp->xExt * lScale *
  249.                          GetDeviceCaps (hDC, HORZRES) /
  250.                          GetDeviceCaps (hDC, HORZSIZE) / 100),
  251.                          (short) ((long) -lpmfp->yExt * lScale *
  252.                          GetDeviceCaps (hDC, VERTRES) /
  253.                          GetDeviceCaps (hDC, VERTSIZE) / 100)) ;
  254.              }  // end if < 0
  255.      }  // end if aniso or iso
  256. }
  257.  
  258. Normal disk based metafiles
  259. When displaying a Normal metafile obtained with the GetMetaFile function, you have no idea what mapping mode to use, so you can use a default one, or MM_ANISOTROPIC, or provide a dialog box asking the person what mapping mode they want.  After that, all you do is call the PlayMetaFile function.
  260. The least you should do is use the SetMapMode and SetViewportExt functions so in case the metafile doesn't have this information you will still see something.  
  261.  
  262. Placeable disk based metafiles
  263. After determining that the disk metafile is Placeable, you can set the display based on the values in the PLACEABLEMETAHEADER structure.  
  264.  
  265. Here is a sample function that you would use to prepare the display area.
  266.  
  267.   RECT    bbox;
  268.   WORD    inch;
  269. Now the inch value will tell us how many units per inch there are, and the bbox value will tell us how big we can expect the tightest bounding rectangle will be.  To get an idea of this, if the inch value is 500 and the value in bbox.right is 1500 and bbox.bottom is 1000, then we have an object that was designed to be 3 inches wide by 2 inches tall.  This way, you can use the following table to determine how large you should make the object if you are currently in a different mapping mode.
  270.  
  271. mapping mode    divisor        multiplier
  272. MM_LOMETRIC    10
  273. MM_HIMETRIC    100
  274. MM_TWIPS    14400        254
  275. MM_LOENGLISH    10000        2540
  276. MM_HIENGLISH    10000        254
  277. MM_TEXT        same value passed in
  278.  
  279. If the value in 
  280.  
  281.  
  282.     hDC = GetDC (hWnd) ;
  283.     GetClientRect (hWnd, &rect) ;
  284.     SetMapMode (hDC, mfp.mm) ;
  285.     SetViewportExt (hDC, rect.right, rect.bottom ) ;
  286.  
  287.     if (hMF)  // make sure we have a hMF
  288.       PlayMetaFile (hDC, hMF) ;
  289.     ReleaseDC (hWnd, hDC) ;
  290.  
  291.  
  292. Reading Metafiles
  293.  
  294. Since there are two different kinds of disk metafiles, both with the *.WMF file extension, you have to read the first few bytes and check the header to see if is a Placeable metafile or not.  If is a Placeable metafile, below is a description and sample code on reading the file into memory.
  295. For Normal metafiles, you can use the GetMetaFile function.
  296.  
  297. Using Windows Metafile Functions and Aldus Placeable Metafiles  MS Knowledge Base ID: Q66949
  298.  
  299. Many Windows applications import or export Windows metafiles in a format known as the Aldus Placeable Metafile (APM) format.  In this format, these metafiles cannot be used with the Windows metafile functions such as GetMetaFile(), CopyMetaFile(), PlayMetaFile(), etc.  To use these metafiles, the APM header must be removed from the metafile and the remaining metafile bits must be written to a newly created metafile.
  300.  
  301. More Information:
  302.  
  303. The Placeable header is 22 bytes in length and is defined as follows:
  304.  
  305. struct {
  306.     DWORD    key;
  307.     HANDLE    hmf;
  308.     RECT    bbox;
  309.     WORD    inch;
  310.     DWORD    reserved;
  311.     WORD    checksum;
  312. } APMFILEHEADER;
  313.  
  314. The following code fragment demonstrates how to create a memory-based Windows metafile from an Aldus Placeable Metafile (APM) that will work with the metafile functions provided by Windows. For more information regarding the APM format, please contact the Aldus Developer's Desk at (206)622-5500 (ask for the Third-party Developer's Desk).
  315.  
  316. BOOL RenderPlaceableMetafile (fh)
  317. int   fh; // a file handle to the APM metafile is passed in
  318. {
  319.     HANDLE        hData;
  320.     LPSTR            lpData;
  321.     DWORD            OffsetToMeta;
  322.     METAHEADER        mfHeader;
  323.     APMFILEHEADER    APMHeader;
  324.  
  325.     OffsetToMeta = sizeof(APMHeader);
  326.  
  327.     // Seek to beginning of file and read APM header
  328.     _llseek(fh, 0, 0);
  329.     if (!_lread(fh, (LPSTR)&APMHeader, sizeof(APMFILEHEADER)))
  330.       return(FALSE);  // Error in reading the file
  331.  
  332.     // Return to read metafile header
  333.     _llseek(fh, OffsetToMeta, 0);
  334.     if (!_lread(fh, (LPSTR)&mfHeader, sizeof(METAHEADER)))
  335.       return(FALSE);  // Error in reading the file
  336.  
  337.     // Allocate memory for memory based metafile
  338.     if (!(hData = GlobalAlloc(GHND, (mfHeader.mtSize * 2L))))
  339.       return(FALSE);  // GlobalAlloc failed
  340.     // Were we successful
  341.     if (!(lpData = GlobalLock(hData)))
  342.     {
  343.       // Error in locking memory
  344.       GlobalFree(hData);
  345.       return(FALSE);
  346.     }
  347.  
  348.     _llseek(fh, OffsetToMeta, 0);  // Read metafile bits
  349.     if (!_lread(fh, lpData, (mfHeader.mtSize * 2L)))
  350.     {
  351.       // Error in reading
  352.       GlobalUnlock(hData);
  353.       GlobalFree(hData);
  354.       return(FALSE);
  355.     }
  356.  
  357.     // Create the METAFILE with the bits we read in.
  358.     if (!(hMF = SetMetaFileBits(hData)))
  359.       return(FALSE);
  360.  
  361.     GlobalUnlock(hData);
  362.     _lclose(fh);    // Close the APM file
  363.     return(TRUE);   // Return success
  364.  
  365. }
  366.  
  367. Passing Metafiles via DDE
  368. In order to send a metafile via DDE messages, what you have to do is pass, at the end of the DDEDATA structure, a handle to a global shared data structure of type METAFILEPICT.  The handle to the actual memory metafile is then placed into the hMF field of the METAFILEPICT structure.
  369.  
  370. Passing METAFILEPICT Structures Through DDE  Knowledge Base ID: Q69883
  371.  
  372. When an application sends a WM_DDE_REQUEST message and the server application replies with a WM_DDE_DATA message, the format of the data returned in the Value[] member of the DDEDATA structure is not always the same, but depends on the value of the cfFormat member of the structure.
  373.  
  374. More Information:
  375.  
  376. The server application defines the data returned in the Value[] member of the DDEDATA structure. This data must be in one of the formats used to pass data to the Clipboard. The standard clipboard data formats and a description of their data are defined in the  documentation for the SetClipboardData() function on pages 4-370 and 4-371 in the "Microsoft Windows Software Development Kit Reference Volume 1". For example, for the CF_TEXT format, the actual data is returned from the DDE server, and for the CF_BITMAP format, a handle to the bitmap is sent.
  377.  
  378. The table is not explicit as to what data is returned for the CF_METAFILEPICT data format. The Value[] parameter contains a handle to a global memory block containing a METAFILEPICT structure. To access this structure, call GlobalLock() to get a pointer to the memory and cast the pointer to LPMETAFILEPICT. For example:
  379.  
  380.     LPMETAFILEPICT    lpMFP;
  381.     lpMFP = (LPMETAFILEPICT)GlobalLock(lpDDEData->Value);
  382.  
  383. At this stage, the metafile bits may be copied with the following statement:
  384.  
  385.     hMFBits = CopyMetaFile(lpMFP->hMF, NULL);
  386. You can then use PlayMetaFile function to display the metafile.
  387.  
  388.