Grafické metaformáty WMF a EMF

11. 10. 2007
Doba čtení: 13 minut

Sdílet

V dnešním článku si popíšeme metaformát WMF a částečně i EMF. Navzdory svému jménu a primárnímu určení se tyto grafické metaformáty rozšířily ze své domovské platformy MS Windows i na další operační systémy, včetně Linuxu. Dokonce i ve formátu EPS lze ukládat obrázky právě ve struktuře shodné s WMF.

Obsah

1. Grafické metaformáty WMF a EMF
2. Podpora WMF a EMF v Linuxu
3. Hlavička souborů typu WMF
4. Typy grafických entit, které se vyskytují v souboru typu WMF
5. Bitmapy a bitmapové operace
6. Čísla jednotlivých příkazů
7. Formát WMF a funkce dostupné z Windows API
8. Odkazy na další informační zdroje
9. Obsah dalšího pokračování tohoto seriálu

1. Grafické metaformáty WMF a EMF

Grafické metaformáty WMF (Windows Metafile) a EMF (Enhanced Windows Metafile) byly navrženy firmou Microsoft pro její známé operační systémy Microsoft Windows. Metaformát WMF existuje už od šestnáctibitových verzí Windows, zatímco EMF odráží nové možnosti grafického subsystému třicetidvoubitových verzí (počínaje Windows NT a Windows 95). Jedná se o formáty, ve kterých mohou být uloženy jak vektorové objekty (úsečky, oblouky, elipsy, výplně, texty), tak i bitmapy a dokonce vybrané příkazy určené pro změnu stavu takzvaného kontextu zařízení (device context), přes který je vykreslování obsahu souboru prováděno. Na formátech WMF a EMF je z programátorského hlediska zajímavá především skutečnost, že se jedná o součást grafického subsystému Microsoft Windows (GDI), což mj. znamená, že se vytváření těchto souborů provádí stejným způsobem, jako vykresování grafiky na obrazovku nebo její tisk na tiskárnu. Liší se pouze kontext zařízení, přes který se vykresluje.

5901
Obrázek 1: Výkres vytvořený v OpenOffice.org, který byl exportován do formátu WMF

Jedna uživatelem naprogramovaná funkce v nějaké aplikaci tedy může vykreslovat dokumenty na obrazovce, provádět tisk na libovolné tiskárně, renderovat dokumenty do bitmapy či vytvářet soubory WMF či EMF – jedná se vlastně o základ systému WYSIWYG (What You See Is What You Get). Integrace obou grafických metaformátů se také odráží v možnosti přenosu (převážně) vektorových obrázků mezi různými aplikacemi přes schránku (clipboard), přičemž interním formátem schránky může být právě WMF či EMF s upravenou hlavičkou (konkrétně se jedná o formáty představované konstantami CF_ENHMETAFILE a CF_METAFILEPICT). Vektorový obrázek je možné použít i v těch nejzákladnějších aplikacích, například WordPadu či PaintBrushi (Malování). V prvním případě zůstane vektorový formát výkresů zachován, ve druhém případě systém provede jejich rasterizaci, tj. převod na bitmapu (tím pádem samozřejmě dochází ke ztrátě důležitých informací a původní výkres už nebude možné zcela zrekonstruovat).

5902
Obrázek 2: Tentýž výkres po otevření v textovém editoru WordPad

Velká závislost struktury metaformátů WMF a EMF na aplikačním rozhraní Microsoft Windows je však také nebezpečná – soubory tohoto typu mohou obsahovat volání funkcí Windows API, které neprovádí pouze vykreslení, ale i jiné činnosti, typicky spuštění programového kódu. V minulosti se už podobné útoky odehrály, proto byly funkce pracující s WMF/EMF upraveny tak, aby se možnost těchto útoků zminimalizovala – jedná se například o potenciálně nebezpečnou funkci PlayMetaFile() a PlayMetaFileRe­cord(). Aplikace, které soubory typu WMF/EMF zpracovávají svými vlastními metodami (a kontrolují tak, které funkce Windows API se ve skutečnosti volají), jsou proti těmto útokům do značné míry odolné.

