Tekninen artikkeli

Gigatavuisten PDF-tiedostojen yhdistys ja jako PDFlibPasilla

Kahden gigatavun PDF:n yhdistäminen tai jakaminen ilmeisellä tavalla maksaa sinulle kaksi asiaa kerralla: seinäkeloaikaa ja osoitetilaa. Ilmeinen tapa on ladata kukin syöte, tehdä työ, kirjoittaa tuloste. Lataaminen on missä se rikkoutuu. Skanniava arkisto joka siirtyy 300:sta 600 DPI:hin kaksinkertaistaa lineaarisen resoluutionsa ja likimain nelinkertaistaa levllä, joten sama kokoonpanotyö, joka hoiti 400 MB tiedostoja koko vuoden, alkaa trashaamaan heti kun syöte ylittää gigatavun, usein tekemättä muuta kuin sivujen laskemista. Työ ei koskaan tullut vaikeammaksi. Avaa, laske, valitse välit, ketjuta on koko se. Koko puun lataaminen yksinkertaisesti lakkasi olemasta järkevä oletus tuossa koossa. PDF Library for Delphi, losLabin PDF-kirjasto Delphille ja C++Builderille, vastaa tähän Direct Access -kerroksellaan: perhe DA-etuliitellä varustettuja funktioita, joiden takana on suoratoistolukija, joka kävelee ristiviittaustaulukon paikallaan ennemmin kuin rakentaa koko dokumentin muistiin

Minne muisti menee täyssuoritusLatauksessa

PDF:n "normaali" lataaminen tarkoittaa xref:n jäsentämistä, joka epäsuora objekti resolvoidaan muistissa olevaksi puuksi, objektivirtojen dekoodaamista, ja sivupuun, fonttien ja merkintöjen kytkemistä objekteiksi joita voit muokata. Editointivuokia varten tuo on oikea vaihto. Yhdistämis-, jakamis- ja tarkastustyölle se on enimmäkseen hävikkiä. 30 000-sivuinen skannausarkisto voi pitää sisällään miljoonia epäsuoria objekteja, ja jakotyö tarvitsee lukea muutama sataa niistä: pyydetyn välin sivusolmut, plus mitä nuo solmut viittaavat

Direct Access -kerros kääntää mallin päinvastaiseksi. DAOpenFile ja DAOpenFileReadOnly jäsentävät trailerin ja xref:n, muutaman kilotavun tiedoston hännässä, ja palauttavat tiedostokahvan. Objektit haetaan laiskasti kun kutsu niitä tarvitsee. Käytännön seuraus on, että usean gigatavun tiedoston avaaminen kestää suunnilleen yhtä kauan kuin pienen avaaminen, ja muisti seuraa sitä mitä kosketat ennemmin kuin sitä mitä tiedosto sisältää

PDF Library for Delphi -vertailu gigatavuisen PDF:n lataamisesta täyteen muistiobjektipuuhun ja sen avaamisesta suoralla pääsyllä, jossa jäsennys pysähtyy traileriin ja xref-tauluun ja kahva palvelee laiskkoja objektilukuja
Täysi lataus purkaa jokaisen epäsuoran objektin ennen kuin yhdistäminen voi alkaa, joten RAM ja avausaika skaalautuvat arkiston mukaan. Suoran käsittelyn polku palauttaa toimivan handlen luettuaan kilobyteja ja antaa jokaisen kutsun noutaa vain tarvitsemansa objektit

Valtavan tiedoston tutkiminen lataamatta

Alla oleva malli tulee kirjaston omasta suurtiedosto-benchmarkista: avaa vain luku -tilassa, kysy kysymyksiä, sulje. Dokumenttipuuta ei koskaan ole olemassa

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;

Vain luku -tila on suositeltavaa aina kun voit: se antaa sisäänoton vaiheen ajaa silloin kun muut prosessit pitävät tiedostoa, ja se dokumentoi tarkoituksen. Tutkintavaihe, joka vahingossa kutsuu mutatoivaa funktiota, epäonnistuu nopeasti ennemmin kuin korruptoi arkiston

PageRef on objektikahva, ei sivunumero

Yksittäisin yleisin DA-API-virhe on sivunumeron välittäminen sinne missä funktio odottaa PageRef:iä. Lähes jokainen per-sivu-DA-kutsu ottaa viitekahvan sivuobjektiin ennemmin kuin sivunumeron: DAExtractPageText, DARenderPageToFile, DARotatePage ja DACapturePage kaikki odottavat viitettä. Saat yhden kääntämällä ihmiskohtaisen numeron DAFindPage:n kautta:

