Tekninen artikkeli

PDFlibPas EMF-tuonti: PolyDraw-, Polyline- ja Bezier-säännöt

PDFlibPas, losLabin Delphi-PDF-kirjasto, muuntaa EMF:n Poly*-tietueet PDF-poluiksi noudattamalla jokaisen tietueen määritelmää [MS-EMF]:ssä: 32-bittinen EMR_POLYBEZIER alkaa pisteestä 0, polylinet pysyvät avoimina ja niistä tulee vain viiva, PT_CLOSEFIGURE tietueessa EMR_POLYDRAW on flagi, ja jokainen pistemäärä tarkistetaan tietueen kokoa vasten. Kyseiset säännöt toteutuivat versioiden v3.539.39, v3.539.41 ja v3.539.43 aikana. Ennen niitä kaavio saattoi tulla ulos funktiosta ImportEMFFromFile täytetyn sektorin kera trendtiviivan paikalla, Bezier-käyrä taipuneena väärän ohjauspisteen suuntaan tai suljettu ääriviiva ilman viimeistä sivuaan. Mikään näistä ei nostanut virhettä, ja säännöt pätevät mille tahansa Delphi EMF–PDF-muuntimelle tai GDI-tietueparserille

Miksi EMF:n Poly*-tietueet menevät pieleen PDF-muunnoksessa?

Poly*-tietueet menevät pieleen, koska kukin kantaa osan merkityksestään pisteidensä ulkopuolella: onko kuvio avoin, alkaako se nykyisestä sijainnista, mitkä kynä ja sivellin ovat voimassa ja mistä tietueen pisteet alkavat. Enhanced metafile on tallenne GDI-kutsuista laitekontekstia vasten, joten muuntimen on toistettava koordinaattien lisäksi myös laitekontekstin tila. PDF:ssä ei ole laitekontekstia. Siellä on polku, polun sisällä nykyinen piste ja piirto-operaattori, joka valitsee strokin (S), täytön (f) ja molempien (B) väliltä. Jokainen kahden mallin välinen ristiriita muuttuu hiljaiseksi renderöintieroksi

Poly*-perheestä on olemassa myös kapeampi sarja. Jokaisella 32-bittisellä tietueella on 16-bittinen kaksoispari — EMR_POLYLINE ja EMR_POLYLINE16 — joka tallettaa pisteet SmallInt-pareina. GDI tallentaa yleensä kompaktin muodon, kun jokainen koordinaatti mahtuu, joten muuntimen 32-bittiset käsittelijät voivat olla vuosia vialla, sillä arkipäivän testipiirrokset eivät koskaan yletä niihin. Nopein tarkastus on syöttää samat pisteet molempien tietueiden kautta ja verrata syntyviä polkuja. Täällä käsitellyt tietueet kuuluvat kaikki [MS-EMF]:n piirustustietueiden ryhmään (2.3.5 Drawing Record Types)

TietueAlkaa kohdastaSuljettu?Nykyinen sijainti
EMR_POLYBEZIERPiste 0EiEi käytössä, ei päivity
EMR_POLYLINEPiste 0Ei (vain kynä)Ei käytössä, ei päivity
EMR_POLYLINETONykyinen sijaintiEi (vain kynä)Käytössä ja päivittyy
EMR_POLYPOLYLINEJokaisen murtoviivan ensimmäinen pisteEi (vain kynä)Ei käytössä, ei päivity
EMR_POLYDRAWEnsimmäinen PT_MOVETO tai nykyinen sijaintiVain kun PT_CLOSEFIGURE on asetettuKäytössä ja päivittyy

Missä EMR_POLYBEZIER-käyrä oikeastaan alkaa?

EMR_POLYBEZIER-käyrä alkaa pisteestä 0, ja vain indeksistä 1 eteenpäin olevat pisteet ryhmitellään kolmen ryhmiin: ohjauspiste, ohjauspiste, päätepiste. Seitsemän pisteen tietue piirtää siis kaksi kuutisegmenttiä: 0 on alkupiste, 1–3 muodostavat ensimmäisen segmentin ja 4–6 toisen. 16-bittinen käsittelijä teki tämän PDFlibPasissa jo ennestään. 32-bittinen käsittelijä aloitti ryhmittelyn pisteestä 0, joten alkupiste kului ensimmäisenä ohjauspisteenä ja jokainen myöhempi segmentti siirtyi yhdellä. Käyrä renderöityi kyllä, se oli vain väärä. Versiosta v3.539.41 alkaen molemmat versiot avaavat polun m:llä pisteessä 0 ja kirjoittavat yhden c-operaattorin jokaista täydellistä kolmikkoa kohden sen jälkeen