5903
Obrázek 3: Po vložení vektorového obrázku ze schránky do programu PaintBrush (Malování) dojde ke vložení jeho rastrové podoby

2. Podpora WMF a EMF v Linuxu

Po přečtení předchozích třech odstavců by se mohlo zdát, že formáty WMF a EMF jsou používány pouze na operačních systémech Microsoft Windows. Ve skutečnosti jsou však tyto formáty poměrně často využívány i na dalších operačních systémech, už jenom proto, že existuje velké množství hotových vektorových výkresů (klipartů), které je samozřejmě vhodné použít i na jiných platformách. Kromě toho jsou obrázky uložené v těchto formátech zakomponovány i do souborů EPS (Encapsulated PostScript) pro náhledy a samozřejmě také v dokumentech tvořených balíkem Microsoft Office (vložené vektorové obrázky, export z programu PowerPoint) či OpenOffice.org.

5904
Obrázek 4: Tentýž vektorový obrázek zobrazený v prohlížeči Eye of Gnome (všimněte si nekorektního nastavení poměru stran)

V Linuxu jsou grafické formáty WMF a EMF podporovány například skupinou filtrů z balíku Image Magick; jedná se především o filtry wmf2eps, wmf2fig, wmf2gd, wmf2svg a wmf2×. Mezi další aplikace, které s WMF a EMF pracují, patří OpenOffice.org, GhostView, Gnumeric a velké množství prohlížečů grafických souborů (Eye of GNOME, gThumb apod.). Z dalších aplikací jmenujme například yFiles (aplikace psaná v Javě) či knihovnu Batik (opět určeno primárně pro Javu) nebo původní Linuxovou knihovnu libwmf, kterou lze samostatně použít i když není nainstalovaný Image Magick.

5905
Obrázek 5: I prohlížeč gThumb umí pracovat s formátem WMF

3. Hlavička souborů typu WMF

Jako většina grafických formátů a metaformátů, které jsou uloženy v samostatných souborech, obsahují soubory typu WMF a EMF binárně zakódovanou hlavičku, za kterou již následují grafická data. Vzhledem k tomu, že WMF a dnes především EMF prochází postupným vývojem, který sleduje rozšiřování funkčností samotného Windows API, můžeme se v praxi setkat s několika typy hlaviček. Standardní hlavička grafického souboru uloženého ve formátu WMF, která má délku 18 bytů, což odpovídá devíti dvoubytovým slovům, má následující strukturu (všechny vícebytové položky jsou uloženy ve formátu little endian a termínem slovo mám v tomto kontextu na mysli dvoubytovou hodnotu bez znaménka, tj. uloženou v přímém kódu):

Offset (dec) Délka (byty) Název Význam datové položky
0 2 FileTypeStorage způsob uložení souboru:
2-na disku, 1-v paměti
2 2 HeaderSize velikost hlavičky ve slovech (prakticky vždy 9)
4 2 Version verze OS, ve které byl soubor vytvořen,
většinou hodnota 0×0010 (bez bitmap) či 0×0030 (s bitmapami nezávislými na zařízení)
6 4 FileSize velikost celého souboru ve slovech
10 2 NumOfObjects celkový počet objektů uložených v souboru
12 4 MaxRecordSize velikost největšího záznamu ve slovech (pravděpodobně pro potřeby bufferingu)
16 2 ParametersNumb rezervováno, nastavené na nulu

V ojedinělých případech, typicky při exportu grafických dat z aplikace OpenOffice.org, se můžeme setkat i se soubory typu WMF, které používají takzvanou rozšířenou výměnnou hlavičku, ve které jsou uvedeny informace o geometrii celého výkresu, tj. jeho rozměry a měřítko, přesněji řečeno počet délkových jednotek na jeden palec (i v případě této hlavičky se stále jedná o soubory typu WMF a ne EMF!). WMF soubory s touto hlavičkou se nazývají umístitelné metasoubory (placeable metafiles), protože je lze v rámci vykreslovací plochy jednoduše posouvat a měnit jejich velikost bez ohledu na to, jak složité jsou grafické objekty v nich umístěné – jedná se tedy o zjednodušenou analogii formátu EPS. Tyto soubory nejsou přímo zpracovatelné pomocí Windows API, ale jejich úprava na standardní WMF je velmi jednoduchá: postačuje odříznout prvních 22 bytů (například posunem ukazatele v paměti) a výsledkem je standardní (základní) soubor typu WMF.

