Η γραμμοσκίαση που αποδίδεται ως ένα επίπεδο γκρι μπλοκ είναι η κλασική αποτυχία tiling pattern. Το HotPDF, το εγγενές VCL component PDF για Delphi και C++Builder, ζωγραφίζει το PatternType 1 μετατρέποντας την τρέχουσα διαδρομή σε προσωρινό clip και επαναλαμβάνοντας το ρεύμα περιεχομένου του pattern μία φορά ανά ορατό tile, με την επιλογή pattern κρατημένη στην κατάσταση γραφικών και επαναφερόμενη από τα q και Q
Τα συμπτώματα έρχονται σε δύο γεύσεις, και φαίνονται άσχετα μέχρι να γνωρίζετε την αιτία. Ένα σχέδιο CAD χάνει τη γραμμοσκίαση τμήματος του και επιστρέφει ως συμπαγή γεμίσματα, επειδή ο renderer έλυσε το pattern σε ένα μέσο χρώμα και ζωγράφισε αυτό. Ή η γραμμοσκίαση διαφεύγει: ένα title block που θα έπρεπε να είναι απλό λευκό παίρνει τις διαγώνιες γραμμές από μια λεπτομερή προβολή δύο διαδρομές νωρίτερα. Και τα δύο είναι προβλήματα κατάστασης pattern, και μόνο ένα από αυτά αφορά καθόλου το ζωγράφισμα tiles
Γιατί ένα tiling pattern «ματώνει» στην επόμενη διαδρομή;
Επειδή το επιλεγμένο όνομα pattern είναι μέρος της κατάστασης γραφικών, όχι ιδιότητα του τελεστή που το χρησιμοποίησε. Το ISO 32000-1 §8.6.6.2 ορίζει έναν χώρο χρώματος Pattern ως έναν του οποίου η τιμή χρώματος είναι ένα όνομα pattern που παρέχεται στο scn ή SCN, και κάθε άλλο συστατικό της κατάστασης χρώματος αποθηκεύεται από το q και επαναφέρεται από το Q. Το όνομα pattern πρέπει να ακολουθεί τον ίδιο κανόνα. Το HotPDF το κρατά στην εγγραφή κατάστασης ως FillPatternName και StrokePatternName, δίπλα στην οικογένεια χώρου χρώματος γεμίσματος και περιγράμματος, ώστε ένα Q να επαναφέρει την προηγούμενη επιλογή ακριβώς όπως επαναφέρει το προηγούμενο CTM
Αποθηκεύστε αυτό το όνομα σε τοπική μεταβλητή μέσα στον διανομέα τελεστών αντ' αυτού, και επιβιώνει από κάθε Q στο ρεύμα. Η αποτυχία τότε εμφανίζεται κάπου απρόσμενα: ένα Form XObject που σχεδιάζεται μετά τη μοτιβοποιημένη διαδρομή κληρονομεί μια επιλογή pattern που το δικό του ρεύμα περιεχομένου ποτέ δεν έκανε, και τα γεμίσματά του βγαίνουν με γραμμοσκίαση. Οι εμφωλευμένες φόρμες το κάνουν χειρότερο, επειδή κάθε επίπεδο εμφώλευσης προωθεί και ανασύρει κατάσταση που η αδέσποτη μεταβλητή αγνοεί. Ο ορισμός ενός χώρου χρώματος που δεν είναι pattern με το cs ή CS, ή η έκδοση ενός απλού g / rg / k, πρέπει επίσης να καθαρίσει το όνομα pattern, αλλιώς η παλιά επιλογή επιζεί του χώρου χρώματος που της έδωσε νόημα
q
/Pattern cs % pattern colour space, ISO 32000-1 8.6.6.2
/P1 scn % coloured tiling pattern, PaintType 1
10 10 200 120 re f % this rectangle is hatched
Q
0 0 300 200 re f % must be black again, not hatched
q
/Cs2 cs % [/Pattern /DeviceCMYK] array
0 0.6 1 0 /P2 scn % uncoloured pattern plus its underlying colour
20 20 160 90 re f*
Q
Ένα pattern ζωγραφίζεται μέσω ενός clip, ποτέ ως γέμισμα
Το σωστό μοντέλο είναι αφαιρετικό: περιορίστε το clip της συσκευής στο σχήμα που ζωγραφίζεται, μετά τρέξτε το περιεχόμενο του pattern μέσα του. Το HotPDF ποτέ δεν σχεδιάζει πρώτα μια συμπαγή προσέγγιση και τη ζωγραφίζει από πάνω, επειδή το ενδιάμεσο συμπαγές θα ήταν ορατό μέσα από τα κενά ανάμεσα στα tiles και θα συγκρουόταν με οποιαδήποτε διαφάνεια στο περιεχόμενο του tile. Το §8.7.3.2 περιγράφει ένα tiling pattern ως ρεύμα περιεχομένου που αναπαράγεται σε σταθερά οριζόντια και κάθετα διαστήματα, και η αναπαραγωγή έχει νόημα μόνο έναντι ενός clip που έχει ήδη το σωστό σχήμα. Για γεμίσματα η μετατροπή είναι άμεση: η HPDFSelectFillPathClip ορίζει τη λειτουργία γεμίσματος πολυγώνου σε ALTERNATE για f*, B* και b* και σε WINDING για τις nonzero παραλλαγές, χτίζει τη διαδρομή GDI, και τη τέμνει στο clip με το SelectClipPath. Αυτή η μία γραμμή είναι που κάνει ένα γέμισμα με pattern even-odd να αφήνει τις ίδιες τρύπες με ένα συμπαγές γέμισμα even-odd, που είναι ακριβώς αυτό που χρειάζεται μια περιοχή γραμμοσκιασμένη σε σχήμα κρίκου
Τα περιγράμματα (strokes) είναι το μέρος που είναι εύκολο να πάει λάθος. Μια διαδρομή με stroke δεν έχει εσωτερικό, οπότε η τομή της ίδιας της διαδρομής στο clip δίνει κενή περιοχή και τίποτα δεν ζωγραφίζεται. Η HPDFSelectStrokePathClip επομένως χτίζει πρώτα μια γεωμετρική πένα από την τρέχουσα κατάσταση, χρησιμοποιώντας PS_GEOMETRIC με το cap άκρου από το J, την ένωση από το j, το όριο miter από το M, και PS_USERSTYLE όταν είναι ενεργός ένας πίνακας dash, μετά καλεί την WidenPath για να μετατρέψει το περίγραμμα με stroke σε περιοχή που μπορεί να γεμιστεί πριν κόψει. Η συμπεριφορά cap, join, miter και dash σε μια διαδρομή με stroke pattern τότε ταιριάζει με ένα κανονικό stroke εκ κατασκευής και όχι από δεύτερη υλοποίηση. Δύο ειλικρινή όρια ζουν εδώ: τα πλάτη γραμμής κάτω από μία μονάδα συσκευής περιορίζονται σε ένα pixel, και ο πίνακας dash περικόπτεται στις δεκαέξι καταχωρίσεις, που είναι το ανώτατο όριο που δέχεται το ExtCreatePen
Ποια tiles είναι στην πραγματικότητα ορατά;
Το ορατό εύρος προέρχεται από την εκτέλεση του μετασχηματισμού προς τα πίσω. Η τοποθέτηση tile συμβαίνει στον χώρο pattern, αλλά το μόνο πράγμα που ξέρει πόσο από τη σελίδα αγγίζεται είναι το κουτί clip της συσκευής, που είναι στον χώρο συσκευής. Το HotPDF συνθέτει BaseMatrix := CTM * PatternMatrix, το αντιστρέφει, και απεικονίζει τις τέσσερις γωνίες του κουτιού clip GDI πίσω μέσω του αντιστρόφου. Τα ευθυγραμμισμένα με τους άξονες όρια αυτών των τεσσάρων απεικονισμένων γωνιών δίνουν το ορθογώνιο στον χώρο pattern που μπορεί ενδεχομένως να καλυφθεί, και η διαίρεση αυτού του ορθογωνίου με XStep και YStep έναντι του BBox του pattern δίνει κλειστά εύρη δεικτών. Κάθε κελί μετά αποδίδεται με ένα CTM του CTM * PatternMatrix * Translate(i * XStep, j * YStep), και κόβεται μια δεύτερη φορά στο δικό του μετασχηματισμένο πολύγωνο BBox. Αυτό το δεύτερο clip έχει σημασία όταν το XStep είναι μικρότερο από το πλάτος του κουτιού οριοθέτησης, που είναι ο τρόπος με τον οποίο εκφράζονται επικαλυπτόμενα σχέδια tile· χωρίς αυτό, γειτονικά κελιά θα ζωγράφιζαν το ένα πάνω στο άλλο έξω από τη δηλωμένη έκτασή τους. Αν το clip ανά κελί επιστρέψει ως NULLREGION, το κελί παραλείπεται χωρίς να γίνει tokenizing ή εκτέλεση οτιδήποτε
// Map the device clip box back into pattern space through the inverse of
// CTM * PatternMatrix, then convert those bounds into tile index ranges.
BaseMatrix := HPDFMatMul(FGSStack.State.CTM, PatternMatrix);
if not HPDFMatInvert(BaseMatrix, InverseMatrix) then Exit; // singular: refuse
if GetClipBox(FDC, ClipRect) = ERROR then Exit;
// MinX..MaxY are the axis-aligned bounds of the four mapped clip corners.
I0 := Floor((MinX - BBox[2]) / StepXAbs);
I1 := Ceil ((MaxX - BBox[0]) / StepXAbs);
J0 := Floor((MinY - BBox[3]) / StepYAbs);
J1 := Ceil ((MaxY - BBox[1]) / StepYAbs);
PlannedTiles := Int64(I1 - I0 + 1) * Int64(J1 - J0 + 1);
if (PlannedTiles <= 0) or (PlannedTiles > FPatternTilesRemaining) then Exit;
Dec(FPatternTilesRemaining, Integer(PlannedTiles));
Άχρωμα patterns και το χρώμα που έρχεται απ' έξω
Ένα pattern PaintType 2 φέρει σχήμα αλλά όχι χρώμα, και το χρώμα φτάνει με το όνομα pattern. Το §8.7.3.2 προσδιορίζει ότι ένα άχρωμο pattern χρησιμοποιείται μόνο με έναν χώρο χρώματος Pattern που δηλώνει έναν υποκείμενο χώρο, οπότε το scn λαμβάνει πρώτα τις τιμές συστατικών και το όνομα pattern τελευταίο. Το HotPDF επιλύει αυτά τα συστατικά μέσω του υποκείμενου χώρου αποθηκευμένου στην καταχώριση χώρου χρώματος pattern, που σημαίνει ότι μια άχρωμη γραμμοσκίαση μπορεί να αποχρωματιστεί με ένα μελάνι Separation ή έναν συνδυασμό DeviceN ακριβώς όπως κάθε άλλο γέμισμα· οι μηχανισμοί αυτής της επίλυσης καλύπτονται στην απόδοση χρωμάτων spot Separation και DeviceN. Μέσα στο tile, οι δύο τύποι ζωγραφικής αποκλίνουν έντονα. Για το PaintType 2 ο renderer ορίζει μια σημαία καταστολής τελεστή χρώματος για τη διάρκεια του tile, οπότε κάθε g, rg, k ή scn στο περιεχόμενο του pattern αγνοείται και κάθε σημάδι παίρνει το εξωτερικά παρεχόμενο χρώμα. Για το PaintType 1 ισχύει το αντίθετο: η κατάσταση γεμίσματος και περιγράμματος επαναφέρεται στις προεπιλογές PDF, DeviceGray μαύρο με χώρο χρώματος ταυτότητας, και το tile χρωματίζει τον εαυτό του. Η παράλειψη αυτής της επαναφοράς αφήνει το χρώμα που έτυχε να είναι τρέχον στον τελεστή f να διαρρεύσει σε ένα pattern που υποτίθεται ότι περιέγραφε τον εαυτό του
Γιατί πρέπει να επαναφέρεται το βάθος στοίβας κατάστασης γραφικών μετά από κάθε tile;
Επειδή ένα ρεύμα περιεχομένου pattern επιτρέπεται να είναι μη ισορροπημένο, και η ζημιά συσσωρεύεται κατά μήκος κελιών. Ένα tile του οποίου το ρεύμα περιέχει τρεις τελεστές q και δύο τελεστές Q αφήνει τη στοίβα ένα καρέ βαθύτερα από ό,τι ξεκίνησε. Επαναφέρετε μόνο την τρέχουσα εγγραφή κατάστασης ανάμεσα σε κελιά και το βάθος συνεχίζει να μεγαλώνει, οπότε το κελί νούμερο διακόσια εκτελείται από ένα καρέ στοίβας που ανήκει στο κελί νούμερο εκατόν ενενήντα εννέα, με όποιο CTM και clip έφερε εκείνο το καρέ. Το HotPDF επομένως παίρνει στιγμιότυπο της εγγραφής κατάστασης και του βάθους στοίβας πριν τον βρόχο tile και καλεί το RestoreSnapshot στην κορυφή κάθε επανάληψης, που περικόπτει τη στοίβα πίσω στο αποθηκευμένο μήκος και επανεγκαθιστά την αποθηκευμένη κατάσταση σε ένα βήμα. Το λεξικό Resources σελίδας και η σημαία καταστολής τελεστή χρώματος επαναφέρονται στο ίδιο όριο, αφού ένα tile μπορεί να αναφέρει τους δικούς του πόρους και δεν πρέπει να τους δώσει στον γείτονά του. Η κατάσταση clip GDI παίρνει την ίδια μεταχείριση μέσω ενός ζεύγους SaveDC / RestoreDC γύρω από κάθε κελί, ώστε ένα tile που εγκαθιστά το δικό του clip W n να μην μπορεί να συρρικνώσει την περιοχή διαθέσιμη στο επόμενο
Budgets, αρνήσεις, και τι δεν θα σχεδιάσει ο renderer
Τα tiling patterns είναι το ευκολότερο σημείο σε ένα PDF για να γραφτεί αρχείο άρνησης υπηρεσίας, οπότε τα όρια είναι σκληροί αριθμοί αντί για ευρετικές. Η εμφώλευση pattern έχει ανώτατο όριο βάθος 4, την ίδια φρουρά που χρησιμοποιείται για την αναδρομή Form XObject, που σταματά ένα pattern που αναφέρει τον εαυτό του μέσω του δικού του λεξικού πόρων. Ένα μεμονωμένο ζωγράφισμα διαδρομής μπορεί να εκτελέσει το πολύ 16.384 tiles συνολικά, μετρημένα προς τα κάτω κατά μήκος εμφωλευμένων patterns και επανεκκινούμενα μόνο όταν αρχίζει το εξωτερικότερο ζωγράφισμα pattern. Ένα πλέγμα tile του οποίου ο προγραμματισμένος αριθμός κελιών υπερβαίνει ό,τι έχει απομείνει από αυτό το budget απορρίπτεται εντελώς, πριν τρέξει ούτε ένα κελί
Η εκφυλισμένη γεωμετρία απορρίπτεται αντί να προσεγγιστεί. Ένα λείπον ή μηδενικής επιφάνειας BBox, ένα XStep ή YStep του οποίου το μέγεθος είναι κάτω από 1e-6, ένα γινόμενο CTM * PatternMatrix χωρίς αντίστροφο, απεικονισμένες συντεταγμένες clip πέρα από 1e9, ή ένα μέγεθος δείκτη πέρα από ένα εκατομμύριο όλα προκαλούν την επιστροφή του ζωγραφίσματος pattern χωρίς να σχεδιαστεί τίποτα. Το αποτέλεσμα είναι μια αζωγράφιστη περιοχή αντί για ένα κρεμασμένο νήμα απόδοσης, που είναι η ανταλλαγή που θέλετε σε έναν μετατροπέα παρτίδων. Η απόδοση προέρχεται από μία απόφαση: το ρεύμα pattern γίνεται tokenized μία φορά ανά ζωγράφισμα με το HPDFTokenizeContentStream και ο πίνακας tokens επαναχρησιμοποιείται σε κάθε ορατό κελί, οπότε ο αριθμός tiles πολλαπλασιάζει το κόστος εκτέλεσης αλλά ποτέ το κόστος lexing
Απόδοση μιας μοτιβοποιημένης σελίδας από Delphi
Τίποτα σχετικά με την υποστήριξη pattern δεν αλλάζει τον κώδικα κλήσης. Φορτώστε το έγγραφο, ζητήστε μια σελίδα, και η δουλειά tiling συμβαίνει μέσα στον διερμηνέα ρεύματος περιεχομένου που η απόδοση σελίδας σε bitmap ήδη οδηγεί. Ο ίδιος διερμηνέας τροφοδοτεί contexts συσκευής bitmap, metafile και εκτυπωτή, οπότε ένα σχέδιο με γραμμοσκίαση που φαίνεται σωστό σε ένα thumbnail προεπισκόπησης εκτυπώνεται με την ίδια γεωμετρία tile. Τα patterns σκίασης PatternType 2 παίρνουν διαφορετικό κλάδο που μοιράζεται τη διαδρομή αξιολόγησής του με τον γυμνό τελεστή sh, που περιγράφεται λεπτομερώς στην απόδοση axial και radial shading
var
Pdf: THotPDF;
Bmp: TBitmap;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('assembly-drawing.pdf') > 0 then
begin
// Section hatching that previously flattened to a solid block now
// replays the tile content once per visible cell.
Bmp := Pdf.RenderLoadedPageToBitmap(0, 200);
if Assigned(Bmp) then
try
Bmp.SaveToFile('sheet1.bmp');
finally
Bmp.Free;
end;
end;
finally
Pdf.Free;
end;
end;
Όταν μια μοτιβοποιημένη περιοχή εξακολουθεί να φαίνεται λάθος, ελέγξτε τις τρεις κατηγορίες αποτυχίας με σειρά. Μια περιοχή εντελώς κενή συνήθως σημαίνει άρνηση: επιθεωρήστε τα XStep, YStep και BBox για εκφυλισμένες τιμές, ή μετρήστε τα tiles που θα χρειαζόταν το πλέγμα έναντι του ανώτατου ορίου 16.384. Μια περιοχή ζωγραφισμένη σε ένα μόνο επίπεδο χρώμα σημαίνει ότι το όνομα pattern ποτέ δεν έφτασε στον τελεστή ζωγραφίσματος, που δείχνει στη σειρά cs και scn στο ρεύμα. Ένα pattern που εμφανίζεται εκεί όπου δεν ανήκει σημαίνει επαναφορά κατάστασης, και το σημείο για να κοιτάξετε είναι ο χειρισμός q / Q γύρω από τη φόρμα ή τη διαδρομή που το κληρονόμησε
Τα tiling patterns είναι ένα από εκείνα τα χαρακτηριστικά PDF που παραμένουν αόρατα μέχρι το αρχείο που τα χρειάζεται να προσγειωθεί στα εισερχόμενά σας, και τότε είναι ολόκληρη η δουλειά. Αν χτίζετε viewers σχεδίων, μετατροπείς εγγράφων μηχανικής ή renderers αναφορών σε Delphi ή C++Builder, το πλήρες component και το API απόδοσής του τεκμηριώνονται στη σελίδα του HotPDF Delphi PDF component