Как я могу создавать абзацы фиксированной ширины с помощью PDFbox?

Я могу вставить простой текст следующим образом:

document = new PDDocument();
page = new PDPage(PDPage.PAGE_SIZE_A4);
document.addPage(page);
PDPageContentStream content = new PDPageContentStream(document, page);
content.beginText();
content.moveTextPositionByAmount (10 , 10);
content.drawString ("test text");
content.endText();
content.close();

но как я могу создать абзац, похожий на HTML, используя атрибут ширины?

<p style="width:200px;">test text</p>

person Qpi    schedule 17.05.2012    source источник


Ответы (2)


Предупреждение: этот ответ относится к старой версии PDFBox и основан на функциях, которые с тех пор устарели. См. комментарии ниже для более подробной информации.

В соответствии с этот ответ невозможно вставить разрывы строк в некоторый текст и правильно отобразить PDF (будь то с помощью PDFBox или чего-то еще else), поэтому я считаю, что автообтекание некоторого текста, чтобы он соответствовал некоторой ширине, также может быть чем-то, что он не может сделать автоматически. (кроме того, есть много способов обернуть текст - только целые слова, разбить их на более мелкие части и т. д.)

Этот ответ на другой вопрос (о центрировании строки) дает некоторые советы о том, как сделать это самостоятельно. Предполагая, что вы написали функцию possibleWrapPoints(String):int[] для перечисления всех точек в тексте, где может произойти перенос слов (исключая «ноль», включая «длину текста»), одним из возможных решений может быть:

PDFont font = PDType1Font.HELVETICA_BOLD; // Or whatever font you want.
int fontSize = 16; // Or whatever font size you want.
int paragraphWidth = 200;
String text = "test text";

int start = 0;
int end = 0;
int height = 10;
for ( int i : possibleWrapPoints(text) ) {
    float width = font.getStringWidth(text.substring(start,i)) / 1000 * fontSize;
    if ( start < end && width > paragraphWidth ) {
        // Draw partial text and increase height
        content.moveTextPositionByAmount(10 , height);
        content.drawString(text.substring(start,end));
        height += font.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * fontSize;
        start = end;
    }
    end = i;
}
// Last piece of text
content.moveTextPositionByAmount(10 , height);
content.drawString(text.substring(start));

Одним из примеров possibleWrapPoints, который позволяет выполнять перенос в любой точке, не являющейся частью слова (ссылка), может быть:

int[] possibleWrapPoints(String text) {
    String[] split = text.split("(?<=\\W)");
    int[] ret = new int[split.length];
    ret[0] = split[0].length();
    for ( int i = 1 ; i < split.length ; i++ )
        ret[i] = ret[i-1] + split[i].length();
    return ret;
}

Обновление: дополнительная информация:

  • Формат PDF-файла был разработан таким образом, чтобы он выглядел одинаково в разных ситуациях. Функциональность, подобная той, которую вы запрашивали, имеет смысл в редакторе/создателе PDF, но не в самом PDF-файле как таковом. По этой причине большинство «низкоуровневых» инструментов, как правило, концентрируются на работе с самим форматом файла и опускают подобные вещи.

  • Инструменты более высокого уровня OTOH обычно имеют средства для выполнения этого преобразования. Примером может служить Platypus (однако для Python), который < strong>do имеют простые способы создания абзацев и полагаются на функции нижнего уровня ReportLab для сделать фактический рендеринг PDF. Я не знаю подобных инструментов для PDFBox, но этот пост дает некоторые советы о том, как для преобразования содержимого HTML в PDF в среде Java с использованием свободно доступных инструментов. Сам не пробовал, но пишу сюда, так как это может быть полезно (на случай, если моей попытки, сделанной вручную выше, недостаточно).

person mgibsonbr    schedule 01.01.2013
comment
В PDF можно сделать разрыв строки. (см. мой ответ) - person Lukas; 05.03.2013
comment
Просто для справки: в этом ответе используется API, который был актуален в то время и теперь устарел. Теперь текст можно добавить с помощью showText и разрывы строк, добавленные непосредственно newLine . - person Simon Sobisch; 30.04.2019
comment
Это довольно старый пост, и многое изменилось с PDFBox. Также взгляните на PDFBox-Layout, который упрощает решение всех этих проблем - упаковка тексты, разные макеты и т. д. - person Jay; 17.08.2019

Я работал, комбинируя ответ Луки с mgibsonbr, и пришел к этому. Я думаю, что это более полезно для людей, которые ищут нестандартное решение.

private void write(Paragraph paragraph) throws IOException {
    out.beginText();
    out.appendRawCommands(paragraph.getFontHeight() + " TL\n");
    out.setFont(paragraph.getFont(), paragraph.getFontSize());
    out.moveTextPositionByAmount(paragraph.getX(), paragraph.getY());
    out.setStrokingColor(paragraph.getColor());

    List<String> lines = paragraph.getLines();
    for (Iterator<String> i = lines.iterator(); i.hasNext(); ) {
        out.drawString(i.next().trim());
        if (i.hasNext()) {
            out.appendRawCommands("T*\n");
        }
    }
    out.endText();

}

public class Paragraph {

    /** position X */
    private float x;

    /** position Y */
    private float y;

    /** width of this paragraph */
    private int width = 500;

    /** text to write */
    private String text;

    /** font to use */
    private PDType1Font font = PDType1Font.HELVETICA;

    /** font size to use */
    private int fontSize = 10;

    private int color = 0;

    public Paragraph(float x, float y, String text) {
        this.x = x;
        this.y = y;
        this.text = text;
    }

    /**
     * Break the text in lines
     * @return
     */
    public List<String> getLines() throws IOException {
        List<String> result = Lists.newArrayList();

        String[] split = text.split("(?<=\\W)");
        int[] possibleWrapPoints = new int[split.length];
        possibleWrapPoints[0] = split[0].length();
        for ( int i = 1 ; i < split.length ; i++ ) {
            possibleWrapPoints[i] = possibleWrapPoints[i-1] + split[i].length();
        }

        int start = 0;
        int end = 0;
        for ( int i : possibleWrapPoints ) {
            float width = font.getStringWidth(text.substring(start,i)) / 1000 * fontSize;
            if ( start < end && width > this.width ) {
                result.add(text.substring(start,end));
                start = end;
            }
            end = i;
        }
        // Last piece of text
        result.add(text.substring(start));
        return result;
    }

    public float getFontHeight() {
        return font.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * fontSize;
    }

    public Paragraph withWidth(int width) {
        this.width = width;
        return this;
    }

    public Paragraph withFont(PDType1Font font, int fontSize) {
        this.font = font;
        this.fontSize = fontSize;
        return this;
    }

    public Paragraph withColor(int color) {
        this.color = color;
        return this;
    }

    public int getColor() {
        return color;
    }

    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }

    public int getWidth() {
        return width;
    }

    public String getText() {
        return text;
    }

    public PDType1Font getFont() {
        return font;
    }

    public int getFontSize() {
        return fontSize;
    }

}
person Nacho Coloma    schedule 21.12.2013