Existenci rozšířené výměnné hlavičky je možné zjistit z „magického čísla“ (magic number), které je rovno hodnotě 0×9ac6cdd7, ovšem uloženého ve formátu little-endian, tj. jako sekvence bytů 0×d7 0×cd 0×c6 0×9a. Vzhledem k tomu, že normální hlavička začíná bytem s hodnotou 0×01 či 0×02, je existence rozšířené hlavičky vždy zjištěna jednoznačně. Formát výměnné hlavičky, za níž následuje obvyklá hlavička WMF souborů, je následující:

Offset (dec) Délka (byty) Název Význam datové položky
0 4 Password magická hodnota 0×9ac6cdd7
4 2 Handle číslo souboru, většinou je zde uložena nula
6 2 LeftCorn souřadnice levého okraje (délková jednotka twip)
8 2 TopCorn souřadnice horního okraje (délková jednotka twip)
10 2 RightCorn souřadnice pravého okraje (délková jednotka twip)
12 2 BottomCorn souřadnice dolního okraje (délková jednotka twip)
14 2 Twip_p_Inch počet délkových jednotek (twip) na palec, implicitně je použita hodnota 1440
16 4 Reserved tato čtveřice bytů musí být nulová
20 2 Checksum kontrolní součet hlavičky

Kontrolní součet hlavičky se počítá po jednotlivých slovech (dvojici bytů) a může se provádět například následujícím C-čkovým kódem, ve kterém je celá výměnná hlavička uložena ve struktuře nazvané phm:

pmh.Checksum= 0;
pmh.Checksum^=(pmh.Key & 0x0000FFFFUL);
pmh.Checksum^=((pmh.Key & 0xFFFF0000UL) >> 16);
pmh.Checksum^=pmh.Handle;
pmh.Checksum^=pmh.Left;
pmh.Checksum^=pmh.Top;
pmh.Checksum^=pmh.Right;
pmh.Checksum^=pmh.Bottom;
pmh.Checksum^=pmh.Inch;
pmh.Checksum^=(pmh.Reserved & 0x0000FFFFUL);
pmh.Checksum^=((pmh.Reserved & 0xFFFF0000UL) >> 16); 

4. Typy grafických entit, které se vyskytují v souboru typu WMF

Po hlavičce či obou hlavičkách (výměnné a základní) jsou v souborech typu WMF uloženy informace o jednotlivých grafických entitách. Kromě samotných grafických entit zde mohou být uloženy příkazy pro změnu barvy, zarovnání textu, úpravu barvové palety, nastavení operací prováděných při vykreslování bitmap (ROP – Raster Operation) apod. Každá grafická entita či příkaz jsou uloženy v samostatných blocích, které mají jednotnou hlavičku se základními údaji, za kterou následují jejich parametry. Počet a význam parametrů se samozřejmě v jednotlivých příkazech liší:

Offset Délka (byty) Název Význam
0 4 Size velikost celého bloku zadaná ve slovech (2 byty)
4 2 Function Číslo GDI funkce (lze získat z referenční příručky nebo souboru wingdi.h)
6 2×? Parameters[] Pole parametrů uložené v obráceném pořadí

Některé složitější příkazy používají místo pole parametrů odlišnou datovou strukturu. Jedná se například o příkaz Polyline sloužící k vykreslení polyčáry s libovolným počtem vrcholů či příkaz BitBlt využívaná pro zobrazení bitmapy. Strukturu parametrů lze opět získat z referenční příručky WinAPI nebo přímo z hlavičkových souborů (ty jsou dodávány i ke GNU překladači gcc, který je na platformě Microsoft Windows šířen například v populárním balíku djgpp nebo MinGW).

