Skip to content

Instantly share code, notes, and snippets.

@moshensky
Forked from ecmel/PDFlowStream.java
Created January 24, 2016 21:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save moshensky/5fb01a67974845bfee7c to your computer and use it in GitHub Desktop.
Save moshensky/5fb01a67974845bfee7c to your computer and use it in GitHub Desktop.
PDFBox 2 Text Helper
package portal.core;
import java.awt.Color;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Deque;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.graphics.image.PDInlineImage;
import org.apache.pdfbox.pdmodel.graphics.shading.PDShading;
import org.apache.pdfbox.pdmodel.interactive.viewerpreferences.PDViewerPreferences;
import org.apache.pdfbox.util.Matrix;
/**
*
* @author Ecmel Ercan
*/
public final class PDFlowStream implements Closeable {
private final static float magic = 0.551784f;
private final Deque<State> states = new ArrayDeque<>();
private final PDDocument document;
private PDPage page;
private PDPageContentStream stream;
private Repeater repeater;
private float xpos = 0.0f;
private float ypos = 0.0f;
private float top = cm(2.0f);
private float right = cm(2.0f);
private float bottom = cm(2.0f);
private float left = cm(2.0f);
private float fontSize = 10.0f;
private PDFont font = null;
private float indent = 0.0f;
private Color strokingColor = Color.BLACK;
private Color nonStrokingColor = Color.BLACK;
public PDFlowStream(PDDocument document)
{
PDViewerPreferences prefs = new PDViewerPreferences(new COSDictionary());
prefs.setPrintScaling(PDViewerPreferences.PRINT_SCALING.None);
document.getDocumentCatalog().setViewerPreferences(prefs);
this.document = document;
}
private void reset()
{
indent = 0.0f;
strokingColor = Color.BLACK;
nonStrokingColor = Color.BLACK;
}
public float cm(float cm)
{
return cm * 72 / 2.54f;
}
private float pt(float f)
{
return f / 1000 * fontSize;
}
public PDDocument document()
{
return document;
}
public PDPage page()
{
return page;
}
public float pageWidth()
{
return page.getMediaBox().getWidth();
}
public float pageHeight()
{
return page.getMediaBox().getHeight();
}
public float width()
{
return pageWidth() - left - right;
}
public float height()
{
return pageHeight() - top - bottom;
}
public float top()
{
return top;
}
public float right()
{
return right;
}
public float bottom()
{
return bottom;
}
public float left()
{
return left;
}
public float indent()
{
return indent;
}
public float ypos()
{
return ypos;
}
public float indentWidth()
{
return width() - indent;
}
public float charWidth(int c) throws IOException
{
return textWidth("W") * c;
}
public float textWidth(String text) throws IOException
{
return pt(font.getStringWidth(text));
}
public float fontCap()
{
return pt(font.getFontDescriptor().getCapHeight());
}
public float fontAscent()
{
return pt(font.getFontDescriptor().getAscent());
}
public float fontDescent()
{
return pt(font.getFontDescriptor().getDescent());
}
public float fontHeight()
{
return pt(font.getFontDescriptor().getFontBoundingBox().getHeight());
}
public PDFlowStream font(PDFont font) throws IOException
{
if (stream != null) {
stream.setFont(font, fontSize);
}
this.font = font;
return this;
}
public PDFlowStream fontSize(float fontSize) throws IOException
{
if (stream != null && font != null) {
stream.setFont(font, fontSize);
}
this.fontSize = fontSize;
return this;
}
public PDFlowStream top(float top)
{
this.top = top;
return this;
}
public PDFlowStream right(float right)
{
this.right = right;
return this;
}
public PDFlowStream bottom(float bottom)
{
this.bottom = bottom;
return this;
}
public PDFlowStream left(float left)
{
this.left = left;
return this;
}
public PDFlowStream repeater(Repeater repeater)
{
this.repeater = repeater;
return this;
}
public PDFlowStream newPage() throws IOException
{
return newPage(page == null ? PDRectangle.A4 : page.getMediaBox());
}
public PDFlowStream newPage(PDRectangle mediaBox) throws IOException
{
if (stream != null) {
close();
}
page = new PDPage(mediaBox);
document.addPage(page);
stream = new PDPageContentStream(document, page);
stream.setFont(font, fontSize);
stream.setStrokingColor(strokingColor);
stream.setNonStrokingColor(nonStrokingColor);
if (repeater != null) {
saveState();
reset();
repeater.page(this);
beginText();
stream.newLineAtOffset(left, pageHeight() - top);
repeater.header(this);
endText();
restoreState();
}
beginText();
newBreakAt(0);
return this;
}
public PDFlowStream newBreakAt(int line) throws IOException
{
ypos = pageHeight() - top - fontAscent() - fontHeight() * line;
if (ypos < bottom) {
newPage();
} else {
indent(indent);
}
return this;
}
public PDFlowStream newBreak() throws IOException
{
ypos -= fontHeight();
if (ypos < bottom) {
newPage();
} else {
newLine();
}
return this;
}
public PDFlowStream newBreak(int count) throws IOException
{
for (int i = 0; i < count; i++) {
newBreak();
}
return this;
}
public PDFlowStream newLine(int count) throws IOException
{
xpos = 0.0f;
stream.newLineAtOffset(0.0f, -fontHeight() * count);
return this;
}
public PDFlowStream newLine() throws IOException
{
return newLine(1);
}
public PDFlowStream indent(float indent) throws IOException
{
xpos = 0.0f;
transformText(Matrix.getTranslateInstance(left + indent, ypos));
this.indent = indent;
return this;
}
public PDFlowStream showWords(String text, float width) throws IOException
{
return showWords(text.split("(?<=\\s+)"), width);
}
public PDFlowStream showWords(String text) throws IOException
{
return showWords(text, width());
}
private PDFlowStream showWords(String[] words, float width) throws IOException
{
float w;
for (String word : words) {
w = textWidth(word);
if (w > width) {
showWords(word.split(""), width);
} else {
xpos += w;
if (xpos > width) {
newBreak();
xpos = w;
}
stream.showText(word);
}
}
return this;
}
public PDFlowStream showTextCenter(String text) throws IOException
{
return showText(((indent == 0 ? width() : 0) - textWidth(text)) / 2, text);
}
public PDFlowStream showTextRight(String text) throws IOException
{
return showText((indent == 0 ? width() : 0) - textWidth(text), text);
}
private PDFlowStream showText(float tx, String text) throws IOException
{
stream.newLineAtOffset(tx, 0);
stream.showText(text);
stream.newLineAtOffset(-tx, 0);
return this;
}
public PDFlowStream showTextCircle(float x, float y, float r, double theta, String text) throws IOException
{
String[] ch = text.split("");
Matrix m;
float w;
for (int i = 0; i < ch.length; i++) {
w = textWidth(ch[i]);
m = Matrix.getTranslateInstance(x, y);
m.rotate(theta);
m.translate(-w / 2, r);
transformText(m);
showText(ch[i]);
if (i < (ch.length - 1)) {
theta -= Math.atan((w + textWidth(ch[i + 1])) / 2 / r);
}
}
return this;
}
public PDFlowStream addEllipse(float x, float y, float xr, float yr) throws IOException
{
float xm = xr * magic;
float ym = yr * magic;
stream.moveTo(x - xr, y);
stream.curveTo(x - xr, y + ym, x - xm, y + yr, x, y + yr);
stream.curveTo(x + xm, y + yr, x + xr, y + ym, x + xr, y);
stream.curveTo(x + xr, y - ym, x + xm, y - yr, x, y - yr);
stream.curveTo(x - xm, y - yr, x - xr, y - ym, x - xr, y);
return this;
}
public PDFlowStream addCircle(float x, float y, float r) throws IOException
{
return addEllipse(x, y, r, r);
}
public PDFlowStream strokingColor(Color color) throws IOException
{
stream.setStrokingColor(color);
strokingColor = color;
return this;
}
public PDFlowStream nonStrokingColor(Color color) throws IOException
{
stream.setNonStrokingColor(color);
nonStrokingColor = color;
return this;
}
public PDFlowStream saveState() throws IOException
{
stream.saveGraphicsState();
states.push(new State(this));
return this;
}
public PDFlowStream restoreState() throws IOException
{
stream.restoreGraphicsState();
states.pop().restore(this);
return this;
}
public PDFlowStream beginBlock() throws IOException
{
endText();
saveState();
return this;
}
public PDFlowStream endBlock() throws IOException
{
restoreState();
beginText();
indent(indent);
return this;
}
public PDFlowStream beginText() throws IOException
{
stream.beginText();
return this;
}
public PDFlowStream endText() throws IOException
{
stream.endText();
return this;
}
public PDFlowStream showText(String text) throws IOException
{
stream.showText(text);
return this;
}
public PDFlowStream transformText(Matrix matrix) throws IOException
{
stream.setTextMatrix(matrix);
return this;
}
public PDFlowStream transform(Matrix matrix) throws IOException
{
stream.transform(matrix);
return this;
}
public PDFlowStream lineWidth(float lineWidth) throws IOException
{
stream.setLineWidth(lineWidth);
return this;
}
public PDFlowStream lineJoinStyle(JoinStyle style) throws IOException
{
stream.setLineJoinStyle(style.ordinal());
return this;
}
public PDFlowStream lineCapStyle(CapStyle style) throws IOException
{
stream.setLineCapStyle(style.ordinal());
return this;
}
public PDFlowStream lineDashPattern(float[] pattern, float phase) throws IOException
{
stream.setLineDashPattern(pattern, phase);
return this;
}
public PDFlowStream addRect(float x, float y, float width, float height) throws IOException
{
stream.addRect(x, y, width, height);
return this;
}
public PDFlowStream moveTo(float x, float y) throws IOException
{
stream.moveTo(x, y);
return this;
}
public PDFlowStream lineTo(float x, float y) throws IOException
{
stream.lineTo(x, y);
return this;
}
public PDFlowStream curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException
{
stream.curveTo(x1, y1, x2, y2, x3, y3);
return this;
}
public PDFlowStream curveTo2(float x2, float y2, float x3, float y3) throws IOException
{
stream.curveTo2(x2, y2, x3, y3);
return this;
}
public PDFlowStream curveTo1(float x1, float y1, float x3, float y3) throws IOException
{
stream.curveTo1(x1, y1, x3, y3);
return this;
}
public PDFlowStream closePath() throws IOException
{
stream.closePath();
return this;
}
public PDFlowStream clip() throws IOException
{
stream.clip();
return this;
}
public PDFlowStream clipEvenOdd() throws IOException
{
stream.clipEvenOdd();
return this;
}
public PDFlowStream stroke() throws IOException
{
stream.stroke();
return this;
}
public PDFlowStream fill() throws IOException
{
stream.fill();
return this;
}
public PDFlowStream shadingFill(PDShading shading) throws IOException
{
stream.shadingFill(shading);
return this;
}
public PDFlowStream drawImage(PDImageXObject image, float x, float y) throws IOException
{
stream.drawImage(image, x, y);
return this;
}
public PDFlowStream drawImage(PDImageXObject image, float x, float y, float width, float height) throws IOException
{
stream.drawImage(image, x, y, width, height);
return this;
}
public PDFlowStream drawImage(PDInlineImage image, float x, float y) throws IOException
{
stream.drawImage(image, x, y);
return this;
}
public PDFlowStream drawImage(PDInlineImage image, float x, float y, float width, float height) throws IOException
{
stream.drawImage(image, x, y, width, height);
return this;
}
@Override
public void close() throws IOException
{
endText();
if (repeater != null) {
saveState();
reset();
beginText();
stream.newLineAtOffset(left, bottom);
repeater.footer(this);
endText();
restoreState();
}
stream.close();
}
public static enum JoinStyle {
MITER,
ROUND,
BEVEL
}
public static enum CapStyle {
BUTT,
ROUND,
SQUARE
}
private static final class State {
private final float xpos;
private final float ypos;
private final float top;
private final float right;
private final float bottom;
private final float left;
private final float fontSize;
private final PDFont font;
private final float indent;
private final Color strokingColor;
private final Color nonStrokingColor;
State(PDFlowStream fs) throws IOException
{
xpos = fs.xpos;
ypos = fs.ypos;
top = fs.top;
right = fs.right;
bottom = fs.bottom;
left = fs.left;
fontSize = fs.fontSize;
font = fs.font;
indent = fs.indent;
strokingColor = fs.strokingColor;
nonStrokingColor = fs.nonStrokingColor;
}
void restore(PDFlowStream fs) throws IOException
{
fs.xpos = xpos;
fs.ypos = ypos;
fs.top = top;
fs.right = right;
fs.bottom = bottom;
fs.left = left;
fs.fontSize = fontSize;
fs.font = font;
fs.indent = indent;
fs.strokingColor = strokingColor;
fs.nonStrokingColor = nonStrokingColor;
}
}
public static abstract class Repeater {
public void page(PDFlowStream fs) throws IOException
{
}
public void header(PDFlowStream fs) throws IOException
{
}
public void footer(PDFlowStream fs) throws IOException
{
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment