table cells use font descriptor now

This commit is contained in:
Jörg Prante 2023-08-26 09:38:03 +02:00
parent 26d8f2756a
commit b62aacfe59
20 changed files with 289 additions and 266 deletions

View file

@ -1,5 +1,5 @@
group = org.xbib.graphics
name = graphics
version = 4.5.4
version = 4.5.6
org.gradle.warning.mode = ALL

View file

@ -6,6 +6,7 @@ import org.xbib.graphics.pdfbox.layout.element.Paragraph;
import org.xbib.graphics.pdfbox.layout.font.Font;
import org.xbib.graphics.pdfbox.layout.element.scripting.Engine;
import org.xbib.graphics.pdfbox.layout.element.scripting.State;
import org.xbib.graphics.pdfbox.layout.font.FontDescriptor;
import org.xbib.graphics.pdfbox.layout.table.BorderStyle;
import org.xbib.graphics.pdfbox.layout.table.BorderStyleInterface;
import org.xbib.graphics.pdfbox.layout.table.HorizontalAlignment;
@ -19,6 +20,8 @@ import org.xbib.settings.Settings;
import java.awt.Color;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static org.xbib.graphics.pdfbox.layout.util.PdfUtil.mmToPt;
@ -39,10 +42,18 @@ public class CellCommand implements Command {
cell.padding(paddingLeft, paddingRight, paddingTop, paddingBottom);
}
cell.text(settings.get("text"));
cell.fontSize(settings.getAsFloat("fontsize", 11.0f));
Document document = state.getDocument();
Font font = document.getFont(settings.get("font", "helvetica").toUpperCase(Locale.ROOT));
cell.font(font);
List<Font> list = new ArrayList<>();
for (String fontName : settings.getAsArray("font")) {
Font font = document.getFont(fontName);
if (font == null) {
throw new IllegalArgumentException("font not specified in document: " + fontName);
} else {
list.add(font);
}
}
float fontSize = settings.getAsFloat("fontsize", 11.0f);
cell.fontDescriptor(new FontDescriptor(list, fontSize));
Color color = ColorFactory.web(settings.get("color", "black"));
cell.textColor(color);
if (settings.containsSetting("backgroundcolor")) {
@ -84,10 +95,11 @@ public class CellCommand implements Command {
paragraph.setLineSpacing(linespacing);
cell.paragraph(paragraph);
String value = settings.get("markup");
float size = settings.getAsFloat("fontsize", 11.0f);
Document document = state.getDocument();
Font font = document.getFont(settings.get("font", "helvetica"));
cell.add(new Markup().setValue(value).setFont(font).setFontSize(size));
List<Font> list = new ArrayList<>();
for (String fontName : settings.getAsArray("font")) {
list.add(state.getDocument().getFont(fontName));
}
cell.add(new Markup().setValue(value).setFontDescriptor(new FontDescriptor(list, settings.getAsFloat("fontsize", 11.0f))));
cell.lineSpacing(linespacing);
state.getElements().peek().add(cell.build());
}

View file

@ -1,9 +1,10 @@
package org.xbib.graphics.pdfbox.layout.element.scripting.command;
import org.xbib.graphics.pdfbox.layout.color.ColorFactory;
import org.xbib.graphics.pdfbox.layout.element.Document;
import org.xbib.graphics.pdfbox.layout.element.scripting.Engine;
import org.xbib.graphics.pdfbox.layout.element.scripting.State;
import org.xbib.graphics.pdfbox.layout.font.Font;
import org.xbib.graphics.pdfbox.layout.font.FontDescriptor;
import org.xbib.graphics.pdfbox.layout.table.BorderStyle;
import org.xbib.graphics.pdfbox.layout.table.BorderStyleInterface;
import org.xbib.graphics.pdfbox.layout.table.HorizontalAlignment;
@ -13,6 +14,8 @@ import org.xbib.settings.Settings;
import java.awt.Color;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class RowCommand implements Command {
@ -29,9 +32,18 @@ public class RowCommand implements Command {
float paddingBottom = Float.parseFloat(paddings[3]);
row.padding(paddingLeft, paddingRight, paddingTop, paddingBottom);
}
row.fontSize(settings.getAsFloat("fontsize", 11.0f));
Document document = state.getDocument();
row.font(document.getFont(settings.get("font", "helvetica")));
List<Font> list = new ArrayList<>();
for (String fontName : settings.getAsArray("font")) {
Font font = state.getDocument().getFont(fontName);
if (font == null) {
throw new IllegalArgumentException("font not specified in document: " + fontName);
} else {
list.add(font);
}
list.add(font);
}
float fontSize = settings.getAsFloat("fontsize", 11.0f);
row.fontDescriptor(new FontDescriptor(list, fontSize));
Color color = ColorFactory.web(settings.get("color", "black"));
row.textColor(color);
if (settings.containsSetting("backgroundcolor")) {

View file

@ -26,6 +26,10 @@ public class FontDescriptor {
public FontDescriptor(List<Font> fonts, float size, boolean bold, boolean italic) {
this.fonts = fonts;
// we do not accept null fonts
for (Font font : fonts) {
Objects.requireNonNull(font, "no null font allowed");
}
this.size = size;
this.bold = bold;
this.italic = italic;

View file

@ -1,6 +1,6 @@
package org.xbib.graphics.pdfbox.layout.table;
import org.xbib.graphics.pdfbox.layout.font.Font;
import org.xbib.graphics.pdfbox.layout.font.FontDescriptor;
import org.xbib.graphics.pdfbox.layout.util.PdfUtil;
import java.awt.Color;
@ -18,12 +18,8 @@ public abstract class AbstractTextCell extends AbstractCell {
return Math.max((getVerticalPadding() + getTextHeight()), super.getMinHeight());
}
public Font getFont() {
return parameters.getFont();
}
public Float getFontSize() {
return parameters.getFontSize();
public FontDescriptor getFontDescriptor() {
return parameters.getFontDescriptor();
}
public Color getTextColor() {
@ -44,9 +40,9 @@ public abstract class AbstractTextCell extends AbstractCell {
if (this.textHeight != null) {
return this.textHeight;
}
this.textHeight = PdfUtil.getFontHeight(getFont(), getFontSize());
this.textHeight = PdfUtil.getFontHeight(getText(), getFontDescriptor());
if (parameters.isWordBreak()) {
final int size = PdfUtil.getOptimalTextBreakLines(getText(), getFont(), getFontSize(), getMaxWidth()).size();
final int size = PdfUtil.getOptimalTextBreakLines(getText(), getFontDescriptor(), getMaxWidth()).size();
final float heightOfTextLines = size * this.textHeight;
final float heightOfLineSpacing = (size - 1) * this.textHeight * getLineSpacing();
this.textHeight = heightOfTextLines + heightOfLineSpacing;
@ -56,12 +52,12 @@ public abstract class AbstractTextCell extends AbstractCell {
public float getWidthOfText() {
assertIsRendered();
final float notBrokenTextWidth = PdfUtil.getStringWidth(getText(), getFont(), getFontSize());
final float notBrokenTextWidth = PdfUtil.getStringWidth(getText(), getFontDescriptor());
if (parameters.isWordBreak()) {
final float maxWidth = getMaxWidthOfText() - getHorizontalPadding();
List<String> textLines = PdfUtil.getOptimalTextBreakLines(getText(), getFont(), getFontSize(), maxWidth);
List<String> textLines = PdfUtil.getOptimalTextBreakLines(getText(), getFontDescriptor(), maxWidth);
return textLines.stream()
.map(line -> PdfUtil.getStringWidth(line, getFont(), getFontSize()))
.map(line -> PdfUtil.getStringWidth(line, getFontDescriptor()))
.max(Comparator.naturalOrder())
.orElse(notBrokenTextWidth);
}

View file

@ -1,14 +1,12 @@
package org.xbib.graphics.pdfbox.layout.table;
import org.xbib.graphics.pdfbox.layout.element.Paragraph;
import org.xbib.graphics.pdfbox.layout.font.Font;
import org.xbib.graphics.pdfbox.layout.font.FontDescriptor;
import org.xbib.graphics.pdfbox.layout.text.annotations.AnnotatedStyledText;
import org.xbib.graphics.pdfbox.layout.text.annotations.Annotations;
import java.awt.Color;
import java.util.Collections;
import java.util.List;
public class Hyperlink implements ParagraphProcessor {
@ -16,24 +14,18 @@ public class Hyperlink implements ParagraphProcessor {
private String url;
private Font font;
private Float fontSize;
private FontDescriptor fontDescriptor;
private Color color = Color.BLUE;
private float baselineOffset = 1f;
public void setFontSize(Float fontSize) {
this.fontSize = fontSize;
public void setFontDescriptor(FontDescriptor fontDescriptor) {
this.fontDescriptor = fontDescriptor;
}
public void setFont(Font font) {
this.font = font;
}
public Font getFont() {
return font;
public FontDescriptor getFontDescriptor() {
return fontDescriptor;
}
public void setText(String text) {
@ -68,16 +60,11 @@ public class Hyperlink implements ParagraphProcessor {
return url;
}
public Float getFontSize() {
return fontSize;
}
@Override
public void process(Paragraph paragraph, Parameters parameters) {
Annotations.HyperlinkAnnotation hyperlink =
new Annotations.HyperlinkAnnotation(getUrl(), Annotations.HyperlinkAnnotation.LinkStyle.ul);
FontDescriptor fontDescriptor = new FontDescriptor(List.of(getFont() != null ? getFont() : parameters.getFont()),
getFontSize() != null ? getFontSize() : parameters.getFontSize());
FontDescriptor fontDescriptor = getFontDescriptor() != null ? getFontDescriptor() : parameters.getFontDescriptor();
paragraph.add(new AnnotatedStyledText(getText(), fontDescriptor,
getColor(), getBaselineOffset(), 0, 0, Collections.singleton(hyperlink)));
}

View file

@ -1,17 +1,13 @@
package org.xbib.graphics.pdfbox.layout.table;
import org.xbib.graphics.pdfbox.layout.element.Paragraph;
import org.xbib.graphics.pdfbox.layout.font.Font;
import java.util.List;
import org.xbib.graphics.pdfbox.layout.font.FontDescriptor;
public class Markup implements ParagraphProcessor {
private String value;
private Font font;
private Float fontSize;
private FontDescriptor fontDescriptor;
public Markup setValue(String value) {
this.value = value;
@ -22,27 +18,17 @@ public class Markup implements ParagraphProcessor {
return value;
}
public Markup setFont(Font font) {
this.font = font;
public Markup setFontDescriptor(FontDescriptor fontDescriptor) {
this.fontDescriptor = fontDescriptor;
return this;
}
public Font getFont() {
return font;
}
public Markup setFontSize(Float fontSize) {
this.fontSize = fontSize;
return this;
}
public Float getFontSize() {
return fontSize;
public FontDescriptor getFontDescriptor() {
return fontDescriptor;
}
@Override
public void process(Paragraph paragraph, Parameters parameters) {
float fontSize = getFontSize() != null ? getFontSize() : parameters.getFontSize();
paragraph.addMarkup(getValue(), fontSize, List.of(font));
paragraph.addMarkup(getValue(), fontDescriptor);
}
}

View file

@ -1,13 +1,14 @@
package org.xbib.graphics.pdfbox.layout.table;
import org.xbib.graphics.pdfbox.layout.font.Font;
import org.xbib.graphics.pdfbox.layout.font.BaseFont;
import org.xbib.graphics.pdfbox.layout.font.FontDescriptor;
import java.awt.Color;
import java.util.List;
public class Parameters {
private Font font;
private Float fontSize;
private FontDescriptor fontDescriptor;
private Color textColor;
@ -47,6 +48,10 @@ public class Parameters {
// For callers outside it should expose only the primitive though.
private Boolean wordBreak;
public Parameters() {
setFontDescriptor(new FontDescriptor(List.of(BaseFont.HELVETICA), 11.0f));
}
public boolean isWordBreak() {
return wordBreak != null && wordBreak;
}
@ -133,12 +138,8 @@ public class Parameters {
return horizontalAlignment;
}
public Float getFontSize() {
return fontSize;
}
public Font getFont() {
return font;
public FontDescriptor getFontDescriptor() {
return fontDescriptor;
}
public VerticalAlignment getVerticalAlignment() {
@ -185,12 +186,8 @@ public class Parameters {
this.borderWidthTop = borderWidthTop;
}
public void setFont(Font font) {
this.font = font;
}
public void setFontSize(Float fontSize) {
this.fontSize = fontSize;
public void setFontDescriptor(FontDescriptor fontDescriptor) {
this.fontDescriptor = fontDescriptor;
}
public void setHorizontalAlignment(HorizontalAlignment horizontalAlignment) {
@ -311,11 +308,8 @@ public class Parameters {
}
private void fillingMergeFontSettings(Parameters parameters) {
if (getFont() == null && parameters.getFont() != null) {
font = parameters.getFont();
}
if (getFontSize() == null && parameters.getFontSize() != null) {
fontSize = parameters.getFontSize();
if (getFontDescriptor() == null && parameters.getFontDescriptor() != null) {
setFontDescriptor(parameters.getFontDescriptor());
}
}
}

View file

@ -3,7 +3,7 @@ package org.xbib.graphics.pdfbox.layout.table;
import org.xbib.graphics.pdfbox.layout.element.Drawable;
import org.xbib.graphics.pdfbox.layout.element.Element;
import org.xbib.graphics.pdfbox.layout.element.HorizontalRuler;
import org.xbib.graphics.pdfbox.layout.font.Font;
import org.xbib.graphics.pdfbox.layout.font.FontDescriptor;
import java.awt.Color;
import java.util.ArrayList;
@ -112,13 +112,8 @@ public class Row {
return this;
}
public Builder font(Font font) {
parameters.setFont(font);
return this;
}
public Builder fontSize(float fontSize) {
parameters.setFontSize(fontSize);
public Builder fontDescriptor(FontDescriptor fontDescriptor) {
parameters.setFontDescriptor(fontDescriptor);
return this;
}

View file

@ -1,20 +1,16 @@
package org.xbib.graphics.pdfbox.layout.table;
import org.xbib.graphics.pdfbox.layout.element.Paragraph;
import org.xbib.graphics.pdfbox.layout.font.Font;
import org.xbib.graphics.pdfbox.layout.font.FontDescriptor;
import org.xbib.graphics.pdfbox.layout.util.PdfUtil;
import java.awt.Color;
import java.util.List;
public class StyledText implements ParagraphProcessor {
private String text;
private Float fontSize;
private Font font;
private FontDescriptor fontDescriptor;
private Color color;
@ -26,20 +22,12 @@ public class StyledText implements ParagraphProcessor {
return text;
}
public void setFontSize(Float fontSize) {
this.fontSize = fontSize;
public void setFontDescriptor(FontDescriptor fontDescriptor) {
this.fontDescriptor = fontDescriptor;
}
public Float getFontSize() {
return fontSize;
}
public void setFont(Font font) {
this.font = font;
}
public Font getFont() {
return font;
public FontDescriptor getFontDescriptor() {
return fontDescriptor;
}
public void setColor(Color color) {
@ -52,16 +40,14 @@ public class StyledText implements ParagraphProcessor {
@Override
public void process(Paragraph paragraph, Parameters parameters) {
final float actualFontSize = getFontSize() != null ? getFontSize() : parameters.getFontSize();
final Font actualFont = getFont() != null ? getFont() : parameters.getFont();
final FontDescriptor fontDescriptor = getFontDescriptor() != null ? getFontDescriptor() : parameters.getFontDescriptor();
final Color actualColor = getColor() != null ? getColor() : parameters.getTextColor();
// TODO this is a complete mess to handle new lines!!!
String[] lines = getText().split(PdfUtil.NEW_LINE_REGEX);
for (int i = 0; i < lines.length; i++) {
FontDescriptor fontDescriptor = new FontDescriptor(List.of(actualFont), actualFontSize);
paragraph.add(new org.xbib.graphics.pdfbox.layout.text.StyledText(lines[i], fontDescriptor, actualColor, 0f, 0, 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(List.of(actualFont), actualFontSize)));
paragraph.add(new org.xbib.graphics.pdfbox.layout.text.NewLine(fontDescriptor));
}
}
}

View file

@ -1,7 +1,7 @@
package org.xbib.graphics.pdfbox.layout.table;
import org.xbib.graphics.pdfbox.layout.font.BaseFont;
import org.xbib.graphics.pdfbox.layout.font.Font;
import org.xbib.graphics.pdfbox.layout.font.FontDescriptor;
import java.awt.Color;
import java.awt.Point;
@ -14,9 +14,8 @@ import java.util.Set;
public class Table {
private static final Font DEFAULT_FONT = BaseFont.HELVETICA;
private static final Float DEFAULT_FONT_SIZE = 11f;
private static final FontDescriptor DEFAULT_FONT_DESCRIPTOR =
new FontDescriptor(List.of(BaseFont.HELVETICA), 11.0f);
private static final Color DEFAULT_TEXT_COLOR = Color.BLACK;
@ -94,10 +93,6 @@ public class Table {
return DEFAULT_HORIZONTAL_ALIGNMENT;
}
public static float getDefaultFontSize() {
return DEFAULT_FONT_SIZE;
}
public int getNumberOfColumns() {
return numberOfColumns;
}
@ -110,10 +105,6 @@ public class Table {
return rows;
}
public static Font getDefaultFont() {
return DEFAULT_FONT;
}
public Set<Point> getRowSpanCells() {
return rowSpanCells;
}
@ -155,8 +146,7 @@ public class Table {
private float maximumHeight;
private Builder() {
parameters.setFont(DEFAULT_FONT);
parameters.setFontSize(DEFAULT_FONT_SIZE);
parameters.setFontDescriptor(DEFAULT_FONT_DESCRIPTOR);
parameters.setTextColor(DEFAULT_TEXT_COLOR);
parameters.setBorderColor(DEFAULT_BORDER_COLOR);
parameters.setBorderStyleTop(DEFAULT_BORDER_STYLE);
@ -228,13 +218,8 @@ public class Table {
return this;
}
public Builder font(Font font) {
parameters.setFont(font);
return this;
}
public Builder fontSize(Float fontSize) {
parameters.setFontSize(fontSize);
public Builder fontDescriptor(FontDescriptor fontDescriptor) {
parameters.setFontDescriptor(fontDescriptor);
return this;
}

View file

@ -1,6 +1,6 @@
package org.xbib.graphics.pdfbox.layout.table;
import org.xbib.graphics.pdfbox.layout.font.Font;
import org.xbib.graphics.pdfbox.layout.font.FontDescriptor;
import org.xbib.graphics.pdfbox.layout.table.render.Renderer;
import org.xbib.graphics.pdfbox.layout.table.render.TextCellRenderer;
import java.awt.Color;
@ -48,13 +48,8 @@ public class TextCell extends AbstractTextCell {
return this;
}
public Builder font(Font font) {
parameters.setFont(font);
return this;
}
public Builder fontSize(Float fontSize) {
parameters.setFontSize(fontSize);
public Builder fontDescriptor(FontDescriptor fontDescriptor) {
parameters.setFontDescriptor(fontDescriptor);
return this;
}

View file

@ -1,6 +1,6 @@
package org.xbib.graphics.pdfbox.layout.table.render;
import org.xbib.graphics.pdfbox.layout.font.Font;
import org.xbib.graphics.pdfbox.layout.font.FontDescriptor;
import java.awt.Color;
@ -12,18 +12,15 @@ public class PositionedStyledText {
private final String text;
private final Font font;
private final float fontSize;
private final FontDescriptor fontDescriptor;
private final Color color;
public PositionedStyledText(float x, float y, String text, Font font, float fontSize, Color color) {
public PositionedStyledText(float x, float y, String text, FontDescriptor fontDescriptor, Color color) {
this.x = x;
this.y = y;
this.text = text;
this.font = font;
this.fontSize = fontSize;
this.fontDescriptor = fontDescriptor;
this.color = color;
}
@ -39,12 +36,8 @@ public class PositionedStyledText {
return text;
}
public Font getFont() {
return font;
}
public float getFontSize() {
return fontSize;
public FontDescriptor getFontDescriptor() {
return fontDescriptor;
}
public Color getColor() {

View file

@ -3,7 +3,7 @@ package org.xbib.graphics.pdfbox.layout.table.render;
import static org.xbib.graphics.pdfbox.layout.table.HorizontalAlignment.CENTER;
import static org.xbib.graphics.pdfbox.layout.table.HorizontalAlignment.JUSTIFY;
import static org.xbib.graphics.pdfbox.layout.table.HorizontalAlignment.RIGHT;
import org.xbib.graphics.pdfbox.layout.font.Font;
import org.xbib.graphics.pdfbox.layout.font.FontDescriptor;
import org.xbib.graphics.pdfbox.layout.table.AbstractTextCell;
import org.xbib.graphics.pdfbox.layout.util.PdfUtil;
import org.xbib.graphics.pdfbox.layout.util.RenderUtil;
@ -23,16 +23,15 @@ public class TextCellRenderer<T extends AbstractTextCell> extends AbstractCellRe
@Override
public void renderContent(TableRenderContext tableRenderContext) {
float startX = tableRenderContext.getStartingPoint().x;
Font currentFont = cell.getFont();
float currentFontSize = cell.getFontSize();
FontDescriptor fontDescriptor = cell.getFontDescriptor();
Color currentTextColor = cell.getTextColor();
float yOffset = tableRenderContext.getStartingPoint().y + getAdaptionForVerticalAlignment();
float xOffset = startX + cell.getPaddingLeft();
final List<String> lines = calculateAndGetLines(currentFont, currentFontSize, cell.getMaxWidth());
final List<String> lines = calculateAndGetLines(fontDescriptor, cell.getMaxWidth());
for (int i = 0; i < lines.size(); i++) {
final String line = lines.get(i);
yOffset -= calculateYOffset(currentFont, currentFontSize, i);
final float textWidth = PdfUtil.getStringWidth(line, currentFont, currentFontSize);
yOffset -= calculateYOffset(line, fontDescriptor, i);
final float textWidth = PdfUtil.getStringWidth(line, fontDescriptor);
if (cell.isHorizontallyAligned(RIGHT)) {
xOffset = startX + (cell.getWidth() - (textWidth + cell.getPaddingRight()));
} else if (cell.isHorizontallyAligned(CENTER)) {
@ -44,8 +43,8 @@ public class TextCellRenderer<T extends AbstractTextCell> extends AbstractCellRe
throw new UncheckedIOException(exception);
}
}
PositionedStyledText positionedStyledText = new PositionedStyledText(xOffset, yOffset, line,
currentFont, currentFontSize, currentTextColor);
PositionedStyledText positionedStyledText =
new PositionedStyledText(xOffset, yOffset, line, fontDescriptor, currentTextColor);
RenderUtil.drawText(tableRenderContext.getContentStream(), positionedStyledText);
}
}
@ -55,9 +54,9 @@ public class TextCellRenderer<T extends AbstractTextCell> extends AbstractCellRe
return cell.getTextHeight();
}
private float calculateYOffset(Font currentFont, float currentFontSize, int lineIndex) {
return PdfUtil.getFontHeight(currentFont, currentFontSize) +
(lineIndex > 0 ? PdfUtil.getFontHeight(currentFont, currentFontSize) * cell.getLineSpacing() : 0f);
private float calculateYOffset(String text, FontDescriptor fontDescriptor, int lineIndex) {
return PdfUtil.getFontHeight(text, fontDescriptor) +
(lineIndex > 0 ? PdfUtil.getFontHeight(text, fontDescriptor) * cell.getLineSpacing() : 0f);
}
private static boolean isNotLastLine(List<String> lines, int i) {
@ -67,7 +66,7 @@ public class TextCellRenderer<T extends AbstractTextCell> extends AbstractCellRe
private float calculateCharSpacingFor(String line) {
float charSpacing = 0;
if (line.length() > 1) {
float size = PdfUtil.getStringWidth(line, cell.getFont(), cell.getFontSize());
float size = PdfUtil.getStringWidth(line, cell.getFontDescriptor());
float free = cell.getWidthOfText() - size;
if (free > 0) {
charSpacing = free / (line.length() - 1);
@ -76,9 +75,9 @@ public class TextCellRenderer<T extends AbstractTextCell> extends AbstractCellRe
return charSpacing;
}
private List<String> calculateAndGetLines(Font currentFont, float currentFontSize, float maxWidth) {
private List<String> calculateAndGetLines(FontDescriptor fontDescriptor, float maxWidth) {
return cell.isWordBreak()
? PdfUtil.getOptimalTextBreakLines(cell.getText(), currentFont, currentFontSize, maxWidth)
? PdfUtil.getOptimalTextBreakLines(cell.getText(), fontDescriptor, maxWidth)
: Collections.singletonList(cell.getText());
}
}

View file

@ -3,6 +3,7 @@ package org.xbib.graphics.pdfbox.layout.table.render;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.util.Matrix;
import org.xbib.graphics.pdfbox.layout.font.Font;
import org.xbib.graphics.pdfbox.layout.font.FontDescriptor;
import org.xbib.graphics.pdfbox.layout.util.PdfUtil;
import org.xbib.graphics.pdfbox.layout.table.VerticalTextCell;
@ -27,8 +28,7 @@ public class VerticalTextCellRenderer extends AbstractCellRenderer<VerticalTextC
public void renderContent(TableRenderContext tableRenderContext) {
float startX = tableRenderContext.getStartingPoint().x;
float startY = tableRenderContext.getStartingPoint().y;
Font currentFont = cell.getFont();
float currentFontSize = cell.getFontSize();
FontDescriptor fontDescriptor = cell.getFontDescriptor();
Color currentTextColor = cell.getTextColor();
float yOffset = startY + cell.getPaddingBottom();
float height = cell.getRow().getHeight();
@ -38,13 +38,13 @@ public class VerticalTextCellRenderer extends AbstractCellRenderer<VerticalTextC
height = cell.calculateHeightForRowSpan();
}
final List<String> lines = cell.isWordBreak()
? PdfUtil.getOptimalTextBreakLines(cell.getText(), currentFont, currentFontSize, (height - cell.getVerticalPadding()))
? PdfUtil.getOptimalTextBreakLines(cell.getText(), fontDescriptor, (height - cell.getVerticalPadding()))
: Collections.singletonList(cell.getText());
float xOffset = startX + cell.getPaddingLeft() - PdfUtil.getFontHeight(currentFont, currentFontSize);
float xOffset = startX + cell.getPaddingLeft(); /* - PdfUtil.getFontHeight(currentFont, currentFontSize)*/;
for (int i = 0; i < lines.size(); i++) {
final String line = lines.get(i);
xOffset += (PdfUtil.getFontHeight(currentFont, currentFontSize) + (i > 0 ? PdfUtil.getFontHeight(currentFont, currentFontSize) * cell.getLineSpacing() : 0f));
drawText(line, currentFont, currentFontSize, currentTextColor, xOffset, yOffset, tableRenderContext.getContentStream());
xOffset += (PdfUtil.getFontHeight(line, fontDescriptor) + (i > 0 ? PdfUtil.getFontHeight(line, fontDescriptor) * cell.getLineSpacing() : 0f));
drawText(line, fontDescriptor, currentTextColor, xOffset, yOffset, tableRenderContext.getContentStream());
}
}
@ -54,22 +54,31 @@ public class VerticalTextCellRenderer extends AbstractCellRenderer<VerticalTextC
return 0;
}
protected void drawText(String text, Font font, float fontSize, Color color, float x, float y, PDPageContentStream contentStream) {
protected void drawText(String text,
FontDescriptor fontDescriptor,
Color color,
float x,
float y,
PDPageContentStream contentStream) {
try {
// Rotate by 90 degrees counter clockwise
AffineTransform transform = AffineTransform.getTranslateInstance(x, y);
transform.concatenate(AffineTransform.getRotateInstance(Math.PI * 0.5));
transform.concatenate(AffineTransform.getTranslateInstance(-x, -y - fontSize));
contentStream.moveTo(x, y);
contentStream.beginText();
contentStream.setTextMatrix(new Matrix(transform));
contentStream.setNonStrokingColor(color);
// TODO select correct font
contentStream.setFont(font.getRegularFont(), fontSize);
contentStream.newLineAtOffset(x, y);
contentStream.showText(text);
contentStream.endText();
contentStream.setCharacterSpacing(0);
for (Font font : fontDescriptor.getFonts()) {
if (font.canWrite(text)) {
// Rotate by 90 degrees counter clockwise
AffineTransform transform = AffineTransform.getTranslateInstance(x, y);
transform.concatenate(AffineTransform.getRotateInstance(Math.PI * 0.5));
transform.concatenate(AffineTransform.getTranslateInstance(-x, -y - fontDescriptor.getSize()));
contentStream.moveTo(x, y);
contentStream.beginText();
contentStream.setTextMatrix(new Matrix(transform));
contentStream.setNonStrokingColor(color);
// TODO select correct font style
contentStream.setFont(font.getRegularFont(), fontDescriptor.getSize());
contentStream.newLineAtOffset(x, y);
contentStream.showText(text);
contentStream.endText();
contentStream.setCharacterSpacing(0);
}
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}

View file

@ -1,6 +1,7 @@
package org.xbib.graphics.pdfbox.layout.util;
import org.xbib.graphics.pdfbox.layout.font.Font;
import org.xbib.graphics.pdfbox.layout.font.FontDescriptor;
import org.xbib.graphics.pdfbox.layout.table.CouldNotDetermineStringWidthException;
import java.io.IOException;
@ -10,7 +11,6 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Stack;
import java.util.stream.Collectors;
/**
* Provides some helping functions.
@ -29,16 +29,15 @@ public final class PdfUtil {
* Computes the width of a String (in points).
*
* @param text Text
* @param font Font of Text
* @param fontSize FontSize of String
* @param fontDescriptor Font of Text
* @return Width (in points)
*/
public static float getStringWidth(String text, Font font, float fontSize) {
public static float getStringWidth(String text, FontDescriptor fontDescriptor) {
return Arrays.stream(text.split(NEW_LINE_REGEX))
.max(Comparator.comparing(String::length))
.map(x -> {
try {
return getWidthOfStringWithoutNewlines(x, font, fontSize);
return getWidthOfStringWithoutNewlines(x, fontDescriptor);
} catch (IOException exception) {
return 0f;
}
@ -46,16 +45,25 @@ public final class PdfUtil {
.orElseThrow(CouldNotDetermineStringWidthException::new);
}
private static float getWidthOfStringWithoutNewlines(String text, Font font, float fontSize) throws IOException {
private static float getWidthOfStringWithoutNewlines(String text, FontDescriptor fontDescriptor) throws IOException {
List<String> codePointsAsString = text.codePoints()
.mapToObj(codePoint -> new String(new int[]{codePoint}, 0, 1))
.collect(Collectors.toList());
.toList();
List<Float> widths = new ArrayList<>();
for (String codepoint : codePointsAsString) {
try {
widths.add(font.getRegularFont().getStringWidth(codepoint) * fontSize / 1000F);
} catch (final IllegalArgumentException | IOException e) {
widths.add(font.getRegularFont().getStringWidth("") * fontSize / 1000F);
boolean found = false;
for (Font font : fontDescriptor.getFonts()) {
if (font.canWrite(codepoint)) {
try {
widths.add(font.getRegularFont().getStringWidth(codepoint) * fontDescriptor.getSize() / 1000F);
} catch (IllegalArgumentException | IOException e) {
widths.add(font.getRegularFont().getStringWidth("") * fontDescriptor.getSize() / 1000F);
}
found = true;
}
}
if (!found) {
throw new IllegalArgumentException("unable to get width of string " + codepoint);
}
}
return widths.stream().reduce(0.0f, Float::sum);
@ -64,37 +72,40 @@ public final class PdfUtil {
/**
* Computes the height of a font.
*
* @param font Font
* @param fontSize FontSize
* @return Height of font
* @param fontDescriptor font
* @return height of font
*/
public static float getFontHeight(Font font, float fontSize) {
return font.getRegularFont().getFontDescriptor().getCapHeight() * fontSize / 1000F;
public static float getFontHeight(String text, FontDescriptor fontDescriptor) {
for (Font font : fontDescriptor.getFonts()) {
if (font.canWrite(text)) {
return font.getRegularFont().getFontDescriptor().getCapHeight() * fontDescriptor.getSize() / 1000F;
}
}
throw new IllegalArgumentException("unable to get font height for text " + text + ", fonts = " + fontDescriptor.getFonts());
}
/**
* Split a text into multiple lines to prevent a text-overflow.
*
* @param text Text
* @param font Used font
* @param fontSize Used fontSize
* @param fontDescriptor Used font
* @param maxWidth Maximal width of resulting text-lines
* @return A list of lines, where all are smaller than maxWidth
*/
public static List<String> getOptimalTextBreakLines(String text, Font font, float fontSize, float maxWidth) {
public static List<String> getOptimalTextBreakLines(String text, FontDescriptor fontDescriptor, float maxWidth) {
List<String> result = new ArrayList<>();
for (String line : text.split(NEW_LINE_REGEX)) {
if (PdfUtil.doesTextLineFit(line, font, fontSize, maxWidth)) {
if (doesTextLineFit(line, fontDescriptor, maxWidth)) {
result.add(line);
} else {
result.addAll(PdfUtil.wrapLine(line, font, fontSize, maxWidth));
result.addAll(wrapLine(line, fontDescriptor, maxWidth));
}
}
return result;
}
private static List<String> wrapLine(String line, Font font, float fontSize, float maxWidth) {
if (doesTextLineFit(line, font, fontSize, maxWidth)) {
private static List<String> wrapLine(String line, FontDescriptor fontDescriptor, float maxWidth) {
if (doesTextLineFit(line, fontDescriptor, maxWidth)) {
return Collections.singletonList(line);
}
List<String> goodLines = new ArrayList<>();
@ -102,30 +113,30 @@ public final class PdfUtil {
Arrays.asList(line.split("(?<=[\\\\. ,-])")).forEach(allWords::push);
Collections.reverse(allWords);
while (!allWords.empty()) {
goodLines.add(buildALine(allWords, font, fontSize, maxWidth));
goodLines.add(buildALine(allWords, fontDescriptor, maxWidth));
}
return goodLines;
}
private static List<String> splitBySize(String line, Font font, float fontSize, float maxWidth) {
private static List<String> splitBySize(String line, FontDescriptor fontDescriptor, float maxWidth) {
List<String> returnList = new ArrayList<>();
for (int i = line.length() - 1; i > 0; i--) {
String fittedNewLine = line.substring(0, i) + "-";
String remains = line.substring(i);
if (PdfUtil.doesTextLineFit(fittedNewLine, font, fontSize, maxWidth)) {
if (PdfUtil.doesTextLineFit(fittedNewLine, fontDescriptor, maxWidth)) {
returnList.add(fittedNewLine);
returnList.addAll(PdfUtil.wrapLine(remains, font, fontSize, maxWidth));
returnList.addAll(PdfUtil.wrapLine(remains, fontDescriptor, maxWidth));
break;
}
}
return returnList;
}
private static String buildALine(Stack<String> words, Font font, float fontSize, float maxWidth) {
private static String buildALine(Stack<String> words, FontDescriptor fontDescriptor, float maxWidth) {
StringBuilder line = new StringBuilder();
float width = 0;
while (!words.empty()) {
float nextWordWidth = getStringWidth(words.peek(), font, fontSize);
float nextWordWidth = getStringWidth(words.peek(), fontDescriptor);
if (line.length() == 0 && words.peek().length() == 1 && nextWordWidth > maxWidth) {
return words.pop();
}
@ -137,23 +148,23 @@ public final class PdfUtil {
}
}
if (width == 0 && !words.empty()) {
List<String> cutBySize = splitBySize(words.pop(), font, fontSize, maxWidth);
List<String> cutBySize = splitBySize(words.pop(), fontDescriptor, maxWidth);
Collections.reverse(cutBySize);
cutBySize.forEach(words::push);
return buildALine(words, font, fontSize, maxWidth);
return buildALine(words, fontDescriptor, maxWidth);
}
return line.toString().trim();
}
private static boolean doesTextLineFit(String textLine, Font font, float fontSize, float maxWidth) {
return doesTextLineFit(PdfUtil.getStringWidth(textLine, font, fontSize), maxWidth);
private static boolean doesTextLineFit(String textLine, FontDescriptor fontDescriptor, float maxWidth) {
return doesTextLineFit(getStringWidth(textLine, fontDescriptor), maxWidth);
}
private static boolean doesTextLineFit(float stringWidth, float maxWidth) {
if (isEqualInEpsilon(stringWidth, maxWidth)) {
private static boolean doesTextLineFit(float stringWidth, float max) {
if (isEqualInEpsilon(stringWidth, max)) {
return true;
}
return maxWidth > stringWidth;
return max > stringWidth;
}
private static boolean isEqualInEpsilon(float x, float y) {

View file

@ -2,6 +2,7 @@ package org.xbib.graphics.pdfbox.layout.util;
import static org.xbib.graphics.pdfbox.layout.table.BorderStyle.SOLID;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.xbib.graphics.pdfbox.layout.font.Font;
import org.xbib.graphics.pdfbox.layout.table.render.PositionedLine;
import org.xbib.graphics.pdfbox.layout.table.render.PositionedRectangle;
import org.xbib.graphics.pdfbox.layout.table.render.PositionedStyledText;
@ -16,17 +17,21 @@ public class RenderUtil {
}
public static void drawText(PDPageContentStream contentStream, PositionedStyledText styledText) {
try {
contentStream.beginText();
contentStream.setNonStrokingColor(styledText.getColor());
// TODO select correct font
contentStream.setFont(styledText.getFont().getRegularFont(), styledText.getFontSize());
contentStream.newLineAtOffset(styledText.getX(), styledText.getY());
contentStream.showText(styledText.getText());
contentStream.endText();
contentStream.setCharacterSpacing(0);
} catch (IOException e) {
throw new UncheckedIOException(e);
for (Font font : styledText.getFontDescriptor().getFonts()) {
if (font.canWrite(styledText.getText())) {
try {
contentStream.beginText();
contentStream.setNonStrokingColor(styledText.getColor());
// TODO select correct font style
contentStream.setFont(font.getRegularFont(), styledText.getFontDescriptor().getSize());
contentStream.newLineAtOffset(styledText.getX(), styledText.getY());
contentStream.showText(styledText.getText());
contentStream.endText();
contentStream.setCharacterSpacing(0);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
}

View file

@ -12,6 +12,7 @@ import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.xbib.graphics.pdfbox.layout.font.BaseFont;
import org.xbib.graphics.pdfbox.layout.font.FontDescriptor;
import org.xbib.graphics.pdfbox.layout.table.AbstractCell;
import org.xbib.graphics.pdfbox.layout.table.BorderStyle;
import org.xbib.graphics.pdfbox.layout.table.Column;
@ -26,6 +27,7 @@ import org.xbib.graphics.pdfbox.layout.table.TextCell;
import java.awt.Color;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
public class TableTest {
@ -104,13 +106,13 @@ public class TableTest {
final Table table = Table.builder()
.addColumnOfWidth(12)
.addColumnOfWidth(34)
.fontSize(12f)
.fontDescriptor(new FontDescriptor(List.of(BaseFont.HELVETICA), 12f))
.addRow(Row.builder()
.add(TextCell.builder().text("11").paddingTop(35).paddingBottom(15).build())
.add(TextCell.builder().text("12").paddingTop(15).paddingBottom(25).build())
.build())
.build();
final float actualFontHeight = PdfUtil.getFontHeight(table.getSettings().getFont(), 12);
final float actualFontHeight = PdfUtil.getFontHeight("test", table.getSettings().getFontDescriptor());
assertThat(table.getHeight(), equalTo(50 + actualFontHeight));
}
@ -137,7 +139,7 @@ public class TableTest {
.add(TextCell.builder().text("This text should break because too long").colSpan(2).borderWidth(1).build())
.add(TextCell.builder().text("Booz").build())
.wordBreak(true)
.font(BaseFont.COURIER).fontSize(8)
.fontDescriptor(new FontDescriptor(List.of(BaseFont.COURIER), 8.0f))
.build();
row.getHeight();
});
@ -148,13 +150,12 @@ public class TableTest {
Table.Builder tableBuilder = Table.builder()
.addColumnsOfWidth(10, 10, 10)
.horizontalAlignment(HorizontalAlignment.CENTER)
.fontSize(10f)
.font(BaseFont.HELVETICA)
.fontDescriptor(new FontDescriptor(List.of(BaseFont.HELVETICA), 10.0f))
.wordBreak(false);
Row row = Row.builder()
.add(TextCell.builder().text("iVgebALheQlBkxtDyNDrhKv").colSpan(2).borderWidth(1).build())
.add(TextCell.builder().text("Booz").build())
.font(BaseFont.COURIER).fontSize(8)
.fontDescriptor(new FontDescriptor(List.of(BaseFont.COURIER), 8.0f))
.build();
tableBuilder.addRow(row);
tableBuilder.build();

View file

@ -128,15 +128,21 @@
"elements": [
{
"type": "cell",
"text": "Cell A"
"text": "Cell A",
"font": "helvetica",
"fontsize": 11
},
{
"type": "cell",
"text": "Cell B"
"text": "Cell B",
"font": "helvetica",
"fontsize": 11
},
{
"type": "cell",
"text": "Cell C"
"text": "Cell C",
"font": "helvetica",
"fontsize": 11
}
]
},
@ -145,15 +151,21 @@
"elements": [
{
"type": "cell",
"text": "Cell D"
"text": "Cell D",
"font": "helvetica",
"fontsize": 11
},
{
"type": "cell",
"text": "Cell E"
"text": "Cell E",
"font": "helvetica",
"fontsize": 11
},
{
"type": "cell",
"text": "Cell F"
"text": "Cell F",
"font": "helvetica",
"fontsize": 11
}
]
}
@ -168,15 +180,21 @@
"elements": [
{
"type": "cell",
"text": "Cell 1"
"text": "Cell 1",
"font": "helvetica",
"fontsize": 11
},
{
"type": "cell",
"text": "Cell 2"
"text": "Cell 2",
"font": "helvetica",
"fontsize": 11
},
{
"type": "cell",
"text": "Cell 3"
"text": "Cell 3",
"font": "helvetica",
"fontsize": 11
}
]
},
@ -186,18 +204,24 @@
{
"type": "cell",
"text": "Cell 4",
"font": "helvetica",
"fontsize": 11,
"padding": "5 5 5 5",
"borderwidth": 0.5
},
{
"type": "cell",
"text": "Cell 5",
"font": "helvetica",
"fontsize": 11,
"padding": "5 5 5 5",
"borderwidth": 0.5
},
{
"type": "cell",
"text": "Cell 6",
"font": "helvetica",
"fontsize": 11,
"padding": "5 5 5 5",
"borderwidth": 0.5
}

View file

@ -3,8 +3,7 @@
"margin": "0 0 0 0",
"author": "Jörg Prante",
"font": [
"helvetica",
"notosans"
"helvetica"
],
"elements": [
{
@ -17,15 +16,21 @@
"elements": [
{
"type": "cell",
"text": "Cell A"
"text": "Cell A",
"font": "helvetica",
"fontsize": 11
},
{
"type": "cell",
"text": "Cell B"
"text": "Cell B",
"font": "helvetica",
"fontsize": 11
},
{
"type": "cell",
"text": "Cell C"
"text": "Cell C",
"font": "helvetica",
"fontsize": 11
}
]
},
@ -34,15 +39,21 @@
"elements": [
{
"type": "cell",
"text": "Cell D"
"text": "Cell D",
"font": "helvetica",
"fontsize": 11
},
{
"type": "cell",
"text": "Cell E"
"text": "Cell E",
"font": "helvetica",
"fontsize": 11
},
{
"type": "cell",
"text": "Cell F"
"text": "Cell F",
"font": "helvetica",
"fontsize": 11
}
]
}
@ -59,15 +70,21 @@
{
"type": "cell",
"text": "This is very long text for cell 1 This is very long text for cell 1 This is very long text for cell 1",
"linespacing": 0.5
"linespacing": 0.5,
"font": "helvetica",
"fontsize": 11
},
{
"type": "cell",
"text": "Cell 2"
"text": "Cell 2",
"font": "helvetica",
"fontsize": 11
},
{
"type": "cell",
"text": "Cell 3"
"text": "Cell 3",
"font": "helvetica",
"fontsize": 11
}
]
},
@ -77,18 +94,24 @@
{
"type": "cell",
"text": "Cell 4",
"font": "helvetica",
"fontsize": 11,
"padding": "5 5 5 5",
"borderwidth": 0.5
},
{
"type": "cell",
"text": "Cell 5",
"font": "helvetica",
"fontsize": 11,
"padding": "5 5 5 5",
"borderwidth": 0.5
},
{
"type": "cell",
"text": "Cell 6",
"font": "helvetica",
"fontsize": 11,
"padding": "5 5 5 5",
"borderwidth": 0.5
}
@ -100,18 +123,24 @@
{
"type": "cell",
"text": "Cell 7",
"font": "helvetica",
"fontsize": 11,
"padding": "5 5 5 5",
"borderwidth": 0.5
},
{
"type": "cell",
"text": "Cell 8",
"font": "helvetica",
"fontsize": 11,
"padding": "5 5 5 5",
"borderwidth": 0.5
},
{
"type": "cell",
"text": "Cell 9",
"font": "helvetica",
"fontsize": 11,
"padding": "5 5 5 5",
"borderwidth": 0.5
}