第18章 图元文件_18.2 增强型图元文件(emf)(2)

18.2.7 增强型图元文件的查看和打印程序

(1)传递EMF到剪贴板,剪贴板类型应为:CF_ENHMETAFILE

(2)CopyEnhMetaFile用于复制图元文件

(3)剪贴板中的图元文件会自动在老式与增强型图元文件间转换。

(4)自定义函数CreatePaletteFromMetaFile用于从图元文件中创建逻辑调色板。

【EmfView程序】

    

/*------------------------------------------------------------
   EMFVIEW.C -- View Enhanced Metafiles
                 (c) Charles Petzold, 1998
  ------------------------------------------------------------*/

#include <windows.h>
#include "resource.h"
 
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

TCHAR szAppName[] = TEXT("EmfView");
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;
     HACCEL       hAccel;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = szAppName ;
     wndclass.lpszClassName = szAppName ;

     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     
     hwnd = CreateWindow (szAppName,                  // window class name
                          TEXT ("Enhanced Metafile Viewer"), // window caption
                          WS_OVERLAPPEDWINDOW,        // window style
                          CW_USEDEFAULT,              // initial x position
                          CW_USEDEFAULT,              // initial y position
                          CW_USEDEFAULT,              // initial x size
                          CW_USEDEFAULT,              // initial y size
                          NULL,                       // parent window handle
                          NULL,                       // window menu handle
                          hInstance,                  // program instance handle
                          NULL) ;                     // creation parameters
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;
     
     hAccel = LoadAccelerators(hInstance, szAppName);

     while (GetMessage (&msg, NULL, 0, 0))
     {
         if (!TranslateAccelerator(hwnd,hAccel,&msg))
         {
             TranslateMessage(&msg);
             DispatchMessage(&msg);
         }
     }
     return msg.wParam ;
}

