Как да разделя PDF въз основа на ограничение на размера?

Претърсих много места, но не можах да намеря доста добро решение като такова. Така че това, което се опитвам да постигна, е следното: Моята програма ще има доста PDF документи, които ще трябва да изпратя по пощата. Има ограничение на пощенския сървър от 4 MB. Така че, ако всички PDF файлове са по-малко от 4 MB, те ще бъдат изпратени като едно писмо. В противен случай ще трябва да създам няколко файла, всеки по-малък от 4 MB. Сега моята програма работи добре за следните случаи: 1: Много файлове, но всеки по-малък от 4 MB и следователно поддържа раздел по време на сливането, така че нито един от обединените файлове да надвишава 4 MB. 2: Всички файлове са доста малки и следователно обединяването им не достига ограничението от 4 MB.

Но може да има сценарий, при който има един файл, който е, да речем, 14 MB. Мога да разделя този документ по страници. Но това също не е добро решение, тъй като размерът на страницата също не е равномерно разпределен между страниците. Използвал съм iText и PDFBox. Всяка помощ/насока ще бъде високо оценена!


person Tanmoy Roy    schedule 19.02.2015    source източник


Отговори (2)


Представете си документ от 3000 KB с десет страници и следните обекти:

  • четири поднабора шрифтове, използвани на всяка страница, всеки около 50 KB
  • десет изображения, които фигурират на една страница, всяко около 200 KB (едно изображение на страница)
  • четири изображения, които фигурират на всяка страница, всяко около 50 KB
  • десет страници с потоци от съдържание от около 25 KB всяка
  • около 350 KB за обекти като каталога, информационния речник, дървото на страниците, таблицата за кръстосани препратки и т.н.

Една страница ще се нуждае от поне: - четирите поднабора шрифтове: 4 пъти по 50 KB - единичното изображение: 1 път 200 KB - четирите изображения: 4 пъти 50 KB - единичен поток от съдържание: 1 път 50 KB - леко намален таблица с препратки, леко намалено дърво на страниците, почти идентичен каталог, информационен речник с идентичен размер,... 200 KB

Заедно това е 850 KB. Това означава, че в крайна сметка получавате 8500 KB (10 пъти по 850 KB), ако разделите PDF документ от 10 страници от 3000 KB на 10 отделни страници.

Този пример е резултат от предположения (въз основа на опит) и предполага, че PDF файлът е предвидим. Повечето PDF файлове не са:

  • някои страници ще изискват изображения с висока разделителна способност (може би дори мегабайта), други страници няма да имат изображения,
  • някои страници ще се нуждаят от много различни шрифтове и подмножества шрифтове (много килобайтове), други страници ще се състоят само от някои векторни рисунки (малък поток от съдържание, ако е компресиран).
  • различни страници могат да споделят голямо количество ресурси (XObjects на Form, XObjects на изображения,...), други страници няма да споделят никакви ресурси.
  • и така нататък...

Вие сами сте забелязали това, докато пишете: Мога да разделя този документ на страници. Но това също не е добро решение, тъй като размерът на страницата също не е равномерно разпределен между страниците.

Точно затова вашият въпрос не може да има друг отговор освен: ще трябва да направите проба и грешка. Никой софтуер не може да предвиди колко място е необходимо на дадена страница, преди да погледнете какво е необходимо за нея страница.

Актуализация:

Както Дейвид посочва в коментарите, възможно е да се изчислят всички ресурси, необходими за една страница, и да се провери дали текущите ресурси плюс необходимите ресурси надвишават максималния размер на файла.

Написах малък пример:

public void manipulatePdf(String src, String dest)
    throws IOException, DocumentException {
    Document document = new Document();
    PdfCopy copy = new PdfSmartCopy(document, new FileOutputStream(dest));
    document.open();
    PdfReader reader = new PdfReader(src);
    for (int i = 1; i <= reader.getNumberOfPages(); i++) {
        // check resources needed for reader.getPageN(i);
        copy.addPage(copy.getImportedPage(reader, i));
        System.out.println("After adding page: " + copy.getOs().getCounter());
    }
    document.close();
    System.out.println("After closing document: " + copy.getOs().getCounter());
    reader.close();
}

Изпълних примера на PDF пример с 18 страници и това беше резултатът:

After adding page: 56165
After adding page: 111398
After adding page: 162691
After adding page: 210035
After adding page: 253419
After adding page: 273429
After adding page: 330696
After adding page: 351564
After adding page: 400351
After adding page: 456545
After adding page: 495321
After adding page: 523640
After adding page: 576468
After adding page: 633525
After adding page: 751504
After adding page: 907490
After adding page: 957164
After adding page: 999140
After closing document: 1002509

Виждате как размерът на файла на копието постепенно нараства с всяка добавена страница. След като всички страници са добавени, размерът е 999140 байта и след това дървото на страницата и потокът от кръстосани препратки се записват, като се добавят още 3369 байта.

