42

My question is very simple: how can I center a text on a PDF, using PDFBox?

I don't know the string in advance, I can't find the middle by trial. The string doesn't always have the same width.

I need either:

  • A method that can center the text, something like addCenteredString(myString)
  • A method that can give me the width of the string in pixels. I can then calculate the center, for I know the dimensions of the PDF.

Any help is welcome!

YoungHobbit
  • 12,384
  • 9
  • 44
  • 70
SteeveDroz
  • 5,148
  • 5
  • 28
  • 57

2 Answers2

74

Ok, I found the answer myself. Here is how to center some text on a page:

String title = "This is my wonderful title!"; // Or whatever title you want.
int marginTop = 30; // Or whatever margin you want.

PDDocument document = new PDDocument();
PDPage page = new PDPage()
PDPageContentStream stream = new PDPageContentStream(document, page);
PDFont font = PDType1Font.HELVETICA_BOLD; // Or whatever font you want.

int fontSize = 16; // Or whatever font size you want.
float titleWidth = font.getStringWidth(title) / 1000 * fontSize;
float titleHeight = font.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * fontSize;

stream.beginText();
stream.setFont(font, fontSize);
// Deprecated, only before 2.0:
// stream.moveTextPositionByAmount((page.getMediaBox().getWidth() - titleWidth) / 2, page.getMediaBox().getHeight() - marginTop - titleHeight);
// From 2.0 and beyond:
stream.newLineAtOffset((page.getMediaBox().getWidth() - titleWidth) / 2, page.getMediaBox().getHeight() - marginTop - titleHeight);
stream.drawString(title);
stream.endText();
stream.close();
HydraHatRack
  • 978
  • 11
  • 16
