HotPDF kirjoittaa linearisoituja PDF-tiedostoja, asettelua, jota Acrobat kutsuu nimellä Fast Web View, THotPDF:n LinearizeOutput-ominaisuuden kautta. Sen asettaminen ennen BeginDoc:ia saa HotPDF:n järjestämään valmiin objektigraafin uudelleen niin, että tavualueita ymmärtävä lukija voi näyttää sivun yksi noudettuaan vain tiedoston alkuosan sen sijaan, että lataisi koko asiakirjan ensin. Mekanismi on ISO 32000-1 liite F
Syy, miksi tällä on merkitystä, on epäglamouröösi. Tavallinen PDF sijoittaa ristiviittaustaulukkonsa loppuun, joten lukijan täytyy saavuttaa viimeinen tavu ennen kuin se tietää, missä mikäkin on. Anna selaimelle 200-sivuinen skannattu raportti, ja käyttäjä tuijottaa latausanimaatiota koko siirron ajan, vaikka ainoa asia, jota hän halusi, oli sivu 1. Linearisointi korjaa tämän maksamalla kustannuksen kirjoitushetkellä. Tämä artikkeli käsittelee nimenomaan tuota kirjoituspolkua, osiointia, mittaussilmukkaa ja kovia rajoja; käsitteellisen taustan siitä, mitä Fast Web View tuo mukanaan, kattaa aiempi selitys PDF-linearisoinnista ja Fast Web View'sta
Mitä linearisoitu asettelu todella takaa
Linearisoitu tiedosto on tavallinen PDF, jolla on erittäin tarkka fyysinen järjestys, ja jokainen sen tarjoama takuu tulee tuosta järjestyksestä eikä mistään uudesta objektityypistä. HotPDF antaa osat liitteen F määräämässä järjestyksessä: linearisointiparametrisanakirja ensimmäisten 1024 tavun sisällä, varhainen ristiviittaustaulukko, asiakirjatason objektit, ensisijainen vihjevirta, ensimmäinen sivu ja sen yksityiset objektit, sitten loput sivut, sitten jaetut objektit, sitten kaikki muu, ja lopuksi pääristiviittaustaulukko
Osiointi johdetaan, ei julisteta. HotPDF kävelee viittausgraafin läpi jokaisesta sivuobjektista ja tallentaa jokaiselle epäsuoralle objektille, kuinka moni sivu saavuttaa sen ja mikä sivu saavutti sen ensimmäisenä. Objektista, jota käyttää täsmälleen yksi sivu, tulee kyseiselle sivulle yksityinen. Objektista, jonka saavuttaa useampi kuin yksi, tulee jaettu. Luettelo, plus mitä tahansa se viittaa kohteissa /ViewerPreferences, /OpenAction, /Threads ja /AcroForm, plus salaussanakirja kun suojaus on aktiivinen, muodostavat asiakirjatason ryhmän, jonka on edellettävä kaikkea muuta. Sivupuun solmut pidätetään tarkoituksella, jotta ne eivät saastuta ensimmäisen sivun osiota
Parametrisanakirja kantaa luvut, joita lukija tarvitsee ennen kuin se on lukenut mitään muuta: /L koko tiedoston pituudelle, /H vihjevirran siirtymälle ja pituudelle, /O ensimmäisen sivun objektinumerolle, /E tavulle, johon ensimmäisen sivun osio päättyy, /N sivumäärälle ja /T pääristiviittaustaulukon merkinnän siirtymälle. Jokainen näistä on tavusiirtymä tiedostoon, jota ei vielä ole olemassa sillä hetkellä, kun ne täytyy kirjoittaa
Miksi vihjetaulukon siirtymien täytyy konvergoitua?
Koska parametrisanakirjan luvut kuvaavat tiedostoa, joka sisältää ne, ja minkä tahansa niistä muuttaminen muuttaa tiedostoa. Se on linearisoidun kirjoittajan keskeinen vaikeus, ja siksi HotPDF mittaa toistuvasti sen sijaan, että kirjoittaisi kerran. Levennä /T 6 numerosta 7:ään, ja parametrisanakirja kasvaa tavulla; otsikko kasvaa; jokainen objekti siirtyy; pääristiviittaustaulukko liikkuu; /T tarvitsee nyt eri arvon. Asettelun täytyy saavuttaa kiintopiste ennen kuin yhtäkään todellisen tulosteen tavua sitoutetaan
HotPDF käsittelee tämän rajoitetulla iteraatiolla. Se ensin serialisoi jokaisen objektin laskevaan virtaan, joka tallentaa pituuden pitämättä tavuja, joten jokaisella objektilla on tunnettu serialisoitu koko. Se ajaa sitten asetteluvaiheen, joka antaa siirtymät asiakirjatason ryhmälle, vihjevirralle, ensimmäisen sivun ryhmälle, myöhemmille sivuryhmille, jaetulle ryhmälle ja loppuosalle, ja raportoi, minne pääristiviittaustaulukko päätyisi. Tuo tulos syötetään takaisin seuraavan vaiheen syötteeksi. Silmukka on rajattu kahdeksaan yritykseen, ja konvergoitumattomuus nostaa poikkeuksen sen sijaan, että tuottaisi tiedoston, jossa on uskottavan näköiset mutta väärät siirtymät
CandidateMainOffset := 0;
for Attempt := 0 to 7 do
begin
CalculateLayout(CandidateMainOffset, FirstXRefData,
HintOffset, EndFirstPage, NewMainOffset);
if NewMainOffset = CandidateMainOffset then
Break;
CandidateMainOffset := NewMainOffset;
end;
if NewMainOffset <> CandidateMainOffset then
raise Exception.Create('Linearization layout did not converge');
Kaksi yksityiskohtaa estää silmukkaa säppäilemästä. Parametrisanakirja kirjoitetaan kiinteään 384-tavun paikkaan, täytettynä välilyönneillä, joten sen oma kasvu ei voi koskaan horjuttaa asettelua; jos sanakirjateksti koskaan ylittäisi tuon varauksen, HotPDF nostaisi poikkeuksen sen sijaan, että hiljaa siirtäisi kaikkea. Ja konvergoitumisen jälkeen HotPDF ajaa vielä yhden vahvistavan asetteluvaiheen ja tarkistaa vihjevirran pituuden uudelleen, koska vihjevirta itse koodaa siirtymiä, jotka tunnettiin vasta kun asettelu vakautui. Kaiken tämän mittauksen tuotto on, ettei HotPDF koskaan puskuroi toista kopiota asiakirjasta: kun siirtymät on kiinnitetty, objektit serialisoidaan suoraan kohdevirtaan, ja jokaisen osiorajan kohdalla on vakuutus, että kirjoitetut tavut täsmäävät luvattuun siirtymään
Sen kytkeminen päälle Delphistä
API-pinta on yksi Boolean, ja sen ainoa vaatimus on, että se asetetaan ennen generoinnin alkua. LinearizeOutput on oletuksena False, ja asetteluvaihe ajetaan, kun asiakirja kirjoitetaan, joten sen asettaminen EndDoc:in jälkeen ei saa mitään aikaan
var
PDF: THotPDF;
begin
PDF := THotPDF.Create(nil);
try
PDF.FileName := 'fast-view.pdf';
PDF.Version := pdf17;
PDF.LinearizeOutput := True; // must precede BeginDoc
PDF.BeginDoc;
PDF.Canvas.TextOut(72, 72, 'First page');
PDF.EndDoc;
finally
PDF.Free;
end;
end;
Yksi käyttöönottovaroitus ylittää arvoltaan kaiken koodipuolella olevan. Linearisointi kannattaa vain, kun kuljetus tukee HTTP-tavualuepyyntöjä. Palvele samaa tiedostoa päätepisteestä, joka suoratoistaa sen kokonaisena, tai CDN-asetuksesta, joka jättää Range:n huomiotta, ja olet ostanut itsellesi hitaamman kirjoituspolun ja suuremman tiedoston ilman käyttäjälle näkyvää hyötyä. Tarkista palvelin ennen kuin tarkistat koodin
Miksi linearisointi ohittaa UseXRefStream- ja UseObjectStreams-asetukset?
Koska linearisoitu kirjoittaja tarvitsee jokaiselle objektille oman suoraan osoitettavan tavusiirtymän, ja molemmat nuo ominaisuudet vievät sen pois. HotPDF antaa siis perinteiset tekstiristiviittaustaulukot ja purkamattomat epäsuorat objektit aina, kun LinearizeOutput on käytössä, vaikka kutsuja olisi myös asettanut UseXRefStream:in tai UseObjectStreams:in. Tämä on tarkoituksellinen ohitus, ei ristiriita, joka pitäisi ratkaista itse
Perustelu seuraa vihjetaulukoista. Vihjetaulukko kuvaa, mistä sivuosio alkaa ja kuinka pitkä se on, joten lukija voi pyytää täsmälleen tuota aluetta. Objektilla, joka on pakattu /ObjStm-säilöön, ei ole lainkaan itsenäistä siirtymää; se on olemassa vain siivuna toisen pakatun virran sisällä, joka täytyy noutaa ja purkaa yhtenä yksikkönä. Jos luotit objektivirtoihin tiedostokoon suhteen, ymmärrä, että linearisointi ja pakkaus vetävät tässä vastakkaisiin suuntiin, ja lue kompromissi rinnakkaisartikkelista objektivirrat ja asteittaiset päivitykset HotPDF:ssä. Sama jännite muovaa hybridiviittaustiedostoja, jotka ovat olemassa juuri pitääkseen vanhemmat lukijat toimimassa virtapohjaisten taulukoiden rinnalla, kuten käsitellään artikkelissa hybridiristiviittausvirrat Office-tuotetuissa PDF-tiedostoissa
Olemassa on myös versiolattia. Linearisointi vaatii PDF 1.2:n tai uudemman. Jos valittu versio on vanhempi, HotPDF nostaa sitä automaattisesti, ellei StrictVersionLock ole asetettu, jolloin kirjoittaminen nostaa poikkeuksen sen sijaan, että hiljaa korottaisi asiakirjaa, jonka lukitsit tarkoituksella
4 GiB:n muuri, ja miksi HotPDF kieltäytyy sen sijaan, että katkaisisi
Linearisoinnin vihjetaulukot tallentavat siirtymät 32-bittisinä arvoina, joten linearisoitu tiedosto ei voi osoittaa mitään 4 GiB:n kohdalla tai sen yli, ja HotPDF hylkää tällaisen tulosteen nimenomaisella poikkeuksella sen sijaan, että kirjoittaisi tiedoston, jossa on kiertyneet siirtymät. Raja ei ole HotPDF:n toteutusvalinta; se on liitteen F määrittelemien kenttien leveys
Tarkistus tehdään kolmessa paikassa, ja kaikki kolme merkitsevät. HotPDF validoi jokaisen objektin heti kun sen serialisoitu pituus on tiedossa, validoi jokaisen sivuosion pituuden vihjemerkintöjä rakennettaessa ja validoi lopullisen tiedoston pituuden sen jälkeen kun pääristiviittaustaulukko on mitoitettu. Varhainen epäonnistuminen on koko pointti: vihjetaulukko, jossa on hiljaa katkaistu siirtymä, tuottaa tiedoston, joka avautuu oikein katseluohjelmassa, joka lataa sen kokonaisena, ja epäonnistuu vain tavualue-asiakkaalle, jota varten linearisointi oli olemassa, mikä on pahin mahdollinen vikamoodi, koska testikatseluohjelmasi ei koskaan toista sitä. Jos tuotat monen gigatavun tulostetta, linearisointi ei ole työkalu, ja suoratoistolähestymistapa, joka on kuvattu artikkelissa Direct File API suurten PDF-työnkulkujen kanssa, on suunta, johon kannattaa katsoa
Linearisoinnin tunnistaminen ladatusta tiedostosta
THotPDF.IsLoadedLinearized raportoi, oliko parhaillaan ladattu asiakirja jo kirjoitettu linearisoidussa muodossa, ja se vastaa ennen jäsentämistä otetun tilannekuvan perusteella, ei elävästä virrasta. HotPDF lukee ensimmäiset 1024 tavua lähdevirran positiosta nolla, skannaa niistä ensimmäisen obj-avainsanan ja sitten /Linearized-merkinnän arvolla 1, ja välimuistittaa totuusarvotuloksen
var
PDF: THotPDF;
PageCount: Integer;
begin
PDF := THotPDF.Create(nil);
try
PageCount := PDF.LoadFromFile('incoming.pdf');
if (PageCount > 0) and (not PDF.IsLoadedLinearized) then
Writeln('Source is not Fast Web View ready');
finally
PDF.Free;
end;
end;
Kaksi rajoitetta tuossa kuvauksessa ovat kantavia. Tunnistus ei voi luottaa virran positioon, koska siihen mennessä, kun sovelluskoodi kysyy asiaa, jäsennin on siirtänyt sitä, eikä se voi lukea uudelleen tarpeen mukaan, koska LoadFromFile vapauttaa sisäisen lähdevirran heti kun lataaminen päättyy. Siksi ennen-jäsennystä-kaappaa-ja-välimuistita-suunnittelu. Skannaus on myös tarkoituksella kirjaimellinen arvon suhteen: vain /Linearized 1 tai numeerisesti vastaava muoto, jonka murto-osa on kokonaan nolla, hyväksytään, koska tiedosto, jonka parametrisanakirja sanoo jotain muuta, ei anna liitteen F lupausta
Delphi-tietuenansa, joka kannattaa omaksua
Paikalliset tietueet, jotka sisältävät dynaamisia taulukoita, alustavat hallitut kenttänsä ja ei mitään muuta, ja jos pidät tavallista Count-kenttää taulukon vieressä, se täytyy tyhjentää itse. Tämä puri linearisoinnin osiointia kehityksen aikana, ja se on sellainen vika, joka maksaa päivän juuri siksi, että yksi alusta piilottaa sen
type
THPDFLinearIndexList = record
Values: THPDFIntegerArray; // managed field: cleared for you
Count: Integer; // plain field: whatever was on the stack
end;
// Required, not cosmetic:
Part4 := Default(THPDFLinearIndexList);
Part6 := Default(THPDFLinearIndexList);
Part8 := Default(THPDFLinearIndexList);
Part9 := Default(THPDFLinearIndexList);
Dynaaminen taulukkokenttä on viittauslaskettu, joten kääntäjä nollaa sen. Sen vieressä oleva Count on tavallinen kokonaisluku ilman sellaista takuuta, ja alustamaton Count lähettää ensimmäisen liitoksen mielivaltaiseen indeksiin. Win32:ssa pinopaikka sattui pitämään nollaa, liitos päätyi indeksiin 0, ja jokainen testi meni läpi. Win64:ssä sama koodi kirjoitti taulukon lopun yli. Opetus yleistyy paljon linearisointia laajemmalle: kun tietue sekoittaa hallittuja ja hallitsemattomia kenttiä, aseta Default(TRecord) ja lopeta pohtiminen, mitkä kentät kääntäjä kattaa, äläkä koskaan kohtele vihreää Win32-ajoa todisteena siitä, että alustus on oikein
Tässä kuvatut LinearizeOutput- ja IsLoadedLinearized-jäsenet toimitetaan vakiona HotPDF Componentin mukana Delphille ja C++Builderille; tuotesivu kantaa täyden ominaisuusviitteen, mukaan lukien vuorovaikutussäännöt ristiviittausvirtojen, objektivirtojen ja versiolukituksen kanssa