• Как конвертировать CSV-файл в таблицу с повторяющейся строкой заголовков?

    @iText_Q Автор вопроса, куратор тега iText
    Ваша проблема подробно описана в нашем обучающем видео, а именно в примере UnitedStates. В этом примере у нас есть CSV-файл с разными штатами США: united_states.csv

    name;abbr;capital;most populous city;population;square miles;time zone 1;time zone 2;dst
    ALABAMA;AL;Montgomery;Birmingham;4,708,708;52,423;CST (UTC-6);EST (UTC-5);YES
    ALASKA;AK;Juneau;Anchorage;698,473;656,425;AKST (UTC-09) ;HST (UTC-10) ;YES
    ARIZONA;AZ;Phoenix;Phoenix;6,595,778;114,006;MT (UTC-07); ;NO
    ARKANSAS;AR;Little Rock;Little Rock;2,889,450;53,182;CST (UTC-6); ;YES
    CALIFORNIA;CA;Sacramento;Los Angeles;36,961,664;163,707;PT (UTC-8); ;YES

    Мы преобразовываем файл в PDF с повторяющимся заголовком: united_states.pdf

    Код выглядит следующим образом:
    public void createPdf(String data, String dest) throws IOException {
        PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
        Document doc = new Document(pdfDoc, new PageSize(PageSize.A4).rotate());
     
        PdfFont font = PdfFontFactory.createFont(FontConstants.HELVETICA);
        PdfFont bold = PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD);
        Table table = new Table(new float[]{4, 1.5f, 3, 4, 3, 3, 3, 3, 1});
        table.setWidthPercent(100);
        BufferedReader br = new BufferedReader(new FileReader(data));
        String line = br.readLine();
        process(table, line, bold, true);
        while ((line = br.readLine()) != null) {
            process(table, line, font, false);
        }
        br.close();
        doc.add(table);
     
        doc.close();
    }
     
    public void process(Table table, String line, PdfFont font, boolean isHeader) {
        StringTokenizer tokenizer = new StringTokenizer(line, ";");
        while (tokenizer.hasMoreTokens()) {
            if (isHeader) {
                table.addHeaderCell(new Cell().add(new Paragraph(tokenizer.nextToken()).setFont(font)));
            } else {
                table.addCell(new Cell().add(new Paragraph(tokenizer.nextToken()).setFont(font)));
            }
        }
    }


    Обратите внимание на метод process(): у него есть параметр font, благодаря которому можно увеличивать шрифт, делать его жирным для заголовков, а также параметр isHeader, который определяет, когда нужно использовать метод addHeaderCell().
    Ответ написан
    Комментировать
  • Как разделить страницу на N частей и каждую из них заполнять из разных источников?

    @iText_Q Автор вопроса, куратор тега iText
    Если я правильно понимаю ваш вопрос, то вам нужно создать нечто подобное:5d142dfe2f60f512136833.pngПример

    На этом скриншоте показана первая часть первой книги «Записки о Гальской войне» Цезаря. Gallia omnia est divisa in partes tres, каждая страница разделена в следующем порядке: верхняя часть содержит текст на латинском, средняя часть — на английском, а нижняя часть — на французском. Если вы прочитаете текст, то обнаружите, что бельгийцы (а я бельгиец) — самый храбрый народ (хотя мы и не такие цивилизованные, как хотелось бы). Посмотрите документ three_parts.pdf, чтобы ознакомиться с примером PDF-файла.

    Этот PDF-файл создан с примером ThreeParts. В этом примере есть 9 текстовых файлов: liber1_1_la.txt, liber1_1_en.txt, liber1_1_fr.txt, liber1_2_la.txt, liber1_2_en.txt, liber1_2_fr.txt, liber1_3_la.txt, liber1_3_en.txt и liber1_3_fr.txt.

    «Liber» с латыни переводится как «книга», все файлы — это отрывки из первой книги, в частности разделы 1, 2 и 3 на латинском, английском и французском.

    В своем коде я создаю цикл по разным разделам и объект Paragraph для каждого языка:
    PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
    int firstPageNumber = 1;
    for (int section = 0; section < 3; section++) {
        // latin
        addSection(pdfDoc, createParagraph(String.format("./src/test/resources/txt/liber1_%s_la.txt", section + 1)), firstPageNumber, 2);
        // english
        addSection(pdfDoc, createParagraph(String.format("./src/test/resources/txt/liber1_%s_en.txt", section + 1)), firstPageNumber, 1);
        // french
        addSection(pdfDoc, createParagraph(String.format("./src/test/resources/txt/liber1_%s_fr.txt", section + 1)), firstPageNumber, 0);
       firstPageNumber = pdfDoc.getNumberOfPages() + 1;
    }


    Мы создаем параграфы Paragraph следующим образом:
    public Paragraph createParagraph(String path) throws IOException {
        Paragraph p = new Paragraph();
        BufferedReader in = new BufferedReader(
                new InputStreamReader(new FileInputStream(path), "UTF-8"));
        StringBuffer buffer = new StringBuffer();
        String line = in.readLine();
        while (null != line) {
            buffer.append(line);
            line = in.readLine();
        }
        in.close();
        p.add(buffer.toString());
        return p;
    }


    Затем используем созданные параграфы в методе addSection():

    public void addSection(PdfDocument pdfDoc, Paragraph paragraph, int pageNumber, int sectionNumber) throws IOException {
        LayoutResult layoutResult;
        ParagraphRenderer renderer = (ParagraphRenderer) paragraph.createRendererSubTree();
        renderer.setParent(new DocumentRenderer(new Document(pdfDoc)));
        while (((layoutResult = renderer.layout(new LayoutContext(new LayoutArea(pageNumber, new Rectangle(36, 36 + ((842 - 72) / 3) * sectionNumber, 523, (842 - 72) / 3)))))).getStatus() != LayoutResult.FULL) {
            if (pdfDoc.getNumberOfPages() < pageNumber) {
                pdfDoc.addNewPage();
            }
            layoutResult.getSplitRenderer().draw(new DrawContext(pdfDoc, new PdfCanvas(pdfDoc.getPage(pageNumber++)), false));
            renderer = (ParagraphRenderer) layoutResult.getOverflowRenderer();
        }
        if (pdfDoc.getNumberOfPages() < pageNumber) {
            pdfDoc.addNewPage();
        }
        renderer.draw(new DrawContext(pdfDoc, new PdfCanvas(pdfDoc.getPage(pageNumber)), false));
    }


    Здесь мы инициализировали объект ParagraphRenderer и изменили его LayoutArea согласно параметру sectionNumber. Мы проверяем статус LayoutResult и обрабатываем содержимое одной или нескольких страниц, пока не будут готовы все тексты для всех страниц.

    Из примера видно, что мы также создаем новую страницу для каждого нового раздела. Это делать необязательно, но латинский текст может быть переведен на английский и французский по-разному, а значит, могут возникнуть серьезные несоответствия, например, когда раздел X с латинским текстом начинается на одной странице, а тот же раздел на английском или французском языках начинается на следующей странице. Поэтому я создаю новую страницу, хотя это и необязательно в таком небольшом примере.
    Ответ написан
    Комментировать
  • Какие типы изображений поддерживает iText?

    @iText_Q Автор вопроса, куратор тега iText
    . iText поддерживает изображения форматов JPEG, JPEG2000, GIF, PNG, BMP, WMF, TIFF, CCITT и JBIG2. Это не значит, что эти типы изображений также поддерживаются в PDF. Если определенный тип изображения (например, PNG) не поддерживается в PDF, iText конвертирует его в тот тип изображения, который определен в спецификации PDF.

    •Изображения формата JPEG хранятся в iText в исходном виде. Можно взять потоковое содержимое Image XObject формата JPEG, скопировать его в файл, и у вас получится правильное изображение в формате JPEG. Следующий фильтр позволяет распознать эти изображения: /DCTDecode.

    •Формат JPEG2000 поддерживается с версии PDF 1.5. Название фильтра — JPXDecode.

    •PDF поддерживает изображения со сжатием LZW (оно используется для GIF-изображений), но iText декодирует GIF-изображения в изображения RAW. Если создать Image с помощью iText и указать путь к GIF-файлу, то в PDF-файле получится изображение с фильтром /FlateDecode.

    •Изображения в формате PNG не поддерживаются в PDF-файлах, поэтому iText декодирует PNG-изображения в RW. Если пространство цветов изображения DeviceGray и имеет 1 бит на компонент, то будут использованы метод сжатия CCITT и фильтр /CCITTFaxDecode. В обратном случае будет использован фильтр /FlateDecode.

    •BMP-файлы хранятся в группе сжатых пикселей с использованием фильтра /FlateDecode.

    •С форматом WMF ситуация особая. Если вставить WMF-файл в PDF-документ с помощью iText, то iText конвертирует изображение в синтаксис PDF. iText создаст объект Form XObject вместо добавления Image XObject.

    •Когда данные изображения кодируются с использованием факсимильного стандарта CCITT, используется фильтр /CCITTFaxDecode. Это обычно монохромные изображения с одним битом на пиксель.

    •Помимо этого, в iText могут обрабатываться TIFF-файлы. В зависимости от параметров TIFF-файлов iText решает использовать /CCITTFaxDecode, /FlateDecode или даже /DCTDecode в качестве фильтра.

    •Для JBIG2 используется фильтр /JBIG2Decode.

    Обычно с форматом изображений сложности не возникают. Класс Image сам выбирает правильный метод сжатия.

    Дополнительная информация. PDF-файлы поддерживают прозрачность, однако стоит помнить, что прозрачные изображения не поддерживаются в PDF. Одно изображение, содержащее прозрачные области, может быть конвертировано в два разных изображения.
    1. Непрозрачное изображение.
    2. Маска изображения.
    Все это происходит в самом PDF-файле. Если добавить прозрачное изображение в PDF-файл, то его нельзя будет оттуда экспортировать. Придется выгружать два изображения: непрозрачную версию и маску.
    Ответ написан
    Комментировать