HPALETTE CreatePaletteFromMetaFile(HENHMETAFILE hemf)
{
    HPALETTE hPalette;
    LOGPALETTE* plp;
    int iNum;

    if (!hemf)
        return NULL;
    
    //获取图元文件中的调色板数目
    if (0 == (iNum = GetEnhMetaFilePaletteEntries(hemf, 0, NULL)))
        return NULL;

    plp = malloc(sizeof(LOGPALETTE)+(iNum - 1)*sizeof(PALETTEENTRY));

    plp->palVersion = 0x0300;
    plp->palNumEntries = iNum;
    
    GetEnhMetaFilePaletteEntries(hemf, iNum, plp->palPalEntry); //获取图元文件调色板中的各个颜色条目

    hPalette = CreatePalette(plp);
    free(plp);

    return hPalette;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static OPENFILENAME ofn;
     static TCHAR   szFilter[] = TEXT("Enhanced Metafiles(*.EMF)*.emf")
                                 TEXT("All Files (*.*)*.*");
     static TCHAR  szFileName[MAX_PATH], szTitleName[MAX_PATH];

     static DOCINFO di = { sizeof(DOCINFO), TEXT("EmfView:Printing") };
     static PRINTDLG printdlg = { sizeof(PRINTDLG) };
     BOOL   bSuccess;
     HDC    hdcPrn;

     HDC         hdc ;
     PAINTSTRUCT ps ;
     RECT        rect ;

     static HENHMETAFILE hemf;
     HENHMETAFILE  hemfCopy;

     HMENU hMenu;
     int iEnable;
     HPALETTE hPalette;

     PTSTR pBuffer;
     int i,iLength;
     ENHMETAHEADER header;

     switch (message)
     {
     case WM_CREATE:
          
          //初始化OPENFILENAME结构体
          memset(&ofn, 0, sizeof(OPENFILENAME));
          ofn.lStructSize = sizeof(OPENFILENAME);
          ofn.hwndOwner = hwnd;
          ofn.lpstrFilter = szFilter;
          ofn.lpstrFile = szFileName;
          ofn.nMaxFile = MAX_PATH;
          ofn.lpstrFileTitle = szTitleName;
          ofn.nMaxFileTitle = MAX_PATH;
          ofn.lpstrDefExt = TEXT("emf");
          return 0 ;

     case WM_INITMENUPOPUP:
          hMenu = GetMenu(hwnd);

          iEnable = hemf ? MF_ENABLED : MF_GRAYED;

          EnableMenuItem(hMenu, IDM_FILE_SAVE_AS, iEnable);
          EnableMenuItem(hMenu, IDM_FILE_PRINT, iEnable);
          EnableMenuItem(hMenu, IDM_FILE_PROPERTIES, iEnable);
          EnableMenuItem(hMenu, IDM_EDIT_CUT, iEnable);
          EnableMenuItem(hMenu, IDM_EDIT_COPY, iEnable);
          EnableMenuItem(hMenu, IDM_EDIT_DELETE, iEnable);

          EnableMenuItem(hMenu, IDM_EDIT_PASTE, 
                               IsClipboardFormatAvailable(CF_ENHMETAFILE) ? MF_ENABLED : MF_GRAYED);
          return 0;

     case WM_COMMAND:
         switch (LOWORD(wParam))
         {
         case IDM_FILE_OPEN:
             //显示打开文件对话框
             ofn.Flags = 0;
             if (!GetOpenFileName(&ofn))
                 return 0;

             //如果emf文件己经在内存中,则删掉。
             if (hemf)
             {
                 DeleteEnhMetaFile(hemf);
                 hemf = NULL;
             }

             //加载emf文件
             SetCursor(LoadCursor(NULL, IDC_WAIT));
             ShowCursor(TRUE);

             hemf = GetEnhMetaFile(szFileName);

             ShowCursor(FALSE);
             SetCursor(LoadCursor(NULL, IDC_ARROW));

             //发送重绘客户区消息
             InvalidateRect(hwnd, NULL, TRUE);

             if (hemf == NULL)
             {
                 MessageBox(hwnd, TEXT("Cannot load metafile"), szAppName, MB_ICONEXCLAMATION | MB_OK);
             }

             return 0;

         case IDM_FILE_PRINT:
             if (!hemf)
                 return 0;

             //显示打印对话框
             printdlg.Flags = PD_RETURNDC | PD_NOPAGENUMS | PD_NOSELECTION;

             if (!PrintDlg(&printdlg))
                 return 0;

             if (NULL ==(hdcPrn=printdlg.hDC))
             {
                 MessageBox(hwnd, TEXT("Cannot obtain printer DC"), 
                                  szAppName, MB_ICONEXCLAMATION | MB_OK);
                 return 0;
             }

             //获得页面的可打印区域
             rect.left = 0;
             rect.right = GetDeviceCaps(hdcPrn, HORZRES);
             rect.top = 0;
             rect.bottom = GetDeviceCaps(hdcPrn, VERTRES);

             bSuccess = FALSE;

             //在打印机设备中回放emf文件,即将emf绘制在打印机上
             SetCursor(LoadCursor(NULL, IDC_WAIT));
             ShowCursor(TRUE);

             if (StartDoc(hdcPrn,&di)>0 && (StartPage(hdcPrn)>0))
             {
                 PlayEnhMetaFile(hdcPrn, hemf, &rect);
                 if (EndPage(hdcPrn)>0)
                 {
                     bSuccess = TRUE;
                     EndDoc(hdcPrn);
                 }
             }

             ShowCursor(FALSE);
             SetCursor(LoadCursor(NULL, IDC_ARROW));

             DeleteDC(hdcPrn);

             if (!bSuccess)
             {
                 MessageBox(hwnd, TEXT("Could not print metafile"),
                     szAppName, MB_ICONEXCLAMATION | MB_OK);
             }

             return 0;

         case IDM_FILE_PROPERTIES:
             if (!hemf)
                 return 0;

             iLength = GetEnhMetaFileDescription(hemf, 0, NULL); //字符串的字符个数
             pBuffer = malloc((iLength + 256)*sizeof(TCHAR));

             GetEnhMetaFileHeader(hemf, sizeof(ENHMETAHEADER), &header);

             //格式化头记录信息
             i = wsprintf(pBuffer, TEXT("Bounds =(%i,%i,) to (%i,%i) pixels
"),
                                         header.rclBounds.left,header.rclBounds.top,
                                         header.rclBounds.right,header.rclBounds.bottom);

             i += wsprintf(pBuffer+i, TEXT("Frame =(%i,%i,) to (%i,%i) mms
"),
                                         header.rclFrame.left, header.rclFrame.top,
                                         header.rclFrame.right, header.rclFrame.bottom);

             i += wsprintf(pBuffer + i, TEXT("Resolution =(%i,%i,) pixels ")
                                        TEXT(" =(%i,%i,) mms
"),
                                      header.szlDevice.cx,header.szlDevice.cy,
                                      header.szlMillimeters.cx,header.szlMillimeters.cy);

             i += wsprintf(pBuffer + i, TEXT("Size = %i, Recoreds = %i, ")
                                        TEXT("Handles = %i, Palette Entries = %i
"),
                                        header.nBytes,header.nRecords,
                                        header.nHandles,header.nPalEntries);
             if (iLength)
             {
                 i += wsprintf(pBuffer + i, TEXT("Description = "));
                 GetEnhMetaFileDescription(hemf, iLength, pBuffer + i); //注意缓冲区大小为iLength

                 // pBuffer内容形如"......EMF3EMF3 Demo #3"。MessageBox遇时会结束,为了
                 //防止出现这种情况,可将把字符串中间出现的替换成空格。此字符串中间只有一个,
                 //又因lstrlen()函数会计算字符串长度,也是遇结束并且长度不含,可用
                 //如下代码替换。
                 pBuffer[lstrlen(pBuffer)] = TEXT(' ');
             }

             MessageBox(hwnd, pBuffer, TEXT("Metafile Properties"), MB_OK);

             free(pBuffer);

             return 0;

         case IDM_FILE_SAVE_AS:
             if (!hemf)
                 return 0;

             //打开“保存”文件对话框
             ofn.Flags = OFN_OVERWRITEPROMPT;

             if (!GetSaveFileName(&ofn))
                 return 0;

             //保存emf到磁盘
             SetCursor(LoadCursor(NULL, IDC_WAIT));
             ShowCursor(TRUE);

             //CopyEnhMetaFile将hemf拷到szFileName指定的文件中,并返回副本的句柄.
             hemfCopy = CopyEnhMetaFile(hemf, szFileName);

             ShowCursor(FALSE);
             SetCursor(LoadCursor(NULL, IDC_ARROW));

             if (hemfCopy)
             {
                 DeleteEnhMetaFile(hemf);
                 hemf = hemfCopy; //将拷贝的副本设为当前的图元文件
             }
             else
                 MessageBox(hwnd, TEXT("Cannot Save metafile"), szAppName, MB_ICONEXCLAMATION | MB_OK);

             return 0;

         case IDM_EDIT_COPY:
         case IDM_EDIT_CUT:

             if (!hemf)
                 return 0;

             hemfCopy = CopyEnhMetaFile(hemf, NULL);
             OpenClipboard(hwnd);
             EmptyClipboard();
             SetClipboardData(CF_ENHMETAFILE, hemfCopy);
             CloseClipboard();

             if (LOWORD(wParam) == IDM_EDIT_COPY)
                 return 0;

                 //如果是剪切,则继续执行下去。

         case IDM_EDIT_DELETE:
             if (hemf)
             {
                 DeleteEnhMetaFile(hemf);
                 hemf = NULL;
                 InvalidateRect(hwnd, NULL, TRUE);
             }
             return 0;

         case IDM_EDIT_PASTE:
             OpenClipboard(hwnd);
             hemfCopy = GetClipboardData(CF_ENHMETAFILE);
             CloseClipboard();

             if (hemfCopy && hemf)
             {
                 DeleteEnhMetaFile(hemf);
                 hemf = NULL;
             }

             hemf = CopyEnhMetaFile(hemfCopy, NULL);
             DeleteEnhMetaFile(hemfCopy);

             InvalidateRect(hwnd, NULL, TRUE);
             return 0;

         case IDM_APP_EXIT:
             SendMessage(hwnd, WM_CLOSE, 0, 0);
             return 0;

         case IDM_APP_ABOUT:
             MessageBox(hwnd, TEXT("Enhanced Metafile Viewer
")
                              TEXT("(c)Charles Petzold,1998"),
                        szAppName,MB_OK);
             return 0;
         }

     case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
          
          GetClientRect (hwnd, &rect) ;
          
          if (hemf)
          {
              if (hPalette = CreatePaletteFromMetaFile(hemf))
              {
                  SelectPalette(hdc, hPalette, FALSE);
                  RealizePalette(hdc);
              }

              //回放图元文件
              PlayEnhMetaFile(hdc, hemf, &rect);

              if (hPalette)
              {
                  DeleteObject(hPalette);
              }
          }
          
          EndPaint (hwnd, &ps) ;
          return 0 ;
        
     case WM_QUERYNEWPALETTE: //窗口被激活时,收到此消息
          if (!hemf || !(hPalette = CreatePaletteFromMetaFile(hemf)))
             return FALSE;

          hdc = GetDC(hwnd);
          SelectPalette(hdc, hPalette, FALSE);
          RealizePalette(hdc);
          InvalidateRect(hwnd, NULL, FALSE);

          DeleteObject(hPalette);
          ReleaseDC(hwnd, hdc);
          return TRUE;

     case WM_PALETTECHANGED:
         if ((HWND)wParam == hwnd)
             break;
           
         if (!hemf || !(hPalette = CreatePaletteFromMetaFile(hemf)))
             break;

         hdc = GetDC(hwnd);
         SelectPalette(hdc, hPalette, FALSE);
         RealizePalette(hdc);
         UpdateColors(hdc);

         DeleteObject(hPalette);
         ReleaseDC(hwnd, hdc);
         break;

     case WM_DESTROY:
         if (hemf)
             DeleteEnhMetaFile(hemf);
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 EmfView.rc 使用
//

#define IDM_FILE_OPEN                   40001
#define IDM_FILE_SAVE_AS                40002
#define IDM_FILE_PRINT                  40003
#define IDM_FILE_PROPERTIES             40004
#define IDM_APP_EXIT                    40005
#define IDM_EDIT_CUT                    40006
#define IDM_EDIT_COPY                   40007
#define IDM_EDIT_PASTE                  40008
#define IDM_EDIT_DELETE                 40009
#define IDM_APP_ABOUT                   40010

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40019
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//EmfView.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""
"
    ""
END

3 TEXTINCLUDE 
BEGIN
    "
"
    ""
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

EMFVIEW MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Open	Ctrl+O",               IDM_FILE_OPEN
        MENUITEM "Save &As...",                 IDM_FILE_SAVE_AS
        MENUITEM "&Print...	Ctrl+P",           IDM_FILE_PRINT
        MENUITEM SEPARATOR
        MENUITEM "&Peroperties",                IDM_FILE_PROPERTIES
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       IDM_APP_EXIT
    END
    POPUP "&Edit"
    BEGIN
        MENUITEM "Cu&t	Ctrl+X",                IDM_EDIT_CUT
        MENUITEM "&Copy	Ctrl+C",               IDM_EDIT_COPY
        MENUITEM "&Paste	Ctrl+V",              IDM_EDIT_PASTE
        MENUITEM "&Delete	Del",                IDM_EDIT_DELETE
    END
    POPUP "Help"
    BEGIN
        MENUITEM "&About EmfView...",           IDM_APP_ABOUT
    END
END


/////////////////////////////////////////////////////////////////////////////
//
// Accelerator
//

EMFVIEW ACCELERATORS
BEGIN
    "C",            IDM_EDIT_COPY,          VIRTKEY, CONTROL, NOINVERT
    "O",            IDM_FILE_OPEN,          VIRTKEY, CONTROL, NOINVERT
    "P",            IDM_FILE_PRINT,         VIRTKEY, CONTROL, NOINVERT
    "V",            IDM_EDIT_PASTE,         VIRTKEY, CONTROL, NOINVERT
    VK_DELETE,      IDM_EDIT_DELETE,        VIRTKEY, NOINVERT
    "X",            IDM_EDIT_CUT,           VIRTKEY, CONTROL, NOINVERT
END

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

18.2.8 显示精确尺寸的图元文件

(1)图元文件的好处是可自由缩放,但有时需要按特定尺寸或纵横比来显示。如本例中的为了保持6英寸宽和1英寸高的刻尺大小不变,必须保证其显示区域的大小不变,即也是6×1英寸。

(2)本例中CreateEnhMetaFile的第1个参数为NULL,所以创建出来的图元文件的设备环境是以视频显示设备作为参考设备环境的。所以调用GetDeviceCaps得到的实际上是视频显示的设备环境信息。

(3)画6英寸宽和1英寸高的刻度尺,需要知道设备的分辨率。因为刻度尺的边界大小是以视频DC为参考被写进ENHMETAHEADER的rclBounds字段的(单位是像素),所以在如果按该字段设置的显示矩形,在打印机上显示出来的刻度尺将明显变小。而rclFrame指定了图像的物理尺寸(单位0.01mm),可以通过该字段与目标设备环境结合,来换算出图像的像素大小。从而保证,在不同的设备环境都能正确显示较为真实的刻度尺出来。

【Emf8和Emf9程序】

 //Emf.c——是本程序的框架,也是后续程序的框架

/*------------------------------------------------------------
   EMF.C -- Enhanced Metafile Demostration Shell Program
                 (c) Charles Petzold, 1998
  ------------------------------------------------------------*/

#include <windows.h>
#include "resource.h"

extern void CreateRoutine(HWND);
extern void PaintRoutine(HWND, HDC, int, int);
extern  TCHAR szClass[];
extern  TCHAR szTitle[];

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

HANDLE hInst;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;
     TCHAR szResource[] = TEXT("EMF");
     hInst = hInstance;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = szResource ;
     wndclass.lpszClassName = szClass;

     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
                      szClass, MB_ICONERROR) ;
          return 0 ;
     }
     
     hwnd = CreateWindow (szClass,                  // window class name
                          szTitle, // window caption
                          WS_OVERLAPPEDWINDOW,        // window style
                          CW_USEDEFAULT,              // initial x position
                          CW_USEDEFAULT,              // initial y position
                          CW_USEDEFAULT,              // initial x size
                          CW_USEDEFAULT,              // initial y size
                          NULL,                       // parent window handle
                          NULL,                       // window menu handle
                          hInstance,                  // program instance handle
                          NULL) ;                     // creation parameters
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;
     
     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}

BOOL PrintRoutine(HWND hwnd)
{
    static DOCINFO di;
    static PRINTDLG printdlg = { sizeof(PRINTDLG) };
    static TCHAR szMessage[32];

    BOOL  bSuccess = FALSE;
    HDC hdcPrn;
    int cxPage, cyPage;

    printdlg.Flags = PD_RETURNDC | PD_NOPAGENUMS | PD_NOSELECTION;

    if (!PrintDlg(&printdlg))
        return TRUE;

    if (NULL == (hdcPrn = printdlg.hDC))
        return FALSE;

    cxPage = GetDeviceCaps(hdcPrn, HORZRES);
    cyPage = GetDeviceCaps(hdcPrn, VERTRES);

    lstrcpy(szMessage, szClass);
    lstrcat(szMessage, TEXT(": Printing"));

    di.cbSize = sizeof(DOCINFO);
    di.lpszDocName = szMessage;

    if (StartDoc(hdcPrn,&di)>0)
    {
        if (StartPage(hdcPrn) > 0)
        {
            PaintRoutine(hwnd, hdcPrn, cxPage, cyPage);
            if (EndPage(hdcPrn)>0)
            {
                EndDoc(hdcPrn);
                bSuccess = TRUE;
            }
        }

    }

    DeleteDC(hdcPrn);
    return bSuccess;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     HDC         hdc ;
     PAINTSTRUCT ps ;
     BOOL        bSuccess;
     static int cxClient, cyClient;

     switch (message)
     {
     case WM_CREATE:
          CreateRoutine(hwnd);
          return 0 ;

     case WM_SIZE:
         cxClient = LOWORD(lParam);
         cyClient = HIWORD(lParam);
         return 0;

     case WM_COMMAND:
         switch (LOWORD(wParam))
         {
         case IDM_PRINT:
             SetCursor(LoadCursor(NULL, IDC_WAIT));
             ShowCursor(TRUE);

             bSuccess = PrintRoutine(hwnd);

             ShowCursor(FALSE);
             SetCursor(LoadCursor(NULL, IDC_ARROW));

             if (!bSuccess)
                 MessageBox(hwnd, TEXT("Error encountered during printing"),
                            szClass, MB_ICONASTERISK | MB_OK);

             return 0;
            
         case IDM_EXIT:
             SendMessage(hwnd, WM_CLOSE, 0, 0);
             return 0;

         case IDM_ABOUT:
             MessageBox(hwnd, TEXT("Enhanced Metafile Demo Program
")
                              TEXT("Copyright(c) Charles Petzold,1998"),
                        szClass,MB_ICONINFORMATION | MB_OK);
             return 0;

         }
         break;

     case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
          
          PaintRoutine(hwnd, hdc, cxClient, cyClient);

          EndPaint (hwnd, &ps) ;
          return 0 ;
          
     case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}

//Emf8(与课本的Emf9合并了)

/*--------------------------------------------------
   EMF8 —— Enhanced Metafile Demo #8
           (c)Charles Petzold,1998
----------------------------------------------------*/
#pragma warning(disable: 4996) //win8.1以上GetVersion己过时,加上这句关闭
#include <windows.h>

TCHAR szClass[] = TEXT("EMF8");
TCHAR szTitle[] = TEXT("EMF8: Enhanced Metafile Demo #8");

void DrawRuler(HDC hdc, int cx, int cy)
{
    int iAdj,i,iHeight;
    LOGFONT lf;
    TCHAR  ch;

    iAdj = GetVersion() & 0x80000000 ? 0 : 1;

    //创建1磅宽的黑色画笔 cx为6英寸,因为1英寸=72磅,所以6英寸/72=6磅,再除以6得1磅
    SelectObject(hdc, CreatePen(PS_SOLID, cx / 72 / 6, 0));

    //画围绕整个标尺的矩形
    Rectangle(hdc, iAdj, iAdj, cx + iAdj + 1, cy + iAdj + 1);


    //刻度尺,共6英寸,每1/16英寸(即6/96)为一个刻度。
    for ( i = 1; i < 96; i++)
    {
        if (i % 16 == 0)     iHeight = cy / 2; //每隔1英寸,高度为cy/2
        else if (i % 8 == 0) iHeight = cy / 3; //每隔1/2英寸,高度为cx/3;
        else if (i % 4 == 0) iHeight = cy / 5; //每隔1/4英寸,高度为cx/5;
        else if (i % 2 == 0) iHeight = cy / 8; //每隔1/8英寸,高度为cx/8;
        else                 iHeight = cy / 12; //每隔1/16英寸,高度为cx/12;
        MoveToEx(hdc, i*cx / 96, cy, NULL);
        LineTo(hdc, i*cx / 96, cy - iHeight);
    }

    //创建逻辑字体,FillMemory最终也是调用memset函数
    FillMemory(&lf, sizeof(lf), 0); //结构体各字段初始化为0;
    lf.lfHeight = cy / 2; //字体高度为0.5英寸()
    lstrcpy(lf.lfFaceName, TEXT("Times New Roman"));

    SelectObject(hdc, CreateFontIndirect(&lf));
    SetTextAlign(hdc, TA_BOTTOM | TA_CENTER);
    SetBkMode(hdc, TRANSPARENT);

    //显示数字
    for (i = 1; i <=5; i++)
    {
        ch = (TCHAR)(i + '0');
        TextOut(hdc, i*cx / 6, cy / 2, &ch, 1);
    }

    //清除
    DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
    DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));
}

void CreateRoutine(HWND hwnd)
{
    HDC hdcEMF;
    HENHMETAFILE hemf;
    int cxMms, cyMms, cxPix, cyPix, xDpi, yDpi;

    //注意,第1个参数为NULL,会将视频显示设备环境作为“参考设备环境”
    hdcEMF = CreateEnhMetaFile(NULL, TEXT("emf8.emf"), NULL, 
                                TEXT("EMF8EMF Demo #8"));

    if (hdcEMF == NULL)
        return;

    //因为CreateEnhMetaFile的第1个参数为NULL,所以尽管以下以hdcEMF为参数,
    //但实际上得到的却是视频设备环境的DC的信息
    cxMms = GetDeviceCaps(hdcEMF, HORZSIZE); //以毫米为单位
    cyMms = GetDeviceCaps(hdcEMF, VERTSIZE);
    cxPix = GetDeviceCaps(hdcEMF, HORZRES);//像素规模(单位:像素)
    cyPix = GetDeviceCaps(hdcEMF, VERTRES);

    //分辨率
    xDpi = 254 * cxPix / cxMms / 10;//25.4 * cxPix / cxMms; //单位:像素/英寸
    yDpi = 254 * cyPix / cyMms / 10;// 25.4*  cyPix / cyMms;

    //宽6*xDpi表示6英寸,高1英寸
    DrawRuler(hdcEMF, 6 * xDpi, yDpi); //传入的数值为6英寸内的像素总量和1英寸内的像素总量

    hemf = CloseEnhMetaFile(hdcEMF);
    DeleteEnhMetaFile(hemf);
}

void PaintRoutine(HWND hwnd, HDC hdc, int cxArea, int cyArea)
{

    ENHMETAHEADER emh;
    HENHMETAFILE hemf;
    int cxImage, cyImage,cxMms,cyMms,cxPix,cyPix;
    RECT rect;

    hemf = GetEnhMetaFile(TEXT("emf8.emf"));

    GetEnhMetaFileHeader(hemf, sizeof(emh), &emh);



    /*方案1——利用图像的像素尺寸
      通过rclBounds字段(是设备单位:像素)显示出来的刻度尺,因本例指定图元文件
    的参考设备为视频DC,所以在视频显示器显示正常,但在打印中,刻度尺明显变小。
    */

    //cxImage = emh.rclBounds.right - emh.rclBounds.left;
    //cyImage = emh.rclBounds.bottom - emh.rclBounds.top;

    /*方案2——利用图像的物理尺寸
       通过rclFrame字段(是设备单位:0.01mm)显示出来的刻度尺,这样不管在视频显示器
    还是打印机上,显示出来的刻度尺都较为真实
    */

    //目标设备信息
    cxMms = GetDeviceCaps(hdc,HORZSIZE); //宽度(单位:mm)
    cyMms = GetDeviceCaps(hdc, VERTSIZE);//高度(单位:mm)
    cxPix = GetDeviceCaps(hdc, HORZRES);//宽度(单位:像素)
    cyPix = GetDeviceCaps(hdc, VERTRES);//高度(单位:像素)

    cxImage = emh.rclFrame.right - emh.rclFrame.left; //单位:0.01mm
    cyImage = emh.rclFrame.bottom - emh.rclFrame.top;

    //将图元文件大小(0.01mm为单位)转换为像素大小
    cxImage = cxImage* cxPix / cxMms / 100;
    cyImage = cyImage* cyPix / cyMms / 100;

    //在指定的矩形区内,水平和垂直居中显示图元文件,同时保证了区域的大小为cxImage和cyImage
    rect.left = (cxArea - cxImage) / 2;
    rect.right = (cxArea + cxImage) / 2;
    rect.top = (cyArea - cyImage) / 2;
    rect.bottom = (cyArea + cyImage) / 2;

    PlayEnhMetaFile(hdc, hemf, &rect); //回放(绘制)图元文件

    DeleteEnhMetaFile(hemf);
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 Emf8.rc 使用
//

#define IDM_PRINT                       40001
#define IDM_EXIT                        40002
#define IDM_ABOUT                       40003

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40004
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//Emf8.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""
"
    ""
END

3 TEXTINCLUDE 
BEGIN
    "
"
    ""
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

EMF MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Print...",                   IDM_PRINT
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       IDM_EXIT
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&About...",                   IDM_ABOUT
    END
END

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

18.2.9 缩放比和纵横比

(1)本例是在Emf9的基础上改进的,刻度尺大小可以改变,但纵横比约束为6:1

 (2) 取客户区宽度和高度与原图像宽度和高度的比值,最小者为最终图像纵横比

【Emf10程序】

 

/*--------------------------------------------------
   EMF10 —— Enhanced Metafile Demo #10
           (c)Charles Petzold,1998
----------------------------------------------------*/
#pragma warning(disable: 4996) //win8.1以上GetVersion己过时,加上这句关闭
#include <windows.h>

TCHAR szClass[] = TEXT("EMF10");
TCHAR szTitle[] = TEXT("EMF10: Enhanced Metafile Demo #10");

void DrawRuler(HDC hdc, int cx, int cy)
{
    int iAdj,i,iHeight;
    LOGFONT lf;
    TCHAR  ch;

    iAdj = GetVersion() & 0x80000000 ? 0 : 1;

    //创建1磅宽的黑色画笔 cx为6英寸,因为1英寸=72磅,所以6英寸/72=6磅,再除以6得1磅
    SelectObject(hdc, CreatePen(PS_SOLID, cx / 72 / 6, 0));

    //画围绕整个标尺的矩形
    Rectangle(hdc, iAdj, iAdj, cx + iAdj + 1, cy + iAdj + 1);


    //刻度尺,共6英寸,每1/16英寸(即6/96)为一个刻度。
    for ( i = 1; i < 96; i++)
    {
        if (i % 16 == 0)     iHeight = cy / 2; //每隔1英寸,高度为cy/2
        else if (i % 8 == 0) iHeight = cy / 3; //每隔1/2英寸,高度为cx/3;
        else if (i % 4 == 0) iHeight = cy / 5; //每隔1/4英寸,高度为cx/5;
        else if (i % 2 == 0) iHeight = cy / 8; //每隔1/8英寸,高度为cx/8;
        else                 iHeight = cy / 12; //每隔1/16英寸,高度为cx/12;
        MoveToEx(hdc, i*cx / 96, cy, NULL);
        LineTo(hdc, i*cx / 96, cy - iHeight);
    }

    //创建逻辑字体,FillMemory最终也是调用memset函数
    FillMemory(&lf, sizeof(lf), 0); //结构体各字段初始化为0;
    lf.lfHeight = cy / 2; //字体高度为0.5英寸()
    lstrcpy(lf.lfFaceName, TEXT("Times New Roman"));

    SelectObject(hdc, CreateFontIndirect(&lf));
    SetTextAlign(hdc, TA_BOTTOM | TA_CENTER);
    SetBkMode(hdc, TRANSPARENT);

    //显示数字
    for (i = 1; i <=5; i++)
    {
        ch = (TCHAR)(i + '0');
        TextOut(hdc, i*cx / 6, cy / 2, &ch, 1);
    }

    //清除
    DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
    DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));
}

void CreateRoutine(HWND hwnd)
{
    HDC hdcEMF;
    HENHMETAFILE hemf;
    int cxMms, cyMms, cxPix, cyPix, xDpi, yDpi;

    //注意,第1个参数为NULL,会将视频显示设备环境作为“参考设备环境”
    hdcEMF = CreateEnhMetaFile(NULL, TEXT("emf10.emf"), NULL, 
                                TEXT("EMF10EMF Demo #10"));

    if (hdcEMF == NULL)
        return;

    //因为CreateEnhMetaFile的第1个参数为NULL,所以尽管以下以hdcEMF为参数,
    //但实际上得到的却是视频设备环境的DC的信息
    cxMms = GetDeviceCaps(hdcEMF, HORZSIZE); //以毫米为单位
    cyMms = GetDeviceCaps(hdcEMF, VERTSIZE);
    cxPix = GetDeviceCaps(hdcEMF, HORZRES);//像素规模(单位:像素)
    cyPix = GetDeviceCaps(hdcEMF, VERTRES);

    //分辨率
    xDpi = 254 * cxPix / cxMms / 10;//25.4 * cxPix / cxMms; //单位:像素/英寸
    yDpi = 254 * cyPix / cyMms / 10;// 25.4*  cyPix / cyMms;

    //宽6*xDpi表示6英寸,高1英寸
    DrawRuler(hdcEMF, 6 * xDpi, yDpi); //传入的数值为6英寸内的像素总量和1英寸内的像素总量

    hemf = CloseEnhMetaFile(hdcEMF);
    DeleteEnhMetaFile(hemf);
}

void PaintRoutine(HWND hwnd, HDC hdc, int cxArea, int cyArea)
{

    ENHMETAHEADER emh;
    HENHMETAFILE hemf;
    int cxImage, cyImage,cxMms,cyMms,cxPix,cyPix;
    RECT rect;
    float fScale;
    hemf = GetEnhMetaFile(TEXT("emf10.emf"));

    GetEnhMetaFileHeader(hemf, sizeof(emh), &emh);

    /*利用图像的物理尺寸
       通过rclFrame字段(是设备单位:0.01mm)显示出来的刻度尺,这样不管在视频显示器
    还是打印机上,显示出来的刻度尺都较为真实
    */

    //目标设备信息
    cxMms = GetDeviceCaps(hdc,HORZSIZE); //宽度(单位:mm)
    cyMms = GetDeviceCaps(hdc, VERTSIZE);//高度(单位:mm)
    cxPix = GetDeviceCaps(hdc, HORZRES);//宽度(单位:像素)
    cyPix = GetDeviceCaps(hdc, VERTRES);//高度(单位:像素)

    cxImage = emh.rclFrame.right - emh.rclFrame.left; //单位:0.01mm
    cyImage = emh.rclFrame.bottom - emh.rclFrame.top;

    //将图元文件大小(0.01mm为单位)转换为像素大小
    cxImage = cxImage* cxPix / cxMms / 100;
    cyImage = cyImage* cyPix / cyMms / 100;

    //约束纵横比,会将cxImage或cyImage调整成客户区大小,但cxImage:cyImage保持6:1
    fScale = min((float)cxArea / cxImage, (float)cyArea / cyImage);
    cxImage = (int)(fScale*cxImage);  //即cxImage和cyImage同时按fScale这个系数缩放
    cyImage = (int)(fScale*cyImage);  //也就可以保持纵横比保持不变(即原来的6:1)

    //在指定的矩形区内,水平和垂直居中显示图元文件,同时保证了区域的大小为cxImage和cyImage
    rect.left = (cxArea - cxImage) / 2;
    rect.right = (cxArea + cxImage) / 2;
    rect.top = (cyArea - cyImage) / 2;
    rect.bottom = (cyArea + cyImage) / 2;

    PlayEnhMetaFile(hdc, hemf, &rect); //回放(绘制)图元文件

    DeleteEnhMetaFile(hemf);
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 Emf11.rc 使用
//

#define IDM_PRINT                       40001
#define IDM_EXIT                        40002
#define IDM_ABOUT                       40003

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40004
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//Emf10.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""
"
    ""
END

3 TEXTINCLUDE 
BEGIN
    "
"
    ""
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

EMF MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Print...",                   IDM_PRINT
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       IDM_EXIT
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&About...",                   IDM_ABOUT
    END
END

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

18.2.10 图元文件中的映射模式——在图元文件内部使用映射模式

(1)本例中SetMapMode会被写进图元文件中,在CreateRoutine设置,省去了通过GetDeviceCaps求分辨率的过程

(2)设置映射模式为MM_LOENGLISH,即逻辑单位的坐标为0.01英寸。要画6×1英寸的刻度尺,即需要传给DrawRuler的参数为600×100

(3)MM_LOENGLISH中,纵坐标向上增加,会影响MoveToEx和LineTo等画刻度线函数。

【Emf11程序】——效果与Emf9基本一样。

/*--------------------------------------------------
   EMF11 —— Enhanced Metafile Demo #11
           (c)Charles Petzold,1998
----------------------------------------------------*/
#pragma warning(disable: 4996) //win8.1以上GetVersion己过时,加上这句关闭
#include <windows.h>

TCHAR szClass[] = TEXT("EMF11");
TCHAR szTitle[] = TEXT("EMF11: Enhanced Metafile Demo #11");

void DrawRuler(HDC hdc, int cx, int cy)
{
    int i,iHeight;
    LOGFONT lf;
    TCHAR  ch;

    //创建1磅宽的黑色画笔 cx为6英寸,因为1英寸=72磅,所以6英寸/72=6磅,再除以6得1磅
    SelectObject(hdc, CreatePen(PS_SOLID, cx / 72 / 6, 0)); //画笔宽度:为逻辑单位

    //画围绕整个标尺的矩形
    if(GetVersion() & 0x80000000)  //windows 98
        Rectangle(hdc, 0, -2, cx + 2, cy);
    else
        Rectangle(hdc, 0, -1, cx + 1, cy);


    //刻度尺,共6英寸,每1/16英寸(即6/96)为一个刻度。
    for ( i = 1; i < 96; i++)
    {
        if (i % 16 == 0)     iHeight = cy / 2; //每隔1英寸,高度为cy/2
        else if (i % 8 == 0) iHeight = cy / 3; //每隔1/2英寸,高度为cx/3;
        else if (i % 4 == 0) iHeight = cy / 5; //每隔1/4英寸,高度为cx/5;
        else if (i % 2 == 0) iHeight = cy / 8; //每隔1/8英寸,高度为cx/8;
        else                 iHeight = cy / 12; //每隔1/16英寸,高度为cx/12;
        MoveToEx(hdc, i*cx / 96, 0, NULL);
        LineTo(hdc, i*cx / 96, iHeight);
    }

    //创建逻辑字体,FillMemory最终也是调用memset函数
    FillMemory(&lf, sizeof(lf), 0); //结构体各字段初始化为0;
    lf.lfHeight = cy / 2; //字体高度为0.5英寸()
    lstrcpy(lf.lfFaceName, TEXT("Times New Roman"));

    SelectObject(hdc, CreateFontIndirect(&lf));
    SetTextAlign(hdc, TA_BOTTOM | TA_CENTER);
    SetBkMode(hdc, TRANSPARENT);

    //显示数字
    for (i = 1; i <=5; i++)
    {
        ch = (TCHAR)(i + '0');
        TextOut(hdc, i*cx / 6, cy / 2, &ch, 1);
    }

    //清除
    DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
    DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));
}

void CreateRoutine(HWND hwnd)
{
    HDC hdcEMF;
    HENHMETAFILE hemf;

    //注意,第1个参数为NULL,会将视频显示设备环境作为“参考设备环境”
    hdcEMF = CreateEnhMetaFile(NULL, TEXT("emf11.emf"), NULL, 
                                TEXT("EMF11EMF Demo #11"));

    if (hdcEMF == NULL)
        return;

    SetMapMode(hdcEMF,MM_LOENGLISH); //逻辑坐标的单位为0.01英寸
    //600×100为逻辑坐标,即宽6英寸,高1英寸
    DrawRuler(hdcEMF, 600, 100); 

    hemf = CloseEnhMetaFile(hdcEMF);
    DeleteEnhMetaFile(hemf);
}

void PaintRoutine(HWND hwnd, HDC hdc, int cxArea, int cyArea)
{
    ENHMETAHEADER emh;
    HENHMETAFILE hemf;
    int cxImage, cyImage,cxMms,cyMms,cxPix,cyPix;
    RECT rect;

    hemf = GetEnhMetaFile(TEXT("emf11.emf"));

    GetEnhMetaFileHeader(hemf, sizeof(emh), &emh);

    /*方案1——利用图像的像素尺寸
      通过rclBounds字段(是设备单位:像素)显示出来的刻度尺,因本例指定图元文件
    的参考设备为视频DC,所以在视频显示器显示正常,但在打印中,刻度尺明显变小。
    */

    //cxImage = emh.rclBounds.right - emh.rclBounds.left;
    //cyImage = emh.rclBounds.bottom - emh.rclBounds.top;

    /*方案2——利用图像的物理尺寸
       通过rclFrame字段(是设备单位:0.01mm)显示出来的刻度尺,这样不管在视频显示器
    还是打印机上,显示出来的刻度尺都较为真实
    */

    //目标设备信息
    cxMms = GetDeviceCaps(hdc,HORZSIZE); //宽度(单位:mm)
    cyMms = GetDeviceCaps(hdc, VERTSIZE);//高度(单位:mm)
    cxPix = GetDeviceCaps(hdc, HORZRES);//宽度(单位:像素)
    cyPix = GetDeviceCaps(hdc, VERTRES);//高度(单位:像素)

    cxImage = emh.rclFrame.right - emh.rclFrame.left; //单位:0.01mm
    cyImage = emh.rclFrame.bottom - emh.rclFrame.top;

    //将图元文件大小(0.01mm为单位)转换为像素大小
    cxImage = cxImage* cxPix / cxMms / 100;
    cyImage = cyImage* cyPix / cyMms / 100;

    //在指定的矩形区内,水平和垂直居中显示图元文件,同时保证了区域的大小为cxImage和cyImage
    rect.left = (cxArea - cxImage) / 2;
    rect.right = (cxArea + cxImage) / 2;
    rect.top = (cyArea - cyImage) / 2;
    rect.bottom = (cyArea + cyImage) / 2;

    PlayEnhMetaFile(hdc, hemf, &rect); //回放(绘制)图元文件

    DeleteEnhMetaFile(hemf);
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 Emf11.rc 使用
//

#define IDM_PRINT                       40001
#define IDM_EXIT                        40002
#define IDM_ABOUT                       40003

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40004
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//Emf11.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""
"
    ""
END

3 TEXTINCLUDE 
BEGIN
    "
"
    ""
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

EMF MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Print...",                   IDM_PRINT
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       IDM_EXIT
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&About...",                   IDM_ABOUT
    END
END

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

18.2.11 使用映射模式显示图元文件

(1)因为图元文件的rcFrame的单位为0.01mm,所以使用MM_HIMETRIC来显示,可减少单位的换算。(注意MM_HIMETRIC逻辑坐标的单位也是0.01mm)

(2)MM_HIMETRIC的纵坐标向上为正,意味着客户区的y坐标为负数。为了便于操作,可将视口原点移到客户区的左下角。

(3)通过把客户区左上角的设备点(cxArea,0)传给DPToLP,可得到客户区的尺寸(单位:0.01mm)

(4)可以使用映射模式来创建一个图元文件,再通过映射模式来显示这个图元文件。

【Emf12程序】使用映射模式来显示图元文件

效果图与Emf8一样 

/*--------------------------------------------------
   EMF12 —— Enhanced Metafile Demo #12
           (c)Charles Petzold,1998
----------------------------------------------------*/
#pragma warning(disable: 4996) //win8.1以上GetVersion己过时,加上这句关闭
#include <windows.h>

TCHAR szClass[] = TEXT("EMF12");
TCHAR szTitle[] = TEXT("EMF12: Enhanced Metafile Demo #12");

void DrawRuler(HDC hdc, int cx, int cy)
{
    int iAdj,i,iHeight;
    LOGFONT lf;
    TCHAR  ch;

    iAdj = GetVersion() & 0x80000000 ? 0 : 1;

    //创建1磅宽的黑色画笔 cx为6英寸,因为1英寸=72磅,所以6英寸/72=6磅,再除以6得1磅
    SelectObject(hdc, CreatePen(PS_SOLID, cx / 72 / 6, 0));

    //画围绕整个标尺的矩形
    Rectangle(hdc, iAdj, iAdj, cx + iAdj + 1, cy + iAdj + 1);


    //刻度尺,共6英寸,每1/16英寸(即6/96)为一个刻度。
    for ( i = 1; i < 96; i++)
    {
        if (i % 16 == 0)     iHeight = cy / 2; //每隔1英寸,高度为cy/2
        else if (i % 8 == 0) iHeight = cy / 3; //每隔1/2英寸,高度为cx/3;
        else if (i % 4 == 0) iHeight = cy / 5; //每隔1/4英寸,高度为cx/5;
        else if (i % 2 == 0) iHeight = cy / 8; //每隔1/8英寸,高度为cx/8;
        else                 iHeight = cy / 12; //每隔1/16英寸,高度为cx/12;
        MoveToEx(hdc, i*cx / 96, cy, NULL);
        LineTo(hdc, i*cx / 96, cy - iHeight);
    }

    //创建逻辑字体,FillMemory最终也是调用memset函数
    FillMemory(&lf, sizeof(lf), 0); //结构体各字段初始化为0;
    lf.lfHeight = cy / 2; //字体高度为0.5英寸()
    lstrcpy(lf.lfFaceName, TEXT("Times New Roman"));

    SelectObject(hdc, CreateFontIndirect(&lf));
    SetTextAlign(hdc, TA_BOTTOM | TA_CENTER);
    SetBkMode(hdc, TRANSPARENT);

    //显示数字
    for (i = 1; i <=5; i++)
    {
        ch = (TCHAR)(i + '0');
        TextOut(hdc, i*cx / 6, cy / 2, &ch, 1);
    }

    //清除
    DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
    DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));
}

void CreateRoutine(HWND hwnd)
{
    HDC hdcEMF;
    HENHMETAFILE hemf;
    int cxMms, cyMms, cxPix, cyPix, xDpi, yDpi;

    //注意,第1个参数为NULL,会将视频显示设备环境作为“参考设备环境”
    hdcEMF = CreateEnhMetaFile(NULL, TEXT("emf12.emf"), NULL, 
                                TEXT("EMF12EMF Demo #12"));

    if (hdcEMF == NULL)
        return;

    //因为CreateEnhMetaFile的第1个参数为NULL,所以尽管以下以hdcEMF为参数,
    //但实际上得到的却是视频设备环境的DC的信息
    cxMms = GetDeviceCaps(hdcEMF, HORZSIZE); //以毫米为单位
    cyMms = GetDeviceCaps(hdcEMF, VERTSIZE);
    cxPix = GetDeviceCaps(hdcEMF, HORZRES);//像素规模(单位:像素)
    cyPix = GetDeviceCaps(hdcEMF, VERTRES);

    //分辨率
    xDpi = 254 * cxPix / cxMms / 10;//25.4 * cxPix / cxMms; //单位:像素/英寸
    yDpi = 254 * cyPix / cyMms / 10;// 25.4*  cyPix / cyMms;

    //宽6*xDpi表示6英寸,高1英寸
    DrawRuler(hdcEMF, 6 * xDpi, yDpi); //传入的数值为6英寸内的像素总量和1英寸内的像素总量

    hemf = CloseEnhMetaFile(hdcEMF);
    DeleteEnhMetaFile(hemf);
}

//使用映射模式来显示图元文件
void PaintRoutine(HWND hwnd, HDC hdc, int cxArea, int cyArea)
{

    ENHMETAHEADER emh;
    HENHMETAFILE hemf;
    int cxImage, cyImage;
    RECT rect;
    POINT pt;

    SetMapMode(hdc, MM_HIMETRIC);  //逻辑单位:0.01mm
    SetViewportOrgEx(hdc, 0, cyArea, NULL); //将视口原点设在左下角

    pt.x = cxArea;
    pt.y = 0;

    DPtoLP(hdc, &pt, 1); //pt.x这客户区的宽度,pt.y为客户区的高度(单位0.01mm)

    hemf = GetEnhMetaFile(TEXT("emf12.emf"));

    GetEnhMetaFileHeader(hemf, sizeof(emh), &emh);

    /*方案2——利用图像的物理尺寸
       通过rclFrame字段(是设备单位:0.01mm)显示出来的刻度尺,这样不管在视频显示器
    还是打印机上,显示出来的刻度尺都较为真实
    */

    cxImage = emh.rclFrame.right - emh.rclFrame.left; //单位:0.01mm
    cyImage = emh.rclFrame.bottom - emh.rclFrame.top;


    //在指定的矩形区内,水平和垂直居中显示图元文件,同时保证了区域的大小为cxImage和cyImage
    rect.left = (pt.x- cxImage) / 2;
    rect.right = (pt.x + cxImage) / 2;
    rect.top = (pt.y + cyImage) / 2;     //注意,这里与前面例子不同
    rect.bottom = (pt.y - cyImage) / 2;  //注意,这里与前面例子不同

    PlayEnhMetaFile(hdc, hemf, &rect); //回放(绘制)图元文件

    DeleteEnhMetaFile(hemf);
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 Emf12.rc 使用
//

#define IDM_PRINT                       40001
#define IDM_EXIT                        40002
#define IDM_ABOUT                       40003

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40004
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//Emf12.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""
"
    ""
END

3 TEXTINCLUDE 
BEGIN
    "
"
    ""
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

EMF MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Print...",                   IDM_PRINT
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       IDM_EXIT
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&About...",                   IDM_ABOUT
    END
END

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

【Emf13程序】使用映射模式来创建和显示图元文件。

/*--------------------------------------------------
   EMF13 —— Enhanced Metafile Demo #13
           (c)Charles Petzold,1998
----------------------------------------------------*/
#pragma warning(disable: 4996) //win8.1以上GetVersion己过时,加上这句关闭
#include <windows.h>

TCHAR szClass[] = TEXT("EMF13");
TCHAR szTitle[] = TEXT("EMF13: Enhanced Metafile Demo #13");

void DrawRuler(HDC hdc, int cx, int cy)
{
    int i,iHeight;
    LOGFONT lf;
    TCHAR  ch;


    //创建1磅宽的黑色画笔 cx为6英寸,因为1英寸=72磅,所以6英寸/72=6磅,再除以6得1磅
    SelectObject(hdc, CreatePen(PS_SOLID, cx / 72 / 6, 0)); //画笔宽度:为逻辑单位

    //画围绕整个标尺的矩形
    if(GetVersion() & 0x80000000)  //windows 98
        Rectangle(hdc, 0, -2, cx + 2, cy);
    else
        Rectangle(hdc, 0, -1, cx + 1, cy);


    //刻度尺,共6英寸,每1/16英寸(即6/96)为一个刻度。
    for ( i = 1; i < 96; i++)
    {
        if (i % 16 == 0)     iHeight = cy / 2; //每隔1英寸,高度为cy/2
        else if (i % 8 == 0) iHeight = cy / 3; //每隔1/2英寸,高度为cx/3;
        else if (i % 4 == 0) iHeight = cy / 5; //每隔1/4英寸,高度为cx/5;
        else if (i % 2 == 0) iHeight = cy / 8; //每隔1/8英寸,高度为cx/8;
        else                 iHeight = cy / 12; //每隔1/16英寸,高度为cx/12;
        MoveToEx(hdc, i*cx / 96, 0, NULL);
        LineTo(hdc, i*cx / 96, iHeight);
    }

    //创建逻辑字体,FillMemory最终也是调用memset函数
    FillMemory(&lf, sizeof(lf), 0); //结构体各字段初始化为0;
    lf.lfHeight = cy / 2; //字体高度为0.5英寸()
    lstrcpy(lf.lfFaceName, TEXT("Times New Roman"));

    SelectObject(hdc, CreateFontIndirect(&lf));
    SetTextAlign(hdc, TA_BOTTOM | TA_CENTER);
    SetBkMode(hdc, TRANSPARENT);

    //显示数字
    for (i = 1; i <=5; i++)
    {
        ch = (TCHAR)(i + '0');
        TextOut(hdc, i*cx / 6, cy / 2, &ch, 1);
    }

    //清除
    DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
    DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));
}