PDFlibPas-kaavio EMR_POLYBEZIER-tietueesta, jossa on seitsemän pistettä: piste nolla avaa polun m-operaattorilla ja pisteet yhdestä kolmeen sekä neljästä kuuteen muodostavat kukin yhden kuutisen c-segmentin, vastakohtana v3.539.41 korjattu 32-bittinen käsittelijä ja vanha ryhmittely, joka kulutti alkupisteen ohjauspisteenä
Piste 0 on alkupiste, ja vain täydelliset kolmikot sen jälkeen muuttuvat kuutisegmenteiksi, joten seitsemän pisteen PolyBezier renderöityy m-operaattoriksi plus kahdeksi c-operaattoriksi

Omaan parseriisi: pistemäärä, joka ei ole 1 plus 3:n kerrannainen, on epäkelpo, ja jäljellä olevat pisteet kannattaa ohittaa sen sijaan, että ne ommeltaisiin kiinni käyrään

PolyDraw: PT_CLOSEFIGURE on flagi, ei pistetyyppi

Tietueessa EMR_POLYDRAW PT_CLOSEFIGURE (arvo 1) on bitti, joka yhdistyy bittien PT_LINETO (2) tai PT_BEZIERTO (4) kanssa, joten kelvollinen tyyppitavu voi olla 3 tai 5. Pistetyyppi on tavu, jolta kyseinen bitti on maskattu pois, ja flagi tarkoittaa, että kuvio suljetaan segmentin jälkeen, joka päättyy tähän pisteeseen. Vanha PDFlibPas-käsittelijä vertasi tavua yksittäisiin arvoihin case-lauseessa, joten tyyppien 3 ja 5 pisteet eivät osuneet mihinkään ja jätettiin kokonaan väliin. PolyDrawilla piirretty suorakulmio menetti sulkevan sivunsa, ja Bezier-kolmikko, jonka viimeinen piste kantoi flagia, menetti kyseisen pisteen, mikä työnsi jokaisen myöhemmän kolmikon rytmin jälkeen

Versiosta v3.539.39 alkaen tyyppi luetaan muodossa Types[i] and not PT_CLOSEFIGURE, ja sulku kirjoitetaan vasta täyden segmentin jälkeen: viivan jälkeen suljetun PT_LINETOn kohdalla ja Bezier-ryhmän kolmannen pisteen jälkeen. Rikkinäinen tiedosto, joka asettaa flagin kolmikon ensimmäiselle tai toiselle pisteelle, ei sulje kuviota etuajassa. Sama julkaisu toi mukanaan kaksi aiheeseen liittyvää korjausta:

  • Jokainen PT_MOVETO 16-bittisen EMR_POLYDRAW16-tietueen sisällä käynnisti koko polun uudelleen, joten kolme kuviota sisältänyt tietue piti vain viimeisen; nyt ensimmäinen siirto käynnistää polun ja myöhemmät siirrot avaavat alipolkuja
  • PolyDraw-tietue, joka ei ala PT_MOVETOlla, alkaa nykyisestä sijainnista, kuten tietueen määritelmä sanoo, sen sijaan että kirjoittaisi l- tai c-operaattorin ilman edeltävää m:ää
PDFlibPas-tarkastelu EMR_POLYDRAW-tietueen tyyppitavusta, jossa PT_CLOSEFIGURE on flagibitti nolla, joka on OR-yhdistetty bittien PT_LINETO tai PT_BEZIERTO kanssa, joten kelvolliset tyyppitavut 3 ja 5 on maskattava komennolla and not PT_CLOSEFIGURE ennen case-haarautusta; vanha case-lause ohitti molemmat tavut ja suljetuista kuvioista katosi viimeinen sivu
Maskoi sulkuflagi pois ennen case-haarautusta ja kirjoita sulku vasta valmiin viivan tai Bezier-kolmikon jälkeen, muuten PolyDraw pudottaa pisteitä hiljaisesti

Miksi EMF:n polylinea ei saa koskaan täyttää PDF:ssä?

