Το PDFlibPas, η βιβλιοθήκη PDF της losLab για Delphi, μετατρέπει τα EMF records της οικογένειας Poly* σε διαδρομές PDF τηρώντας τον ορισμό κάθε record στο [MS-EMF]: ένα 32-bit EMR_POLYBEZIER ξεκινά από το σημείο 0, τα polylines μένουν ανοιχτά και παίρνουν μόνο stroke, το PT_CLOSEFIGURE στο EMR_POLYDRAW είναι flag, και κάθε πλήθος σημείων ελέγχεται σε σχέση με το μέγεθος του record. Οι κανόνες αυτοί μπήκαν σε τρεις εκδόσεις, v3.539.39, v3.539.41 και v3.539.43. Πριν από αυτά, ένα γράφημα αναφοράς μπορούσε να βγει από το ImportEMFFromFile με μια γεμάτη σφήνα εκεί που έπρεπε να είναι γραμμή τάσης, μια καμπύλη Bezier λυγισμένη προς λάθος control point, ή ένα κλειστό περίγραμμα που έλειπε την τελευταία του πλευρά. Κανένα από αυτά δεν πέταγε σφάλμα, και οι κανόνες ισχύουν για οποιονδήποτε μετατροπέα Delphi από EMF σε PDF ή parser GDI records
Γιατί πηγαίνουν στραβά τα EMF records Poly* στη μετατροπή σε PDF;
Τα EMF records Poly* πηγαίνουν στραβά επειδή το καθένα κουβαλά μέρος της σημασίας του έξω από τα σημεία του: αν το σχήμα είναι ανοιχτό, αν ξεκινά από την τρέχουσα θέση, ποια pen και ποιο brush ισχύουν, και πού μέσα στο record αρχίζουν τα σημεία. Ένα enhanced metafile είναι η καταγραφή κλήσεων GDI πάνω σε ένα device context, οπότε ένας converter πρέπει να ξαναπαίξει την κατάσταση εκείνου του device context εκτός από τις συντεταγμένες. Το PDF δεν έχει device context. Έχει μια διαδρομή, ένα τρέχον σημείο μέσα της, και έναν τελεστή ζωγραφικής που αποφασίζει ανάμεσα σε stroke (S), fill (f) και τα δύο (B). Κάθε ασυμφωνία των δύο μοντέλων γίνεται αθόρυβη διαφορά απεικόνισης
Η οικογένεια Poly* έρχεται επίσης σε δύο πλάτη. Κάθε 32-bit record όπως το EMR_POLYLINE έχει ένα 16-bit δίδυμο όπως το EMR_POLYLINE16 που αποθηκεύει τα σημεία ως ζεύγη SmallInt. Το GDI συνήθως γράφει τη συμπαγή μορφή όταν χωράει κάθε συντεταγμένη, οπότε οι 32-bit handlers ενός converter μπορούν να μένουν λάθος για χρόνια ενώ τα καθημερινά test σχέδια δεν τους φτάνουν ποτέ. Ο γρηγορότερος έλεγχος είναι να περάσετε τα ίδια σημεία και από τα δύο records και να συγκρίνετε τις προκύπτουσες διαδρομές. Τα records που καλύπτονται εδώ ανήκουν όλα στην ομάδα drawing records του [MS-EMF] (2.3.5 Drawing Record Types)
| Record | Ξεκινά από | Κλείνει; | Τρέχουσα θέση |
|---|---|---|---|
EMR_POLYBEZIER | Σημείο 0 | Όχι | Δεν χρησιμοποιείται, δεν ενημερώνεται |
EMR_POLYLINE | Σημείο 0 | Όχι (μόνο pen) | Δεν χρησιμοποιείται, δεν ενημερώνεται |
EMR_POLYLINETO | Τρέχουσα θέση | Όχι (μόνο pen) | Χρησιμοποιείται και ενημερώνεται |
EMR_POLYPOLYLINE | Πρώτο σημείο κάθε polyline | Όχι (μόνο pen) | Δεν χρησιμοποιείται, δεν ενημερώνεται |
EMR_POLYDRAW | Πρώτο PT_MOVETO, ή τρέχουσα θέση | Μόνο όπου είναι ορισμένο το PT_CLOSEFIGURE | Χρησιμοποιείται και ενημερώνεται |
Πού ξεκινά στην πραγματικότητα μια καμπύλη EMR_POLYBEZIER;
Μια καμπύλη EMR_POLYBEZIER ξεκινά από το σημείο 0, και μόνο τα σημεία από τον δείκτη 1 και μετά ομαδοποιούνται ανά τρία ως control point, control point, end point. Ένα record με 7 σημεία σχεδιάζει λοιπόν δύο κυβικά τμήματα: το 0 είναι η αρχή, τα 1 έως 3 σχηματίζουν το πρώτο τμήμα, τα 4 έως 6 το δεύτερο. Ο 16-bit handler στο PDFlibPas το έκανε ήδη αυτό. Ο 32-bit handler ξεκινούσε την ομαδοποίηση από το σημείο 0, οπότε το σημείο εκκίνησης καταναλώνονταν ως πρώτο control point και κάθε επόμενο τμήμα ολίσθαινε κατά ένα. Η καμπύλη αποδιδόταν, απλώς η λάθος. Από το v3.539.41 και οι δύο πλάτες ανοίγουν τη διαδρομή με m στο σημείο 0 και εκπέμπουν ένα c ανά πλήρη τριάδα μετά
Για τον δικό σας parser: ένα πλήθος που δεν είναι 1 συν πολλαπλάσιο του 3 είναι κακοδιατυπωμένο, και τα σημεία στο τέλος πρέπει να αγνοηθούν αντί να ραφτούν σε μια καμπύλη
PolyDraw: το PT_CLOSEFIGURE είναι flag, όχι τύπος σημείου
Στο EMR_POLYDRAW, το PT_CLOSEFIGURE (τιμή 1) είναι ένα bit που συνδυάζεται με το PT_LINETO (2) ή το PT_BEZIERTO (4), οπότε ένα έγκυρο byte τύπου μπορεί να είναι 3 ή 5. Ο τύπος του σημείου είναι το byte με εκείνο το bit αποκομμένο με mask, και το flag σημαίνει κλείσε το σχήμα μετά το τμήμα που τελειώνει σε αυτό το σημείο. Ο παλιός handler του PDFlibPas ταίριαζε το byte με μονές τιμές σε μια εντολή case, οπότε σημεία τύπου 3 και 5 δεν ταίριαζαν σε τίποτα και παραλείπονταν εντελώς. Ένα ορθογώνιο σχεδιασμένο με PolyDraw έχανε την κλείσιμη πλευρά του, και μια τριάδα Bezier του οποίου το τελευταίο σημείο κρατούσε το flag έχανε εκείνο το σημείο, που έσπρωχνε κάθε επόμενη τριάδα εκτός συγχρονισμού
Από το v3.539.39 ο τύπος διαβάζεται ως Types[i] and not PT_CLOSEFIGURE, και το κλείσιμο εκπέμπεται μόνο μετά από πλήρες τμήμα: μετά τη γραμμή για ένα κλειστό PT_LINETO, και μετά το τρίτο σημείο μιας ομάδας Bezier. Ένα κακοδιατυπωμένο αρχείο που θέτει το flag στο πρώτο ή δεύτερο σημείο μιας τριάδας δεν κλείνει το σχήμα πρόωρα. Δύο συγγενικές διορθώσεις βγήκαν στην ίδια έκδοση:
- Κάθε
PT_MOVETOστο 16-bitEMR_POLYDRAW16ξεκινούσε από την αρχή όλη τη διαδρομή, οπότε ένα record με τρία σχήματα κρατούσε μόνο το τελευταίο· τώρα η πρώτη κίνηση ξεκινά τη διαδρομή και οι επόμενες ανοίγουν subpaths - Ένα record PolyDraw που δεν αρχίζει με
PT_MOVETOξεκινά από την τρέχουσα θέση, όπως λέει ο ορισμός του record, αντί να γράψει τελεστήlήcχωρίς προηγούμενοm
Γιατί δεν πρέπει ποτέ να γεμίζει ένα EMF polyline στο PDF;
Ένα EMF polyline δεν πρέπει ποτέ να γεμίζει γιατί τα EMR_POLYLINE και EMR_POLYPOLYLINE είναι ανοιχτά σχήματα που σχεδιάζονται μόνο με την pen, και το γέμισμα μιας ανοιχτής διαδρομής στο PDF την κλείνει σιωπηλά. Το ISO 32000-1 §8.5.3 λέει ότι οι τελεστές fill κλείνουν κάθε ανοιχτό subpath πριν το ζωγραφίσουν. Ένας converter που εκπέμπει B ή f για ένα polyline τριών σημείων ζωγραφίζει λοιπόν ένα γεμάτο τρίγωνο στο τρέχον χρώμα του brush: τη γεμάτη σφήνα κάτω από τη γραμμή τάσης ενός γραφήματος. Πριν το v3.539.41, το PDFlibPas γέμιζε και τις δύο πλάτες polyline με το brush, και το 32-bit record κλεινόταν επιπλέον ρητά. Σήμερα και οι δύο πλάτες τελειώνουν μόνο με stroke, και η διάκριση του GDI διατηρείται: το Polygon κλείνει και γεμίζει, το Polyline ποτέ
Το PolylineTo ξεκινά από την τρέχουσα θέση
Το EMR_POLYLINETO σχεδιάζει από την τρέχουσα θέση μέσα από κάθε σημείο του record, μένει ανοιχτό, και αφήνει την τρέχουσα θέση στο τελευταίο σημείο. Ο παλιός handler περιείχε επίσης μια ειδική περίπτωση που έσβηνε την pen όταν τα δύο πρώτα σημεία μοιράζονταν συντεταγμένη y, και τίποτα δεν την ξανά άναβε, οπότε κάθε επόμενο record του αρχείου έχανε το περίγραμμά του. Η κατάσταση της pen ανήκει στο EMR_SELECTOBJECT και στο EMR_CREATEPEN· ένας handler drawing record δεν έχει καμία δουλειά να την αλλάζει. Εκείνη η ειδική περίπτωση αφαιρέθηκε στο v3.539.41, και η μορφή του record με ένα μόνο σημείο δεν διαβάζει πια πέρα από τα δικά του σημεία (διορθώθηκε στο v3.539.39)
Τα σημεία του PolyPolyline αρχίζουν μετά τον πίνακα counts
Το 32-bit EMR_POLYPOLYLINE αποθηκεύει nPolys πλήθη και μετά cptl σημεία, και τα σημεία αρχίζουν στο byte offset 32 + nPolys * 4. Η παγίδα είναι στο RTL: η unit Windows δηλώνει το TEMRPolyPolyline με aPolyCounts και aptl ως πίνακες ενός στοιχείου, οπότε το aptl[0] είναι το πρώτο σημείο μόνο όταν το nPolys είναι 1. Κώδικας που δεικτοδοτεί απευθείας το aptl διαβάζει τιμές πλήθους ως συντεταγμένες σε κάθε record πολλών γραμμών. Ο παλιός handler του PDFlibPas υπολόγιζε επιπλέον τον έλεγχο ορίων πάνω σε αυτή τη λάθος διάταξη, οπότε έγκυρα records πολλών γραμμών απορρίπτονταν και μονόγραμμα δεν σχεδίαζαν τίποτα. Από το v3.539.41 το PDFlibPas εντοπίζει τον πίνακα σημείων από το υπολογισμένο offset, όπως έκανε πάντα ο handler του PolyPolygon, και σχεδιάζει κάθε polyline ως δικό του ανοιχτό subpath με ένα stroke στο τέλος. Στο v3.539.43 το 16-bit δίδυμο πήρε την ίδια μεταχείριση· σχεδίαζε τμήμα ανά τμήμα, που έσπαγε τις ενώσεις γραμμών και αγνοούσε επιλεγμένο NULL_PEN
Η προεπιλεγμένη pen και brush, και οι αγκύλες path
Δύο κανόνες κατάστασης συμπληρώνουν τις διορθώσεις polyline στο v3.539.43:
- Ένα φρέσκο device context του GDI έχει ήδη επιλεγμένα
BLACK_PENκαιWHITE_BRUSH, οπότε ένα metafile που σχεδιάζει χωρίς κανέναEMR_SELECTOBJECTεξακολουθεί να σχεδιάζει μαύρα περιγράμματα· ο converter ξεκινούσε χωρίς pen και χωρίς fill και έγραφεn(τέλος διαδρομής, καμία ζωγραφιά) για τέτοια records - Μέσα σε μια αγκύλη
BeginPath/EndPath, έναPolylineούτε χρησιμοποιεί ούτε ενημερώνει την τρέχουσα θέση, οπότε πρέπει να ανοίξει νέο subpath στο πρώτο του σημείο αντί να συνδεθεί με το προηγούμενο σχήμα, και τίποτα δεν επιτρέπεται να ζωγραφιστεί μέχρι η αγκύλη να πάρει stroke ή fill
Χτίσιμο ενός αρχείου δοκιμής EMF με TMetafileCanvas
Ο γρηγορότερος τρόπος να ελέγξετε έναν converter απέναντι σε αυτούς τους κανόνες είναι να καταγράψετε τις τρεις επικίνδυνες κλήσεις σε ένα enhanced metafile με το TMetafileCanvas. Το σχέδιο παρακάτω καταγράφει τις καμπύλες με κούφιο brush και μετά επιλέγει σκόπιμα ένα συμπαγές κίτρινο brush για το polyline: ένας σωστός converter πρέπει να αγνοήσει εκείνο το brush για το polyline, οπότε κάθε κίτρινο στο PDF εξόδου είναι bug. Το PolyDraw δεν έχει wrapper σε TCanvas, οπότε καλείται μέσω του Windows API με το handle του canvas, με bytes τύπου 3 και 5 για να δοκιμαστεί το flag κλεισίματος
uses
Winapi.Windows, System.Types, Vcl.Graphics;
procedure BuildPolyTestEmf(const FileName: string);
const
// Ένα κλειστό τετράγωνο (3 = LINETO + CLOSEFIGURE), μετά ένα κλειστό
// σχήμα Bezier του οποίου η τελευταία τριάδα control τελειώνει με 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; // μόνο περιγράμματα για τις καμπύλες
// Το σημείο 0 είναι η αρχή· τα 1..3 και 4..6 είναι δύο κυβικά τμήματα
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));
// Ανοιχτό σχήμα V με επιλεγμένο συμπαγές brush: παίρνει stroke, δεν κλείνει
// σε κίτρινο τρίγωνο
Canvas.Brush.Style := bsSolid;
Canvas.Brush.Color := clYellow;
Canvas.Polyline([Point(20, 240), Point(120, 160), Point(220, 240)]);
finally
Canvas.Free; // τελειώνει την καταγραφή
end;
Mf.SaveToFile(FileName);
finally
Mf.Free;
end;
end;
Επειδή αυτές οι συντεταγμένες χωράνε σε SmallInt, το GDI κανονικά θα αποθηκεύσει τις 16-bit παραλλαγές. Για να φτάσετε τους 32-bit handlers χρειάζεστε έναν producer που τους γράφει, ή records που χτίζετε στο χέρι. Τα χειροποίητα αρχεία κουβαλούν τη δική τους παγίδα: το VCL TMetafile.LoadFromStream μεταχειρίζεται το stream ως EMF μόνο όταν το υπόλοιπο μήκος είναι αυστηρά μεγαλύτερο από το 108-byte TEnhMetaHeader. Ένα μίνιμαλ χειρόγραφο EMF με κοντό header, ή ένα κενό που είναι ακριβώς 108 bytes, παίρνεται για WMF και απορρίπτεται με "Metafile is not valid". Γράφετε πάντα το πλήρες header των 108 bytes, συμπεριλαμβανομένων των πεδίων επέκτασης, πριν τα test records σας
Εισαγωγή του EMF σε PDF με το PDFlibPas
Το PDFlibPas εισάγει ένα EMF με ImportEMFFromFile ή ImportEMFFromStream, που επιστρέφουν μη μηδενικό image ID σε επιτυχία και 0 σε αποτυχία. GeneralOptions = 0 κρατά τη διαδρομή vector που αφορά αυτό το άρθρο· το 1 ραστεροποιεί το metafile σε bitmap. FontOptions = 1 προσθέτει τις γραμματοσειρές του metafile ως μη ενσωματωμένες TrueType. Η παραλλαγή με stream γυρνά το stream στη θέση 0 πριν τη φόρτωση, οπότε περάστε ένα stream που περιέχει μόνο το metafile
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); // αρχή πάνω αριστερά για το DrawImage
PDF.SetMeasurementUnits(0); // μονάδες points
// FontOptions 1 = προσθήκη γραμματοσειρών ως μη ενσωματωμένες TrueType
// GeneralOptions 0 = εισαγωγή vector, 1 = bitmap
ImageID := PDF.ImportEMFFromFile(EmfFile, 1, 0);
if ImageID = 0 then
raise Exception.Create('The metafile could not be imported');
PDF.SelectImage(ImageID);
// Για EMF, τα ImageWidth / ImageHeight είναι το μέγεθος πλαισίου σε points
PDF.DrawImage(36, 36, PDF.ImageWidth, PDF.ImageHeight);
// Η σελίδα μόνο καλεί τη φόρμα που εισήχθη: 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;
Μια εισαγωγή vector EMF γίνεται form XObject, οπότε το GetPageContentToString επιστρέφει μόνο την ακολουθία save, transform, Do και restore. Οι τελεστές m, l, c, h και S που παράγονται από τα records Poly* μένουν στο stream του form XObject, το οποίο είναι συμπιεσμένο. Για να τους ελέγξετε, αποσυμπιέστε το αποθηκευμένο αρχείο σε ένα PDF object inspector και διαβάστε το stream της φόρμας: για το αρχείο δοκιμής παραπάνω θα πρέπει να δείτε το polyline να τελειώνει σε S χωρίς h μπροστά του, ένα h σε κάθε flag κλεισίματος στα σχήματα PolyDraw, και κανένα f ή B σε κανένα από αυτά τα subpaths. Το DrawImage κλιμακώνει επίσης ένα εισαγμένο EMF ομοιόμορφα με το μικρότερο από Width και Height, οπότε το σχέδιο κρατά τον λόγο διαστάσεων του ακόμα κι αν το πλαίσιο που περνάτε δεν ταιριάζει
Για στόχους Free Pascal, δείτε πώς χτίζει ο εισαγωγέας vector EMF του PDFlibPas κάτω από Free Pascal· η σημασιολογία των records είναι η ίδια όπου κι αν μεταγλωττίζεται ο εισαγωγέας
Πώς πρέπει να μεταχειρίζεται ένας EMF parser τα πλήθη σημείων από το αρχείο;
Ένας EMF parser πρέπει να μεταχειρίζεται κάθε πλήθος σημείων ως μη αξιόπιστη είσοδο και να το ελέγχει σε σχέση με το μέγεθος του record πριν αντιγράψει έστω ένα σημείο. Το EnumEnhMetaFile εγγυάται μόνο ότι κάθε nSize record μένει μέσα στο αρχείο. Δεν ελέγχει ότι το cptl συμφωνεί με το nSize, οπότε ένας handler που αντιγράφει cptl σημεία με Move θα διαβάσει τα επόμενα records, ή πέρα από το τέλος του metafile, όταν το πλήθος είναι πλαστό ή κατεστραμμένο. Από το v3.539.39 το PDFlibPas ελέγχει για τα PolyDraw, PolyBezier, PolyBezierTo, Polyline, PolylineTo και Polygon και στις δύο πλάτες το σταθερό header συν πλήθος επί bytes ανά σημείο σε σχέση με το nSize, με ένα extra byte ανά σημείο για τα bytes τύπου του PolyDraw. Για τα records PolyPoly τα πλήθη ανά σχήμα πρέπει επίσης να αθροίζουν το πολύ το δηλωμένο σύνολο, και τα σχήματα με μηδέν σημεία παραλείπονται
Ο ίδιος έλεγχος είναι αρκετά σύντομος για να τον αντιγράψετε στον δικό σας parser. Αυτή η εκδοχή επικυρώνει ένα 32-bit EMR_POLYPOLYLINE και επιστρέφει δείκτη στον πραγματικό του πίνακα σημείων:
uses
Winapi.Windows;
// Επιστρέφει nil εκτός αν το record κρατά πραγματικά τα σημεία που δηλώνει.
// Τα σημεία αρχίζουν μετά τον πίνακα counts: 32 + nPolys * 4 bytes μέσα, όχι στο
// aptl[0], που το RTL δηλώνει ως πίνακα ενός στοιχείου
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; // πλαστό ή κομμένο πλήθος
Total := 0;
Count := @P^.aPolyCounts[0]; // βήμα με δείκτη: το [0..0] σπάει range checks
for I := 1 to P^.nPolys do
begin
Inc(Total, Count^);
Inc(Count);
end;
if Total > P^.cptl then
Exit; // τα σχήματα διεκδικούν περισσότερα σημεία από όσα υπάρχουν
Result := PPoint(NativeUInt(Rec) + NativeUInt(PointsOffset));
end;
Το test χωρητικότητας των σημείων τρέχει πρώτο, οπότε ο πίνακας counts είναι γνωστό ότι βρίσκεται μέσα στο record πριν ο βρόχος τον διασχίσει. Η αριθμητική είναι Int64 επειδή τα nPolys * 4 και cptl * 8 υπολογισμένα σε 32 bits μπορούν να κάνουν wrap-around και να περάσουν τη σύγκριση
Σύντομη αναφορά: κανόνες EMF Poly* για μετατροπή EMF σε PDF
EMR_POLYBEZIER: το σημείο 0 είναι το σημείο εκκίνησης· ομαδοποίηση από το σημείο 1 ανά τρία· διορθώθηκε για το 32-bit record στο v3.539.41EMR_POLYLINE/EMR_POLYPOLYLINE: ανοιχτά σχήματα, stroke μεS, ποτέh,fήB, επειδή το fill του PDF κλείνει τα ανοιχτά subpathsEMR_POLYLINETO: εκκίνηση από την τρέχουσα θέση, μένει ανοιχτό, ενημερώνει την τρέχουσα θέση, δεν αλλάζει ποτέ την κατάσταση της pen- 32-bit
EMR_POLYPOLYLINE: τα σημεία αρχίζουν στο byte32 + nPolys * 4, όχι στοaptl[0] EMR_POLYDRAW: αφαιρέστε τοPT_CLOSEFIGUREμε mask πριν τη διακλάδωση, κλείστε μετά το ολοκληρωμένο τμήμα, ξεκινήστε από την τρέχουσα θέση όταν το πρώτο σημείο δεν είναιPT_MOVETO- Η προεπιλεγμένη κατάσταση device context είναι
BLACK_PENσυνWHITE_BRUSH· το v3.539.43 και μεταγενέστερα τη σέβονται - Μέσα σε
BeginPath/EndPath, κάθε polyline ανοίγει το δικό του subpath και τίποτα δεν ζωγραφίζεται μέχρι η αγκύλη να χρησιμοποιηθεί - Επικυρώστε κάθε
cptl/cptsσε σχέση με τοnSizeσε 64-bit αριθμητική πριν αντιγράψετε σημεία - Τα χειροποίητα test EMF θέλουν το πλήρες header των 108 bytes, αλλιώς το
TMetafile.LoadFromStreamτα διαβάζει ως WMF
Αν οι αναφορές σας περνούν από άλλο component, η ίδια σημασιολογία records ισχύει· το HotPDF EMF και WMF vector import καλύπτει πώς εκείνο το component μετατρέπει gradient και hatch brushes σε PDF patterns, και το vector graphics, shaders και gradients στο PDFlibPas καλύπτει το σχέδιο των ίδιων σχημάτων απευθείας με το API της βιβλιοθήκης αντί μέσω metafile
Το PDFlibPas v3.539.43 ή νεότερο περιλαμβάνει όλους τους παραπάνω κανόνες. Λεπτομέρειες και δοκιμαστικές λήψεις βρίσκονται στη σελίδα προϊόντος PDFlibPas Delphi PDF library