Там, където пише // check resources needed for reader.getPageN(i);, можете да направите предположение за размера, който ще бъде добавен за страницата, и да излезете от цикъла, ако надвиши максималната стойност.

Защо това би било предположение:

  1. Може да броите обекти, които вече са добавени. Ако следите обектите (не е толкова трудно), вашето предположение ще бъде по-точно.
  2. Използвам PdfSmartCopy. Да предположим, че във вашия PDF има два еднакви обекта. Лошият PDF софтуер често причинява подобни проблеми. Например: едни и същи байтове на изображението се добавят два пъти към файла. PdfSmartCopy може да открие това и ще използва повторно първия обект, който срещне, вместо да добавя излишните байтове на допълнителния обект.

В момента нямаме reader.getTotalPageBytes() в PdfReader, защото PdfReader се опитва да използва възможно най-малко памет. Той няма да зареди никакви обекти в паметта, докато тези обекти не са необходими. Следователно той не знае размера на всеки обект, преди страницата да бъде импортирана.

Въпреки това ще се погрижа такъв метод да бъде добавен в следващото издание.

Актуализация:

В следващата версия ще намерите инструмент с име SmartPdfSplitter, който зависи от нов клас с име PdfResourceCounter. Можете да го използвате по следния начин:

PdfReader reader = new PdfReader(src);
SmartPdfSplitter splitter = new SmartPdfSplitter(reader);
int part = 1;
while (splitter.hasMorePages()) {
    splitter.split(new FileOutputStream("results/merge/part_" + part + ".pdf"), 200000);
    part++;
}
reader.close();

Обърнете внимание, че това може да доведе до PDF от една страница, който надвишава ограничението (което е зададено на 200000 байта в примерния код), в случай че една страница не може да бъде намалена до по-малко байтове. В такъв случай splitter.isOverSized() ще върне true и ще трябва да намерите друг начин за намаляване на PDF файла.

person Bruno Lowagie    schedule 19.02.2015
comment
Господине, току-що потвърдихте съмнението ми! Благодаря много! Ще променя дизайна си. - person Tanmoy Roy; 19.02.2015
comment
Не разбирам защо бихте нарекли това невъзможно - прав сте, че е трудно, но софтуерът всъщност може да направи тези изчисления и да излезе с правилния отговор, нали? - person David van Driessche; 20.02.2015
comment
@DavidvanDriessche може би хипертрофичното его на Бруно смята, че във вселената на PDF софтуера няма нищо друго освен iText: всичко, което iText не може да направи, НЕ МОЖЕ да бъде направено от какъвто и да е друг софтуер, по дефиниция! ;-) (шегувам се!) - person Stefano Chizzolini; 20.02.2015
comment
@DavidvanDriessche дори не е трудно да се приложи такова изчисление -- когато софтуерът е добре написан, той ви позволява да правите мощни неща със (сравнително) малко усилия: алгоритъмът за прогнозиране на размера на данните на PDF Clown отнема само 20 реда чист код! Той извършва обхождане на модела, като взема предвид споделените ресурси на страницата: без никакво странно хакване, можете постепенно да симулирате размера на документ и, когато прецените, че е наред, можете да извършите действителния запис. - person Stefano Chizzolini; 20.02.2015
comment
@StefanoChizzolini Тъй като iText е добре написан, няма да е проблем да добавите тази функция в следващото издание на iText. Виждам, че предизвикваш и егото ми. Приемам предизвикателството. Ако сте експерт по PDF, ще присъствате на срещите на комисията по ISO в централата на Adobe в Сан Хосе (Калифорния) от 19 до 24 април. Нека се срещнем там и да поговорим. Ако вие не сте там, нито някой от вашата компания, ще предположа, че PDF не е толкова важен за вас, тъй като не допринасяте за предстоящия стандарт PDF 2.0 ;-) - person Bruno Lowagie; 20.02.2015
comment
@BrunoLowagie Оспорвах вашето категорично предположение за пробата и грешката; сега вашата актуализация го коригира, така че мога просто да се съглася с вашето решение (изчакване за внедряване на проактивна проверка на ресурсите). - person Stefano Chizzolini; 20.02.2015
comment
Заминавам за Калифорния този уикенд. Бих могъл да помоля персонала на iText да внедри това, но ще бъде забавно да го направя сам ;-) Като се има предвид това, ще се присъединиш ли към комитета на ISO, @StefanoChizzolini? - person Bruno Lowagie; 20.02.2015
comment
Ако вие не сте там, нито някой от вашата компания, ще предположа, че PDF не е толкова важен за вас, тъй като не допринасяте за предстоящия стандарт PDF 2.0 ;-) Това е много ниско, Бруно, и много гранично обида. Със сигурност няма място в Stackoverflow. - person David van Driessche; 20.02.2015
comment
@BrunoLowagie Очевидно не: аз съм сред хилядите софтуерни професионалисти, които очакват, надявам се, комисията да свърши правилната работа. ;-) - person Stefano Chizzolini; 20.02.2015
comment
@DavidvanDriessche Веднъж бях в позицията на Стефано: имах проста PDF библиотека и никакъв бизнес модел (2000 г.). Правех пари с ежедневната си работа, не с iText. Хиляди хора го използваха, но само шепа дадоха своя принос. AFAIK сега е така и за PdfClown. Тогава синът ми се разболя от рак (2008) и iText беше почти изоставен. Появиха се имитатори. Това боли. Но след това намерих бизнес модел (2009) и започнах да печеля пари с iText. Наех персонал (сега сме около 20 души) и iText стана най-широко използваната PDF библиотека. Когато Стефано се подигра с това, аз му върнах услугата ;-) - person Bruno Lowagie; 20.02.2015

