Å slå sammen eller dele opp en PDF på to gigabyte på den opplagte måten koster deg to ting på én gang: klokketid og adresserom. Den opplagte måten er å laste hver inndatafil, gjøre jobben og skrive utdataene. Lastingen er der det ryker. Et skannearkiv som går fra 300 til 600 DPI, dobler den lineære oppløsningen og firedobler seg omtrent på disk, så den samme sammenstillingsjobben som håndterte filer på 400 MB hele året, begynner å tråkke rundt i det øyeblikket en inndatafil passerer en gigabyte, ofte mens den ikke gjør annet enn å telle sider. Oppgaven ble aldri vanskeligere. Åpne, tell, plukk områder, sett sammen er hele greia. Fullstendig lasting av treet sluttet rett og slett å være et fornuftig standardvalg ved den størrelsen. PDF Library for Delphi, losLabs PDF-bibliotek for Delphi og C++Builder, svarer på dette med sitt Direct Access-lag: en familie funksjoner med DA-prefiks, tuftet på en strømmende leser som går gjennom kryssreferansetabellen der den ligger, i stedet for å bygge hele dokumentet i minnet
Hvor minnet blir av i en full lasting
Å laste en PDF «normalt» betyr å parse xref-en, løse opp hvert indirekte objekt til et tre i minnet, dekode objektstrømmer og koble sidetreet, skriftene og annotasjonene sammen til objekter du kan manipulere. For redigeringsarbeidsflyter er det den rette avveiningen. For sammenslåing, oppdeling og inspeksjon er det stort sett bortkastet. Et skannearkiv på 30 000 sider kan romme millioner av indirekte objekter, og en oppdelingsjobb trenger å lese noen få hundre av dem: sidenodene i det forespurte området, pluss det de nodene refererer til
Direct Access-laget snur modellen. DAOpenFile og DAOpenFileReadOnly parser traileren og xref-en, noen få kilobyte helt bakerst i filen, og returnerer et filhåndtak. Objekter hentes først når et kall trenger dem. Den praktiske følgen er at å åpne en fil på flere gigabyte tar omtrent like lang tid som å åpne en liten, og at minnebruken følger det du berører, ikke det filen inneholder
Å sondere en enorm fil uten å laste den
Mønsteret nedenfor kommer fra bibliotekets egen ytelsestest for store filer: åpne skrivebeskyttet, still spørsmål, lukk. Det finnes aldri noe dokumenttre
var
Lib: TPDFlib;
Handle, Pages: Integer;
begin
Lib := TPDFlib.Create;
try
Handle := Lib.DAOpenFileReadOnly('archive-2025.pdf', '');
if Handle = 0 then
raise Exception.Create('Direct access open failed');
Pages := Lib.DAGetPageCount(Handle);
Writeln('pages : ', Pages);
Writeln('title : ', Lib.DAGetInformation(Handle, 'Title'));
Lib.DACloseFile(Handle);
finally
Lib.Free;
end;
end;
Skrivebeskyttet modus er verdt å foretrekke når du kan: den lar mottakstrinnet kjøre mens andre prosesser holder filen, og den dokumenterer hensikten. Et sonderingstrinn som ved et uhell kaller en muterende funksjon, feiler raskt i stedet for å ødelegge arkivet
PageRef er et objekthåndtak, ikke et sidenummer
Den desidert vanligste tabben med DA-API-et er å sende inn et sidenummer der en funksjon venter en PageRef. Nesten hvert DA-kall som gjelder én side, tar et referansehåndtak til sideobjektet snarere enn et sidenummer: DAExtractPageText, DARenderPageToFile, DARotatePage og DACapturePage venter alle en ref. Du får en ved å oversette det menneskevendte nummeret gjennom DAFindPage:
PageRef := Lib.DAFindPage(Handle, 250); // sidenummer -> objekthåndtak
if PageRef <> 0 then
begin
Text := Lib.DAExtractPageText(Handle, PageRef, 0);
Lib.DARenderPageToFile(Handle, PageRef, 5, 150, 'page250.png');
end;
Å sende inn det rå tallet 250 i stedet utløser ingen feil. Det adresserer det objektet som tilfeldigvis ligger bak den håndtaksverdien, noe som på en god dag feiler synlig og på en dårlig dag trekker ut tekst fra feil side inn i et dokument kunden skal se. Pakker du DA-laget inn i din egen tjenestekode, gjør oversettelsen umulig å hoppe over: ta imot sidenumre i grensesnittet, kall DAFindPage med én gang, og send bare refer videre internt
Å slå sammen hundrevis av filer med en navngitt liste
For to filer holder MergeFiles(First, Second, Output). Batchsammenstilling skalerer bedre gjennom fillister: registrer inndatafilene under et listenavn, og slå så sammen listen i én runde
Lib.AddToFileList('Statements', 'jan.pdf');
Lib.AddToFileList('Statements', 'feb.pdf');
Lib.AddToFileList('Statements', 'mar.pdf');
Lib.MergeFileList('Statements', 'q1-statements.pdf');
// Verifiser resultatet på den billige måten: direkte tilgang igjen
Handle := Lib.DAOpenFileReadOnly('q1-statements.pdf', '');
Writeln('merged pages: ', Lib.DAGetPageCount(Handle));
Lib.DACloseFile(Handle);
Sammenslåingsfamilien har tre varianter, og forskjellen er ikke hastighet alene. MergeFileListFast hopper over bevaring av strukturtreet; MergeFileListStrict håndhever streng modus; versjonen uten suffiks er det balanserte standardvalget. Driftsregelen som faller ut: er noen av inndatafilene en Tagged PDF hvis tilgjengelighetsstruktur må overleve, der alt som lages for PDF/UA er det opplagte tilfellet, grip til standard- eller Strict-varianten, for Fast forkaster strukturtreet stilltiende. For rene skannearkiver uten tagging er Fast gratis ytelse. Bestem per pipeline, ikke per utviklerhumør, og noter varianten som ble brukt, i jobbloggen
Oppdeling uten lasting: uttrekk av områder
Oppdeling følger den samme filosofien om å ikke laste. ExtractFilePages(InputFileName, Password, OutputFileName, RangeList) trekker et sideområde rett fra fil til fil, med en områdeliste som '1-500', '501-1000' eller kommaseparerte utvalg, og kilden blir aldri et dokumenttre. Når et dokument allerede er lastet av andre grunner, produserer ExtractPageRanges et nytt dokument i minnet fra det gjeldende, og CopyPageRanges henter områder over fra et annet lastet dokument ved ID. For oppdeling per kontoutskrift av samlede utskriftsstrømmer er formen fra fil til fil den som hindrer en inndatafil på 4 GB i noensinne å svulme opp i RAM
Filer som lyver om sin egen geometri
Pipeliner for store filer møter ødelagte filer i et omfang pipeliner for små filer aldri ser, rett og slett fordi inndataene passerer flere systemer. To feilformer fortjener uttrykkelig håndtering
For det første forskjøvne filhoder. Postgatewayer og utskriftskøer legger av og til byte foran en PDF, slik at %PDF-markøren ikke lenger ligger på offset 0, og hver xref-offset i filen er feil med det samme beløpet. Den strømmende leseren oppdager dette og eksponerer det (DAShiftedHeader på det flate nivået, ShiftedHeader på TSmartPDFReader), og kompenserer så for det under lesing. Hjemmesnekret offsetaritmetikk gjør vanligvis ikke det, og derfor er «virker på hver fil vi selv lager, feiler på filer fra kunde X» det klassiske symptomet
For det andre ødelagte kryssreferansetabeller. DACopyFile(InputFileName, OutputFileName, PageCount) strømmer hele filen til en ny kopi mens den bygger xref-en på nytt, og returnerer sideantallet som et biprodukt. Å kjøre den som et normaliseringstrinn foran en kresen konsument nedstrøms gjør en hel klasse sporadiske parsefeil om til ett forutsigbart reparasjonstrinn. Og når dine egne redigeringer må lagres, skriver DAAppendFile dem som en inkrementell oppdatering, og legger til en ny revisjon framfor å skrive om gigabyte, noe som holder lagringskostnaden proporsjonal med endringen i stedet for med filen
Leveringsdetaljer: linearisering og komposisjon
To beslektede muligheter runder av en pipeline for store filer. Når de sammenstilte utdataene serveres over HTTP for visning i nettleseren, omorganiserer LinearizeFile dem for strømming med byteområder, slik at den første siden vises før resten av en pakke på 500 MB er ferdig nedlastet. Kjør den som siste trinn, etter all sammenslåing, for enhver senere endring avlineariserer filen igjen. Og når pakker trenger komposisjon snarere enn ren sammenkjeding, si et forsideark stemplet bak hver kontoutskrift eller to kildesider satt opp på ett utdataark, gjør DACapturePage enhver side om til en gjenbrukbar mal som DADrawCapturedPage plasserer på en målside i et vilkårlig rektangel, fortsatt uten en full dokumentlasting av kilden på flere gigabyte
Grenser og hva som forblir skrivebeskyttet
Selve formatet går tom for plass lenge før Direct Access gjør det. Offsetene er Int64 hele veien gjennom DA-laget, så de virkelige takene er tilgjengelig diskplass og det tisifrede xref-offsetfeltet i klassiske kryssreferansetabeller uten strøm. Skannearkiver på flere gigabyte er upåfallende i praksis, og minnebruken forblir avgrenset uansett filstørrelse, fordi objekter først leses når et kall ber om dem
To spørsmål kommer ofte nok til å fortjene et direkte svar. Sammenslåing gjennom standardveien tar dokumentstrukturen med over, så bokmerker og lenker overlever; Fast-varianten er den som bytter bort strukturtreet mot fart, og det er hele grunnen til å forbeholde den for utaggede inndata. Den trygge vanen er å åpne det sammenslåtte resultatet, gå gjennom disposisjonen og stikkprøve noen interne lenker før det sendes videre. Når det gjelder redigering: det finnes en nyttig mellomting mellom skrivebeskyttet sondering og en full lasting. Operasjoner på sidenivå virker direkte på håndtaket, DARotatePage, DAMovePage og DAHidePage blant dem, sammen med lesing av skjemafelter, og DAAppendFile lagrer de redigeringene som en inkrementell revisjon. Redigering på innholdsnivå, alt som skriver om markeringsoperatorene inne i en side, hører fortsatt hjemme i det fulle dokumentlaget
Beslektede artikler
Må de sammenslåtte utdataene forbli tilgjengelige, dekkes bakgrunnen om strukturtreet i artikkelen om tilgjengelighet i Tagged PDF, som forklarer nøyaktig hva Fast-varianten av sammenslåing ville forkastet. For å hente innhold ut av områdene du deler opp, se guiden til uttrekk av tekst, bilder og skrifter
Den fullstendige funksjonslisten for Direct Access følger med biblioteket; utgaver og prøvenedlastinger finnes på produktsiden for PDF Library for Delphi