PDFlibPas fäster en inbäddad fil vid en specifik sida i stället för vid dokumentet som helhet, genom att skriva ett /AF-fält i sidordlistan medan nyttolasten själv förblir registrerad i dokumentets EmbeddedFiles-namnträd. Den uppdelningen är vad ISO 32000-2 §14.13 beskriver, och det är vad som låter en läsare svara på frågan en bilaga på dokumentnivå inte kan: till vilken sida hör dessa data
Användningsfallen är mer specifika än allmänna bilagor. En undersökningsrapport där varje sida bär på mätserien bakom sitt diagram. En inskannad sats där varje sida sparar OCR-resultatet som producerade dess textlager. Ett ritningsset där varje blad bär CAD-utdraget det renderades från. I vart och ett av fallen skulle en bilagelista på dokumentnivå vara en hög filer med sidnummer inbakade i namnen, vilket är en konvention snarare än en struktur
En nyttolast, två ställen den refereras från
Den viktiga strukturella punkten är att associering på sidonivå inte skapar någon andra kopia av något. Filen bäddas in en gång och registreras i EmbeddedFiles-namnträdet precis som en dokumentnivå-bilaga, med samma filspecifikationsmaskineri. Det som skiljer är var referensen och dess relationsnyckel skrivs: i sidordlistan i stället för i dokumentkatalogen
Två konsekvenser följer. För det första hittar en läsare som bara kan dokumentnivå-bilagor ändå nyttolasten, eftersom den ligger i det namnträd en sådan läsare tittar i. För det andra tar borttagning av sidassocieringen bort bindningen, inte filen. ClearPageAssociatedFiles kopplar loss sidan från sina associerade filer och lämnar nyttolasterna nåbara via namnträdet, vilket är det konservativa beteendet: en operation som säger koppla loss associeringen ska inte tyst förstöra data som en annan del av dokumentet kan referera
Den funktionen har ett medvetet snävt framgångsvillkor värt att känna till. Den rapporterar framgång endast när sidan faktiskt bar ett /AF-fält. En sida som aldrig haft associeringar returnerar misslyckande i stället för en glad bekräftelse, så en anropare kan inte ta en no-op för en genomförd städning
var
Lib: TPDFlib;
Idx, I: Integer;
begin
Lib := TPDFlib.Create(nil);
try
Lib.LoadFromFile('survey-report.pdf');
// Fäst mätserien som låg bakom diagrammet på sida 3
Idx := Lib.AddPageAssociatedFileFromFile(3,
'series-03.csv', // fil på disk
'measurements.csv', // visningsnamn inuti PDF:en
'text/csv', // MIME-typ
'Raw measurement series for figure 3',
'Data'); // AFRelationship, ISO 32000-2 14.13
if Idx < 0 then
raise Exception.Create('page association refused');
for I := 0 to Lib.GetPageAssociatedFileCount(3) - 1 do
Writeln('page 3 associated file, embedded index ',
Lib.GetPageAssociatedFileEmbeddedIndex(3, I));
Lib.SaveToFile('survey-report-with-data.pdf');
finally
Lib.Free;
end;
end;
Relationssträngen är inte fri text i praktiken. ISO 32000-2 definierar en vokabulär, Source, Data, Alternative, Supplement, EncryptedPayload, FormData, Schema och Unspecified, och konsumenter stödjer sig på den. Data för talen bakom ett diagram, Source för dokumentet en sida genererades från, Alternative för en ekvivalent representation. Välj ur vokabulären även när inget i din pipeline läser den än, för nästa verktyg i kedjan kanske gör det
Varför behöver samma uppslag FollowRef i båda riktningarna?
Därför att referensföljning svarar på två olika frågor, och koden måste veta vilken den ställer. En nyckeluppslag som följer indirekta referenser returnerar objektet referensen pekar på. Ett uppslag som inte följer returnerar referensen själv. Båda är korrekta, och att använda fel ger ett tyst felbeteende i stället för ett fel
Läsning av en associerad fil demonstrerar första riktningen. För att få objektnumret för den inbäddade strömmen bakom filspecifikationens /EF- och /F-fält får uppslaget inte följa, eftersom upplösning löser referensen till strömobjektet och objektnumret är borta. Regeln generaliseras: varje kodväg som behöver en objektidentitet snarare än objektinnehåll måste ta den råa referensen
Optional content visar motsatt riktning, och den kostade mer att hitta. Ordlistan för optional content-egenskaper skrivs in i katalogen som ett indirekt objekt, så kod som läser tillbaka den utan att följa får en referens i stället för en ordlista. En typkontroll på det värdet fallerar då, och den naturliga reservgrenen, finns ingen konfiguration, skapa en, körs och skriver över konfigurationen som redan fanns där. Inget raise:as. Lagren som beskrivs i optional content-grupper och lager tappar bara sitt standardvisningstillstånd
Lärdomen generaliserar bortom båda fallen. När ett uppslag kan returnera antingen en referens eller objektet är en naken typkontroll inte felhantering: det är en gren som förr eller senare tas av fel anledning. Bestäm explicit vad varje anropsplats behöver, och föredra det publika API som svarar på frågan direkt, som en optional content-räknare, framför att gräva i en skyddad accessor efter katalogordlistan
// Dokumentnivå-bilagor och sidonivå-associeringar kan samexistera. En
// inbäddad fil kan markeras som associerad på dokumentnivå också
if Lib.IsEmbeddedFileAssociated(0) = 0 then
Lib.SetEmbeddedFileAssociated(0, 1, 'Supplement');
Writeln('document associated files: ', Lib.GetAssociatedFileCount);
Writeln('page 3 associated files : ',
Lib.GetPageAssociatedFileCount(3));
// Att rensa lossar sidbindningen; nyttolasten stannar i namnträdet
if Lib.ClearPageAssociatedFiles(3) > 0 then
Writeln('page 3 associations removed, payloads still reachable');
Vad gör konformanslägena med bilagor?
Arkivprofiler begränsar vad som får bäddas in, och begränsningen verkställs vid ingångspunkten i stället för vid sparandet. PDF/A-1 förbjuder inbäddade filer helt, PDF/A-2 tillåter endast inbäddade PDF/A-dokument, och PDF/A-3 är profilen som öppnade inbäddning för godtyckliga filtyper, vilket är precis varför hybrida fakturaformat byggs på den
PDFlibPas avslår bilagan när det aktiva konformansläget inte tillåter den, vid anropet, inte hundratals operationer senare under utmatningen. Det är ett medvetet val om var ett fel är billigast att agera på: en avvisning vid anropsplatsen namnger filen du höll på att lägga till, medan en avvisning vid spartillfället namnger ett dokument och lämnar åt dig att räkna ut vilken av fyrtio bilagor som orsakade det
Detta är också varför associerade filer dyker upp så ofta i elektronisk fakturering. En hybridfaktura är en PDF en människa läser med en maskinläsbar XML-nyttolast bifogad och märkt med rätt relation, och både containerprofilen och relationsnyckeln är del av specifikationen snarare än konventioner. Det byggeriet tas upp i att bygga Factur-X- och ZUGFeRD-hybridfakturor, med metadatasidan i PDF/A-3 XMP-tilläggschemat
När bör associeringen vara per sida i stället för per dokument?
När en konsument behöver veta vilken sida data hör till, och endast då. Dokumentnivå-bilagor är enklare, har bredare stöd i visare och räcker närhelst nyttolasten beskriver hela dokumentet, en faktura-XML, ett signaturmanifest, ett källarkiv. Sträck efter sidonivå-associering när nyttolasten verkligen är sidavgränsad och sididentiteten är del av dess innebörd
Stödet är den praktiska begränsningen. Sidonivå-associerade filer är en PDF 2.0-konstruktion, och visarstödet är tunnare än för dokumentnivå-bilagor. Eftersom nyttolasten ändå ligger i namnträdet visar en visare som ignorerar /AF på sidor ändå filen i sin bilagelista, så nedgraderingen är graciös. Men om sidbindningen är väsentlig för din konsument snarare än användbar metadata, verifiera den läsare du faktiskt riktar dig mot i stället för att anta
Sidonivå-associerade filer, dokumentnivå-bilagor och de arkivprofilgrindar som styr båda medföljer PDFlibPas Delphi PDF library. Om du också reparerar äldre filer på vägen in är metadata- och konformansarbetet i konvertering till PDF/A med metadatareparation det som avgör vilka av dessa bilagevägar som överhuvudtaget står till buds