Une hachure qui se rend comme un seul bloc gris plat est l échec classique des motifs en mosaïque. HotPDF, le composant VCL PDF natif pour Delphi et C++Builder, peint le PatternType 1 en transformant le chemin actuel en écrêtage temporaire et en rejouant le flux de contenu du motif une fois par tuile visible, avec la sélection du motif conservée dans l état graphique et restaurée par q et Q
Les symptômes arrivent en deux saveurs, et elles paraissent sans rapport tant qu on n en connaît pas la cause. Un dessin CAO perd sa hachure de section et revient sous forme de remplissages solides, car le moteur de rendu a résolu le motif en une couleur moyenne et a peint cela. Ou la hachure s échappe : un cartouche de titre censé être blanc uni récupère les lignes diagonales d une vue de détail deux chemins plus tôt. Les deux sont des problèmes d état de motif, et un seul d entre eux concerne réellement le dessin des tuiles
Pourquoi un motif en mosaïque déborde-t-il sur le chemin suivant ?
Parce que le nom de motif sélectionné fait partie de l état graphique, pas une propriété de l opérateur qui l a utilisé. La norme ISO 32000-1 §8.6.6.2 définit un espace de couleur Pattern comme un dont la valeur de couleur est un nom de motif fourni à scn ou SCN, et chaque autre composante de l état de couleur est sauvegardée par q et restaurée par Q. Le nom du motif doit suivre la même règle. HotPDF le conserve dans l enregistrement d état sous FillPatternName et StrokePatternName, aux côtés de la famille d espace de couleur de remplissage et de trait, de sorte qu un Q remet la sélection précédente exactement comme il remet la CTM précédente
Stockez ce nom dans une variable locale à l intérieur du répartiteur d opérateurs à la place, et il survit à chaque Q du flux. L échec apparaît alors quelque part d inattendu : un XObject de formulaire dessiné après le chemin en mosaïque hérite d une sélection de motif que son propre flux de contenu n a jamais faite, et ses remplissages ressortent hachurés. Les formulaires imbriqués aggravent les choses, car chaque niveau d imbrication empile et dépile un état que la variable errante ignore. Définir un espace de couleur non-motif avec cs ou CS, ou émettre un simple g / rg / k, doit aussi effacer le nom du motif, sinon la sélection périmée survit à l espace de couleur qui lui donnait un sens
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
Un motif est peint à travers un écrêtage, jamais comme un remplissage
Le modèle correct est soustractif : restreindre l écrêtage du périphérique à la forme peinte, puis exécuter le contenu du motif à l intérieur. HotPDF ne dessine jamais une approximation solide d abord pour la surpeindre ensuite, car le solide intermédiaire serait visible à travers les interstices entre les tuiles et se heurterait à toute transparence dans le contenu de la tuile. Le §8.7.3.2 décrit un motif en mosaïque comme un flux de contenu répliqué à des intervalles horizontaux et verticaux fixes, et la réplication n a de sens que contre un écrêtage qui a déjà la bonne forme. Pour les remplissages, la conversion est directe : HPDFSelectFillPathClip définit le mode de remplissage du polygone à ALTERNATE pour f*, B* et b* et à WINDING pour les variantes non nulles, construit le chemin GDI, et l intersecte dans l écrêtage avec SelectClipPath. Cette seule ligne est ce qui fait qu un remplissage en mosaïque pair-impair laisse les mêmes trous qu un remplissage solide pair-impair, ce qui est exactement ce dont a besoin une région hachurée en forme de beignet
Les traits sont la partie facile à mal faire. Un chemin tracé n a pas d intérieur, donc intersecter le chemin lui-même dans l écrêtage produit une région vide et rien n est peint. HPDFSelectStrokePathClip construit donc d abord un stylo géométrique à partir de l état actuel, en utilisant PS_GEOMETRIC avec le bouchon d extrémité de J, la jonction de j, la limite de biseau de M, et PS_USERSTYLE quand un tableau de tirets est actif, puis appelle WidenPath pour convertir le contour tracé en une région remplissable avant l écrêtage. Le comportement de bouchon, de jonction, de biseau et de tirets sur un chemin tracé en mosaïque correspond alors à un trait normal par construction plutôt que par une seconde implémentation. Deux limites honnêtes existent ici : les largeurs de ligne inférieures à une unité de périphérique sont plafonnées à un pixel, et le tableau de tirets est tronqué à seize entrées, ce qui est le plafond que ExtCreatePen accepte
Quelles tuiles sont réellement visibles ?
La plage visible provient de l exécution de la transformation à l envers. Le placement des tuiles se fait dans l espace du motif, mais la seule chose qui sait quelle partie de la page est touchée est la boîte d écrêtage du périphérique, qui est dans l espace du périphérique. HotPDF compose BaseMatrix := CTM * PatternMatrix, l inverse, et mappe les quatre coins de la boîte d écrêtage GDI en arrière à travers l inverse. Les limites alignées sur les axes de ces quatre coins mappés donnent le rectangle en espace du motif qui peut possiblement être couvert, et diviser ce rectangle par XStep et YStep contre le BBox du motif donne des plages d indices fermées. Chaque cellule se rend alors avec une CTM de CTM * PatternMatrix * Translate(i * XStep, j * YStep), et est écrêtée une seconde fois à son propre polygone BBox transformé. Ce second écrêtage compte quand XStep est plus petit que la largeur de la boîte englobante, ce qui est la façon dont les motifs de tuiles chevauchantes sont exprimés ; sans cela, les cellules voisines se peindraient l une sur l autre en dehors de leur étendue déclarée. Si l écrêtage par cellule revient comme NULLREGION, la cellule est ignorée sans tokeniser ni exécuter quoi que ce soit
// 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));
Motifs non colorés et la couleur qui vient de l extérieur
Un motif PaintType 2 porte une forme mais pas de couleur, et la couleur arrive avec le nom du motif. Le §8.7.3.2 spécifie qu un motif non coloré n est utilisé qu avec un espace de couleur Pattern qui déclare un espace sous-jacent, donc scn reçoit d abord les valeurs de composante puis le nom du motif en dernier. HotPDF résout ces composantes à travers l espace sous-jacent stocké sur l entrée d espace de couleur du motif, ce qui signifie qu une hachure non colorée peut être teintée avec une encre Separation ou une combinaison DeviceN exactement comme n importe quel autre remplissage ; la mécanique de cette résolution est couverte dans le rendu des couleurs d accompagnement Separation et DeviceN. À l intérieur de la tuile, les deux types de peinture divergent nettement. Pour le PaintType 2, le moteur de rendu définit un indicateur de suppression d opérateur de couleur pour la durée de la tuile, de sorte que tout g, rg, k ou scn dans le contenu du motif est ignoré et chaque marque prend la couleur fournie de l extérieur. Pour le PaintType 1, l inverse s applique : l état de remplissage et de trait est réinitialisé aux valeurs par défaut PDF, DeviceGray noir avec un espace de couleur identité, et la tuile se colore elle-même. Sauter cette réinitialisation laisse la couleur qui se trouvait être actuelle à l opérateur f s infiltrer dans un motif censé se décrire lui-même
Pourquoi la profondeur de la pile d état graphique doit-elle être restaurée après chaque tuile ?
Parce qu un flux de contenu de motif est autorisé à être déséquilibré, et le dommage se cumule à travers les cellules. Une tuile dont le flux contient trois opérateurs q et deux opérateurs Q laisse la pile un cadre plus profonde qu au départ. Ne restaurer que l enregistrement d état actuel entre les cellules et la profondeur continue de croître, de sorte que la cellule numéro deux cents s exécute depuis un cadre de pile appartenant à la cellule numéro cent quatre-vingt-dix-neuf, avec quelle que soit la CTM et l écrêtage que ce cadre portait. HotPDF prend donc un instantané de l enregistrement d état et de la profondeur de pile avant la boucle de tuiles et appelle RestoreSnapshot au début de chaque itération, ce qui tronque la pile à la longueur sauvegardée et réinstalle l état sauvegardé en une seule étape. Le dictionnaire Resources de la page et l indicateur de suppression d opérateur de couleur sont restaurés à la même limite, puisqu une tuile peut référencer ses propres ressources et ne doit pas les transmettre à sa voisine. L état d écrêtage GDI reçoit le même traitement via une paire SaveDC / RestoreDC autour de chaque cellule, de sorte qu une tuile qui installe son propre écrêtage W n ne peut pas rétrécir la région disponible pour la suivante
Budgets, refus, et ce que le moteur de rendu ne dessinera pas
Les motifs en mosaïque sont l endroit le plus facile d un PDF pour écrire un fichier de déni de service, donc les limites sont des chiffres stricts plutôt que des heuristiques. L imbrication de motifs est plafonnée à une profondeur de 4, la même garde utilisée pour la récursion de XObject de formulaire, ce qui arrête un motif qui se référence lui-même à travers son propre dictionnaire de ressources. Une seule peinture de chemin peut exécuter au maximum 16 384 tuiles au total, décomptées à travers les motifs imbriqués et réinitialisées seulement quand la peinture du motif le plus externe commence. Une grille de tuiles dont le nombre de cellules prévu dépasse ce qu il reste de ce budget est rejetée d emblée, avant qu une seule cellule ne s exécute
La géométrie dégénérée est refusée plutôt qu approximée. Un BBox manquant ou de surface nulle, un XStep ou YStep dont la magnitude est inférieure à 1e-6, un produit CTM * PatternMatrix sans inverse, des coordonnées d écrêtage mappées au-delà de 1e9, ou une magnitude d indice au-delà d un million provoquent tous le retour de la peinture du motif sans dessiner. Le résultat est une région non peinte plutôt qu un fil de rendu bloqué, ce qui est le compromis que vous voulez dans un convertisseur par lots. La performance vient d une décision : le flux du motif est tokenisé une fois par peinture avec HPDFTokenizeContentStream et le tableau de jetons est réutilisé pour chaque cellule visible, de sorte que le nombre de tuiles multiplie le coût d exécution mais jamais le coût d analyse lexicale
Rendre une page à motif depuis Delphi
Rien dans le support des motifs ne change le code appelant. Chargez le document, demandez une page, et le travail de mosaïque se produit à l intérieur de l interpréteur de flux de contenu que le rendu page-vers-bitmap pilote déjà. Le même interpréteur alimente les contextes de périphérique bitmap, métafichier et imprimante, donc un dessin hachuré qui paraît correct dans une vignette d aperçu s imprime avec la même géométrie de tuiles. Les motifs de dégradé PatternType 2 empruntent une branche différente qui partage son chemin d évaluation avec l opérateur nu sh, décrit en détail sous le rendu des dégradés axiaux et radiaux
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;
Quand une région à motif paraît toujours incorrecte, vérifiez les trois classes d échec dans l ordre. Une région entièrement vide signifie généralement un refus : inspectez XStep, YStep et BBox pour des valeurs dégénérées, ou comptez les tuiles dont la grille aurait besoin contre le plafond de 16 384. Une région peinte d une seule couleur plate signifie que le nom du motif n a jamais atteint l opérateur de peinture, ce qui pointe vers l ordre cs et scn dans le flux. Un motif qui apparaît là où il ne devrait pas signifie une restauration d état, et l endroit à regarder est la gestion q / Q autour du formulaire ou du chemin qui l a hérité
Les motifs en mosaïque sont une de ces fonctionnalités PDF qui restent invisibles jusqu à ce que le fichier qui en a besoin atterrisse dans votre boîte de réception, et alors elles deviennent tout le travail. Si vous construisez des visualiseurs de dessins, des convertisseurs de documents d ingénierie ou des moteurs de rendu de rapports sur Delphi ou C++Builder, le composant complet et son API de rendu sont documentés sur la page composant PDF Delphi HotPDF