EMF:n polylinea ei saa koskaan täyttää, koska EMR_POLYLINE ja EMR_POLYPOLYLINE ovat avoimia kuvioita, jotka piirretään pelkällä kynällä, ja avoimen polun täyttäminen PDF:ssä sulkee sen implisiittisesti. ISO 32000-1 §8.5.3 sanoo, että täyttöoperaattorit sulkevat jokaisen avoimen alipolun ennen sen maalaamista. Muunnin, joka kirjoittaa B:n tai f:n kolmen pisteen polylinelle, maalaa siis täytetyn kolmion nykyisellä siveltimen värillä: täytetyn sektorin kaavion trendtiviivan alla. Ennen v3.539.41 PDFlibPas täytti molemmat polyline-versiot siveltimellä, ja 32-bittinen tietue suljettiin myös eksplisiittisesti. Nykyään molemmat versiot päättyvät pelkkään strokeen, ja GDI:n erottelu säilyy: Polygon sulkee ja täyttää, Polyline ei koskaan

PDFlibPas-vertailu avoimesta V-muotoisesta polynesta, joka viedään EMR_POLYLINE-tietueesta: oikea muunnin päättää polun stroke-operaattoriin S ja ohittaa valitun siveltimen, kun taas f:n tai B:n kirjoittaminen sulkee avoimen alipolun implisiittisesti ISO 32000-1 8.5.3:n nojalla ja maalaa täytetyn sektorin, kaavion klassisen vian
Täyttöoperaattori sulkee jokaisen avoimen alipolun ennen maalaamista, joten polylinet on päästettävä S:ään ilman h:ta, f:tä tai B:ä alipolulla

PolylineTo alkaa nykyisestä sijainnista

EMR_POLYLINETO piirtää nykyisestä sijainnista tietueen jokaisen pisteen kautta, pysyy avoimena ja jättää nykyisen sijainnin viimeiseen pisteeseen. Vanha käsittelijä sisälsi myös erikoistapauksen, joka sammutti kynän, kun kahdella ensimmäisellä pisteellä oli sama y-koordinaatti, eikä mikään koskaan kääntänyt sitä takaisin päälle, joten jokainen myöhempi tietue tiedostossa menetti ääriviivansa. Kynän tila kuuluu tietueille EMR_SELECTOBJECT ja EMR_CREATEPEN; piirustustietueen käsittelijällä ei ole asiaa muuttaa sitä. Kyseinen erikoistapaus poistettiin versiossa v3.539.41, eikä tietueen yhden pisteen muoto enää lue omiensa pisteiden yli (korjattu v3.539.39)

PolyPolyline-tietueen pisteet alkavat määrätaulukon jälkeen

32-bittinen EMR_POLYPOLYLINE tallettaa ensin nPolys määrää ja sitten cptl pistettä, ja pisteet alkavat tavuoffsetilta 32 + nPolys * 4. Ansa piilee RTL:ssä: Windows-yksikkö määrittelee TEMRPolyPolylinen siten, että aPolyCounts ja aptl ovat yhden elementin taulukoita, joten aptl[0] on ensimmäinen piste vain silloin, kun nPolys on 1. Koodi, joka indeksoi aptl:tä suoraan, lukee määräarvot koordinaatteina jokaiselta useita viivoja sisältävältä tietueelta. Vanha PDFlibPas-käsittelijä mitoitti myös rajatarkistuksensa tuon väärän rakenteen mukaan, joten kelvolliset moniviivatietueet hylättiin ja yksiviivaiset eivät piirtäneet mitään. Versiosta v3.539.41 alkaen PDFlibPas paikantaa pistetaulukon lasketulta offsetilta, samaan tapaan kuin sen PolyPolygon-käsittelijä on aina tehnyt, ja piirtää jokaisen murtoviivan omaksi avoimeksi alipolukseen yhdellä strokilla lopussa. Versiossa v3.539.43 myös 16-bittinen pari sai saman kohtelun; se oli piirtänyt segmentti kerrallaan, mikä rikkoi viivojen liitokset ja ohitti valitun NULL_PENin

Oletuskynä ja -sivellin sekä polun sulkeet

Kaksi tilasääntöä täydentävät polyn korjaukset versiossa v3.539.43:

  • Tuoreessa GDI-laitekontekstissa on valittuina jo BLACK_PEN ja WHITE_BRUSH, joten metatiedosto, joka piirtää ilman mitään EMR_SELECTOBJECTia, piirtää silti mustat ääriviivat; muunnin aloitti aiemmin ilman kynää ja ilman täyttöä ja kirjoitti tällaisille tietueille n:n (lopeta polku, älä maalaa mitään)
  • BeginPath/EndPath-sulkeiden sisällä Polyline ei käytä eikä päivitä nykyistä sijaintia, joten sen on avattava uusi alipolku ensimmäisestä pisteestään edelliseen kuvioon kytkeytymisen sijaan, eikä mitään saa maalata ennen kuin sulkeet on vedetty viivana tai täytetty

