Поставете 80 MB сканиран отчет зад връзка, отворете го в браузър и вижте какво се случва: програмата за преглед стои на празен панел, докато не пристигне голяма част от тези байтове, след което изрисува първа страница наведнъж. Скочете до страница 40 и при лошо изграден файл цялото изтегляне може да започне отначало. Разочароващата част е, че читателят е искал само първата страница. Линеаризацията е структурният отговор на този проблем. Тя пренарежда PDF така, че програмата за преглед да може да рендира началната страница от малък префикс на файла и да извлече останалото при поискване, поради което Adobe предлага функцията на пазара като "Fast Web View" (Бърз уеб изглед)
Нищо от това не е различен файлов формат. Линеаризиран PDF е обикновен PDF, който съвместима програма за преглед ще отвори без специална обработка. Трикът е изцяло в това как са подредени байтовете и в две допълнителни структури, които файлът носи. ISO 32000-1 определя цялата подредба в Приложение F (Annex F) и след като сте видели оформлението, поведението спира да изглежда като магия и започва да изглежда като преднамерена замяна на реда на файла за латентността на първото изрисуване
Какво всъщност пренарежда линеаризацията
Един нормален PDF може да разпръсне своите обекти в почти всякакъв ред. Таблицата за кръстосани препратки в края на файла е това, което прави това възможно: четецът търси до края, прочита указателя startxref, зарежда xref и оттам може да локализира всеки обект по неговото отместване. Този дизайн е отличен за локални файлове, където търсенето до края не струва нищо, и лош за файл, който се предава по мрежа, където краят е точно частта, която пристига последна. За да рендира страница първа, конвенционалният четец се нуждае от обекта на страницата, неговия поток от съдържание, шрифтовете, които реферира, и всички изображения, които чертае, а в неподреден файл тези неща могат да седят навсякъде, включително в последния мегабайт
Линеаризацията фиксира реда. Обектите, необходими за показване на първата страница, се събират в непрекъснат блок близо до началото, веднага след малка заглавна секция, така че да пристигнат рано в потока от байтове. Всичко останало, останалите страници и ресурсите, които те споделят, следва в предвидима последователност. Втора, пълна таблица за кръстосани препратки все още живее в края за четци, които игнорират оптимизацията, но линеаризиран файл също поставя кръстосана препратка за първа страница и параметрите, от които се нуждае стрийминг четецът, в началото. На четеца вече не му се налага да достига опашката, преди да може да нарисува каквото и да било
Наборът от обекти на първа страница и речникът с параметри за линеаризация
Съвсем първият обект в линеаризиран файл, след заглавката %PDF, е речникът с параметри за линеаризация. Това е нещото, което стрийминг четецът търси, за да реши дали оптимизацията е налице и как да я използва. Речникът записва дължината на целия файл, отместването в байтове, където започва основната секция за кръстосани препратки, номера на обекта на първата страница и местоположението и дължината на потока с подсказки, който следва. С тези числа четецът знае, само от началните килобайти, колко трябва да извлече, за да покаже първа страница, и къде да търси индекса, който му позволява да скочи другаде
Приложение F е стриктно относно това какво означава "първа страница" тук. Секцията за първа страница трябва да съдържа самия обект на страницата, неговите потоци от съдържание и ресурсите, към които тези потоци реферират, така че страницата да е самодостатъчна, след като този префикс бъде изтеглен. Споделени ресурси - шрифт, използван на всяка страница, лого, което се повтаря в колонтитул - се обработват специално: те се появяват достатъчно рано, за да обслужват първата страница, но са маркирани като споделени, така че четецът да не ги извлича отново, когато по-късно рендира страница 30. Това разграничение между частни за страницата и споделени обекти е частта, която повечето самоделни "оптимизатори" бъркат, и бъркането е това, което създава файл, който твърди, че е линеаризиран, но все пак блокира
Потоци с подсказки: индексът, който прави скоковете между страници евтини
Бързото показване на първа страница е само половината от стойността. Другата половина е прескачането до произволна страница без изтегляне на всичко между тях и това е, което осигуряват потоците с подсказки. Линеаризиран файл носи таблица с подсказки за отместване на страница и таблица с подсказки за споделени обекти, съхранени като поток, рефериран от речника с параметри. Таблицата за отместване на страници записва за всяка страница къде започват нейните обекти във файла и колко дълго продължават. Таблицата за споделени обекти прави същото за ресурси, използвани в множество страници
Като се имат предвид тези таблици, четец, който иска страница 40, не парсва файла последователно. Той се консултира с таблицата с подсказки, за да научи диапазона от байтове, който заема страница 40, моли сървъра за точно този диапазон и рендира страницата, след като тези байтове пристигнат, изтегляйки всички споделени ресурси, които вече не притежава, чрез същия механизъм. Потокът с подсказки е в действителност карта за произволен достъп, поставена върху документа, и това е причината добре линеаризиран файл от 500 страници да се усеща отзивчив през бавна връзка, докато неоптимизиран файл със същия размер не
Защо сървърът трябва да сътрудничи
Линеаризацията предполага, че транспортът може да доставя произволни отрязъци от файла, и това предположение си струва да се провери, преди да обвините формата за лоши резултати. Механизмът е HTTP byte-serving: четецът издава заявки за диапазони, а сървърът им отговаря с отговори 206 Partial Content. Ако сървърът не рекламира Accept-Ranges: bytes или ако прокси или CDN пред него свива заявките за диапазони в пълни трансфери, четецът няма начин да извлече страница 40 в изолация и се връща към изтеглянето на целия файл. Структурата вътре в PDF тогава е напълно правилна и напълно пропиляна
Това е провалът, който най-често се диагностицира погрешно като "линеаризацията не работи". Файлът е наред; пътят на доставка не е. Преди да изградите отново документ, потвърдете с условна заявка, че хостът действително връща частично съдържание за URL адреса, който четецът удря. Много статични хостове правят това по подразбиране, а много лошо конфигурирани сървъри за приложения и кеширащи слоеве не го правят
Инкременталните актуализации тихо чупят линеаризацията
Ето ограничението, което изненадва хората, които генерират правилно линеаризирани файлове и след това се чудят защо оптимизацията се изпарява. Линеаризацията зависи от едно-единствено, внимателно подредено оформление с неговия индекс отпред. Една инкрементална актуализация нарушава това по дизайн. Когато даден инструмент добави подпис, попълни поле във формуляр или добави анотация чрез инкрементално записване, той не презаписва файла. Той добавя променените обекти, нова секция за кръстосани препратки и нов трейлър в края, оставяйки оригиналните байтове недокоснати. Това добавяне е целият смисъл на инкременталните актуализации: то е бързо и запазва по-ранната ревизия за одит или валидиране на подпис
Страничният ефект е, че файлът сега има най-новите си данни за кръстосани препратки на опашката, след внимателно поставения блок за първа страница, а речникът с параметри за линеаризация отпред описва оформление, което вече не съвпада с файла. Съвместимата програма за преглед открива несъответствието и третира документа като нормален, нелинеаризиран PDF. Fast Web View е изчезнал, въпреки че оригиналната линеаризирана структура все още седи там в първата половина на файла. Ако добавите няколко актуализации, всяка от тях подрежда друга ревизия в края и разликата между остарелия преден индекс и реалното състояние се разширява
Ако вашият работен процес се нуждае както от редакции, така и от Fast Web View, правилото следва директно от структурата: редактирайте инкрементално, докато документът е в процес на промяна, след което линеаризирайте отново веднъж в края. Пълното пренаписване е това, което възстановява оформлението. В термините на HotPDF, това означава, че редактиране в ход преминава през BeginIncrementalUpdate и SaveIncrementalUpdate, които добавят делта, докато завършващата стъпка зарежда целия документ и го сериализира наново с LoadFromFile, последвано от SaveLoadedDocument, което изхвърля натрупаните стари ревизии и извежда едно чисто оформление. Същият компромис се появява с потоците от обекти: активирането на UseObjectStreams заедно с UseXRefStream компресира кръстосаната препратка и пакетира обектите плътно, което помага за размера на файла, но като всеки структурен избор, трябва да се приложи по време на това крайно пренаписване, а не да се прикрепя към добавена ревизия
// In-flight edits: append a delta, keep prior revisions intact.
// This leaves the file NOT linearized.
Pdf.BeginIncrementalUpdate('report.pdf');
Pdf.AddPage;
Pdf.CurrentPage.TextOut(72, 760, 0, 'Addendum');
Pdf.SaveIncrementalUpdate('report.pdf');
// Finishing step: full re-serialization produces one clean layout,
// dropping the stacked revisions. Re-run your linearizer on the output.
Pdf.LoadFromFile('report.pdf');
Pdf.SaveLoadedDocument('report-final.pdf');
HotPDF не излага рутина за "линеаризация" с едно извикване, така че практическият модел е да създадете чист, напълно пренаписан файл и да стартирате специализиран оптимизатор върху него. Инструментите за командния ред се справят с пренареждането директно. qpdf пренаписва файл в линеаризирана форма с един единствен флаг:
qpdf --linearize report-final.pdf report-web.pdf
Как да разберете дали даден файл е линеаризиран
Не се доверявайте на името на файла или на инструмента, който твърди, че го е създал; проверете байтовете. Най-директната проверка е главата на файла: отворете го и потърсете речника с параметри за линеаризация като първи обект след заглавката, носещ ключа /Linearized. Пряк път, ориентиран към четеца, е диалоговият прозорец Document Properties на Acrobat, който отчита "Fast Web View: Yes" само когато структурата е действително налице и актуална
За скриптови проверки qpdf отчита както наличието, така и целостта на структурата, което има значение, защото един файл може да носи речник за линеаризация, който вече не отразява неговото оформление, точно състоянието, което една инкрементална актуализация оставя след себе си:
# Reports "File is linearized" and validates hint tables against the layout
qpdf --check report-web.pdf
# Dumps the linearization parameters and hint data in detail
qpdf --show-linearization report-web.pdf
Стъпката на валидиране е тази, която си изработва парите. Едно преминаване, което само потвърждава съществуването на речника, щастливо ще благослови файл, чийто индекс сочи към грешни отмествания; проверка, която съгласува таблиците с подсказки с действителните позиции на обектите, е това, което ви казва, че оптимизацията ще издържи при заявките за диапазон на реален четец
Линеаризацията все още си струва да се прилага към всеки голям документ, обслужван в мрежата, особено към мобилни четци на неравномерни връзки, и струва няколко процента от размера на файла за предварително заредения индекс. Двете неща, които трябва да имате предвид, са, че структурата вътре в PDF и byte-serving-ът извън него трябва да са правилни, и че всяка редакция след факта отменя оптимизацията, докато не пренапишете файла. Третирайте повторната линеаризация като последната стъпка в конвейера, след като всяка друга промяна е уредена. Поведението на кръстосаните препратки, потока от обекти и инкременталната актуализация, описано тук, е част от структурния модел, който HotPDF Component за Delphi и C++Builder внедрява; за по-широк фон на оформлението на файла вижте как е структуриран PDF, а за работния процес с инкрементални актуализации и големи файлове в код вижте обработка на големи PDF файлове от Delphi