add rotation, work on positions, add transform command

This commit is contained in:
Jörg Prante 2022-01-18 18:36:58 +01:00
parent d7066d586e
commit 630d50bce7
38 changed files with 366 additions and 134 deletions

View file

@ -24,6 +24,6 @@ public class ControlElement implements Element {
@Override
public String toString() {
return "ControlElement [NEWPAGE=" + NEWPAGE + ", name=" + name + "]";
return "ControlElement [name=" + name + "]";
}
}

View file

@ -251,6 +251,16 @@ public class PageFormat implements Element {
return this;
}
public PageFormatBuilder mediaBox(float x, float y, float width, float height) {
PDRectangle rectangle = new PDRectangle();
rectangle.setLowerLeftX(x);
rectangle.setLowerLeftY(y);
rectangle.setUpperRightX(x + width);
rectangle.setUpperRightY(y + height);
mediaBox(rectangle);
return this;
}
/**
* Sets the media box to the given size.
*

View file

@ -64,8 +64,10 @@ public class PathElement implements Drawable, Element {
}
@Override
public void draw(PDDocument pdDocument, PDPageContentStream contentStream,
Position upperLeft, DrawListener drawListener) throws IOException {
public void draw(PDDocument pdDocument,
PDPageContentStream contentStream,
Position upperLeft,
DrawListener drawListener) throws IOException {
path.draw(pdDocument, contentStream, upperLeft, getWidth(), getHeight(), color, stroke, drawListener);
}

View file

@ -0,0 +1,74 @@
package org.xbib.graphics.pdfbox.layout.element;
import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.util.Matrix;
import org.xbib.graphics.pdfbox.layout.text.DrawListener;
import org.xbib.graphics.pdfbox.layout.text.Position;
public class TransformElement implements Element, Drawable {
private Float translateX;
private Float translateY;
private Float scaleX;
private Float scaleY;
private Float rotateX;
private Float rotateY;
private Float angle;
@Override
public float getWidth() throws IOException {
return 0;
}
@Override
public float getHeight() throws IOException {
return 0;
}
@Override
public Position getAbsolutePosition() throws IOException {
return null;
}
@Override
public Drawable removeLeadingEmptyVerticalSpace() throws IOException {
return this;
}
public void setTranslate(Float x, Float y) {
this.translateX = x;
this.translateY = y;
}
public void setScale(Float scaleX, Float scaleY) {
this.scaleX = scaleX;
this.scaleY = scaleY;
}
public void setRotate(Float angle, Float rotateX, Float rotateY) {
this.angle = angle;
this.rotateX = rotateX;
this.rotateY = rotateY;
}
@Override
public void draw(PDDocument pdDocument, PDPageContentStream contentStream, Position upperLeft, DrawListener drawListener) throws IOException {
if (translateX != null && translateY != null) {
contentStream.transform(Matrix.getTranslateInstance(translateX, translateY));
}
if (scaleX != null && scaleY != null) {
contentStream.transform(Matrix.getScaleInstance(scaleX, scaleY));
}
if (angle != null && rotateX != null && rotateY != null){
contentStream.transform(Matrix.getRotateInstance(Math.toRadians(angle), rotateX, rotateY));
}
}
}

View file

@ -9,4 +9,6 @@ import org.xbib.graphics.pdfbox.layout.element.Element;
* to layout the element.
*/
public interface LayoutHint {
LayoutHint NOP = new VerticalLayoutHint.VerticalLayoutHintBuilder().resetY(false).build();
}

View file

