PDF Library for Delphi identiteto teksto matricos operatorių 1 0 0 1 0 0 Tm savo išsaugojimo metu vykdomoje peephole turinio srauto optimizacijoje šalina tik tada, kai teksto matrica ir teksto eilutės matrica jau yra identitetas: iškart po BT arba iškart po ankstesnio identiteto Tm. Identiteto cm vis tiek šalinamas visuomet, nes cm daugina CTM, o Tm abi teksto matricas pakeičia iš karto. Nuo v3.539.28 kiekvienas kitas identiteto Tm lieka sraute
Klaida, kurią tai taiso, yra tylioji rūšis. Ataskaitų generatorius išduoda BT (Invoice) Tj 1 0 0 1 0 0 Tm (Total) Tj ET, pasikliaudamas, kad identiteto Tm antrą eilutę grąžins į teksto erdvės pradžią, ir tik tada pritaikys savo pozicionavimo logiką. Senasis optimizuotojas matė šešis skaičius, dėstančius identitetinę matricą, nusprendė, kad operatorius negali nieko pakeisti, ir jį ištrynė. Nieka nepavyko, niekas neįrašė įspėjimo, o išsaugotas puslapis nupiešė „Total“ iškart po „Invoice“ ta pačia pagrindo linija – būtent tos rūšies defektas, kurio niekas nepastebi, kol klientas neatspausdina PDF
Kodėl 1 0 0 1 0 0 Tm ne visada yra no-op?
Identiteto Tm yra no-op tik tada, kai jis pakeistų dvi matricas, kurios jau laiko identitetą, ir tai yra prieš jį esančių operatorių savybė, o ne jo paties operandų. ISO 32000-1 §9.4.1 sako, kad BT abi matricas – teksto (Tm) ir teksto eilutės (Tlm) – inicializuoja į identitetą, o §9.4.2 apibrėžia Tm kaip jų abiejų nustatymą į duotas reikšmes, o ne sudėjimą prie jų. Palyginkite su cm (§8.4.4), kuris dešinėja daugina dabartinę transformavimo matricą: dauginimas iš identiteto palieka bet kurią CTM nepakitusią, tad 1 0 0 1 0 0 cm saugu ištrinti bet kur. Teksto objekto viduje vaizdas kitoks. Td, TD, T* ir ne identiteto Tm visi pajudina Tlm, o kiekvienas tekstą rodantis operatorius (Tj, TJ, ', ") pastumia Tm per nupieštų glifų plotį. Po bet kurio iš jų identiteto Tm yra tikras grąžinimas į pradžią. Jei kada nors esate sekę teksto pozicijas rankomis su turinio srauto CTM ir teksto matricos būsenos sekiku, tai tas pats skirtumas tarp būsenos sudėjimo ir jos pakeitimo
Kaip atgalinė paieška nusprendžia, kurį identiteto Tm šalinti
TPDFContentPeepholeOptimizer.RemoveIdentityMatrices dabar eina atgal nuo kiekvieno identiteto Tm ir jį šalina tik jei paieška pirmiau pasiekia BT ar kitą identiteto Tm. Ankstesnis identiteto Tm galioja nesvarbu, ar jis paliktas, ar pats ką tik pažymėtas šalinimui, nes abiem atvejais jis abi matricas paliko identitete, lygiai kaip daro BT. Taisyklė kiekvieną sutiktą operatorių priskiria vienai iš dviejų grupių:
- Sustoti ir palikti Tm:
Td,TD,T*, ne identitetoTm,Tj,TJ,',",ET, bet koks analizatoriaus neatpažįstamas operatorius arba srauto pradžia - Peržengti ir skenuoti toliau: operatoriai, kurie niekada neliečia Tm ar Tlm, pvz.
Tf,Tc, spalvos nustatytojai,gs, marked-content operatoriai ircm
Konservatyvūs atvejai yra sąmoningi. Neatpažintas operatorius gali būti bet kas, tad paieška atsisako mąstyti už jo. ET užveria teksto objektą, tad Tm po jo neturi BT, kuris liudytų matricos reikšmes. Skenavimas taip pat dirba po vieną turinio srautą, kas svarbu puslapiams, kurių /Contents yra masyvas: sluoksnis, prasidedantis teksto objekto viduryje, be savo BT, laiko savo identiteto Tm net tada, kai ankstesnis sluoksnis jį būtų padaręs nereikalingu. Tai kainuoja kelis baitus keistuose failuose ir niekada nepajudina glifo. Jei redaguojate puslapio tekstą instrukcijų lygyje, kaip simbolių ir turinio baitų atitikimo apraše, tas pats išanalysuotas TPDFContentProgram modelis yra tai, ką optimizuotojas perrašo
uses
PDFlibContentModel, PDFlibContentOptimize;
function OptimizeSnippet(const Source: AnsiString): AnsiString;
var
Prog: TPDFContentProgram;
Optimizer: TPDFContentPeepholeOptimizer;
begin
Result := Source;
Prog := TPDFContentProgram.Create;
try
if not Prog.Parse(Source) then
Exit; // sugadintas srautas: paliekame baitus ramybėje
Optimizer := TPDFContentPeepholeOptimizer.Create(Prog);
try
Optimizer.Run; // grąžina pašalintų instrukcijų skaičių
finally
Optimizer.Free;
end;
Result := Prog.Emit; // po vieną instrukciją eilutei
finally
Prog.Free;
end;
end;
// Pašalinta: Tm iškart po BT, antrasis iš dviejų iš eilės einančių identiteto Tm
// OptimizeSnippet('BT /F1 12 Tf 1 0 0 1 0 0 Tm (hello) Tj ET')
// Palikta: Tm po Td, po Tj, po ne identiteto Tm arba už BT ribų
// OptimizeSnippet('BT (Invoice) Tj 1 0 0 1 0 0 Tm (Total) Tj ET')
Paleiskite pagalbinę funkciją ant įžangos sąskaitos srauto ir identiteto Tm išgyvena, nes atgalinė paieška į Tj užkliūva anksčiau, negu pasiekia BT. Įstatykite /F1 12 Tf, 2 Tc ir 0 g tarp BT ir identiteto Tm – jis vis tiek dingsta, nes niekas iš jų teksto matricų neliečia. Seka, tokia kaip BT 10 20 Td 1 0 0 1 0 0 Tm 1 0 0 1 0 0 Tm, praranda lygiai vieną operatorių: pirmasis identiteto Tm atstato matricą, kurią pajudino Td, o tik antrasis yra nereikalingas
Kada peephole optimizuotojas iš tikrųjų veikia?
Optimizuotojas veikia tik glaudinimo etapo metu, TPDFPageTree.Compress viduje, ir tik turinio srautams, kurie dar nėra Flate suspausti. TPDFlib.SetOptimizeContentStreams(1) yra numatytasis, o tas pats jungiklis atsiskleidžia kaip TPDFlibSaveOptions laukas OptimizeContentStreams; abu CompressContent ir CompressPage jo laikosi. Srautas, kurio /Filter jau /FlateDecode, praleidžiamas visas, tad egzistuojančio suspausto PDF pakrovimas ir pakartotinis išsaugojimas jo operatorių nerašo iš naujo. Jei srautas nepasiduoda analizei, originalūs iškoduoti baitai suspaudžiami nepakitę. TPDFlib.NormalizeContentStreams analizuoja ir iš naujo išduoda turinį su kanoniniais tarpais ir skaičiais, bet optimizuotojo niekada nekviečia, kas jį daro naudingu etalonu, kai norite matyti, kiek dydžio skirtumo duoda peephole taisyklės, greta didesnių laimėjimų, aprašytų PDF failo dydžio mažinime Delphi aplinkoje
var
Lib: TPDFlib;
Options: TPDFlibSaveOptions;
begin
Lib := TPDFlib.Create;
try
if Lib.LoadFromFile('report.pdf', '') <> 1 then
Exit;
// Nesuspausti srautai eina pro peephole taisykles, paskui Flate
Lib.SetOptimizeContentStreams(1);
Lib.CompressContent;
Lib.SaveToFile('report-optimized.pdf');
// Tas pats pasirinkimas per paketuotas išsaugojimo parinktis; False atsisako
Options.CompressContent := True;
Options.CompressFonts := True;
Options.CompressImages := True;
Options.Linearize := False;
Options.KeepModDate := False;
Options.OptimizeContentStreams := False;
Options.GarbageCollect := False;
Options.PackObjectStreams := True;
Lib.SaveToFileOptions('report-plain.pdf', Options);
finally
Lib.Free;
end;
end;
Ką senasis regresijos testas iš tikrųjų garantavo?
Senasis regresijos testas garantavo vieną formą: identiteto Tm iškart po BT yra pašalinamas. Peephole_RemovesIdentityTextMatrix paduoda BT 1 0 0 1 0 0 Tm (hello) Tj ET optimizuotojui ir teigia, kad nelieka jokio Tm. Ankstesnė laida jau buvo pastebėjusi, kad identiteto Tm šalinimas nesaugus, kai Tlm nėra identitetas, bet elgesį vis tiek paliko, nes testas jį „užrakino“. Atidžiai perskaitytas testas nieko nesako apie identiteto Tm po Td ar po parodyto teksto; vieno pavyzdžio aprėpties traktavimas kaip visos taisyklės sutartis buvo ta tikroji klaida. Pataisa palieka tą pirminį atvejį teigiamu ir prideda šešis atvejus, kurie užkalė ir šalinamas, ir paliekamas formas, įskaitant Tm už bet kokio teksto objekto ribų ir tokį, kuris seka po ET
Kompromisą lengva priimti, kai jis užrašytas. Generatoriai, kurie kiekvieną teksto objektą apgaubia kaip BT 1 0 0 1 0 0 Tm ..., vis tiek sulaukia to nereikalingo operatoriaus šalinimo – būtent iš ten atėjo beveik visos taupomos lėšos. Ko optimizuotojas atsisako, tai pavienio identiteto Tm teksto objekto viduryje – saujos baitų puslapiui, kol Flate jų dar net nesumatė – mainais už garantiją, kurią modulio antraštė sako aiškiai: kiekviena transformacija išvestimi ekvivalentiška ir niekada nekeičia matomo puslapio. Dydžio optimizuotojas, judinantis tekstą, nėra optimizuotojas – tai atvaizdavimo klaida su geromis suspaudimo statistikomis
Turinio srautų analizatorius, peephole optimizuotojas ir išsaugojimo metu veikiančios glaudinimo parinktys, aprašyti čia, keliauja kartu su PDF Library for Delphi ir C++Builder, kuri taip pat atskleidžia NormalizeContentStreams, CompressContent ir TPDFlibSaveOptions norintiems derinti, kaip rašomas kiekvienas dokumentas