//在图元文件内部使用映射模式
void CreateRoutine(HWND hwnd)
{
    HDC hdcEMF;
    HENHMETAFILE hemf;

    //注意,第1个参数为NULL,会将视频显示设备环境作为“参考设备环境”
    hdcEMF = CreateEnhMetaFile(NULL, TEXT("emf13.emf"), NULL, 
                                TEXT("EMF13EMF Demo #13"));

    if (hdcEMF == NULL)
        return;

    SetMapMode(hdcEMF,MM_LOENGLISH); //逻辑坐标的单位为0.01英寸
    //600×100为逻辑坐标,即宽6英寸,高1英寸
    DrawRuler(hdcEMF, 600, 100); 

    hemf = CloseEnhMetaFile(hdcEMF);
    DeleteEnhMetaFile(hemf);
}

//使用映射模式来显示图元文件
void PaintRoutine(HWND hwnd, HDC hdc, int cxArea, int cyArea)
{

    ENHMETAHEADER emh;
    HENHMETAFILE hemf;
    int cxImage, cyImage;
    RECT rect;
    POINT pt;

    SetMapMode(hdc, MM_HIMETRIC);//逻辑单位:0.01mm
    SetViewportOrgEx(hdc, 0, cyArea, NULL);//将视口原点设在左下角

    pt.x = cxArea;
    pt.y = 0;
    DPtoLP(hdc, &pt, 1);//pt.x这客户区的宽度,pt.y为客户区的高度(单位0.01mm)

    hemf = GetEnhMetaFile(TEXT("emf13.emf"));

    GetEnhMetaFileHeader(hemf, sizeof(emh), &emh);

    /*方案2——利用图像的物理尺寸
       通过rclFrame字段(是设备单位:0.01mm)显示出来的刻度尺,这样不管在视频显示器
    还是打印机上,显示出来的刻度尺都较为真实
    */
    
    cxImage = emh.rclFrame.right - emh.rclFrame.left; //单位:0.01mm
    cyImage = emh.rclFrame.bottom - emh.rclFrame.top;


    //在指定的矩形区内,水平和垂直居中显示图元文件,同时保证了区域的大小为cxImage和cyImage
    rect.left = (pt.x - cxImage) / 2;
    rect.right = (pt.x + cxImage) / 2;
    rect.top = (pt.y + cyImage) / 2;     //注意,这里用“+”
    rect.bottom = (pt.y - cyImage) / 2;  //注意,这里用“-”

    PlayEnhMetaFile(hdc, hemf, &rect); //回放(绘制)图元文件

    DeleteEnhMetaFile(hemf);
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 Emf13.rc 使用
//

#define IDM_PRINT                       40001
#define IDM_EXIT                        40002
#define IDM_ABOUT                       40003

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40004
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//Emf13.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""
"
    ""
END

3 TEXTINCLUDE 
BEGIN
    "
"
    ""
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

EMF MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Print...",                   IDM_PRINT
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       IDM_EXIT
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&About...",                   IDM_ABOUT
    END
END

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED
原文地址:https://www.cnblogs.com/5iedu/p/4706339.html