Un logo EPS piazzato su una pagina PDF può uscire con il suo contorno mancante, un marchio a un colore stampato come il suo negativo, o Æ trasformato in á. Ogni sintomo segnala un punto dove la semantica grafica di PostScript e PDF è in disaccordo, così l'importazione EPS di PDFlibPas per Delphi interpreta il programma PostScript e scrive ogni differenza come operatori PDF nativi
Interpretare il programma o farlo passare tale e quale
Il PostScript può stare dentro un PDF in due modi, e solo uno di loro mostra qualcosa sullo schermo. ISO 32000-1 §8.8.2 definisce un PostScript XObject, uno stream /Subtype /PS i cui byte vengono copiati, non interpretati, nel lavoro di stampa solo quando il device di output è una stampante PostScript; ovunque altrove non ha effetto, e la panoramica §8.8 dice che i PostScript XObject non dovrebbero essere usati. PDF/A li esclude categoricamente. TPDFlib.NewPostScriptXObject e DrawPostScriptXObject scrivono quella forma a passaggio diretto, mentre ImportEPSFromFile e DrawPostScript fanno girare il programma attraverso un interprete ed emettono percorsi ordinari, immagini inline e testo. Mandate la grafica attraverso l'interprete, e tenete un PS XObject per un frammento destinato al RIP di una sala stampa, il raster image processor davanti a una macchina da stampa o a una stampante di produzione. Interpretare EPS è anche un lavoro diverso dalla importazione vettoriale EMF, dove l'input è una lista di record GDI da riprodurre; un file EPS è un programma
procedure AddPrintRoomHook(PDF: TPDFlib);
var
PSRef: Integer;
begin
// Conservato alla lettera come stream /Subtype /PS e referenziato con Do.
// Si esegue solo su una stampante PostScript; i viewer non disegnano nulla
PSRef := PDF.NewPostScriptXObject(
'userdict /PrintRoomMark known { PrintRoomMark } if');
if PSRef <> 0 then
PDF.DrawPostScriptXObject(PSRef);
end;
| Comportamento PostScript | Comportamento PDF | Risultato di una traduzione letterale |
|---|---|---|
gsave / grestore salvano e ripristinano il percorso corrente e il punto corrente | q / Q lasciano in pace il percorso, e la pittura lo chiude (§8.5.1) | Un contorno o un segmento di continuazione dopo grestore sparisce |
clip conserva il percorso corrente per una pittura successiva | W è seguito da un operatore di pittura, di solito n, che chiude il percorso | Uno stroke dopo il clip non ha nulla da disegnare |
ImageMatrix mappa lo spazio utente nello spazio immagine | Ogni immagine riempie il quadrato unitario, prima riga in cima (§8.9.4) | Immagini bottom-up o ruotate arrivano capovolte o fuori posto |
true imagemask dipinge i bit 1 | Uno stencil con il Decode [0 1] predefinito dipinge i bit 0 (§8.9.6.2) | Un logo a un colore si stampa come il suo negativo |
| I font Roman Standard-14 usano StandardEncoding per default | Il nome StandardEncoding non ha alcun significato speciale (§9.6.6.1) | Accenti, virgolette e legature cambiano lettere |
I commenti %%BoundingBox e %%HiResBoundingBox definiscono l'area della grafica | Il piazzamento è la matrice che il writer emette | La grafica scivola o scala fuori dal suo box previsto |
Dov'è finito il contorno dopo grestore?
Il contorno è svanito perché il PDF non ha mai salvato il percorso. ISO 32000-1 §8.5.1 lo mette in una frase: "Nel PDF (a differenza di PostScript), il percorso corrente non fa parte dello stato grafico", e dopo che un percorso è dipinto non c'è percorso corrente finché m o re non ne inizia uno nuovo. I produttori PostScript contano sulla regola opposta. Il modo consueto di disegnare una forma piena con un contorno è gsave fill grestore stroke: il riempimento consuma una copia salvata del percorso, grestore riporta il percorso, e lo stroke dipinge la stessa geometria. Mappate gsave su q, fill su f e grestore su Q, e lo stroke arriva con un percorso vuoto, così la forma è piena e il contorno è andato. Una lineto dopo grestore che continua il percorso ripristinato fallisce allo stesso modo, come un segmento mancante
uses
SysUtils, PDFlibrary;
const
// Triangolo pieno con un contorno: il riempimento gira dentro gsave/grestore,
// e lo stroke dopo grestore riusa il percorso ripristinato
TriangleEPS =
'%!PS-Adobe-3.0 EPSF-3.0'#10 +
'%%BoundingBox: 0 0 200 120'#10 +
'newpath 20 20 moveto 180 20 lineto 100 100 lineto closepath'#10 +
'gsave 0.85 setgray fill grestore'#10 +
'3 setlinewidth 0 setgray stroke'#10;
var
PDF: TPDFlib;
begin
PDF := TPDFlib.Create;
try
PDF.SetOrigin(1); // Left e Top misurati dal margine in alto a sinistra
// Width e Height a 0 conservano la dimensione del bounding box 200 x 120
if PDF.DrawPostScript(72, 72, 0, 0, TriangleEPS) = 1 then
PDF.SaveToFile('triangle.pdf')
else
Writeln('DrawPostScript failed, code ', PDF.LastErrorCode);
// Atteso: un triangolo grigio chiaro con contorno nero di 3 unità.
// Una traduzione letterale q/f/Q/S riempie il triangolo e butta il contorno
finally
PDF.Free;
end;
end.
PDFlibPas tiene un proprio percorso PostScript, separato da ciò che lo stream di contenuto PDF sta tenendo in quel momento, e scrive il percorso PDF solo quando qualcosa dipinge o ritaglia. Ogni punto viene fissato sotto la matrice di trasformazione in vigore quando moveto o lineto è girato, come in PostScript, dove una successiva scale o concat non muove mai i punti già sul percorso. currentpoint riporta il punto memorizzato indietro attraverso la matrice corrente, così risponde nello spazio utente presente. Una gsave registra un riferimento alla fine della catena dei percorsi più il punto corrente, matrice, font e spazio di colore, così i salvataggi annidati non copiano mai percorsi interi
Il ritaglio è dove i due modelli di percorso prendono di nuovo strade diverse, e l'importazione segue anche lì il PostScript: clip e eoclip emettono W n per il lato PDF ma lasciano il percorso logico al suo posto per lo stroke che spesso segue, mentre rectclip, che inizia un percorso vuoto fresco in PostScript, emette re W n e lo cancella. La memoria dei percorsi è limitata a 100.000 nodi, e le repliche attingono al budget condiviso di operazioni, così un file ostile non può andare in loop di gsave e stroke per gonfiare l'output
Piazzare immagini: la ImageMatrix punta dall'altra parte
Un'immagine EPS arriva capovolta quando la sua ImageMatrix viene trattata come piazzamento, perché descrive la mappatura opposta. Nel PDF, §8.9.4 lega il confine dell'immagine al quadrato unitario dello spazio utente con la riga di campioni superiore in cima, una matrice implicita immagine-utente di [1/w 0 0 -1/h 0 1]. La ImageMatrix di PostScript mappa lo spazio utente nello spazio immagine. La matrice di piazzamento PDF è quindi [w 0 0 -h 0 h] moltiplicata per l'inverso della ImageMatrix. Per la comune matrice top-down [w 0 0 -h 0 h] il prodotto è l'identità. Ecco perché i convertitori ingenui sembrano corretti nell'uso distratto. Una scansione bottom-up scritta con [w 0 0 h 0 0] dà [1 0 0 -1 0 1], un capovolgimento verticale dentro il quadrato unitario, e le matrici ruotate o inclinate non hanno bisogno di casi speciali. Quando una sorgente di dati finita termina in anticipo, PDFlibPas dipinge solo le righe complete, conserva la ImageMatrix originale e scala per le righe effettivamente arrivate, così un'immagine troncata riempie la banda superiore della sua area alla scala corretta; stirare le righe sopravvissute sull'intera altezza produrrebbe una figura deformata che sembra integra
Quale bit dipinge l'imagemask?
Con true come suo operando di polarità, imagemask dipinge i bit 1, e nel PDF questo è Decode [1 0]. ISO 32000-1 §8.9.6.2 definisce [0 1] come il default per una image mask, il che significa che un campione 0 segna la pagina con il colore corrente e un 1 lascia in pace lo sfondo, e [1 0] rovescia questa regola. Il booleano PostScript dice quale valore di campione dipinge. Leggetelo come "inverti il default del PDF" e il logo si stampa come un negativo, un blocco pieno con il marchio ritagliato via. PDFlibPas mappa true a [1 0] e false a [0 1], emette lo stencil come immagine inline con /IM true così dipinge nel colore che il programma ha impostato, e nella forma dizionario accetta solo quelle due matrici Decode
I campioni a dodici bit nascondono una discrepanza minore. Il PDF non ha immagini a 12 bit; §8.9.5.1 ammette 1, 2, 4, 8 e, da PDF 1.5, 16 bit per componente. Spostare ogni campione a sinistra di quattro bit lo fa entrare in 16 bit senza perdere nulla, ma il valore più grande diventa 65520, non 65535, così con il Decode predefinito ogni campione luminoso esce circa 0.023 percento troppo scuro. L'importazione conserva lo spostamento e stira l'estremo superiore del Decode di 65535/65520, il che ripristina ogni valore normalizzato. Un'immagine a 12 bit alza la versione di output a PDF 1.5 automaticamente; in modalità PDF/A o PDF/X, o quando la versione di output è bloccata sotto 1.5, l'importazione si ferma con errore 538 prima che la pagina cambi
Perché Æ esce come á?
Un font Roman in PostScript usa StandardEncoding a meno che il programma non lo ricodifichi, e il PDF non può nominare quella tabella: ISO 32000-1 §9.6.6.1 non dà alcun significato speciale al nome, lasciando WinAnsiEncoding, MacRomanEncoding e MacExpertEncoding come scelte predefinite. Le tabelle dell'Allegato D.2 mostrano che cosa va storto quando i codici vengono consegnati a WinAnsi invariati: l'ottale 341 (0xE1) è AE in StandardEncoding e aacute in WinAnsi, così Æ diventa á; 0xAE è la legatura fi in una e ® nell'altra; 0x27 è quoteright in StandardEncoding e una quotesingle dritta in WinAnsi, il che trasforma gli apostrofi tipografici in apostrofi da macchina da scrivere. Per ogni font Roman Standard-14 che un programma usa, PDFlibPas scrive una risorsa font Type 1 privata il cui dizionario /Encoding ha /BaseEncoding /WinAnsiEncoding e un array /Differences che nomina tutti i 256 codici in ordine StandardEncoding, così nessun codice dipende da come un viewer riempie la sua tabella base. Le larghezze arrivano dalla stessa codifica, una mappa ToUnicode fa restituire Æ a copia e ricerca, e Symbol e ZapfDingbats conservano i loro codici integrati. Il font che il vostro codice ha selezionato prima dell'importazione resta intoccato, dato che ricodificare un font in cache per accontentare l'EPS cambierebbe il testo già piazzato con esso
Quelle risorse nominano i font senza incorporarli, il che collide con i profili che richiedono l'incorporamento. In un documento predisposto per PDF/A, PDF/X o PDF/UA, o dopo SetEmbedAllFonts(1), il testo EPS fallisce con errore 538 e la pagina resta com'era. Per output di archivio la via più pulita è una EPS esportata con il testo convertito in contorni, che si importa come percorsi semplici in qualsiasi modalità; i font già lasciati non incorporati in un file esistente richiedono invece l'incorporamento dei font mancanti per PDF/A. Il testo EPS si mappa sui 14 font standard per i loro nomi esatti; la grafica che incorpora il proprio programma font si importa pulita una volta che il suo testo è stato trasformato in contorni, il che evita anche che un font sostituto locale sposti ogni riga
Bounding box, commenti DSC e file EPS binari
Il piazzamento scala il bounding box della grafica nel rettangolo che passate, così il box deve arrivare dal posto giusto. PDFlibPas legge %%BoundingBox e %%HiResBoundingBox solo da vere righe di commento DSC, cioè commenti di primo livello che aprono una riga, e un box alta risoluzione valido vince su quello intero. Una ricerca di sottostringa qualunque trova anche quelle parole chiave dentro stringhe letterali, corpi di procedura e dati immagine binari. I file EPS portano tutti e tre. Un valore (atend) nell'intestazione rinvia i numeri a un commento nel trailer, letto quando lo scanner lo raggiunge; un file senza alcun box ripiega su 612 per 792, e un box vuoto o invertito fallisce con errore 124. Width e Height scalano il box indipendentemente: passate 0 per entrambi per conservare la dimensione naturale, oppure passateli entrambi nel rapporto di aspetto del box, perché un solo valore diverso da zero stira la grafica lungo un solo asse
I dati binari decidono il punto di ingresso. DrawPostScript prende una WideString, va bene per PostScript costruito nel codice, ma i campioni di immagine grezzi, i dati Flate letti attraverso currentfile e il wrapper DOS EPS contengono byte che non sopravvivono a un viaggio attraverso testo Unicode. ImportEPSFromFile legge il file come byte grezzi, riconosce l'intestazione di anteprima C5 D0 D3 C6, controlla che offset e lunghezza del segmento PostScript cadano dentro il file, ed esegue solo quel segmento ignorando l'anteprima TIFF o WMF. Entrambe le chiamate prendono al massimo 4 MiB di sorgente, richiedono Options = 0, restituiscono 1 o 0, e accodano o tutto il disegno o nulla. Il motivo del fallimento sta in LastErrorCode, che si azzera quando viene letto, così catturatelo una volta
uses
SysUtils, PDFlibrary;
function PlaceEPS(PDF: TPDFlib; const FileName: string;
Left, Top: Double): Boolean;
var
Code: Integer;
begin
// 0, 0: dimensione naturale da %%HiResBoundingBox o %%BoundingBox
Result := PDF.ImportEPSFromFile(Left, Top, 0, 0, FileName) = 1;
if Result then
Exit;
Code := PDF.LastErrorCode; // la lettura azzera il codice, quindi leggetelo una volta
case Code of
402:
Writeln(FileName, ': file cannot be opened');
PDFLIB_ERROR_POSTSCRIPT_INVALID: // 124
Writeln(FileName, ': malformed PostScript, bad bounding box or nothing painted');
PDFLIB_ERROR_POSTSCRIPT_UNSUPPORTED: // 538
Writeln(FileName, ': construct the import does not handle, or Standard-14 text in an archival profile');
PDFLIB_ERROR_POSTSCRIPT_BUDGET: // 539
Writeln(FileName, ': source over 4 MiB or a work limit reached');
else
Writeln(FileName, ': import failed with code ', Code);
end;
end;
var
PDF: TPDFlib;
begin
PDF := TPDFlib.Create;
try
PDF.SetOrigin(1);
if PlaceEPS(PDF, 'logo.eps', 40, 40) then
PDF.SaveToFile('letterhead.pdf');
finally
PDF.Free;
end;
end.
Quando l'errore 538 nomina un operatore, riesportate la grafica dal suo editor come EPS semplice con il testo convertito in contorni: di solito è più rapido che dare la caccia all'operatore, e la grafica con contorni si piazza anche in un documento PDF/A o PDF/UA costruito con la PDFlibPas Delphi PDF Library senza il conflitto di font Standard-14