@ -153,8 +153,7 @@ public class RenderContext implements Renderer, Closeable, DrawContext, DrawList
* current y.
*/
public void resetPositionToLeft() {
currentPosition = new Position(getUpperLeft().getX(),
currentPosition.getY());
currentPosition = new Position(getUpperLeft().getX(), currentPosition.getY());
}
/**
@ -162,8 +161,7 @@ public class RenderContext implements Renderer, Closeable, DrawContext, DrawList
* y of {@link #getMaxPositionOnPage()}.
*/
protected void resetPositionToLeftEndOfPage() {
currentPosition = new Position(getUpperLeft().getX(),
getMaxPositionOnPage().getY());
currentPosition = new Position(getUpperLeft().getX(), getMaxPositionOnPage().getY());
}
/**
@ -339,7 +337,6 @@ public class RenderContext implements Renderer, Closeable, DrawContext, DrawList
this.page = new PDPage(getPageFormat().getMediaBox());
this.pdDocument.addPage(page);
this.contentStream = new PDPageContentStream(pdDocument, page, PDPageContentStream.AppendMode.APPEND, true);
// fix orientation
if (getPageOrientation() != getPageFormat().getOrientation()) {
if (isPageTilted()) {
page.setRotation(0);

View file

@ -78,10 +78,9 @@ public class VerticalLayout implements Layout {
public void render(RenderContext renderContext, Drawable drawable, LayoutHint layoutHint) throws IOException {
if (drawable.getAbsolutePosition() != null) {
renderAbsolute(renderContext, drawable, layoutHint,
drawable.getAbsolutePosition());
renderAbsolute(renderContext, drawable, drawable.getAbsolutePosition());
} else {
renderReleative(renderContext, drawable, layoutHint);
renderRelative(renderContext, drawable, layoutHint);
}
}
@ -90,12 +89,12 @@ public class VerticalLayout implements Layout {
*
* @param renderContext the context providing all rendering state.
* @param drawable the drawable to draw.
* @param layoutHint the layout hint used to layout.
* @param position the left upper position to start drawing at.
* @throws IOException by pdfbox
*/
protected void renderAbsolute(RenderContext renderContext,
Drawable drawable, LayoutHint layoutHint, Position position) throws IOException {
Drawable drawable,
Position position) throws IOException {
drawable.draw(renderContext.getPdDocument(), renderContext.getContentStream(), position, renderContext);
}
@ -104,29 +103,27 @@ public class VerticalLayout implements Layout {
* current position}. This method is responsible taking any top or bottom
* margin described by the (Vertical-)LayoutHint into account. The actual
* rendering of the drawable is performed by
* {@link #layoutAndDrawReleative(RenderContext, Drawable, LayoutHint)}.
* {@link #layoutAndDrawRelative(RenderContext, Drawable, LayoutHint)}.
*
* @param renderContext the context providing all rendering state.
* @param drawable the drawable to draw.
* @param layoutHint the layout hint used to layout.
* @throws IOException by pdfbox
*/
protected void renderReleative(RenderContext renderContext,
Drawable drawable, LayoutHint layoutHint) throws IOException {
protected void renderRelative(RenderContext renderContext,
Drawable drawable,
LayoutHint layoutHint) throws IOException {
VerticalLayoutHint verticalLayoutHint = null;
if (layoutHint instanceof VerticalLayoutHint) {
verticalLayoutHint = (VerticalLayoutHint) layoutHint;
if (verticalLayoutHint.getMarginTop() > 0) {
layoutAndDrawReleative(renderContext, new VerticalSpacer(
verticalLayoutHint.getMarginTop()), verticalLayoutHint);
layoutAndDrawRelative(renderContext, new VerticalSpacer(verticalLayoutHint.getMarginTop()), verticalLayoutHint);
}
}
layoutAndDrawReleative(renderContext, drawable, verticalLayoutHint);
layoutAndDrawRelative(renderContext, drawable, verticalLayoutHint);
if (verticalLayoutHint != null) {
if (verticalLayoutHint.getMarginBottom() > 0) {
layoutAndDrawReleative(renderContext, new VerticalSpacer(
verticalLayoutHint.getMarginBottom()),
verticalLayoutHint);
layoutAndDrawRelative(renderContext, new VerticalSpacer(verticalLayoutHint.getMarginBottom()), verticalLayoutHint);
}
}
}
@ -135,7 +132,7 @@ public class VerticalLayout implements Layout {
* Adjusts the width of the drawable (if it is {@link WidthRespecting}), and
* divides it onto multiple pages if necessary. Actual drawing is delegated
* to
* {@link #drawReletivePartAndMovePosition(RenderContext, Drawable, LayoutHint, boolean)}
* {@link #drawRelativePartAndMovePosition(RenderContext, Drawable, LayoutHint, boolean)}
* .
*
* @param renderContext the context providing all rendering state.
@ -143,11 +140,12 @@ public class VerticalLayout implements Layout {
* @param layoutHint the layout hint used to layout.
* @throws IOException by pdfbox
*/
protected void layoutAndDrawReleative(RenderContext renderContext,
Drawable drawable, LayoutHint layoutHint) throws IOException {
protected void layoutAndDrawRelative(RenderContext renderContext,
Drawable drawable,
LayoutHint layoutHint) throws IOException {
float targetWidth = getTargetWidth(renderContext);
boolean movePosition = true;
VerticalLayoutHint verticalLayoutHint = null;
VerticalLayoutHint verticalLayoutHint;
if (layoutHint instanceof VerticalLayoutHint) {
verticalLayoutHint = (VerticalLayoutHint) layoutHint;
targetWidth -= verticalLayoutHint.getMarginLeft();
@ -171,17 +169,13 @@ public class VerticalLayout implements Layout {
dividable = new Cutter(drawablePart);
}
Dividable.Divided divided = dividable.divide(renderContext.getRemainingHeight(), renderContext.getHeight());
drawReletivePartAndMovePosition(renderContext, divided.getFirst(),
layoutHint, true);
drawRelativePartAndMovePosition(renderContext, divided.getFirst(), layoutHint, true);
// new page
turnPage(renderContext);
drawablePart = divided.getTail();
drawablePart = removeLeadingEmptyVerticalSpace(drawablePart, renderContext);
}
drawReletivePartAndMovePosition(renderContext, drawablePart,
layoutHint, movePosition);
drawRelativePartAndMovePosition(renderContext, drawablePart, layoutHint, movePosition);
if (drawable instanceof WidthRespecting) {
if (oldMaxWidth < 0) {
((WidthRespecting) drawable).setMaxWidth(oldMaxWidth);
@ -199,14 +193,13 @@ public class VerticalLayout implements Layout {
* @param renderContext the context providing all rendering state.
* @param drawable the drawable to draw.
* @param layoutHint the layout hint used to layout.
* @param movePosition indicates if the position should be moved (vertically) after
* drawing.
* @param movePosition indicates if the position should be moved (vertically) after drawing.
* @throws IOException by pdfbox
*/
protected void drawReletivePartAndMovePosition(
final RenderContext renderContext, Drawable drawable,
final LayoutHint layoutHint, final boolean movePosition)
throws IOException {
protected void drawRelativePartAndMovePosition(RenderContext renderContext,
Drawable drawable,
LayoutHint layoutHint,
boolean movePosition) throws IOException {
PDPageContentStream contentStream = renderContext.getContentStream();
PageFormat pageFormat = renderContext.getPageFormat();
float offsetX = 0;

View file

@ -47,7 +47,7 @@ public class BarcodeCommand implements Command {
}
Alignment alignment = Alignment.valueOf(settings.get("alignment", "left").toUpperCase(Locale.ROOT));
String margin = settings.get("margin", "0 0 0 0");
String[] margins = margin.split(" ");
String[] margins = margin.split("\\s+");
float marginleft = Float.parseFloat(margins[0]);
float marginright = Float.parseFloat(margins[1]);
float margintop = Float.parseFloat(margins[2]);

View file

@ -32,7 +32,7 @@ public class CellCommand implements Command {
TextCell.Builder cell = TextCell.builder();
if (settings.containsSetting("padding")) {
String padding = settings.get("padding", "0 0 0 0");
String[] paddings = padding.split(" ");
String[] paddings = padding.split("\\s+");
float paddingLeft = Float.parseFloat(paddings[0]);
float paddingRight = Float.parseFloat(paddings[1]);
float paddingTop = Float.parseFloat(paddings[2]);

View file

@ -44,7 +44,7 @@ public class ChartCommand implements Command {
}
Alignment alignment = Alignment.valueOf(settings.get("alignment", "left").toUpperCase(Locale.ROOT));
String margin = settings.get("margin", "0 0 0 0");
String[] margins = margin.split(" ");
String[] margins = margin.split("\\s+");
float marginleft = Float.parseFloat(margins[0]);
float marginright = Float.parseFloat(margins[1]);
float margintop = Float.parseFloat(margins[2]);

View file

@ -10,36 +10,63 @@ import java.io.IOException;
import java.time.Instant;
import java.util.Locale;
import static org.xbib.graphics.pdfbox.layout.util.PdfUtil.mmToPt;
public class DocumentCommand implements Command {
@Override
public void execute(Engine engine, State state, Settings settings) throws IOException {
String margin = settings.get("margin", "0 0 0 0");
String[] margins = margin.split(" ");
PageFormat pageFormat = PageFormat.builder()
.marginLeft(Float.parseFloat(margins[0]))
.marginRight(Float.parseFloat(margins[1]))
.marginTop(Float.parseFloat(margins[2]))
.marginBottom(Float.parseFloat(margins[3]))
.pageFormat(settings.get("format", "A4"))
.orientation(settings.get("orientiation", "portrait").toUpperCase(Locale.ROOT))
.build();
Document document = new Document(pageFormat);
Instant instant = Instant.now();
document.setCreationDate(instant);
document.setModificationDate(instant);
PageFormat.PageFormatBuilder pageFormat = PageFormat.builder();
if (settings.containsSetting("pageformat")) {
pageFormat.pageFormat(settings.get("pageformat", "A4"));
}
if (settings.containsSetting("mediabox")) {
String mediabox = settings.get("mediabox");
String[] s = mediabox.split("\\s+");
// x y width height
pageFormat.mediaBox(mmToPt(Float.parseFloat(s[0])), mmToPt(Float.parseFloat(s[1])),
mmToPt(Float.parseFloat(s[2])), mmToPt(Float.parseFloat(s[3])));
}
pageFormat.orientation(settings.get("orientation", "portrait").toUpperCase(Locale.ROOT));
if (settings.containsSetting("margin")) {
String margin = settings.get("margin", "0 0 0 0");
String[] margins = margin.split("\\s+");
pageFormat
.marginLeft(Float.parseFloat(margins[0]))
.marginRight(Float.parseFloat(margins[1]))
.marginTop(Float.parseFloat(margins[2]))
.marginBottom(Float.parseFloat(margins[3]));
}
Document document = new Document(pageFormat.build());
if (settings.containsSetting("author")) {
document.setAuthor(settings.get("author"));
}
if (settings.containsSetting("creator")) {
document.setCreator(settings.get("creator"));
}
if (settings.containsSetting("producer")) {
document.setProducer(settings.get("producer"));
}
if (settings.containsSetting("subject")) {
document.setSubject(settings.get("subject"));
}
if (settings.containsSetting("title")) {
document.setTitle(settings.get("title"));
}
if (settings.containsSetting("keywords")) {
document.setKeywords(settings.get("keywords"));
}
Instant instant = Instant.now();
if (settings.containsSetting("creationdate")) {
document.setCreationDate(Instant.parse(settings.get("creationdate")));
} else {
document.setCreationDate(instant);
}
if (settings.containsSetting("modificationddate")) {
document.setModificationDate(Instant.parse(settings.get("modificationddate")));
} else {
document.setModificationDate(instant);
}
state.elements.push(document);
engine.executeElements(settings);
}

View file

@ -18,7 +18,7 @@ public class HorizontalrulerCommand implements Command {
@Override
public void execute(Engine engine, State state, Settings settings) throws IOException {
Stroke.StrokeBuilder strokeBuilder = Stroke.builder()
.capStyle(Stroke.CapStyle.valueOf(settings.get("capstyie", "cap").toUpperCase(Locale.ROOT)))
.capStyle(Stroke.CapStyle.valueOf(settings.get("capstyle", "cap").toUpperCase(Locale.ROOT)))
.joinStyle(Stroke.JoinStyle.valueOf(settings.get("joinstyle", "miter").toUpperCase(Locale.ROOT)))
.lineWidth(settings.getAsFloat("linewidth", 1f));
if (settings.containsSetting("dash")) {

View file

@ -42,7 +42,7 @@ public class ImageCommand implements Command {
}
Alignment alignment = Alignment.valueOf(settings.get("alignment", "left").toUpperCase(Locale.ROOT));
String margin = settings.get("margin", "0 0 0 0");
String[] margins = margin.split(" ");
String[] margins = margin.split("\\s+");
float marginleft = Float.parseFloat(margins[0]);
float marginright = Float.parseFloat(margins[1]);
float margintop = Float.parseFloat(margins[2]);

View file

@ -28,14 +28,17 @@ public class ParagraphCommand implements Command {
paragraph.setAlignment(Alignment.valueOf(settings.get("alignment", "left").toUpperCase(Locale.ROOT)));
}
if (settings.containsSetting("linespacing")) {
paragraph.setLineSpacing(settings.getAsFloat("linespacing", -1f));
paragraph.setLineSpacing(settings.getAsFloat("linespacing", 1.2f));
}
if (settings.containsSetting("rotation")) {
paragraph.setRotation(settings.getAsFloat("rotation", 0f));
}
state.elements.push(paragraph);
engine.executeElements(settings);
state.elements.pop();
Alignment alignment = Alignment.valueOf(settings.get("layout.alignment", "left").toUpperCase(Locale.ROOT));
String margin = settings.get("layout.margin", "0 0 0 0");
String[] margins = margin.split(" ");
String[] margins = margin.split("\\s+");
float marginleft = Float.parseFloat(margins[0]);
float marginright = Float.parseFloat(margins[1]);
float margintop = Float.parseFloat(margins[2]);

View file

@ -2,8 +2,11 @@ package org.xbib.graphics.pdfbox.layout.element.scripting.command;
import org.xbib.graphics.pdfbox.layout.color.ColorFactory;
import org.xbib.graphics.pdfbox.layout.element.PathElement;
import org.xbib.graphics.pdfbox.layout.element.render.LayoutHint;
import org.xbib.graphics.pdfbox.layout.element.scripting.Engine;
import org.xbib.graphics.pdfbox.layout.element.scripting.State;
import org.xbib.graphics.pdfbox.layout.shape.LinePosiiton;
import org.xbib.graphics.pdfbox.layout.shape.MovePosition;
import org.xbib.graphics.pdfbox.layout.shape.Path;
import org.xbib.graphics.pdfbox.layout.shape.Stroke;
import org.xbib.graphics.pdfbox.layout.text.Position;
@ -13,6 +16,7 @@ import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static org.xbib.graphics.pdfbox.layout.util.PdfUtil.mmToPt;
public class PathCommand implements Command {
@ -23,30 +27,40 @@ public class PathCommand implements Command {
return;
}
List<Position> list = new ArrayList<>();
String[] s = value.split(" ");
Position position = null;
String[] s = value.split("\\s+");
Position position = null; //new Position(0, 0);
if (s.length > 0) {
if (settings.getAsBoolean("absolute", false)) {
position = new Position(Float.parseFloat(s[0]), Float.parseFloat(s[1]));
list.add(position);
} else {
Position p = new Position(Float.parseFloat(s[0]), Float.parseFloat(s[1]));
list.add(p);
}
for (int i = 2; i < s.length; i += 2) {
Position p = new Position(Float.parseFloat(s[i]), Float.parseFloat(s[i + 1]));
for (int i = 0; i < s.length; i += 3) {
String command = s[i];
float x = mmToPt(Float.parseFloat(s[i + 1]));
float y = mmToPt(Float.parseFloat(s[i + 2]));
Position p;
switch (command.toUpperCase(Locale.ROOT)) {
case "M":
p = new MovePosition(x, y);
break;
case "L":
p = new LinePosiiton(x, y);
break;
default:
p = new Position(x, y);
break;
}
list.add(p);
if (i == 0 && settings.getAsBoolean("position", false)) {
position = p;
}
}
}
Path path = new Path(list);
Stroke.StrokeBuilder strokeBuilder = Stroke.builder()
.capStyle(Stroke.CapStyle.valueOf(settings.get("capstyie", "cap").toUpperCase(Locale.ROOT)))
.capStyle(Stroke.CapStyle.valueOf(settings.get("capstyle", "cap").toUpperCase(Locale.ROOT)))
.joinStyle(Stroke.JoinStyle.valueOf(settings.get("joinstyle", "miter").toUpperCase(Locale.ROOT)))
.lineWidth(settings.getAsFloat("linewidth", 1f));
if (settings.containsSetting("dash")) {
strokeBuilder.dashPattern(new Stroke.DashPattern(settings.getAsFloat("dash", 1f)));
}
Color color = ColorFactory.web(settings.get("color", "black"));
state.elements.peek().add(new PathElement(path, strokeBuilder.build(), color, position));
state.elements.peek().add(new PathElement(path, strokeBuilder.build(), color, position), LayoutHint.NOP);
}
}

View file

@ -23,7 +23,7 @@ public class RowCommand implements Command {
Row.Builder row = Row.builder();
if (settings.containsSetting("padding")) {
String padding = settings.get("padding", "0 0 0 0");
String[] paddings = padding.split(" ");
String[] paddings = padding.split("\\s+");
float paddingLeft = Float.parseFloat(paddings[0]);
float paddingRight = Float.parseFloat(paddings[1]);
float paddingTop = Float.parseFloat(paddings[2]);

View file

@ -17,7 +17,7 @@ public class TableCommand implements Command {
TableElement tableElement = new TableElement();
if (settings.containsSetting("columnwidths")) {
String columnwidths = settings.get("columnwidths");
String[] widths = columnwidths.split(" ");
String[] widths = columnwidths.split("\\s+");
for (String width : widths) {
tableElement.addColumnOfWidth(mmToPt(Float.parseFloat(width)));
}
@ -27,7 +27,7 @@ public class TableCommand implements Command {
}
if (settings.containsSetting("padding")) {
String padding = settings.get("padding", "0 0 0 0");
String[] paddings = padding.split(" ");
String[] paddings = padding.split("\\s+");
float paddingLeft = Float.parseFloat(paddings[0]);
float paddingRight = Float.parseFloat(paddings[1]);
float paddingTop = Float.parseFloat(paddings[2]);

View file

@ -22,27 +22,34 @@ public class TextCommand implements Command {
@Override
public void execute(Engine engine, State state, Settings settings) {
String value = settings.get("value");
float size = settings.getAsFloat("fontsize", 11.0f);
float fontsize = settings.getAsFloat("fontsize", 11.0f);
Document document = state.getDocument();
Font font = Fonts.valueOf(settings.get("font", "helvetica").toUpperCase(Locale.ROOT)).getFont(document);
Element element = state.elements.peek();
if (element instanceof Paragraph) {
element.add(new TextElement(value, font, size));
element.add(new TextElement(value, font, fontsize));
} else if (element instanceof Document) {
// wrap text into a paragraph
Paragraph paragraph = new Paragraph();
if (settings.containsSetting("x") && settings.containsSetting("y")) {
paragraph.setAbsolutePosition(new Position(mmToPt(settings.getAsFloat("x", 0f)), mmToPt(settings.getAsFloat("y", 0f))));
}
if (settings.containsSetting("width")) {
paragraph.setMaxWidth(settings.getAsFloat("width", 0f));
paragraph.setMaxWidth(mmToPt(settings.getAsFloat("width", 0f)));
}
if (settings.containsSetting("alignment")) {
paragraph.setAlignment(Alignment.valueOf(settings.get("alignment", "left").toUpperCase(Locale.ROOT)));
}
paragraph.add(new TextElement(value, font, size));
if (settings.containsSetting("linespacing")) {
paragraph.setLineSpacing(settings.getAsFloat("linespacing", 1.2f));
}
if (settings.containsSetting("rotation")) {
paragraph.setRotation(settings.getAsFloat("rotation", 0f));
}
paragraph.add(new TextElement(value, font, fontsize));
Alignment alignment = Alignment.valueOf(settings.get("layout.alignment", "left").toUpperCase(Locale.ROOT));
String margin = settings.get("layout.margin", "0 0 0 0");
String[] margins = margin.split(" ");
String[] margins = margin.split("\\s+");
float marginleft = Float.parseFloat(margins[0]);
float marginright = Float.parseFloat(margins[1]);
float margintop = Float.parseFloat(margins[2]);
@ -50,6 +57,8 @@ public class TextCommand implements Command {
boolean resetY = settings.getAsBoolean("layout.resety", false);
VerticalLayoutHint verticalLayoutHint = new VerticalLayoutHint(alignment, marginleft, marginright, margintop, marginbottom, resetY);
element.add(paragraph, verticalLayoutHint);
} else {
throw new UnsupportedOperationException();
}
}
}

View file

@ -0,0 +1,28 @@
package org.xbib.graphics.pdfbox.layout.element.scripting.command;
import java.io.IOException;
import org.xbib.graphics.pdfbox.layout.element.TransformElement;
import org.xbib.graphics.pdfbox.layout.element.render.LayoutHint;
import org.xbib.graphics.pdfbox.layout.element.scripting.Engine;
import org.xbib.graphics.pdfbox.layout.element.scripting.State;
import org.xbib.settings.Settings;
import static org.xbib.graphics.pdfbox.layout.util.PdfUtil.mmToPt;
public class TransformCommand implements Command {
@Override
public void execute(Engine engine, State state, Settings settings) throws IOException {
TransformElement element = new TransformElement();
if (settings.containsSetting("scalex") && settings.containsSetting("scaley")) {
element.setScale(settings.getAsFloat("scalex", null), settings.getAsFloat("scaley", null));
}
if (settings.containsSetting("translatex") && settings.containsSetting("translatey")) {
element.setTranslate(mmToPt(settings.getAsFloat("translatex", null)), mmToPt(settings.getAsFloat("translatey", null)));
}
if (settings.containsSetting("angle") && settings.containsSetting("rotatex") && settings.containsSetting("rotatey")) {
element.setRotate(settings.getAsFloat("angle", null), mmToPt(settings.getAsFloat("rotatex", null)), mmToPt(settings.getAsFloat("rotatey", null)));
}
state.elements.peek().add(element, LayoutHint.NOP);
}
}

View file

@ -0,0 +1,15 @@
package org.xbib.graphics.pdfbox.layout.shape;
import org.xbib.graphics.pdfbox.layout.text.Position;
public class LinePosiiton extends Position {
/**
* Creates a position at the given coordinates for line position.
*
* @param x the x coordinate.
* @param y the y coordinate.
*/
public LinePosiiton(float x, float y) {
super(x, y);
}
}

View file

@ -0,0 +1,15 @@
package org.xbib.graphics.pdfbox.layout.shape;
import org.xbib.graphics.pdfbox.layout.text.Position;
public class MovePosition extends Position {
/**
* Creates a position at the given coordinates for move operation.
*
* @param x the x coordinate.
* @param y the y coordinate.
*/
public MovePosition(float x, float y) {
super(x, y);
}
}

View file

@ -26,22 +26,18 @@ public class Path implements Shape {
Color color,
Stroke stroke,
DrawListener drawListener) throws IOException {
contentStream.saveGraphicsState();
float x = upperLeft.getX();
float y = upperLeft.getY() - stroke.getLineWidth() / 2;
contentStream.setStrokingColor(color);
stroke.applyTo(contentStream);
boolean move = true;
for (Position p : list) {
if (move) {
if (p instanceof MovePosition) {
contentStream.moveTo(x + p.getX(), y + p.getY());
move = false;
} else {
} else if (p instanceof LinePosiiton) {
contentStream.lineTo(x + p.getX(), y + p.getY());
}
}
contentStream.stroke();
contentStream.restoreGraphicsState();
if (drawListener != null) {
drawListener.drawn(this, upperLeft, width, height);
}

View file

@ -58,7 +58,7 @@ public class StyledText implements ParagraphProcessor {
String[] lines = getText().split(PdfUtil.NEW_LINE_REGEX);
for (int i = 0; i < lines.length; i++) {
FontDescriptor fontDescriptor = new FontDescriptor(actualFont, actualFontSize);
paragraph.add(new org.xbib.graphics.pdfbox.layout.text.StyledText(lines[i], fontDescriptor, actualColor, 0f, 0, 0));
paragraph.add(new org.xbib.graphics.pdfbox.layout.text.StyledText(lines[i], fontDescriptor, actualColor, 0f, 0, 0, 0));
if (i < lines.length - 1) {
paragraph.add(new org.xbib.graphics.pdfbox.layout.text.NewLine(new FontDescriptor(actualFont, actualFontSize)));
}

View file

@ -104,7 +104,7 @@ public class Indent extends ControlFragment {
break;
}
}
styledText = new StyledText(label, fontDescriptor, getColor(), 0, marginLeft, marginRight);
styledText = new StyledText(label, fontDescriptor, getColor(), 0, marginLeft, marginRight, 0);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
@ -117,7 +117,7 @@ public class Indent extends ControlFragment {
*/
public Indent(final float indentPt) {
super("", DEFAULT_FONT_DESCRIPTOR);
styledText = new StyledText("", getFontDescriptor(), getColor(), 0, indentPt, 0);
styledText = new StyledText("", getFontDescriptor(), getColor(), 0, indentPt, 0, 0);
}
private float calculateIndent(float indentWidth, SpaceUnit indentUnit, final FontDescriptor fontDescriptor)

View file

@ -7,6 +7,7 @@ package org.xbib.graphics.pdfbox.layout.text;
public class Position {
private final float x;
private final float y;
/**

View file

@ -22,6 +22,8 @@ public class StyledText implements TextFragment {
private final float baselineOffset;
private final float rotation;
/**
* The cached (calculated) width of the text.
*/
@ -45,7 +47,7 @@ public class StyledText implements TextFragment {
* @param color the color to use.
*/
public StyledText(String text, FontDescriptor fontDescriptor, Color color) {
this(text, fontDescriptor, color, 0, 0, 0);
this(text, fontDescriptor, color, 0, 0, 0, 0);
}
/**
@ -57,11 +59,12 @@ public class StyledText implements TextFragment {
* @param baselineOffset the offset of the baseline.
* @param leftMargin the margin left to the text.
* @param rightMargin the margin right to the text.
* @param rotation the rotation of the text, 0 is unrotated
*/
public StyledText(String text,
FontDescriptor fontDescriptor,
Color color,
float baselineOffset, float leftMargin, float rightMargin) {
float baselineOffset, float leftMargin, float rightMargin, float rotation) {
if (text.contains("\n")) {
throw new IllegalArgumentException("StyledText must not contain line breaks, use TextFragment.LINEBREAK for that");
}
@ -77,6 +80,7 @@ public class StyledText implements TextFragment {
this.leftMargin = leftMargin;
this.rightMargin = rightMargin;
this.baselineOffset = baselineOffset;
this.rotation = rotation;
}
/**
@ -124,6 +128,10 @@ public class StyledText implements TextFragment {
return baselineOffset;
}
public float getRotation() {
return rotation;
}
@Override
public Color getColor() {
return color;
@ -164,8 +172,7 @@ public class StyledText implements TextFragment {
}
public StyledText inheritAttributes(String text, float leftMargin, float rightMargin) {
return new StyledText(text, getFontDescriptor(), getColor(),
getBaselineOffset(), leftMargin, rightMargin);
return new StyledText(text, getFontDescriptor(), getColor(), getBaselineOffset(), leftMargin, rightMargin, getRotation());
}
private static float getWidth(FontDescriptor fontDescriptor, String text) {
@ -181,6 +188,6 @@ public class StyledText implements TextFragment {
return "StyledText [text=" + text + ", fontDescriptor="
+ fontDescriptor + ", width=" + width + ", color=" + color
+ ", leftMargin=" + leftMargin + ", rightMargin=" + rightMargin
+ ", baselineOffset=" + baselineOffset + "]";
+ ", baselineOffset=" + baselineOffset + ",rotation=" + rotation + "]";
}
}

View file

@ -53,6 +53,8 @@ public class TextFlow implements TextSequence, WidthRespecting {
private float lineSpacing = DEFAULT_LINE_SPACING;
private float rotation = 0f;
private float maxWidth = -1;
private boolean applyLineSpacingToFirstLine = true;
@ -71,15 +73,15 @@ public class TextFlow implements TextSequence, WidthRespecting {
}
public void addText(String text, float fontSize, Font font) {
add(TextFlowUtil.createTextFlow(text, new FontDescriptor(font, fontSize), lineSpacing));
add(TextFlowUtil.createTextFlow(text, new FontDescriptor(font, fontSize), lineSpacing, rotation));
}
public void addMarkup(String markup, FontDescriptor fontDescriptor) {
add(TextFlowUtil.createTextFlowFromMarkup(markup, fontDescriptor, lineSpacing));
add(TextFlowUtil.createTextFlowFromMarkup(markup, fontDescriptor, lineSpacing, rotation));
}
public void addMarkup(String markup, float fontSize, Font font) {
add(TextFlowUtil.createTextFlowFromMarkup(markup, new FontDescriptor(font, fontSize), lineSpacing));
add(TextFlowUtil.createTextFlowFromMarkup(markup, new FontDescriptor(font, fontSize), lineSpacing, rotation));
}
public void addIndent(String label, float indentWidth, SpaceUnit indentUnit, float fontsize, Font font) {
@ -158,14 +160,6 @@ public class TextFlow implements TextSequence, WidthRespecting {
clearCache();
}
/**
* @return the factor multiplied with the height to calculate the line
* spacing.
*/
public float getLineSpacing() {
return lineSpacing;
}
/**
* Sets the factor multiplied with the height to calculate the line spacing.
*
@ -176,6 +170,14 @@ public class TextFlow implements TextSequence, WidthRespecting {
clearCache();
}
/**
* @return the factor multiplied with the height to calculate the line
* spacing.
*/
public float getLineSpacing() {
return lineSpacing;
}
/**
* Indicates if the line spacing should be applied to the first line. Makes
* sense if there is text above to achieve an equal spacing. In case you
@ -201,6 +203,14 @@ public class TextFlow implements TextSequence, WidthRespecting {
this.applyLineSpacingToFirstLine = applyLineSpacingToFirstLine;
}
public void setRotation(float rotation) {
this.rotation = rotation;
}
public float getRotation() {
return rotation;
}
@Override
public float getWidth() {
Float width = getCachedValue(WIDTH, Float.class);

View file

@ -15,8 +15,9 @@ import java.util.regex.Matcher;
public class TextFlowUtil {
public static TextFlow createTextFlow(String text, FontDescriptor descriptor, float linespacing) {
return createTextFlow(fromPlainText(text), descriptor, linespacing);
public static TextFlow createTextFlow(String text, FontDescriptor descriptor,
float linespacing, float rotation) {
return createTextFlow(fromPlainText(text), descriptor, linespacing, rotation);
}
/**
@ -51,8 +52,9 @@ public class TextFlowUtil {
* @param linespacing the line spacing
* @return the created text flow.
*/
public static TextFlow createTextFlowFromMarkup(String markup, FontDescriptor descriptor, float linespacing) {
return createTextFlow(fromMarkup(markup), descriptor, linespacing);
public static TextFlow createTextFlowFromMarkup(String markup, FontDescriptor descriptor,
float linespacing, float rotation) {
return createTextFlow(fromMarkup(markup), descriptor, linespacing, rotation);
}
/**
@ -63,7 +65,8 @@ public class TextFlowUtil {
* @param linespacing the line spacing
* @return the created text flow.
*/
protected static TextFlow createTextFlow(Iterable<CharSequence> parts, FontDescriptor descriptor, float linespacing) {
protected static TextFlow createTextFlow(Iterable<CharSequence> parts, FontDescriptor descriptor,
float linespacing, float rotation) {
final TextFlow textFlow = new TextFlow();
textFlow.setLineSpacing(linespacing);
boolean bold = false;
@ -133,8 +136,7 @@ public class TextFlowUtil {
}
FontDescriptor fontDescriptor = new FontDescriptor(descriptor.getFont(), currentFontSize, bold, italic);
if (annotationMap.isEmpty()) {
StyledText styledText = new StyledText(fragment.toString(), fontDescriptor,
color, baselineOffset, 0, 0);
StyledText styledText = new StyledText(fragment.toString(), fontDescriptor, color, baselineOffset, 0, 0, rotation);
textFlow.add(styledText);
} else {
AnnotatedStyledText styledText =
@ -165,10 +167,8 @@ public class TextFlowUtil {
* @param text the original text.
* @return the create char sequence.
*/
public static Iterable<CharSequence> fromPlainText(
final Iterable<CharSequence> text) {
Iterable<CharSequence> result = splitByControlCharacter(
ControlCharacters.NEWLINE_FACTORY, text);
public static Iterable<CharSequence> fromPlainText(final Iterable<CharSequence> text) {
Iterable<CharSequence> result = splitByControlCharacter(ControlCharacters.NEWLINE_FACTORY, text);
result = unescapeBackslash(result);
return result;
}

View file

@ -191,6 +191,10 @@ public class TextLine implements TextSequence {
matrix = matrix.multiply(new Matrix(1, 0, 0, 1, gap, baselineDelta));
x += gap;
}
boolean isRotated = styledText.getRotation() > 0f;
if (isRotated) {
matrix.rotate(Math.toRadians(styledText.getRotation()));
}
contentStream.beginText();
contentStream.setTextMatrix(matrix);
if (!styledText.getFontDescriptor().equals(lastFontDesc)) {
@ -271,7 +275,5 @@ public class TextLine implements TextSequence {
public void remove() {
throw new UnsupportedOperationException();
}
}
}

View file

@ -35,7 +35,7 @@ public class AnnotatedStyledText extends StyledText implements Annotated {
final float rightMargin,
final float baselineOffset,
Collection<? extends Annotation> annotations) {
super(text, fontDescriptor, color, baselineOffset, leftMargin, rightMargin);
super(text, fontDescriptor, color, baselineOffset, leftMargin, rightMargin, 0);
if (annotations != null) {
this.annotations.addAll(annotations);
}

View file

@ -316,10 +316,9 @@ public class TextSequenceUtil {
protected static TextFragment deriveFromExisting(TextFragment toDeriveFrom, String text, float leftMargin,
float rightMargin) {
if (toDeriveFrom instanceof StyledText) {
return ((StyledText) toDeriveFrom).inheritAttributes(text,
leftMargin, rightMargin);
return ((StyledText) toDeriveFrom).inheritAttributes(text, leftMargin, rightMargin);
}
return new StyledText(text, toDeriveFrom.getFontDescriptor(), toDeriveFrom.getColor(), 0, leftMargin, rightMargin);
return new StyledText(text, toDeriveFrom.getFontDescriptor(), toDeriveFrom.getColor(), 0, leftMargin, rightMargin, 0);
}
private static Pair<TextFragment> breakWord(TextFragment word,

View file

@ -98,7 +98,7 @@ public class CustomRendererTest {
public void afterPage(RenderContext renderContext) {
String content = String.format("Section %s, Page %s", sectionNumber, renderContext.getPageIndex() + 1);
FontDescriptor fontDescriptor = new FontDescriptor(BaseFont.TIMES, 11);
TextFlow text = TextFlowUtil.createTextFlow(content, fontDescriptor, 1.2f);
TextFlow text = TextFlowUtil.createTextFlow(content, fontDescriptor, 1.2f, 0f);
float offset = renderContext.getPageFormat().getMarginLeft() +
TextSequenceUtil.getOffset(text, renderContext.getWidth(), Alignment.RIGHT);
text.drawText(renderContext.getContentStream(), new Position(

View file

@ -48,7 +48,7 @@ public class ListenerTest {
public void afterPage(RenderContext renderContext) {
String content = String.format("Page %s", renderContext.getPageIndex() + 1);
FontDescriptor fontDescriptor = new FontDescriptor(BaseFont.HELVETICA, 11);
TextFlow text = TextFlowUtil.createTextFlow(content, fontDescriptor, 1.2f);
TextFlow text = TextFlowUtil.createTextFlow(content, fontDescriptor, 1.2f, 0f);
float offset = renderContext.getPageFormat().getMarginLeft()
+ TextSequenceUtil.getOffset(text,
renderContext.getWidth(), Alignment.RIGHT);

View file

@ -54,7 +54,7 @@ public class LowLevelText {
TextFlow text = TextFlowUtil.createTextFlowFromMarkup(
"Hello *bold _italic bold-end* italic-end_. Eirmod\ntempor invidunt ut \\*labore",
new FontDescriptor(BaseFont.TIMES, 11), 1.2f);
new FontDescriptor(BaseFont.TIMES, 11), 1.2f, 0f);
text.addText("Spongebob", 11, BaseFont.COURIER);
text.addText(" is ", 20, BaseFont.HELVETICA);
text.addText("cool", 7, BaseFont.HELVETICA);

View file

@ -12,7 +12,7 @@ import org.xbib.graphics.pdfbox.layout.element.render.VerticalLayoutHint;
import org.xbib.graphics.pdfbox.layout.font.BaseFont;
import java.io.FileOutputStream;
public class RotationTest {
public class PageFormatRotationTest {
@Test
public void test() throws Exception {

View file

@ -0,0 +1,28 @@
package org.xbib.graphics.pdfbox.layout.test;
import java.io.FileOutputStream;
import org.junit.jupiter.api.Test;
import org.xbib.graphics.pdfbox.layout.element.Document;
import org.xbib.graphics.pdfbox.layout.element.Paragraph;
import org.xbib.graphics.pdfbox.layout.font.BaseFont;
public class TextRotationTest {
@Test
public void test() throws Exception {
Document document = new Document(40, 60, 40, 60);
Paragraph paragraph = new Paragraph();
paragraph.addMarkup("Hello there, here is text text text text text text text text text text text text text text text text" +
" text text text text text text text text text text text",
10, BaseFont.HELVETICA);
paragraph.setMaxWidth(100);
document.add(paragraph);
paragraph = new Paragraph();
paragraph.setRotation(90);
paragraph.addMarkup("Hello\u00a0Text\u00a0Rotation", 10, BaseFont.HELVETICA);
document.add(paragraph);
document.render().save(new FileOutputStream("build/textrotation.pdf")).close();
}
}

View file

@ -37,13 +37,13 @@ public class TableTest {
try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
Table myTable = Table.builder()
.addColumnsOfWidth(200, 200)
.padding(2)
.padding(2, 2, 2, 2)
.addRow(Row.builder()
.add(TextCell.builder().text("One One").borderWidth(4).backgroundColor(Color.WHITE).build())
.add(TextCell.builder().text("One Two").borderWidth(0).backgroundColor(Color.YELLOW).build())
.build())
.addRow(Row.builder()
.padding(10)
.padding(10, 10 ,10 ,10)
.add(TextCell.builder().text("Two One").textColor(Color.RED).build())
.add(TextCell.builder().text("Two Two")
.borderWidthRight(1f)

View file

@ -27,7 +27,7 @@
{
"type": "text",
"value": "Hello World 1",
"size": 24,
"fontsize": 24,
"font": "helvetica"
}
]
@ -38,7 +38,7 @@
{
"type": "text",
"value": "Hello World 2",
"size": 24,
"fontsize": 24,
"font": "helvetica"
}
]
@ -49,7 +49,7 @@
{
"type": "text",
"value": "Hello World 3",
"size": 24,
"fontsize": 24,
"font": "helvetica"
}
]
@ -63,7 +63,7 @@
{
"type": "text",
"value": "Hello World 4",
"size": 16,
"fontsize": 16,
"font": "notosans"
}
]
@ -74,7 +74,7 @@
{
"type": "text",
"value": "Hello World 5",
"size": 20,
"fontsize": 20,
"font": "notosans"
}
]
@ -172,7 +172,7 @@
{
"type": "text",
"value": "Hello World 6",
"size": 20,
"fontsize": 20,
"font": "notosans"
}
]