EMF-testitiedoston rakentaminen TMetafileCanvasilla

Nopein tapa tarkistaa muunnin näitä sääntöjä vasten on tallentaa kolme riskialttiinta kutsua yhteen enhanced metatiedostoon TMetafileCanvasilla. Alla oleva piirros tallentaa käyrät ontolla siveltimellä ja valitsee sitten tahallaan tasaisen keltaisen siveltimen polynelle: oikean muuntimen on ohitettava kyseinen sivellin polynen kohdalla, joten mikä tahansa keltainen tulostuvassa PDF:ssä on bugi. PolyDrawilla ei ole TCanvas-käärettä, joten sitä kutsutaan Windows API:n kautta canvas-kahvalla käyttäen tyyppitavuja 3 ja 5 sulkuflagin kokeiluun

uses
  Winapi.Windows, System.Types, Vcl.Graphics;

procedure BuildPolyTestEmf(const FileName: string);
const
  // Suljettu neliö (3 = LINETO + CLOSEFIGURE) ja sitten suljettu Bezier-
  // kuvio, jonka viimeinen ohjauskolmikko päättyy arvoon 5 = BEZIERTO + CLOSEFIGURE
  DrawPts: array[0..7] of TPoint = (
    (X: 300; Y: 40), (X: 380; Y: 40), (X: 380; Y: 120), (X: 300; Y: 120),
    (X: 420; Y: 120), (X: 440; Y: 40), (X: 520; Y: 40), (X: 540; Y: 120));
  DrawTypes: array[0..7] of Byte = (
    PT_MOVETO, PT_LINETO, PT_LINETO, PT_LINETO or PT_CLOSEFIGURE,
    PT_MOVETO, PT_BEZIERTO, PT_BEZIERTO, PT_BEZIERTO or PT_CLOSEFIGURE);
var
  Mf: TMetafile;
  Canvas: TMetafileCanvas;
begin
  Mf := TMetafile.Create;
  try
    Mf.Enhanced := True;
    Mf.Width := 600;
    Mf.Height := 260;
    Canvas := TMetafileCanvas.Create(Mf, 0);
    try
      Canvas.Pen.Color := clNavy;
      Canvas.Pen.Width := 2;
      Canvas.Brush.Style := bsClear;    // vain ääriviivat käyrille
      // Piste 0 on alkupiste; 1..3 ja 4..6 ovat kaksi kuutisegmenttiä
      Canvas.PolyBezier([Point(20, 120), Point(60, 20), Point(100, 220),
        Point(140, 120), Point(180, 20), Point(220, 220), Point(260, 120)]);
      PolyDraw(Canvas.Handle, DrawPts[0], DrawTypes[0], Length(DrawPts));
      // Avoin V-muoto, tasainen sivellin valittuna: vain stroke,
      // ei koskaan suljeta keltaiseksi kolmioksi
      Canvas.Brush.Style := bsSolid;
      Canvas.Brush.Color := clYellow;
      Canvas.Polyline([Point(20, 240), Point(120, 160), Point(220, 240)]);
    finally
      Canvas.Free;   // päättää tallenteen
    end;
    Mf.SaveToFile(FileName);
  finally
    Mf.Free;
  end;
end;

Koska nämä koordinaatit mahtuvat SmallIntiin, GDI tallentaa yleensä 16-bittiset muunnelmat. Päästäksesi 32-bittisiin käsittelijöihin tarvitset tuottajan, joka kirjoittaa niitä, tai tietueita, jotka rakennat käsin. Käsintehtävillä tiedostoilla on oma ansansa: VCL:n TMetafile.LoadFromStream tulkitsee streamin EMF:ksi vain, kun jäljellä oleva pituus on aidosti suurempi kuin 108-tavuinen TEnhMetaHeader. Minimaalinen käsin kirjoitettu EMF lyhyellä otsikolla tai tyhjä, tasan 108 tavun pituinen tiedosto tulkitaan WMF:ksi ja hylätään viestillä "Metafile is not valid". Kirjoita aina täysi 108-tavuinen otsikko laajennuskenttineen ennen omia testitietueitasi