PDF Library for Delphi: sivunumeron PageRefiksi kääntymisen virta, jossa DAFindPage syöttää sivukohtaiset suoran pääsyn kutsut, vastakohtana raaka kokonaislukureferaatti, joka osuu mielivaltaiseen objektiin ja tuottaa hiljaa väärän sivun tekstiä
Jokainen sivukohtainen suora käsittelykutsu kuluttaa DAFindPage:n tuottaman PageRef:in, ei koskaan ihmisen näkemää numeroa. Tuon muunnoksen ohittaminen saa kokonaisluvun esiintymään objektitunnisteena, ja väärän sivun tekstiä voi lähteä ulos näkymättömästi
PageRef := Lib.DAFindPage(Handle, 250);          // page number -> object handle
if PageRef <> 0 then
begin
  Text := Lib.DAExtractPageText(Handle, PageRef, 0);
  Lib.DARenderPageToFile(Handle, PageRef, 5, 150, 'page250.png');
end;

Raon numeron 250 välittäminen sen sijaan ei nosta virhettä. Se osoittaa mihin tahansa objektiin, joka sattuu istumaan tuon kahva-arvon takana, mikä hyvänä päivänä epäonnistuu näkyvästi ja huonona päivänä poimii tekstin väärältä sivulta asiakaskohtaiseen dokumenttiin. Jos käärit DA-kerroksen omaan palvelukoodiisi, tee käännös mahdottomaksi ohittaa: ota sivunumerot rajalla, kutsu DAFindPage:ä heti, ja välitä vain viitteitä sisäisesti

Satojen tiedostojen yhdistäminen nimetyllä listalla

Kahdelle tiedostolle MergeFiles(First, Second, Output) riittää. Eräkokoonpano skaalautuu paremmin tiedostolistojen kautta: rekisteröi syötteet listanimen alle, sitten yhdistä lista yhdessä läpikäynnissä

PDF Library for Delphi: nimetyn tiedostoluettelon työnkulku, jossa tammikuun, helmikuun ja maaliskuun tiliotteet rekisteröityvät yhden luettelonimen alle ja yhdistyvät yhdellä kierroksella, ja Fast-, oletus- ja tiukat muunnelmat vaihtavat rakennepuun säilyttämistä nopeuteen
Sadat rekisteröidyt syötteet kutistuvat yhdeksi MergeFileList-kierrokseksi, jonka tulos varmennetaan millisekunneissa toisella vain luku -kokeella. Variantti on putkikohtainen päätös, koska Fast pudottaa Tagged PDF -rakennepuun
Lib.AddToFileList('Statements', 'jan.pdf');
Lib.AddToFileList('Statements', 'feb.pdf');
Lib.AddToFileList('Statements', 'mar.pdf');
Lib.MergeFileList('Statements', 'q1-statements.pdf');

// Varmista tulos halvalla tavalla: suora käyttö uudelleen
Handle := Lib.DAOpenFileReadOnly('q1-statements.pdf', '');
Writeln('merged pages: ', Lib.DAGetPageCount(Handle));
Lib.DACloseFile(Handle);

Yhdistämisperheellä on kolme varianttia, ja ero ei ole yksin nopeus. MergeFileListFast ohittaa rakennepuun säilyttämisen; MergeFileListStrict pakottaa tiukan tilan; suffikiton versio on tasapainoinen oletus. Käytäntösääntö joka putoaa: jos yksikään syöte on Tagged PDF, jonka saavutettavuusrakenne on säilytettävä, PDF/UA:ta varten tuotettu olkoon ilmeinen tapaus, tavoittele oletusta tai tiukkaa varianttia, koska Fast hiljaa pudottaa rakennepuun. Pelkille skannausarkistoille ilman tagitusta, Fast on ilmaista suorituskykyä. Päätä per putki, ei per kehittäjän mieliala, ja tallenna käytetty variantti työlokiin

Jakaminen lataamatta: välien poiminta

Jakaminen noudattaa samaa ei-lataus-filosofiaa. ExtractFilePages(InputFileName, Password, OutputFileName, RangeList) vetää sivuvälin suoraan tiedostosta tiedostoon, väililuettelolla kuten '1-500', '501-1000', tai pilkuilla erotetuilla valinnoilla, ja lähde ei koskaan tule dokumenttipuuksi. Kun dokumentti on jo ladattu muista syistä, ExtractPageRanges tuottaa uuden muistissa olevan dokumentin nykyisestä, ja CopyPageRanges vetää välejä toisesta ladatusta dokumentista ID:n perusteella. Per-lasku-jako yhdistetyistä tulostusvirroista, tiedostosta-tiedostoon-muoto on se, joka pitää 4 GB syötteen koskaan turpoamasta RAM:iin

Tiedostot, jotka valehtelevat geometriastaan

Suurtiedostoputket kohtaavat vaurioituneita tiedostoja tahtiin, joita pientiedostoputket eivät koskaan näe, yksinkertaisesti koska syötteet kulkevat useampien järjestelmien läpi. Kaksi vikamuotoa ansaitsevat eksplisiittisen käsittelyn

