HotPDF RenderCacheFolder muuttaa HotPDF Delphi -komponentin muistissa olevan renderöityjen sivujen välimuistin pysyväksi levysivuvälimuistiksi: renderöidyt sivut kirjoitetaan PNG-tiedostoina valitsemallesi kansiolle, ja kun sama PDF-lähde avataan seuraavan kerran, RenderLoadedPageToBitmapCached lukee ne takaisin uudelleenrasteroinnin sijaan. Hakujärjestys on muisti, sitten levy, sitten renderöijä
Levytaso on ollut API:ssa versiosta v2.416.0 alkaen, mutta aina versioon v2.770.140 asti se ei koskaan oikeasti palvellut sivua tavalliselle LoadFromFile- tai LoadFromStream-kutsulle. Korjaus pakotti kysymykseen, jonka jokaisen pysyvän välimuistin on vastattava: mistä tiedät, että tänään avaamasi tiedosto on asiakirja, jonka renderöisit eilen, ja mitä välimuistitetuille sivuille tapahtuu, kun ei ole? Alla ovat vastaukset, jotka HotPDF valitsi, mukaan lukien paikat, joissa se kieltäytyy tahallaan välimuistittamasta
Miten HotPDF:n levyrenderöintivälimuisti toimii?
HotPDF:n levyrenderöintivälimuisti on toinen taso muistin rasterivälimuistin takana, ja se osallistuu vain, kun RenderCacheFolder on ei-tyhjä polku. Kutsu RenderLoadedPageToBitmapCached(PageIndex, DPI) skannaa ensin muistin merkinnät, avaimennettuina sivuindeksin, DPI:n ja renderöintiasetusten muunnelman mukaan. Osumatta se kysyy levytasolta; levyosuma dekoodaa PNG:n, ylentää sen takaisin muistiin ja palauttaa kutsujan omistaman kopion. Vasta kun molemmat tasot ohittuvat, sivu kulkee artikkelissa ladatun PDF-sivun renderöinti TBitmap-olioksi kuvatun sisältöstreamitulkinn läpi, ja tuore bittikartta kirjoitetaan silloin myös levylle
Levyllä asettelu on tahallaan tylsä. Jokainen asiakirja saa alikansion, jonka nimi tulee 16 heksamerkkisen dokumenttiavaimen plus 16 heksamerkkisen renderöintimuunnelman perusteella, jokainen sivu talletetaan muodossa <page>@<dpi>.png, ja juuressa oleva index.txt pitää asiakirjat viimeksi käytettyjen järjestyksessä skeematagin takana. Skeemaero tyhjentää kansion ensimmäisellä käyttökerralla. Kirjoitukset menevät ensin väliaikaistiedostoon ja vaihdetaan paikalleen atomisella korvauksella, joten kaatuminen kesken kirjoituksen jättää joko vanhan sivun tai ei mitään, ei koskaan puolikasta PNG:tä. PNG, joka ei dekoodaudu, poistetaan ja lasketaan osumatta jäämiseksi
Kolme rajaa rajoittaa kansiota:
RenderCacheMaxDocuments(oletus 20) rajoittaa asiakirja-alikansioiden määrän; vähiten viimeksi käytetty kansio poistetaan ensinRenderCacheMaxBytes(oletus 524288000, eli 500 MB) rajoittaa kaikkien juuren alla olevien PNG-tiedostojen kokonaiskoon- Jokainen asiakirjakansio pitää enintään 200 sivukuvaa; kyseinen asiakirjakohtainen raja on THotPDF:n kiinteä eikä julkaistu ominaisuus
RenderCacheCapacity (oletus 8) on erillinen vipu: se asettaa, kuinka monta renderöityä sivua muistitaso pitää, eikä sillä ole mitään tekemistä levyn jalanjäljen kanssa
uses
SysUtils, Graphics, HPDFDoc;
procedure WarmThumbnails(const FileName: string);
var
Pdf: THotPDF;
Bmp: TBitmap;
I: Integer;
begin
Pdf := THotPDF.Create(nil);
try
// Määritä levytaso ennen ensimmäistä välimuistittua renderöintiä:
// kansio ja molemmat rajat luetaan, kun tasoa käytetään ensimmäistä kertaa
Pdf.RenderCacheFolder := IncludeTrailingPathDelimiter(
GetEnvironmentVariable('LOCALAPPDATA')) + 'MyViewer\PageCache';
Pdf.RenderCacheMaxDocuments := 50;
Pdf.RenderCacheMaxBytes := Int64(1024) * 1024 * 1024; // 1 GiB
Pdf.RenderCacheCapacity := 16; // sivuja muistissa
if Pdf.LoadFromFile(FileName) > 0 then
for I := 0 to Pdf.LoadedPageCount - 1 do
begin
Bmp := Pdf.RenderLoadedPageToBitmapCached(I, 96);
if Bmp <> nil then
try
// Anna kopio pikkukuvakaistalle täällä
finally
Bmp.Free; // välimuistitettu kutsu palauttaa aina kutsujan omistaman kopion
end;
end;
finally
Pdf.Free; // versiosta v2.770.140 alkaen tämä ei enää poista levymerkintöjä
end;
end;
Aja sama menettely kahdesti, ja toinen ajo ei koskaan rasteroi sivua, joka mahtui välimuistiin. Levyn välimuistiobjekti luodaan vasta ensimmäisellä välimuistitulla renderöinnillä ja elää, kunnes THotPDF-instanssi vapautetaan, joten muutos funktioissa RenderCacheFolder, RenderCacheMaxDocuments tai RenderCacheMaxBytes sen jälkeen ei siirrä eikä muuta kokoa jo avatulle välimuistille. Sivut, jotka ovat liian suuria muistitason ottopolitiikalle (oletuksena yksittäinen merkintä saa olla enintään 64 MiB 32-bittisiä pikseleitä), eivät myöskään säilyy levylle, ja levytasolta kysytään vain, niin kauan kuin RenderFallbackPolicy pitää oletusarvonsa rfpIgnore, koska varapolun diagnostiikkaa ei talleteta PNG:n kylkeen
Miksi RenderCacheFolder ei koskaan toiminut ennen v2.770.140:ää?
RenderCacheFolderilla ei ollut vaikutusta ennen v2.770.140:ää, koska levytaso avaimensi asiakirjat lähdetavujen hashilla, jota tavalliset lataukset eivät koskaan pitäneet hallussaan. Dokumenttiavain tuli SHA-256:sta raakojen PDF-tavujen sisäisen kopion yli, mutta LoadFromFile ja LoadFromStream jäsentävät lähteen paikallaan eivätkä säilytä sellaista kopiota; kenttä täytettiin vain väliaikaisesti salatun palautumisen polulla ja tyhjennettiin heti sen jälkeen. Ilman tavuja avain oli aina tyhjä, ja tyhjä avain tarkoittaa, että levytaso ohitetaan. Ei virhettä, ei varoitusta, vain kansio, joka pysyi tyhjänä
Avaimen ei-tyhjäksi tekeminen paljasti toisen bugin, joka oli ollut kätkyneenä ensimmäisen takana. Vanha InvalidateRenderedPageCache poisti asiakirjan levikansion, ja InvalidateRenderedPageCache ajetaan jokaisen latauksen alussa, jokaisen muokkauksen yhteydessä ja funktion Free sisällä. Heti kun avain olisi toiminut, jokainen katseluohjelmistunto olisi tuhonnut oman välimuistinsa poistuessaan, ja seuraava istunto olisi joka tapauksessa alkanut kylmänä. Pahempaa, avain laskettiin uudelleen samasta lähteestä muokkauksen jälkeen, joten muokatun asiakirjan renderöinnit olisi talletettu alkuperäisen tiedoston avaimen alle ja palveltu seuraavalle istunnolle, joka avasi muokkaamattoman PDF:n. v2.770.140 korjaa identiteetin ja mitätöinnin yhdessä; vain toisen korjaaminen olisi toimittanut joko kuolleen tai valehtelevan välimuistin
Miten HotPDF tunnistaa PDF:n lukematta koko tiedostoa
HotPDF tunnistaa paikallisesta tiedostosta ladatun PDF:n sormenjäljestä, joka koostuu sen koosta, viimeisimmästä kirjoitusajasta sekä ensimmäisistä ja viimeisistä 64 KiB:stä, ja streamin tai random access -lähteen sen koko sisällön SHA-256:sta. Molemmat kaapataan kerran, kun lataus onnistuu, ja SHA-256-tiivisteen ensimmäiset 16 heksamerkkiä (64 bittiä) muuttuvat dokumenttiavaimiksi
| Lähde | Identiteetti | Kustannus | Kaapataan kun |
|---|---|---|---|
LoadFromFile | Koko + LastWriteTime + aloittavat ja päättävät 64 KiB, hashattuna SHA-256:lla | Enintään 128 KiB luettu, riippumaton tiedoston koosta | Jokainen onnistunut lataus, vaikka RenderCacheFolder asetettaisiin myöhemmin |
LoadFromStream | Koko streamin SHA-256 | Yksi täysi kierros lähteen yli | Vain jos RenderCacheFolder oli asetettu ennen latausta |
LoadFromRandomAccessSource | Koko lähteen SHA-256 | Yksi täysi kierros lähteen yli | Vain jos kansio oli asetettu ensin ja koko väli on saatavilla |
Mikä tahansa lähde, jolla on /Encrypt-merkintä | Ei mitään | Ei mitään | Ei koskaan; levytaso ohitetaan |
Tiedostosormenjälki on harkittu kompromissi. 400 MB:n skannatun arkiston hashannaminen kokonaan jokaisella avauksella voi maksaa enemmän kuin kahden sivun renderöinti, joita käyttäjä oikeasti katsoo. Otetut näytteet eivät ole satunnaisia: otsikko istuu tiedoston alussa, ja traileri sekä viimeinen ristiviitausosio istuvat lopussa (ISO 32000-1 §7.5). Inkrementaalinen päivitys liittää uuden rungon, ristiviitausosion ja trailerin (§7.5.6), joten se muuttaa koon ja hännän yhtä aikaa. Täysi uudelleenkirjoitus millä tahansa tavallisella työkalulla muuttaa viimeisimmän kirjoitusajan. Tiedostoille enintään 128 KiB molemmat näytteet kattavat jokaisen tavun, joten pienet asiakirjat hashannataan tehollisesti kokonaan
Jäännösriski on samankokoinen, paikallaan tehty muutos suuren tiedoston keskelle, jonka kirjoittaja palauttaa sitten alkuperäisen aikaleiman. Se vaatii työkalun, joka säilyttää muokkausajat tahallaan muokaten sisältöä, mikä on harvinaista mutta ei mahdotonta, ja silloin välimuisti palvelee vanhentuneita sivuja. Käännetty puoli on hyvänlainen: tiedoston kopioiminen Windowsissa säilyttää yleensä viimeisimmän kirjoitusajan, joten välimuistissa jo olevan asiakirjan kopio osuu samoihin merkintöihin, mikä on oikein, koska tavut ovat identtiset
Streameilla ei ole lainkaan muokkausaikaa, joten ainoa rehellinen identiteetti on sisältö. HotPDF maksaa kyseisen täyden SHA-256-kierroksen vain, kun olet pyytänyt levyn välimuistia ennen lataamista; jokainen muu LoadFromStreamin kutsuja ei näe ylimääräistä kustannusta. Se tekee ominaisuuden sijoitusjärjestyksestä kantavan kuorman:
procedure OpenDownloadedPdf(Pdf: THotPDF; Data: TStream;
const CacheRoot: string);
begin
// Väärä järjestys streameille: sisältöhash lasketaan vain, kun
// kansio on jo asetettu, joten tämä asiakirja ohittaisi levytason
// Pdf.LoadFromStream(Data);
// Pdf.RenderCacheFolder := CacheRoot;
Pdf.RenderCacheFolder := CacheRoot; // aseta ensin
Data.Position := 0;
if Pdf.LoadFromStream(Data) <= 0 then
raise Exception.Create('The stream is not a loadable PDF');
end;
Random access -lähde, joka on yhä latautumassa (jatka alueilta ei ole vielä saatavilla), saa ei identiteettiä vaan osittaisen sisällön hashin sijaan, ja jos identiteetin laskeminen epäonnistuu mistä tahansa syystä, lataus silti onnistuu; asiakirja renderöi yksinkertaisesti ilman levytasoa
Mitä mitätöi HotPDF:n levyvälimuistin merkinnän?
HotPDF:n levyvälimuistin merkintää ei koskaan mitätöidä poistamalla sitä muokkauksen yhteydessä; sen sijaan ladatun asiakirjan muokkaaminen pudottaa asiakirjan identiteetin, joten levytaso ohitetaan kyseisen latauksen lopun ajan, ja talletetut sivut pysyvät kelvollisina muokkaamattomalle lähteelle. Merkinnät poistuvat levyltä vain LRU- ja tavurajojen, rikkinäisen PNG:n tai skeemamuutoksen kautta
Avain kuvaa lähdettä levylle, ei objektigraafia muistissa. Kun leimaat sivun tai muutat annotaatiota, asiakirja ei enää vastaa kyseistä lähdettä, joten lukeminen eikä kirjoittaminen sen avaimen alla olisi oikein. Versiosta v2.770.140 alkaen sekä asiakirjatason että sivutason mitätöinti tyhjentävät identiteetin kansion koskemisen sijaan, ja toinen vartiointi on olemassa muokkauksille, jotka eivät kutsuneet funktiota InvalidateRenderedPageCache: ennen levytason käyttöä THotPDF tarkistaa, onko joku ladattu objekti likainen, ja käsittelee likaisen asiakirjan identiteetittömänä
Renderöintiasetukset toimivat toisin päin. Funktion PageRenderBackend vaihtaminen (tai UseNativeGDIRenderBackendin kutsuminen) sekä funktioiden ConfigureRenderICCWorkflow tai ClearRenderICCWorkflow kutsuminen tyhjentävät muistin sivut mutta pitävät identiteetin, koska asiakirja vastaa yhä lähdettään. Kyseiset asetukset muuttavat pikseleitä olematta osa muistin muunnelmaa, joten levyavain taittaa sisäänsä backendin nimen, black-point-kompensaatioflagin sekä ICC-vedos- ja tulostusprofiilien SHA-256-tiivisteet. Muunnelma itsessään kattaa jo väri-intentin, tulosteen ditheringin, overprint-esikatselun, luminosity-maskin tilan, varapolitiikan ja jokaisen valinnaisen sisältöryhmän näkyvyyden, joten tason kytkeminen renderöityy eri kansioon oletusnäkymän ylikirjoittamisen sijaan
Saadaksesi muokatun asiakirjan takaisin levytasolle, anna sille uusi lähteen identiteetti tallentamalla se ja lataamalla tulos:
procedure CommitEditsAndRekey(Pdf: THotPDF; const EditedFile: string);
begin
// Ladatun asiakirjan muokkaamisen jälkeen: päivitä muistin sivut.
// Lähteen identiteetti on jo poissa, joten alkuperäisen asiakirjan
// levykansiosta ei lueta eikä siihen kirjoiteta mitään
Pdf.InvalidateRenderedPageCache;
// Tallennettu tiedosto saa uuden koon ja viimeisen kirjoitusajan, eli uuden
// identiteetin; tämän latauksen jälkeiset renderöinnit välimuistitetaan uudella avaimella
Pdf.SaveLoadedDocument(EditedFile);
if Pdf.LoadFromFile(EditedFile) <= 0 then
raise Exception.Create('Could not reload the edited document');
end;
Alkuperäisen asiakirjan kansiota ei kosketa, ja se vanhenee funktioiden RenderCacheMaxDocuments ja RenderCacheMaxBytes kautta kuten mikä tahansa muu merkintä. Jos käyttäjä avaa muokkaamattoman alkuperäisen uudelleen, sen sivut ovat yhä siellä
Turvallisuusrajat: salatut lähteet ja linkitetyt kansiot
HotPDF:n levyrenderöintivälimuisti kieltäytyy kahdenlaisista syötteistä tahallaan: se ei koskaan kirjoita salatun PDF:n sivuja levylle, eikä se koskaan seuraa asiakirjan alikansiota, joka on junction tai muu reparse point. Molemmat säännöt vaihtavat välimuistiosumia siihen, ettei dataa vuoda eikä väärää tiedostoja poisteta
Salattuja PDF:ä ei koskaan välimuistiteta levylle
Renderöity sivu on salauksen purkamaa sisältöä. Sen kirjoittaminen pelkkänä PNG:nä välimuistikansioon jättäisi luettavan kopion salasanalla suojatusta asiakirjasta levylle, kirjoittajan valitseman suojan ulkopuolelle (ISO 32000-1 §7.6). HotPDF kaappaa siksi ei identiteettiä millekään lähteelle, jonka traileri kantaa /Encrypt-merkintää, mukaan lukien salasanalla tai tyhjällä käyttäjän salasanalla avatut tiedostot. Kyseiset asiakirjat käyttävät yhä muistitasoa, joka kuolee prosessin mukana
Junction-alikansiot hylätään versiosta v2.770.173 alkaen
Välimuistin juuri on sinun valintasi, ja sen osoittaminen junctioniin on sallittua. Sen alla olevat asiakirja-alikansiot ovat eri asia: välimuisti luo, lukee, koskettaa ja poistaa ne itse käynnistyksen palautumisen aikana (joka poistaa jäljelle jääneet väliaikaistiedostot), haussa (joka päivittää aikaleimoja), talletuksessa, mitätöinnissä ja kolmen poistorajan kohdalla. Jos joku, jolla on kirjoitusoikeus välimuistin juureen, korvaa asiakirjakansion junctionilla toiseen hakemistoon, jokainen kyseinen polku seuraisi sitä, ja poisto poistaisi tiedostoja jostain, jota välimuisti ei koskaan omistanut. Versiosta v2.770.173 alkaen jokainen kyseisistä sisääntulopisteistä tarkistaa reparse point -attribuutin ja ohittaa linkitetyn asiakirjakansion: haku laskee osumatta jäämisen, talletus laskee kirjoitusvirheen, ja poisto jättää sen rauhaan
Unicode-polut ja jaetut juuret
Kaksi aiheeseen liittyvää korjausta merkitsee, jos viet tuotantoon käyttäjäprofiileihin. Ennen v2.770.135 RenderCacheFolder oli AnsiString, joten järjestelmän koodisivun ulkopuolinen kansio (esimerkiksi kiinalainen käyttäjänimi englanninkielisessä Windows-asennuksessa) muunnettiin häviöllisesti ennen kuin välimuisti näki sen; ominaisuus on nykyään Unicode-string, ja atominen korvaus käyttää leveää Windows API:a. Versiosta v2.770.52 alkaen useat THotPDF-instanssit yhdessä prosessissa, jotka osoittavat samaan juureen (polkulaajennuksen jälkeen, verrattuna kirjainkokoa tuskaallisesti), jakavat yhden viitemäärätyn indeksin ja lukon. Aiemmin jokainen instanssi ylikirjoitti index.txtin omalla kopiallaan ja valvoi rajoja osittaista näkymäänsä vasten, joten kansio saattoi kasvaa useita kertoja budjettinsa yli
Kyseinen jakaminen pysähtyy prosessirajaan. Kaksi erillistä prosessia samalla juurella pitävät yhä erilliset muistin indeksit, joten anna jokaiselle samanaikaisesti ajavalle sovellukselle oma välimuistin juurensa. Työsäikeillä renderöivät katseluohjelmat sopivat yhteen yhden prosessin sisällä: sekä PrefetchLoadedPages että artikkelissa taustarenderöinti pyyntöjonolla kuvattu jono kulkevat samaa välimuistitettua polkua ja samaa lukkoa pitkin
Pikaopas: RenderCacheFolder-tarkistuslista
- Aseta
RenderCacheFolder,RenderCacheMaxDocumentsjaRenderCacheMaxBytesennen ensimmäistä kutsua funktioonRenderLoadedPageToBitmapCached; stream- ja random access -latauksille aseta kansio ennen lataamista - Päivitys versioon v2.770.140 tai uudempaan, jos nojaat levytasoon; aiemmat versiot hyväksyvät ominaisuuden mutta eivät koskaan palvele sivua levyltä tavallisille latauksille
- Älä odota levyn välimuistittamista salatuille PDF:ille, latauksen jälkeen muokatuille asiakirjoille tai silloin, kun
RenderFallbackPolicyei olerfpIgnore - Vapauta THotPDF-instanssi normaalisti; versiosta v2.770.140 alkaen kumpikaan funktioista
FreetaiInvalidateRenderedPageCacheei poista levymerkintöjä PageRenderBackendin tai ICC-työnkulun vaihtaminen pitää asiakirjan levytasolla eri avaimen alla- Käytä yhtä välimuistin juurta ajettavaa sovellusta kohden; instanssit yhden prosessin sisällä jakavat indeksin versiosta v2.770.52 alkaen
- Pidä välimuistin juuri käyttäjäkohtaisessa sijainnissa; junctioniksi muutetut asiakirja-alikansiot ohitetaan versiosta v2.770.173 alkaen
Pysyvä sivuvälimuisti maksaa itsensä takaisin parhaiten katseluohjelmassa, joka avaa samat asiakirjat uudelleen koko päivän, mikä on täsmälleen tämän blogin muualla kuvatun artikkelin omavalintaisen PDF-katseluohjelman arkkitehtuurin Delphissä muoto. RenderCacheFolder, muistin rasterivälimuisti ja sivurenderöijä toimitetaan mukana paketissa HotPDF Delphi PDF component Delphille ja C++Builderille