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

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

Но может быть сценарий, когда есть один файл размером, скажем, 14 МБ. Я могу разделить этот документ на страницы. Но это также не очень хорошее решение, так как размер страницы также неравномерно распределен по страницам. Я использовал iText и PDFBox. Любая помощь/указатель будет высоко оценена!


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


Ответы (2)


Представьте себе документ размером 3000 КБ с десятью страницами и следующими объектами:

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

Для одной страницы потребуется как минимум: - четыре набора шрифтов: 4 раза по 50 КБ - одно изображение: 1 раз по 200 КБ - четыре изображения: 4 раза по 50 КБ - один поток содержимого: 1 раз по 50 КБ - немного уменьшенный таблица перекрестных ссылок, немного уменьшенное дерево страниц, почти идентичный каталог, информационный словарь одинакового размера,... 200 КБ

Вместе это 850 КБ. Это означает, что вы получите 8500 КБ (10 раз по 850 КБ), если вы разделите 10-страничный PDF-документ размером 3000 КБ на 10 отдельных страниц.

Этот пример является результатом догадок (основанных на опыте) и предполагает, что PDF предсказуем. Большинство PDF-файлов не являются:

  • на некоторых страницах потребуются изображения высокой четкости (возможно, даже в мегабайтах), на других страницах изображений не будет,
  • некоторым страницам потребуется много разных шрифтов и подмножеств шрифтов (много килобайт), другие страницы будут состоять только из нескольких векторных рисунков (крошечный поток контента при сжатии).
  • разные страницы могут совместно использовать большое количество ресурсов (Form XObjects, Image 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. Тысячи людей использовали его, но лишь немногие вносили свой вклад. Насколько я знаю, это также относится и к 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)
. . .

куда:

  • full – это размер данных страницы, включающий все ее зависимости (например, общие ресурсы). Это размер страницы при извлечении в виде одностраничного документа;
  • дифференциал — это дополнительный размер данных страницы. Это дополнительный контент, который не используется совместно с предыдущими страницами;
  • 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› VisitReferences), см. 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() не использует потоки объектов для достижения дальнейшего сжатия (кстати, я попробовал это на 6-мегабайтном образце файла PDF Box, и результат был довольно хорошим : 6 файлов по 1 МБ - возможно, значительная часть накладных расходов была связана с данными, не связанными напрямую со страницами (дальше я не исследовал)). В любом случае, вы сделали правильное замечание: я оценю удобство сжатия последних байтов через потоки объектов. - person Stefano Chizzolini; 21.02.2015
comment
Я оценю удобство сжатия последних байтов через потоки объектов. - в случае документов, как в связанной проблеме, это будет означать довольно много. Однако я не знаю, насколько они репрезентативны для вариантов использования, на которых вы фокусируетесь на PDF-клоуне. - person mkl; 21.02.2015
comment
Намерение всегда состоит в том, чтобы иметь возможность справиться с самыми разрозненными случаями, но, как вы знаете, дисперсия области PDF почти подавляющая :-) - person Stefano Chizzolini; 21.02.2015