EMF:n tuonti PDF:ään PDFlibPasilla

PDFlibPas tuo EMF:n funktioilla ImportEMFFromFile ja ImportEMFFromStream, jotka palauttavat onnistuessaan nollasta poikkeavan kuvatunnisteen ja epäonnistuessaan arvon 0. GeneralOptions = 0 pitää vektoripolun, josta tämä artikkeli kertoo; arvo 1 rasteroi metatiedoston sen sijaan bittikartaksi. FontOptions = 1 lisää metatiedoston fontit upottamattomina TrueType-fontteina. Stream-versio kelaa streamin takaisin positioon 0 ennen latausta, joten anna stream, joka sisältää vain metatiedoston

uses
  System.SysUtils, PDFlibrary;

procedure EmfToPdf(const EmfFile, PdfFile: WideString);
var
  PDF: TPDFlib;
  ImageID: Integer;
  PageOps: AnsiString;
begin
  PDF := TPDFlib.Create;
  try
    PDF.SetOrigin(1);              // vasen yläkulma alkupisteenä DrawImagelle
    PDF.SetMeasurementUnits(0);    // pisteet
    // FontOptions 1 = lisää fontit upottamattomina TrueType-fontteina
    // GeneralOptions 0 = vektorituonti, 1 = bittikartta
    ImageID := PDF.ImportEMFFromFile(EmfFile, 1, 0);
    if ImageID = 0 then
      raise Exception.Create('The metafile could not be imported');
    PDF.SelectImage(ImageID);
    // EMF:lle ImageWidth / ImageHeight on kehyksen koko pisteinä
    PDF.DrawImage(36, 36, PDF.ImageWidth, PDF.ImageHeight);

    // Sivu ainoastaan kutsuu tuotua formia: q ... cm /Name Do Q
    PageOps := PDF.GetPageContentToString;
    if Pos(AnsiString(' Do'), PageOps) = 0 then
      raise Exception.Create('Expected a form XObject invocation');

    if PDF.SaveToFile(PdfFile) <> 1 then
      raise Exception.Create('The PDF could not be saved');
  finally
    PDF.Free;
  end;
end;

Vektorimuotoinen EMF-tuonti muuttuu form XObjectiksi, joten GetPageContentToString palauttaa vain save-, transform-, Do- ja restore-jonon. Poly*-tietueista syntyvät m-, l-, c-, h- ja S-operaattorit asuvat form XObjectin streamissa, joka on pakattu. Pura pakatut streamit PDF-objekti-inspektorissa ja lue form-stream: yllä olevan testitiedoston kohdalla sinun pitäisi nähdä polynen päättyvän S:ään ilman h:ta sen edellä, h kunkin sulkuflagin kohdalla PolyDraw-kuvioissa, eikä f:tä tai B:tä millään näistä alipoluista. DrawImage skaalaa tuodun EMF:n tasaisesti Widthn ja Heightn pienemmällä, joten piirros säilyttää kuvasuhteensa, vaikka antamasi laatikko ei vastaisi sitä

Free Pascal -kohteissa katso artikkeli miten PDFlibPasin EMF-vektorituonti kääntyy Free Pascalissa; tietueiden semantiikka on sama siellä missä tuoja kääntyykin

Miten EMF-parserin tulee suhtautua tiedoston pistemääriin?

EMF-parserin tulee käsitellä jokaista pistemäärää epäluotettavana syötteenä ja tarkistaa se tietueen kokoa vasten ennen kuin yksikään piste kopioidaan. EnumEnhMetaFile takaa vain, että kunkin tietueen nSize pysyy tiedoston sisällä. Se ei tarkista, että cptl vastaa nSizeä, joten käsittelijä, joka kopioi cptl pistettä Movella, lukee seuraavat tietueet tai metatiedoston loppuun, kun määrä on väärennetty tai rikkoutunut. Versiosta v3.539.39 alkaen PDFlibPas tarkistaa kiinteän otsikon plus määrän kertaa tavua per piste arvoa nSize vasten tietueille PolyDraw, PolyBezier, PolyBezierTo, Polyline, PolylineTo ja Polygon kummassakin leveydessä, yhtä lisätavua per piste PolyDrawin tyyppitavuille. PolyPoly-tietueilla kuvakohtaisten määrien on myös summauduttava enintään ilmoitettuun kokonaismäärään, ja nollapistekuviot ohitetaan