PDF Clown поддържа прогнозиране на размера на данните на страницата без нужда от проба и грешка: от 2010 г. има включва специален метод (org.pdfclown.tools.PageManager.getSize(Page)), който изчислява в паметта действителния размер на данните на страницата, без да е необходимо да ги записвате във файл за проба.

Освен това има друг метод (org.pdfclown .tools.PageManager.split(long maxDataSize)), целенасочено внедрен за справяне с вашия вид сценарий, който използва гореспоменатия метод PageManager.getSize: той автоматично разделя файл въз основа на ограничение на размера, без да създава никакви междинен, грозен, глупав, временен файл за проба и грешка.

Можете да видите практически пример за използването му в org.pdfclown.samples.cli.PageManagementSample (случаи PageDataSizeCalculation и DocumentSplitOnMaximumFileSize), включени в дистрибуцията за изтегляне -- тук е пример за конзолен изход от случая PageDataSizeCalculation:

Page 1: 29380 (full); 29380 (differential); 29380 (incremental)
Page 2: 30493 (full); 1501 (differential); 30881 (incremental)
Page 3: 21888 (full); 1432 (differential); 32313 (incremental)
Page 4: 33781 (full); 4789 (differential); 37102 (incremental)
. . .

където:

  • пълен е размерът на данните за страницата, обхващащ всички нейни зависимости (като споделени ресурси) -- това е размерът на страницата, когато е извлечена като документ от една страница;
  • диференциал е допълнителният размер на данните на страницата -- това е допълнителното съдържание, което не е споделено с предишни страници;
  • инкрементален е размерът на данните на подсписъка на страниците, обхващащ всички предишни страници и текущата.
person Stefano Chizzolini    schedule 20.02.2015
comment
Като се има предвид, че страниците могат да споделят ресурси, има ли и метод за изчисляване на изискванията за памет на селекция от страници, като се броят такива споделени ресурси, но веднъж? - person mkl; 20.02.2015
comment
@mkl да, със сигурност: има претоварване на org.pdfclown.tools.PageManager.getSize (getSize(Page page, Set‹PdfReference› visitedReferences), вижте clown.sourceforge.net/docs/api/org /pdfclown/tools/), който следи споделените ресурси, избягвайки дублиране (използва се и от изпълнението на org.pdfclown.tools.PageManager.split(long)). - person Stefano Chizzolini; 20.02.2015
comment
Добре, тогава функцията звучи използваема и полезна. - person mkl; 20.02.2015
comment
Между другото, предполагам, че това означава, че потоците от обекти не се използват за оптимално компресиране от PDF Clown. Един скорошен въпрос в контекста на PDF Box показва, че използването им може да има голямо значение за получения размер. Не съм сигурен обаче дали някаква PDF библиотека с общо предназначение изобщо използва тази PDF функция. - person mkl; 21.02.2015
comment
PDF Clown поддържа R/W обектни потоци и може да ги запази при записване на файл (като случая, който споменахте). Въпреки това, като се има предвид случая по темата, предположението ви е правилно: PageManager.split() не използва потоци от обекти за постигане на по-нататъшно компресиране (BTW, пробвах го на примерния файл от 6MB на кутията на PDF Box и резултатът беше доста добър : 6 1MB файла -- може би значителна част от режийните разходи се дължат на данни, които не са пряко свързани със страниците (не съм проучвал допълнително)). Както и да е, отбелязахте добре: ще оценя удобството да изстисквам последните байтове през обектни потоци. - person Stefano Chizzolini; 21.02.2015
comment
Ще оценя удобството за изстискване на последните байтове през обектни потоци. - в случай на документи като в свързания проблем, това би означавало доста. Не знам обаче доколко те са представителни за случаите на употреба, върху които фокусирате PDF clown. - person mkl; 21.02.2015
comment
Намерението винаги е да можем да се справим с най-различните случаи, но, знаете ли, вариациите в сферата на PDF са почти огромни :-) - person Stefano Chizzolini; 21.02.2015