Mezi podporované grafické entity patří:

  1. úsečka (kreslená dvojicí příkazů MoveTo a LineTo)
  2. kruhová výseč (vyplněná i nevyplněná)
  3. elipsa (specielně kružnice)
  4. polyčára (lomená čára)
  5. polygon (vyplněný mnohoúhelník)
  6. obdélník (i vyplněný vzorkem)
  7. jednořádkový text
  8. text omezený obdélníkem (může se jednat i o víceřádko­vý text)
  9. obecná výplň omezená libovolnou hranicí (flood fill)

5. Bitmapy a bitmapové operace

V grafických souborech typu WMF se nemusí nacházet pouze vektorové objekty (jejichž seznam byl uveden v předchozí kapitole), ale také bitmapy, tj. rastrové obrázky. WMF, podobně jako grafický subsystém Windows (GDI), podporuje při vykreslování bitmap aplikaci takzvaných rastrových operací (ROP), což je nastavení způsobu kombinace barev vykreslovaných pixelů s barvou pozadí. Vhodně nastavenou rastrovou operací je například možné zabránit překreslování pozadí v těch místech, kde bitmapa obsahuje zcela průhledné pixely. Pomocí operace STRETCHBLT a DIBSTRETCHBLT lze měnit velikost (měřítko) vykreslovaných bitmap, nebo tyto bitmapy zrcadlit.

5906
Obrázek 6: WMF soubor vložený do tabulky v aplikaci Gnumeric

6. Čísla jednotlivých příkazů

Následující seznam příkazů GDI spolu s jejich čísly (ty jsou uloženy v hlavičce každého bloku, viz čtvrtou kapitolu) byl získán z hlavičkových souborů volně dostupných C-čkových překladačů pro Microsoft Windows; ovšem tento seznam se může hodit i lidem, kteří například implementují export WMF v jiném operačním systému (ale i v tomto případě je vhodné si obstarat soubor wingdi.h, který níže uvedený seznam obsahuje ve formě symbolických konstant). Při načítání WMF souborů je obecně nutné respektovat délku každého bloku (ta je uložena v jeho hlavičce) a neznámé bloky či bloky se špatnými parametry prostě ignorovat. Zápis, tj. vytváření WMF souborů, je většinou mnohem jednodušší, protože se používá pouze omezený počet příkazů. Například některé systémy typu CAD si vystačí s příkazy LINETO, MOVETO, ARC, POLYGON a POLYLINE.

META_SETBKCOLOR              0x0201
META_SETBKMODE               0x0102
META_SETMAPMODE              0x0103
META_SETROP2                 0x0104
META_SETRELABS               0x0105
META_SETPOLYFILLMODE         0x0106
META_SETSTRETCHBLTMODE       0x0107
META_SETTEXTCHAREXTRA        0x0108
META_SETTEXTCOLOR            0x0209
META_SETTEXTJUSTIFICATION    0x020A
META_SETWINDOWORG            0x020B
META_SETWINDOWEXT            0x020C
META_SETVIEWPORTORG          0x020D
META_SETVIEWPORTEXT          0x020E
META_OFFSETWINDOWORG         0x020F
META_SCALEWINDOWEXT          0x0410
META_OFFSETVIEWPORTORG       0x0211
META_SCALEVIEWPORTEXT        0x0412
META_LINETO                  0x0213
META_MOVETO                  0x0214
META_EXCLUDECLIPRECT         0x0415
META_INTERSECTCLIPRECT       0x0416
META_ARC                     0x0817
META_ELLIPSE                 0x0418
META_FLOODFILL               0x0419
META_PIE                     0x081A
META_RECTANGLE               0x041B
META_ROUNDRECT               0x061C
META_PATBLT                  0x061D
META_SAVEDC                  0x001E
META_SETPIXEL                0x041F
META_OFFSETCLIPRGN           0x0220
META_TEXTOUT                 0x0521
META_BITBLT                  0x0922
META_STRETCHBLT              0x0B23
META_POLYGON                 0x0324
META_POLYLINE                0x0325
META_ESCAPE                  0x0626
META_RESTOREDC               0x0127
META_FILLREGION              0x0228
META_FRAMEREGION             0x0429
META_INVERTREGION            0x012A
META_PAINTREGION             0x012B
META_SELECTCLIPREGION        0x012C
META_SELECTOBJECT            0x012D
META_SETTEXTALIGN            0x012E
META_CHORD                   0x0830
META_SETMAPPERFLAGS          0x0231
META_EXTTEXTOUT              0x0a32
META_SETDIBTODEV             0x0d33
META_SELECTPALETTE           0x0234
META_REALIZEPALETTE          0x0035
META_ANIMATEPALETTE          0x0436
META_SETPALENTRIES           0x0037
META_POLYPOLYGON             0x0538
META_RESIZEPALETTE           0x0139
META_DIBBITBLT               0x0940
META_DIBSTRETCHBLT           0x0b41
META_DIBCREATEPATTERNBRUSH   0x0142
META_STRETCHDIB              0x0f43
META_EXTFLOODFILL            0x0548
META_SETLAYOUT               0x0149
META_DELETEOBJECT            0x01f0
META_CREATEPALETTE           0x00f7
META_CREATEPATTERNBRUSH      0x01F9
META_CREATEPENINDIRECT       0x02FA
META_CREATEFONTINDIRECT      0x02FB
META_CREATEBRUSHINDIRECT     0x02FC
META_CREATEREGION            0x06FF 