Ensinnä, siirtyneet otsikot. Sähköpostiyhdyskäytävät ja tulostuserottimet joskus prependoivat tavuja PDF:ään, joten %PDF-merkki ei enää istu offsetissa 0 ja jokainen xref-offset tiedostossa on väärin samalla määrällä. Suoratoistolukija havaitsee tämän ja paljastaa sen (DAShiftedHeader flat-tasolla, ShiftedHeader TSmartPDFReader:llä), sitten kompensoi sitä luvuissa. Kotikasvatuset offset-aritmetiikka tyypillisesti ei, mistä syystä "toimii jokaisessa tiedostossa jonka tuotamme, epäonnistuu asiakas X:n tiedostoissa" on klassinen oire

Toiseksi, rikki olevat ristiviittaustaulukot. DACopyFile(InputFileName, OutputFileName, PageCount) suoratoistaa koko tiedoston uuteen kopioon samalla kun rakentaa xref:iä uudelleen, palauttaen sivumäärän sivutuotteena. Sen ajaminen normalisointivaiheena vaativan alavirran kuluttajan edessä muuttaa luokan ajoittaisia parse-epäonnistumisia yhdeksi ennustettavaksi korjausvaiheeksi. Ja kun omat muokkauksesi tarvitsevat tallennusta, DAAppendFile kirjoittaa ne inkrementaalipäivityksenä, liittäen uuden version ennemmin kuin uudelleenkirjoittaen gigatavuja, mikä pitää tallennuskustannuksen suhteessa muutokseen ennemmin kuin tiedostoon

Toimitusyksityiskohdat: linearisointi ja kompositio

Kaksi vierekkäistä kykyä täydentävät suurtiedostoputken. Kun koottu tuloste tarjoillaan HTTP:n yli selaimessa katselua varten, LinearizeFile järjestää sen uudelleen tavuväli-suoratoistoa varten, jolloin ensimmäinen sivu näkyy ennen kuin loput 500 MB paketista on ladattu. Aja se viimeisenä vaiheena, kaiken yhdistämisen jälkeen, koska mikä tahansa myöhempi muutos de-linearisoi tiedoston uudelleen. Ja kun paketit tarvitsevat kompositiota ennemmin kuin pelkkää ketjutusta, sano kansisivu joka leimataan jokaisen laskun taakse tai kaksi lähdesivua jotka asetetaan yhdelle tulostesivulle, DACapturePage muuttaa minkä tahansa sivun uudelleenkäytettäväksi mallipohjaksi, jonka DADrawCapturedPage asettaa kohdesivulle mielivaltaiseen suorakulmioon, yhä ilman täyttä dokumenttilatausta usean gigatavun lähteelle

Rajat ja mikä pysyy vain luku -tilassa

Formaatti itse loppuu tilaa pitkään ennen Direct Accessia. Offsetit ovat Int64 koko matkan DA-kerroksen läpi, joten todelliset katot ovat käytettävissä oleva levy ja klassisten (ei-virta) ristiviittaustaulukoiden 10-numeroinen xref-offset-kenttä. Usean gigatavun skannausarkistot ovat käytännössä tavallisia, ja muisti pysyy rajattuna tiedostokoosta riippumatta koska objekteja luetaan vain kun kutsu niitä pyytää

Kaksi kysymystä nousee tarpeeksi usein vastattavaksi suoraan. Yhdistäminen oletuspolun kautta kantaa dokumenttirakenteen yli, joten kirjanmerkit ja linkit säilyvät; Fast-variantti on se, joka vaihtaa rakennepuun nopeuteen, mikä on koko syy reserveerata se tagittomille syötteille. Turvallinen tapa on avata yhdistetty tuloste, kävellä sen ääriviivat, ja tarkistaa muutama sisäinen linkki ennen toimitusta. Mitä tulee editointiin: on hyödyllinen välimaasto vain-luku-tutkimisen ja täyslatauksen välillä. Sivutason operaatiot toimivat kahvalla suoraan, DARotatePage, DAMovePage ja DAHidePage niiden joukossa, sekä lomakekenttäluvut, ja DAAppendFile pysäyttää nuo muokkaukset inkrementaaliversiona. Sisältötason editointi, mikä tahansa joka uudelleenkirjoittaa merkintäoperaattorit sivun sisällä, kuuluu yhä täydelle dokumenttikerrokselle

Liittyvät artikkelit

Jos yhdistetyn tulosteesi on pysyttävä saavutettavana, rakennepuun taustaa on käsitelty Tagged PDF -saavutettavuusartikkelissa, joka selittää tarkalleen mitä Fast-yhdistämisvariantti heittäisi pois. Sisällön vetämiseksi ulos väleistä, joita jaat, katso tekstin, kuvien ja fonttien poimintaopastus

Täydellinen Direct Access -funktioluettelo toimitetaan kirjaston mukana; editiot ja kokeiluversioiden lataukset löytyvät PDF Library for Delphi -tuotesivulta