Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/main/java/org/vandeseer/easytable/drawing/DrawingUtil.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.vandeseer.easytable.drawing;

import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.vandeseer.easytable.structure.Text;

import java.awt.*;
import java.io.IOException;
Expand All @@ -14,10 +15,16 @@ private DrawingUtil() {

public static void drawText(PDPageContentStream contentStream, PositionedStyledText styledText) throws IOException {
contentStream.beginText();
contentStream.setNonStrokingColor(styledText.getColor());
contentStream.setFont(styledText.getFont(), styledText.getFontSize());
contentStream.newLineAtOffset(styledText.getX(), styledText.getY());
contentStream.showText(styledText.getText());
for (final Text t: styledText.getText()) {
if (t.getColor().isPresent()) {
contentStream.setNonStrokingColor(t.getColor().get());
} else {
contentStream.setNonStrokingColor(styledText.getColor());
}
contentStream.showText(t.getText());
}
contentStream.endText();
contentStream.setCharacterSpacing(0);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import lombok.Builder;
import lombok.Getter;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.vandeseer.easytable.structure.Text;
import java.util.List;

import java.awt.*;

Expand All @@ -12,7 +14,7 @@ public class PositionedStyledText {

private final float x;
private final float y;
private final String text;
private final List<Text> text;
private final PDFont font;
private final int fontSize;
private final Color color;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
import org.vandeseer.easytable.drawing.DrawingContext;
import org.vandeseer.easytable.drawing.DrawingUtil;
import org.vandeseer.easytable.drawing.PositionedStyledText;
import org.vandeseer.easytable.structure.Text;
import org.vandeseer.easytable.structure.cell.AbstractTextCell;
import org.vandeseer.easytable.util.PdfUtil;

import java.awt.*;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import static org.vandeseer.easytable.settings.HorizontalAlignment.*;

Expand All @@ -35,13 +37,13 @@ public void drawContent(DrawingContext drawingContext) {
float yOffset = drawingContext.getStartingPoint().y + getAdaptionForVerticalAlignment();
float xOffset = startX + cell.getPaddingLeft();

final List<String> lines = calculateAndGetLines(currentFont, currentFontSize, cell.getMaxWidth());
final List<List<Text>> lines = calculateAndGetLines(currentFont, currentFontSize, cell.getMaxWidth());
for (int i = 0; i < lines.size(); i++) {
final String line = lines.get(i);
final List<Text> line = lines.get(i);

yOffset -= calculateYOffset(currentFont, currentFontSize, i);

final float textWidth = PdfUtil.getStringWidth(line, currentFont, currentFontSize);
final float textWidth = PdfUtil.getStringWidth(line.stream().map(Text::getText).collect(Collectors.joining()), currentFont, currentFontSize);

// Handle horizontal alignment by adjusting the xOffset
if (cell.isHorizontallyAligned(RIGHT)) {
Expand Down Expand Up @@ -79,24 +81,25 @@ private float calculateYOffset(PDFont currentFont, int currentFontSize, int line
+ (lineIndex > 0 ? PdfUtil.getFontHeight(currentFont, currentFontSize) * cell.getLineSpacing() : 0f); // line spacing
}

static boolean isNotLastLine(List<String> lines, int i) {
static boolean isNotLastLine(List<List<Text>> lines, int i) {
return i != lines.size() - 1;
}

// Code from https://stackoverflow.com/questions/20680430/is-it-possible-to-justify-text-in-pdfbox
protected float calculateCharSpacingFor(String line) {
protected float calculateCharSpacingFor(List<Text> line) {
float charSpacing = 0;
if (line.length() > 1) {
float size = PdfUtil.getStringWidth(line, cell.getFont(), cell.getFontSize());
int joinedLineLength = line.stream().map(Text::getText).collect(Collectors.joining()).length();
if (joinedLineLength > 1) {
float size = PdfUtil.getStringWidth(line.stream().map(Text::getText).collect(Collectors.joining()), cell.getFont(), cell.getFontSize());
float free = cell.getWidthOfText() - size;
if (free > 0) {
charSpacing = free / (line.length() - 1);
charSpacing = free / (joinedLineLength - 1);
}
}
return charSpacing;
}

protected List<String> calculateAndGetLines(PDFont currentFont, int currentFontSize, float maxWidth) {
protected List<List<Text>> calculateAndGetLines(PDFont currentFont, int currentFontSize, float maxWidth) {
return cell.isWordBreak()
? PdfUtil.getOptimalTextBreakLines(cell.getText(), currentFont, currentFontSize, maxWidth)
: Collections.singletonList(cell.getText());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.apache.pdfbox.util.Matrix;
import org.vandeseer.easytable.drawing.DrawingContext;
import org.vandeseer.easytable.settings.VerticalAlignment;
import org.vandeseer.easytable.structure.Text;
import org.vandeseer.easytable.structure.cell.VerticalTextCell;
import org.vandeseer.easytable.util.PdfUtil;

Expand All @@ -15,6 +16,7 @@
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import static org.vandeseer.easytable.settings.HorizontalAlignment.CENTER;
import static org.vandeseer.easytable.settings.HorizontalAlignment.RIGHT;
Expand Down Expand Up @@ -55,13 +57,13 @@ public void drawContent(DrawingContext drawingContext) {
height = cell.calculateHeightForRowSpan();
}

final List<String> lines = cell.isWordBreak()
final List<List<Text>> lines = cell.isWordBreak()
? PdfUtil.getOptimalTextBreakLines(cell.getText(), currentFont, currentFontSize, (height - cell.getVerticalPadding()))
: Collections.singletonList(cell.getText());

float textHeight = 0;
for (String line : lines) {
float currentHeight = PdfUtil.getStringWidth(line, currentFont, currentFontSize);
for (List<Text> line : lines) {
float currentHeight = PdfUtil.getStringWidth(line.stream().map(Text::getText).collect(Collectors.joining()), currentFont, currentFontSize);
textHeight = currentHeight > textHeight ? currentHeight : textHeight;
}
if (cell.isVerticallyAligned(VerticalAlignment.MIDDLE)) {
Expand All @@ -82,7 +84,7 @@ public void drawContent(DrawingContext drawingContext) {
}

for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
List<Text> line = lines.get(i);

xOffset += (
PdfUtil.getFontHeight(currentFont, currentFontSize) // font height
Expand All @@ -100,7 +102,7 @@ protected float calculateInnerHeight() {
}


protected void drawText(String text, PDFont font, int fontSize, Color color, float x, float y, PDPageContentStream contentStream) throws IOException {
protected void drawText(List<Text> text, PDFont font, int fontSize, Color color, float x, float y, PDPageContentStream contentStream) throws IOException {
// Rotate by 90 degrees counter clockwise
final AffineTransform transform = AffineTransform.getTranslateInstance(x, y);
transform.concatenate(AffineTransform.getRotateInstance(Math.PI * 0.5));
Expand All @@ -115,7 +117,12 @@ protected void drawText(String text, PDFont font, int fontSize, Color color, flo
contentStream.setNonStrokingColor(color);
contentStream.setFont(font, fontSize);
contentStream.newLineAtOffset(x, y);
contentStream.showText(text);
for (final Text t : text) {
if (t.getColor().isPresent()) {
contentStream.setNonStrokingColor(t.getColor().get());
}
contentStream.showText(t.getText());
}
contentStream.endText();
contentStream.setCharacterSpacing(0);
}
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/org/vandeseer/easytable/structure/Text.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.vandeseer.easytable.structure;

import lombok.Getter;
import lombok.Setter;

import java.awt.*;
import java.util.Optional;

@Setter
public class Text {

@Getter
private final String text;
private final Color color;

public Text(String text, Color color) {
this.text = text;
this.color = color;
}

public Text(String text) {
this.text = text;
this.color = null;
}

public Optional<Color> getColor() {
return Optional.ofNullable(color);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
import org.vandeseer.easytable.structure.Column;
import org.vandeseer.easytable.structure.Text;
import org.vandeseer.easytable.util.PdfUtil;

import java.awt.*;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

@Getter
@SuperBuilder(toBuilder = true)
Expand All @@ -36,7 +38,7 @@ public Color getTextColor() {

private Float textHeight;

public abstract String getText();
public abstract List<Text> getText();

@Override
public float getMinHeight() {
Expand Down Expand Up @@ -74,18 +76,17 @@ public float getTextHeight() {
public float getWidthOfText() {
assertIsRendered();

final float notBrokenTextWidth = PdfUtil.getStringWidth(getText(), getFont(), getFontSize());
final float notBrokenTextWidth = PdfUtil.getStringWidth(getText().stream().map(Text::getText).collect(Collectors.joining()), getFont(), getFontSize());

if (settings.isWordBreak()) {

final float maxWidth = getMaxWidthOfText() - getHorizontalPadding();
List<String> textLines = PdfUtil.getOptimalTextBreakLines(getText(), getFont(), getFontSize(), maxWidth);
List<List<Text>> textLines = PdfUtil.getOptimalTextBreakLines(getText(), getFont(), getFontSize(), maxWidth);

return textLines.stream()
.map(line -> PdfUtil.getStringWidth(line, getFont(), getFontSize()))
.map(line -> PdfUtil.getStringWidth(line.stream().map(Text::getText).collect(Collectors.joining()), getFont(), getFontSize()))
.max(Comparator.naturalOrder())
.orElse(notBrokenTextWidth);

}

return notBrokenTextWidth;
Expand Down
105 changes: 99 additions & 6 deletions src/main/java/org/vandeseer/easytable/structure/cell/TextCell.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,113 @@
package org.vandeseer.easytable.structure.cell;

import lombok.Getter;
import lombok.NonNull;
import lombok.experimental.SuperBuilder;
import org.vandeseer.easytable.drawing.Drawer;
import org.vandeseer.easytable.drawing.cell.TextCellDrawer;
import org.vandeseer.easytable.structure.Text;

@Getter
@SuperBuilder(toBuilder = true)
public class TextCell extends AbstractTextCell {
import java.util.List;

public class TextCell extends AbstractTextCell {
@NonNull
protected String text;
protected List<Text> text;

protected Drawer createDefaultDrawer() {
return new TextCellDrawer(this);
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not figure out how to extend this class without de-lomboking it. I think the problem was modifying the 'text' method to set this.text to List instead of String.

public static abstract class TextCellBuilder<C extends TextCell, B extends TextCell.TextCellBuilder<C, B>> extends AbstractTextCell.AbstractTextCellBuilder<C, B> {
@java.lang.SuppressWarnings("all")
private List<Text> text;

@java.lang.SuppressWarnings("all")
public B text(@NonNull final String text) {
if (text == null) {
throw new java.lang.NullPointerException("text is marked non-null but is null");
}
this.text = List.of(new Text(text, this.settings.getTextColor()));
return self();
}

@java.lang.SuppressWarnings("all")
public B coloredText(@NonNull final List<Text> text) {
if (text == null) {
throw new java.lang.NullPointerException("text is marked non-null but is null");
}
this.text = text;
return self();
}

@java.lang.Override
@java.lang.SuppressWarnings("all")
protected B $fillValuesFrom(final C instance) {
super.$fillValuesFrom(instance);
TextCell.TextCellBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
return self();
}

@java.lang.SuppressWarnings("all")
private static void $fillValuesFromInstanceIntoBuilder(final TextCell instance, final TextCell.TextCellBuilder<?, ?> b) {
b.coloredText(instance.text);
}

@java.lang.Override
@java.lang.SuppressWarnings("all")
protected abstract B self();

@java.lang.Override
@java.lang.SuppressWarnings("all")
public abstract C build();

@java.lang.Override
@java.lang.SuppressWarnings("all")
public java.lang.String toString() {
return "TextCell.TextCellBuilder(super=" + super.toString() + ", text=" + this.text + ")";
}
}


@java.lang.SuppressWarnings("all")
private static final class TextCellBuilderImpl extends TextCell.TextCellBuilder<TextCell, TextCell.TextCellBuilderImpl> {
@java.lang.SuppressWarnings("all")
private TextCellBuilderImpl() {
}

@java.lang.Override
@java.lang.SuppressWarnings("all")
protected TextCell.TextCellBuilderImpl self() {
return this;
}

@java.lang.Override
@java.lang.SuppressWarnings("all")
public TextCell build() {
return new TextCell(this);
}
}

@java.lang.SuppressWarnings("all")
protected TextCell(final TextCell.TextCellBuilder<?, ?> b) {
super(b);
this.text = b.text;
if (text == null) {
throw new java.lang.NullPointerException("text is marked non-null but is null");
}
}

@java.lang.SuppressWarnings("all")
public static TextCell.TextCellBuilder<?, ?> builder() {
return new TextCell.TextCellBuilderImpl();
}

@java.lang.SuppressWarnings("all")
public TextCell.TextCellBuilder<?, ?> toBuilder() {
return new TextCell.TextCellBuilderImpl().$fillValuesFrom(this);
}

@NonNull
@java.lang.SuppressWarnings("all")
public List<Text> getText() {
return this.text;
}
}

Loading