7. Formát WMF a funkce dostupné z Windows API

Vzhledem k tomu, že grafické metaformáty WMF a EMF byly vyvinuty primárně pro použití v operačních systémech Microsoft Windows, není divu, že jsou nativně podporovány i v aplikačním rozhraní (API) těchto operačních systémů. Vytváření grafických metaformátů probíhá naprosto stejným způsobem jako kreslení na obrazovku nebo tisk na plotteru či tiskárně – stále se používají shodné funkce dostupné z grafického subsystému Microsoft Windows (GDI – Graphics Device Interface), liší se pouze rozměry kreslicího plátna, maximální počet barev a některé rozšiřující možnosti (plottery například nedokážou vyplňovat obecné plochy či kreslit barevné bitmapy). Ve většině aplikací je však jedinou změnou mezi vykreslením obrázku na obrazovku, jeho výstupem na tiskárnu či „tiskem“ do metasouboru použití odlišného kontextu zařízení (DC – device context), což je ukázáno na následujícím fragmentu kódu, ve kterém je nejdříve vytvořen kontext zařízení a posléze je do něj vykreslen jednoduchý vektorový obrázek:

// vytvoření kontextu zařízení grafického metasouboru vytvořeného v paměti
// při předání skutečného jména souboru se metasoubor vytvoří na disku
HDC hdcMeta=CreateMetaFile(NULL);

// vytvoření štětce, kterým se bude vyplňovat kružnice
// RGB je makro pro snazší výběr barvového odstínu
HBRSUH hBrush=CreateSolidBrush(RGB(0, 0, 255));

// vykreslení čtverce o velikosti 100x100
Rectangle(hdcMeta, 0, 0, 100, 100);

// vykreslení úhlopříček obdélníka
// MoveToEx vrací v posledním parametru původní
// souřadnice grafického kurzoru, ty však nepotřebujeme
MoveToEx(hdcMeta,   0,   0, NULL);
LineTo  (hdcMeta, 100, 100);
MoveToEx(hdcMeta,   0, 100, NULL);
LineTo  (hdcMeta, 100,   0);

// vykreslení vyplněné kružnice
SelectObject(hdcMeta, hBrush);
Ellipse(hdcMeta, 20, 20, 80, 80);

// nyní již nepotřebný štětec je vhodné smazat
// aby se zbytečně neplýtvalo prostředky GDI
DeleteObject(hBrush);

// uzavření kontextu zařízení a vrácení handlu
// metasouboru vytvořeného v operační paměti
HMETAFILE hmf=CloseMetaFile(hdcMeta); 