Sama tarkistus on lyhyt kopioitavaksi omaan parseriisi. Tämä versio validoikin 32-bittisen EMR_POLYPOLYLINEin ja palauttaa osoittimen sen todelliseen pistetaulukkoon:

uses
  Winapi.Windows;

// Palauttaa nilin, ellei tietue aidosti sisällä ilmoittamiaan pisteitä.
// Pisteet alkavat määrätaulukon jälkeen: 32 + nPolys * 4 tavua sisään, ei
// kohdasta aptl[0], jonka RTL määrittelee yhden elementin taulukoksi
function PolyPolylinePoints(Rec: PEnhMetaRecord): PPoint;
var
  P: PEMRPolyPolyline;
  Count: PDWORD;
  PointsOffset, Total: Int64;
  I: Cardinal;
begin
  Result := nil;
  if (Rec^.iType <> EMR_POLYPOLYLINE) or (Rec^.nSize < 32) then
    Exit;
  P := PEMRPolyPolyline(Rec);
  if P^.nPolys = 0 then
    Exit;
  PointsOffset := 32 + Int64(P^.nPolys) * SizeOf(DWORD);
  if PointsOffset + Int64(P^.cptl) * SizeOf(TPoint) > Rec^.nSize then
    Exit;                         // väärennetty tai katkaistu määrä
  Total := 0;
  Count := @P^.aPolyCounts[0];    // kulje osoittimella: [0..0] kaataa range checkit
  for I := 1 to P^.nPolys do
  begin
    Inc(Total, Count^);
    Inc(Count);
  end;
  if Total > P^.cptl then
    Exit;                         // kuviot vaativat enemmän pisteitä kuin on olemassa
  Result := PPoint(NativeUInt(Rec) + NativeUInt(PointsOffset));
end;

Pisteiden mahtumistesti ajetaan ensin, joten määrätaulukon tiedetään olevan tietueen sisällä ennen kuin silmukka kulkee sen läpi. Aritmetiikka on Int64ä, koska 32 bitissä lasketut nPolys * 4 ja cptl * 8 voivat kiertyä ympäri ja päästä vertailun läpi

Pikaopas: EMF:n Poly*-säännöt EMF–PDF-muunnoksessa

  • EMR_POLYBEZIER: piste 0 on alkupiste; ryhmittele pisteestä 1 alkaen kolmen ryhmissä; korjattu 32-bittisessä tietueessa versiossa v3.539.41
  • EMR_POLYLINE/EMR_POLYPOLYLINE: avoimia kuvioita, stroke Sllä, ei koskaan h, f tai B, koska PDF:n täyttö sulkee avoimet alipolut
  • EMR_POLYLINETO: alku nykyisestä sijainnista, pysy avoimena, päivitä nykyinen sijainti, älä koske koskaan kynän tilaan
  • 32-bittinen EMR_POLYPOLYLINE: pisteet alkavat tavusta 32 + nPolys * 4, ei kohdasta aptl[0]
  • EMR_POLYDRAW: maskaa PT_CLOSEFIGURE pois ennen haarautusta, sulje valmiin segmentin jälkeen, aloita nykyisestä sijainnista, kun ensimmäinen piste ei ole PT_MOVETO
  • Laitekontekstin oletustila on BLACK_PEN plus WHITE_BRUSH; versio v3.539.43 ja uudemmat kunnioittavat sitä
  • BeginPath/EndPath-sulkeiden sisällä jokainen polyline avaa oman alipolkunsa, eikä mitään maalata ennen kuin sulkeet käytetään
  • Validoi jokainen cptl/cpts arvoa nSize vasten 64-bittisessä aritmetiikassa ennen pisteiden kopiointia
  • Käsintehdyt testi-EMF:t tarvitsevat täyden 108-tavuisen otsikon, muuten TMetafile.LoadFromStream lukee ne WMF:nä

Jos raporttisi kulkevat toisen komponentin kautta, sama tietuesemantiikka pätee; artikkeli HotPDF EMF- ja WMF-vektorituonti kertoo, miten kyseinen komponentti muuttaa liukuväri- ja hatch-siveltimet PDF-kuvioiksi, ja artikkeli vektorigrafiikka, shaderit ja liukuvärit PDFlibPasissa kertoo samojen muotojen piirtämisestä suoraan kirjaston API:lla metatiedoston sijaan

PDFlibPas v3.539.43 tai uudempi sisältää kaikki yllä olevat säännöt. Yksityiskohdat ja kokeiluversiot ovat PDFlibPas Delphi PDF library -tuotesivulla