SteeveDroz
  • 5,148
  • 5
  • 28
  • 57
  • 3
    Be aware that if the page is rotated (i.e., set to landscape mode) page.getMediaBox().getHeight() should be replaced by page.getMediaBox().getHeight(), as getMediaBox() does not take into account that the page is rotated. – hanspeide Jan 26 '12 at 08:47
  • 7
    Why isn't this information in the docs for pdfbox?! They have a font example, but fail to do anything with height. Plus why do we need to remember to divide by 1000 and multiply by fontSize? What's up with that? Just make us a helper method on PDFont! And why isn't that explained in the docs? That last comment sounded really helpful, but it says replace X with X. I wish hanspeide would follow up with a clarification. – chubbsondubs Apr 24 '12 at 18:44
  • @chubbsondubs from javadocs http://pdfbox.apache.org/apidocs/org/apache/pdfbox/pdmodel/font/PDFont.html#getStringWidth(java.lang.String) Returns: The width of the string in 1000 units of text space, ie 333 567... – peenut Dec 01 '12 at 19:19
  • Did you forget `stream.setFont(font, fontSize);` after `stream.beginText();`? [Hesham](http://stackoverflow.com/users/486508/hesham) suggested to fix but it [was rejected](http://stackoverflow.com/review/suggested-edits/1772055). –  Mar 23 '13 at 12:52
  • Hmm, thanks for noticing. Unfortunately, I can't even tell you if this is needed. I asked that question for a project 2 years ago and haven't done any PDFBox since. I assume that if it was rejected (not by me BTW), it had a good reason, like "not precising the font sets the default font", which is possible. Thanks anyway! – SteeveDroz Mar 24 '13 at 07:49
  • Thanks for your reply. I appreciate it. –  Mar 26 '13 at 02:17
  • I had to format it as such: `font.getStringWidth("ID #123456") / (1000*fontSize)` with the 1000*fontSize in parenthesis – Niro Oct 22 '13 at 16:53
  • @user1521536 updated the code to include the setFont. It is indeed needed to get this snippet to work properly. – rancidfishbreath Dec 26 '13 at 19:32
  • @Orin I followed your comment and it didn't work for me. This way it worked for me: float titleWidth = (font.getStringWidth(title) / 1000.0f)*fontSize; – Sebastián Vásquez Oct 23 '15 at 14:17
  • From 2.0 `moveTextPositionByAmount(float, float)` is [deprecated](https://pdfbox.apache.org/docs/2.0.0/javadocs/deprecated-list.html) in favor of `newLineAtOffset(float, float)` – Pablo Sep 30 '20 at 13:37
  • @Pablo I haven't touched PDFBox in 8 years. Please check my edit and tell me if it's correct. – SteeveDroz Oct 05 '20 at 06:07
  • 1
    @SteeveDroz yes, it is – Pablo Oct 05 '20 at 08:36
8

This adds centered text to pages in portrait as well as landscape format:

void addCenteredText(String text, PDFont font, int fontSize, PDPageContentStream content, PDPage page, Point2D.Float offset) throws IOException {
    content.setFont(font, fontSize);
    content.beginText();

    // Rotate the text according to the page orientation
    boolean pageIsLandscape = isLandscape(page);
    Point2D.Float pageCenter = getCenter(page);

    // We use the text's width to place it at the center of the page
    float stringWidth = getStringWidth(text, font, fontSize);
    if (pageIsLandscape) {
        float textX = pageCenter.x - stringWidth / 2F + offset.x;
        float textY = pageCenter.y - offset.y;
        // Swap X and Y due to the rotation
        content.setTextMatrix(Matrix.getRotateInstance(Math.PI / 2, textY, textX));
    } else {
        float textX = pageCenter.x - stringWidth / 2F + offset.x;
        float textY = pageCenter.y + offset.y;
        content.setTextMatrix(Matrix.getTranslateInstance(textX, textY));
    }

    content.showText(text);
    content.endText();
}

boolean isLandscape(PDPage page) {
    int rotation = page.getRotation();
    final boolean isLandscape;
    if (rotation == 90 || rotation == 270) {
        isLandscape = true;
    } else if (rotation == 0 || rotation == 360 || rotation == 180) {
        isLandscape = false;
    } else {
        LOG.warn("Can only handle pages that are rotated in 90 degree steps. This page is rotated {} degrees. Will treat the page as in portrait format", rotation);
        isLandscape = false;
    }
    return isLandscape;
}

Point2D.Float getCenter(PDPage page) {
    PDRectangle pageSize = page.getMediaBox();
    boolean rotated = isLandscape(page);
    float pageWidth = rotated ? pageSize.getHeight() : pageSize.getWidth();
    float pageHeight = rotated ? pageSize.getWidth() : pageSize.getHeight();

    return new Point2D.Float(pageWidth / 2F, pageHeight / 2F);
}

float getStringWidth(String text, PDFont font, int fontSize) throws IOException {
    return font.getStringWidth(text) * fontSize / 1000F;
}

This is how you create a PDF with centered text on a rotated page:

PDDocument pdf = new PDDocument();
// A5 page in landscape format
PDPage page = new PDPage(PDRectangle.A5);
page.setRotation(90);

pdf.addPage(page);
try (PDPageContentStream content = new PDPageContentStream(pdf, page)) {
    int fontSize = 36;

    // Put the text at the page's center, no offset
    Point2D.Float center = new Point2D.Float(0, 0);
    addCenteredText("PDFBox", PDType1Font.HELVETICA_BOLD, fontSize, content, page, center);

    // Put the text centered at the lower end of the page
    Point2D.Float lowerCenter = new Point2D.Float(0, -165);
    addCenteredText("Hi there!", PDType1Font.HELVETICA, fontSize, content, page, lowerCenter);

} catch (IOException e) {
    LOG.warn("Exception while creating content", e);
}

The resulting PDF:

resulting PDF

I used PDFBox 2.0.0-RC2 for creating this PDF.

Matthias Braun
  • 24,493
  • 16
  • 114
  • 144