Po uzavření metasouboru funkcí CloseMetaFile() je metasoubor buď uložen do operační paměti pro další použití (například rychlejší vykreslení složitějších obrázků či jejich přenos přes schránku) nebo je trvale uložen na disk – záleží na tom, s jakým parametrem byla zavolána funkce CreateMetaFile(). Pokud je metasoubor uložen v operační paměti, je možné provést jeho vykreslení pomocí funkce PlayMetaFile(), která opravdu „přehrává“ seznam grafických příkazů uložených v metasouboru na předaném kontextu zařízení. V následující ukázce je metasoubor vykreslen celkem stokrát ve mřížce o velikosti 10×10 buněk:

// začátek kreslení - získání kontextu zařízení
HDC hdc=BeginPaint(hwnd, &ps);

// po systému, resp. GDI nebudeme chtít, aby
// přepočítával předávané souřadnice
SetMapMode(hdc, MM_ANISOTROPIC);
// rozměry kreslicí plochy
SetWindowExtEx(hdc, 1000, 1000, NULL);
// rozměry viewportu podle aktuální velikosti okna
SetViewportExtEx(hdc, cxClient, cyClient, NULL);

// vykreslení obsahu metasouboru ve mřížce 10x10 buněk
for (x=0; x<10; x++) {
    for (y=0; y<10; y++) {
        // původní rozměr obrázku je 100x100 jednotek
        SetWindowOrgEx(hdc, -100*x, -100*y, NULL);
        // "přehrání" metasouboru a vykreslení jeho obsahu
        PlayMetaFile (hdc, hmf);
    }
}
// konec kreslení (popř. lze provést i double-buffering)
EndPaint(hwnd, &ps); 

Pro práci s metaformátem WMF slouží ve Windows API sada jedenácti základních funkcí:

CloseMetaFile()
CopyMetaFile()
CreateMetaFile()
DeleteMetaFile()
EnumMetaFile()
EnumMetaFileProc()
GetMetaFile()
GetMetaFileBitsEx()
PlayMetaFile()
PlayMetaFileRe­cord()
SetMetaFileBitsEx()

Rozšiřující EMF je v API podporován odpovídajícím větším množstvím funkcí, kterých je celkem 17 (pro GDI+ existuje ještě větší množství funkcí, ty si zde však nebudeme popisovat):

CloseEnhMetaFile()
CopyEnhMetaFile()
CreateEnhMetaFile()
DeleteEnhMetaFile()
EnhMetaFileProc()
EnumEnhMetaFile()
GdiComment()
GetEnhMetaFile()
GetEnhMetaFile­Bits()
GetEnhMetaFile­Description()
GetEnhMetaFile­Header()
GetEnhMetaFile­PaletteEntries()
GetWinMetaFile­Bits()
PlayEnhMetaFile()
PlayEnhMetaFi­leRecord()
SetEnhMetaFile­Bits()
SetWinMetaFile­Bits()

bitcoin_skoleni

8. Odkazy na další informační zdroje

  1. Charles Petzold: Programming Windows,
    Microsoft Press, 1998
  2. Microsoft: Platform SDK,
    Microsoft, 1995–2007
  3. Wikipedia EN: Windows Metafile,
    http://en.wiki­pedia.org/wiki/Win­dows_Metafile
  4. Wikipedia EN: Windows Media Format,
    http://en.wiki­pedia.org/wiki/Win­dows_Media_For­mat
  5. Microsoft Windows Metafile,
    http://wvware­.sourceforge.net/ca­olan/ora-wmf.html
  6. Windows Metafiles, a guide for non-windows programmers,
    http://wvware­.sourceforge.net/ca­olan/index.html
  7. Metafile Reference,
    http://msdn2.mi­crosoft.com/en-us/library/ms534300­.aspx
  8. yFiles 2.3.x graph visualization library and WMF,
    http://www.ywor­ks.com/en/pro­ducts_yfiles_ep_yw­mf.htm
  9. yFiles gallery,
    http://www.ywor­ks.com/en/pro­ducts_yfiles_prac­ticalinfo_galle­ry.htm
  10. libwmf – library for converting WMF files,
    http://wvware­.sourceforge.net/lib­wmf.html

9. Obsah dalšího pokračování tohoto seriálu

V následujícím pokračování seriálu o grafických formátech a metaformátech si popíšeme ideového nástupce formátu WMF, kterým je grafický metaformát EMF.

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.