PDFlibPas producerar PDF/X-utskriftsfiler från Delphi via ett anrop, TPDFlib.SetPDFXMode, som väljer PDF/X-1a:2003, PDF/X-3:2003 eller PDF/X-4. Anropet skriver XMP-identifieringen pdfxid, lägger till en GTS_PDFX-output intent, deklarerar /Trapped /False, låser PDF-versionen och vägrar från och med då de ritanrop den valda delen förbjuder. CheckFileCompliance med test 3 granskar den sparade filen
Det är det korta svaret. Det långa spelar roll för att tryckerier inte avvisar filer för att de är fula, de avvisar dem i preflight, oftast en fredag, oftast för något som den genererande koden kunde ha fångat: en RGB-logga i ett X-1a-jobb, ett typsnitt som refererades i stället för att bäddas in, en sida utan trimformat. PDF/X finns för att göra de felen strukturella snarare än visuella, och bibliotekssidan av jobbet är att stoppa dig från att producera dem från början
Vilken PDF/X-del ska en Delphi-applikation sikta på?
Välj PDF/X-4 om inte det mottagande arbetsflödet säger annat, och välj PDF/X-1a när det gör det. De tre delarna skiljer sig längs exakt tre axlar, färg, transparens och bas-PDF-version, och varje axel motsvarar en spärr inuti PDFlibPas:
- PDF/X-1a:2003 (ISO 15930-4): PDF 1.4-syntax, endast CMYK, grått och spotfärger, ingen ICC-baserad färg, ingen transparens. Filen är redan separerad i tryckfärger, så skrivaren gör ingen färgkonvertering alls
- PDF/X-3:2003 (ISO 15930-6): fortfarande PDF 1.4 och fortfarande ingen transparens, men färghanterade färgrymder tillåts, och enhets-RGB accepteras när output intentet själv beskriver ett RGB-utskriftsvillkor
- PDF/X-4 (ISO 15930-7): PDF 1.6-syntax, med live-transparens och optional content (lager). Det är delen moderna RIP:er byggts för, eftersom flattening av transparens på vägen ut är där en stor del av den gamla X-1a-smärtan kom ifrån
X-1a finns fortfarande överallt i annonsleverans och i äldre tryckarbetsflöden som vill ha en fil de kan exponera utan att tänka. X-4 är det bättre tekniska valet närhelst tryckeriet accepterar det: transparensen överlever oskadd, och färgbesluten tas vid RIP:en med den verkliga tryckprofilen i stället för i din kod. Lägesnumren är 1, 2 och 3, och GetInformation(203) rapporterar tillbaka aktivt läge. PDFlibPas definierar också högre lägesnummer för PDF/X-6-familjen på PDF 2.0, som följer andra versions- och output intent-regler och ligger utanför omfånget här
Vad SetPDFXMode skriver in i filen
TPDFlib.SetPDFXMode sköter identifieringen och boilerplaten som en PDF/X-läsare kontrollerar först, så applikationskoden behöver bara leverera den verkliga tryckprofilen och innehållet. Konkret gör ett lyckat anrop fyra saker:
- Skriver utgåvsträngen in i XMP som
pdfxid:GTS_PDFXVersion(PDF/X-1a:2003,PDF/X-3:2003ellerPDF/X-4). 2003-utgåvorna identifierar delen fullständigt genom den strängen, så ingen separat konformitetsegenskap skrivs - Lägger till en output intent om dokumentet saknar en och sätter dess subtyp till
/S /GTS_PDFX, subtypen PDF/X-läsare letar efter (ISO 32000-1 §14.11.5 definierar output intent-ordlistan) - Sätter
/Trapped /Falsei dokumentinformationens ordlista, eftersom skrivaren aldrig trap:ar och PDF/X kräver att trap-tillståndet deklareras i stället för att lämnas okänt - Låser versionsbasen: 1.4 för X-1a och X-3, 1.6 för X-4
Två fler reparationer sker vid sparning. Varje sida utan TrimBox får en som är lika med dess MediaBox, eftersom PDF/X kräver en deklarerad slutstorlek på varje sida (ISO 32000-1 §14.11.2 täcker sidgränserna), och anteckningar får den utskriftsflagg standarden förväntar sig. TrimBox-reserven är ett säkerhetsnät, inte ett layoutbeslut. Ett jobb som ska tryckas kant till kant behöver en riktig trimbox inuti en större mediabox, och guiden till TrimBox-, BleedBox- och CropBox-standarder i PDFlibPas går igenom hur man sätter de rutorna och hur arv kan överraska dig
uses
PDFlibrary;
procedure BuildX1aAd(const OutFile, PressProfile: string);
var
Pdf: TPDFlib;
begin
Pdf := TPDFlib.Create;
try
if Pdf.SetPDFXMode(1) <> 1 then // 1 = PDF/X-1a:2003
raise Exception.CreateFmt('PDF/X mode refused (error %d)',
[Pdf.LastErrorCode]);
// Ersätt standardprofilen med det verkliga tryckvillkoret
if Pdf.LoadOutputIntentProfile(PressProfile, 'DeviceCMYK') <> 1 then
raise Exception.Create('Could not load the CMYK output profile');
// Embed = 1 bäddar in hela fontprogrammet; PDF/X accepterar inte 0
if Pdf.AddTrueTypeFont('Arial {1252}', 1) = 0 then
raise Exception.Create('Font could not be loaded for embedding');
Pdf.SetTextSize(24);
Pdf.SetFillColorCMYK(0, 1, 1, 0); // processröd, ingen RGB
Pdf.DrawText(72, 720, 'Spring catalog, page 1');
Pdf.SaveToFile(OutFile);
finally
Pdf.Free;
end;
end;
Ordningen i den skissen är medveten. LoadOutputIntentProfile ersätter profilen i en output intent som redan finns, så den måste köras efter att SetPDFXMode har skapat en. Anropet kan inte heller trolla fram en intent ur ingenting: på ett dokument utan output intent returnerar den helt enkelt 0
Varför behöver standard-output intenten ersättas?
Output intenten som SetPDFXMode lägger till för lägena 1 till 3 deklarerar DeviceCMYK, men ICC-bytena bakom den är bibliotekets medföljande sRGB-profil med antalet komponenter omskrivet till 4. Den standarden finns för bakåtkompatibilitet och räcker för att göra filen strukturellt PDF/X, men den inbäddade profilen beskriver inget tryckeri. Ett preflight-verktyg som inspekterar profilheadern ser en RGB-profil med CMYK-taklapp, och en tryckare som litar på intenten för korrektur får ren nonsens
Behandla därför LoadOutputIntentProfile(FileName, 'DeviceCMYK') som obligatoriskt för verkligt tryckarbete och skicka med den ICC-profil din tryckleverantör namnger för pappret och pressen. TPDFlib.LoadOutputIntentProfile byter ut DestOutputProfile-strömmen och uppdaterar /N, och den lämnar resten av intent-ordlistan som skriven. I synnerhet bär output condition-identifieraren och de beskrivande strängarna i standard-intenten fortfarande sRGB-liknande värden, så om ditt tryckeri matchar på identifieraren i stället för på den inbäddade profilen, bekräfta med dem vad de kontrollerar innan du levererar. Det är ett verkligt gap i 2003-erans standarder, inte något din kod gjorde fel
Fallgrop ett: versionen du inte valde
Ett nytt PDFlibPas-dokument börjar på skrivarens standard PDF 1.5, vilket ligger över 1.4-basen som PDF/X-1a och PDF/X-3 kräver. TPDFlib.SetPDFXMode löser det tyst i det vanliga fallet: om sparningsversionen inte har låsts sänker den dokumentversionen till delens bas och fortsätter. Den första implementationen vägrade i stället, och varje SetPDFXMode(1) på ett färskt dokument returnerade 0, vilket är ett gott exempel på en regel som är korrekt på pappret och oanvändbar i praktiken
Vägran finns kvar för det enda fall där det är rätt. Har något tidigare anropat LockSaveVersion med en version över basen returnerar SetPDFXMode 0 med LastErrorCode 601, eftersom anroparen låste versionen med flit och biblioteket inte skriver över det. I delad dokumentuppsättningskod, där en hjälpfunktion låser versionen av orelaterade skäl, är det precis där ett X-1a-jobb dör:
Pdf := TPDFlib.Create;
try
Pdf.LockSaveVersion('1.7'); // satt av en generisk "husstandard"-hjälpfunktion
if Pdf.SetPDFXMode(1) = 0 then
// LastErrorCode = 601: 1.7 är låst, PDF/X-1a behöver 1.4.
// Hoppa över låset för tryckjobb. PDF/X-4 är ingen räddning här:
// dess 1.6-bas ligger också under 1.7-låset och faller på samma sätt.
LogError('PDF/X refused, error %d', [Pdf.LastErrorCode]);
finally
Pdf.Free;
end;
Samma spärr förklarar en relaterad begränsning. PDF/X-läget är en dokumentskapningskoppling: SetPDFXMode fungerar på ett nytt dokument och returnerar 0 på ett som laddats från en fil, eftersom biblioteket inte kan stå till svars för innehåll det inte skrev. Att konvertera en befintlig PDF till PDF/X är ett annat jobb som behöver inspektion och reparation, inte en flagga
Fallgrop två: spärrar som vägrar i tysthet
När ett PDF/X-läge är aktivt vägrar PDFlibPas varje innehållsanrop den valda delen förbjuder, i ögonblicket du gör det, i stället för att låta filen fallera i preflight senare. Vägrana täcker fills och strokes i enhets-RGB (hela X-1a; X-3 och X-4 om inte output intenten är RGB), ICC-baserade färger i X-1a, transparens och blend modes i X-1a och X-3, optional content i X-1a och X-3, JavaScript-åtgärder, inbäddade filbilagor, LZW-bildfiltret, Standard 14-typsnitt som skulle hamna utan inbäddning, och kryptering med publik nyckel. Lösenordskryptering via Encrypt spärras inte vid skapandet, men validatoren flaggar varje krypterad fil, så håll tryckjobb okrypterade
Det vägrana inte delar är en felkod, och det är fällan. Färgrymdsspärrarna i SetFillColor, SetLineColor, ICC-baserade färganropen och EncryptPublicKey sätter LastErrorCode till 601, bibliotekets konformitetskod som PDF/A-spärrarna också använder. Transparens-, JavaScript-, bilage-, LZW- och AddStandardFont-spärrarna returnerar bara 0 och lämnar LastErrorCode orört, vilket betyder att den fortfarande kan hålla ett värde från ett tidigare anrop. Kod som bara kontrollerar LastErrorCode efter en bunt ritanrop missar halva vägrana, så kontrollera varje returvärde:
procedure Require(Ok: Integer; const What: string);
begin
if Ok = 0 then
raise EPrintConformance.CreateFmt('%s refused under PDF/X', [What]);
end;
Require(Pdf.SetFillColorCMYK(0, 0, 0, 1), 'CMYK fill');
Require(Pdf.SetTransparency(40), 'transparency'); // 0 i X-1a / X-3
Require(Pdf.AddLinkToJavaScript(72, 72, 120, 20,
'app.alert(1)', 0), 'JavaScript link'); // 0 i varje del
Typsnitt förtjänar en egen varning. AddStandardFont returnerar 0 under PDF/X eftersom biblioteket inte medför Standard 14-konturerna och därmed inte kan bädda in dem. AddTrueTypeFont är annorlunda: i PDF/A-läge höjer den Embed = 0 till en full inbäddning åt dig, men i PDF/X-läge gör den inte det, så ett refererat systemtypsnitt tar sig in i filen och bara validatoren fångar det. Skicka 1 (full) eller 2 (subset) själv, eller anropa SetEmbedAllFonts(1) en gång vid uppsättningen så att en vilsekommen 0 inte kan smyga igenom
Hur verifierar du en PDF/X-fil innan den går till tryckeriet?
Kör CheckFileCompliance med ComplianceTest = 3 på den sparade filen. TPDFlib.CheckFileCompliance returnerar 0 när den registrerade inga fynd, och annars ett stränglistehandtag vars poster är rader med prefixet PDFX-. PDF/X-granskningen läser filen genom samma streaming-läsare som PDF/A- och PDF/UA-preflighten i PDFlibPas, och den kontrollerar reglerna en tryck-preflight kontrollerar först:
- Filen är inte krypterad, XMP:en bär en känd
GTS_PDFXVersion, och PDF-versionen överskrider inte delens bas - Exakt en output intent finns, dess subtyp är
GTS_PDFX, och den bär en destinationsprofil och en output condition-identifierare - Informationsordlistan deklarerar
/Trapped - Varje sida har en TrimBox som ligger inuti MediaBox:en, och en BleedBox, när den finns, omsluter TrimBox:en
- Varje typsnitt som används på en sida har ett inbäddat program, där Type0-typsnitt kontrolleras via sitt efterkommande typsnitt
- Inga förbjudna anteckningstyper (RichMedia, Screen, 3D, Movie, Sound, FileAttachment), ingen JavaScript-öppningsåtgärd eller JavaScript på dokumentnivå, inga inbäddade filer, och inget LZWDecode-filter någonstans i objektgrafen
function PreflightPDFX(Pdf: TPDFlib; const FileName: string;
Report: TStrings): Boolean;
var
ListId, I: Integer;
begin
ListId := Pdf.CheckFileCompliance(FileName, '', 3, 0); // 3 = PDF/X
Result := ListId = 0;
if Result then
Exit;
try
for I := 1 to Pdf.GetStringListCount(ListId) do // posterna är 1-baserade
Report.Add(Pdf.GetStringListItem(ListId, I));
finally
Pdf.ReleaseStringList(ListId);
end;
end;
Var tydlig med vad det godkännandet betyder. PDFlibPas-validatoren granskar de strukturella reglerna ovan, och det är de som fångar generatormisstag. Det är ingen färgkorrekt preflight: den kontrollerar inte total färgtäckning, bildupplösning eller om den inbäddade profilen matchar villkoret ditt tryckeri kör. Använd den som spärren i din bygg- eller exportpipeline, och lämna de tryckspecifika kontrollerna till tryckeriets preflight-profil, vilket är vad de kommer att köra ändå
FAQ
Stöder PDFlibPas PDF/X-4-transparens?
Ja. I SetPDFXMode(3) tillåter PDFlibPas transparens, blend modes och optional content-grupper och skriver en PDF 1.6-fil. Samma anrop returnerar 0 under PDF/X-1a och PDF/X-3
Kan PDFlibPas konvertera en befintlig PDF till PDF/X?
Nej. SetPDFXMode gäller bara dokument som skapats i den aktuella TPDFlib-sessionen och returnerar 0 för en laddad fil. Du kan fortfarande granska vilken befintlig PDF som helst med CheckFileCompliance och test 3
Vilken felkod returnerar ett vägrat PDF/X-anrop?
Vägran för färgrymd och kryptering med publik nyckel sätter LastErrorCode till 601. Andra spärrar, som transparens, JavaScript och Standard 14-typsnitt, returnerar bara 0, så testa alltid funktionsresultatet
PDF/X-utdata, PDF/X-validatoren och resten av tryck- och arkiveringsverktygslådan är en del av PDFlibPas, losLabs inbyggda Object Pascal PDF-bibliotek för Delphi. Se produktssidan för PDFlibPas Delphi PDF Library för utgåvor, plattformar och den fullständiga API-referensen