Skip to content

Commit 74df65d

Browse files
Merge pull request #1 from mobiuscode-com/more-html-tag-support
Additional html tags are now supported: - underlines with `<u>` - hsl color format for `<span>` nodes - `<strong>` in addition to `<b>` for bold text - `<p>` in addition to `<div>` for paragraphs
2 parents 285beeb + d7d1757 commit 74df65d

File tree

6 files changed

+148
-5
lines changed

6 files changed

+148
-5
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.moebiusgames.pdfbox.table;
2+
3+
import java.awt.*;
4+
5+
public final class ColorUtils {
6+
7+
public static Color getHSLColor(int h, int s, int l) {
8+
float sf = (float) s / 100;
9+
float lf = (float) l / 100;
10+
float a = sf * Math.min(lf, 1 - lf);
11+
12+
return new Color(f(0, h, lf, a), f(8, h, lf, a), f(4, h, lf, a));
13+
}
14+
15+
private static float f(float n, float h, float l, float a) {
16+
return l - a * Math.max(-1, Math.min(k(n, h) - 3, Math.min(9 - k(n, h), 1)));
17+
}
18+
19+
private static float k(float n, float h) {
20+
return (n + h / 30) % 12;
21+
}
22+
}
23+

src/main/java/com/moebiusgames/pdfbox/table/PDFTable.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ private PDFTableRow prepareHeadingRow() {
256256
cell.setBackgroundColor(column.getHeadingBackgroundColor());
257257
cell.setFont(column.getHeadingFont());
258258
cell.setFontSize(column.getHeadingFontSize());
259+
cell.setUnderline(column.getUnderline());
259260
}
260261
return headingRow;
261262
}

src/main/java/com/moebiusgames/pdfbox/table/PDFTableCell.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public class PDFTableCell {
6363
private Color fontColor = PDFTable.NOT_SET_COLOR;
6464
private Color backgroundColor = PDFTable.NOT_SET_COLOR;
6565
private float lineSpacingFactor = PDFTable.NOT_SET;
66+
private Boolean underline = null;
6667
private final PDFTable table;
6768

6869
PDFTableCell(PDFTableRow row, int cellIndex, final PDFTable table) {
@@ -311,6 +312,17 @@ public PDFTableCell setBackgroundColor(Color backgroundColor) {
311312
return this;
312313
}
313314

315+
public Boolean getUnderline() {
316+
return underline == null
317+
? table.getColumn(index).getUnderline()
318+
: underline;
319+
}
320+
321+
public PDFTableCell setUnderline(Boolean underline) {
322+
this.underline = underline;
323+
return this;
324+
}
325+
314326
/**
315327
* returns the width of this cell in user space units
316328
*
@@ -440,6 +452,7 @@ private void traverseHTMLLayout(LaidoutContent laidoutContent, Pos xPos, LayoutF
440452

441453
switch (node.nodeName().toLowerCase()) {
442454
case "div":
455+
case "p":
443456
newLineLayout.setConditionalNewLine(1);
444457
break;
445458
case "br":
@@ -456,11 +469,15 @@ private void traverseHTMLLayout(LaidoutContent laidoutContent, Pos xPos, LayoutF
456469
frame.indent++;
457470
break;
458471
case "b":
472+
case "strong":
459473
frame.bold = true;
460474
break;
461475
case "i":
462476
frame.italic = true;
463477
break;
478+
case "u":
479+
frame.underline = true;
480+
break;
464481
case "font":
465482
if (node.hasAttr("size")) {
466483
try {
@@ -473,6 +490,17 @@ private void traverseHTMLLayout(LaidoutContent laidoutContent, Pos xPos, LayoutF
473490
frame.color = Utils.htmlColorToColor(node.attr("color"));
474491
}
475492
break;
493+
case "span":
494+
if (node.hasAttr("style")) {
495+
if(node.attr("style").contains("color")) {
496+
String style = node.attr("style").replaceAll("\\s", "");
497+
String color = style.substring(style.indexOf("color:") + 6);
498+
color = color.substring(0, color.indexOf(";"));
499+
500+
frame.color = Utils.htmlColorToColor(color);
501+
}
502+
}
503+
break;
476504
case "ul":
477505
//if this is the first <ul>, we add an empty line
478506
newLineLayout.setConditionalNewLine(frame.indent == 0 ? 2 : 1);
@@ -767,6 +795,19 @@ private void renderText(PDPageContentStream stream, float x, float y,
767795
stream.showText(block.getContent());
768796
stream.endText();
769797

798+
if (block.getUnderline()) {
799+
float tx = x + getPaddingLeft() + offsetX + rowShiftX;
800+
float ty = y - getPaddingTop() + offsetY;
801+
802+
float stringWidth = block.getFontSize() * block.getFont().getStringWidth(block.getContent()) / 1000;
803+
float lineEndPoint = tx + stringWidth;
804+
805+
stream.setLineWidth(1);
806+
stream.moveTo(tx, ty - 2);
807+
stream.lineTo(lineEndPoint, ty - 2);
808+
stream.stroke();
809+
}
810+
770811
offsetX += block.getWidth();
771812
}
772813
offsetX = 0;
@@ -1005,9 +1046,11 @@ class LaidoutContentBlock {
10051046
private Integer fontSize = null; //not set
10061047
private Color fontColor = null; //not set
10071048
private int indent = 0;
1049+
private Boolean underline = false;
10081050

10091051
public LaidoutContentBlock(LayoutFrame fromFrame) {
10101052
this.indent = fromFrame.indent;
1053+
this.underline = fromFrame.underline;
10111054

10121055
this.bulletPoint = fromFrame.bulletPoint;
10131056
if (fromFrame.bold != null
@@ -1067,6 +1110,12 @@ public Color getFontColor() {
10671110
: this.fontColor;
10681111
}
10691112

1113+
public Boolean getUnderline() {
1114+
return this.underline == null
1115+
? PDFTableCell.this.getUnderline()
1116+
: this.underline;
1117+
}
1118+
10701119
public int getIndent() {
10711120
return indent;
10721121
}
@@ -1113,6 +1162,7 @@ private static class LayoutFrame {
11131162
private Integer htmlSize = null;
11141163
private Boolean bold = null;
11151164
private Boolean italic = null;
1165+
private Boolean underline = null;
11161166

11171167
public LayoutFrame() {
11181168
}
@@ -1124,6 +1174,7 @@ public LayoutFrame(LayoutFrame frame) {
11241174
this.htmlSize = frame.htmlSize;
11251175
this.bold = frame.bold;
11261176
this.italic = frame.italic;
1177+
this.underline = frame.underline;
11271178
}
11281179

11291180
}

src/main/java/com/moebiusgames/pdfbox/table/PDFTableColumn.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public class PDFTableColumn {
6262
private Color backgroundColor = null;
6363
private float lineSpacingFactor = 0.2f;
6464
private String heading = "[N/A]";
65+
private Boolean underline = false;
6566
private final PDFTable table;
6667

6768
PDFTableColumn(final PDFTable table, float width) {
@@ -381,4 +382,13 @@ public PDFTableColumn setBackgroundColor(Color backgroundColor) {
381382
return this;
382383
}
383384

385+
public Boolean getUnderline() {
386+
return underline;
387+
}
388+
389+
public PDFTableColumn setUnderline(Boolean underline) {
390+
this.underline = underline;
391+
return this;
392+
}
393+
384394
}

src/main/java/com/moebiusgames/pdfbox/table/Utils.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import java.io.IOException;
2828
import java.util.HashMap;
2929
import java.util.Map;
30+
import java.util.regex.Matcher;
31+
import java.util.regex.Pattern;
3032
import org.apache.pdfbox.pdmodel.PDPageContentStream;
3133
import org.apache.pdfbox.pdmodel.font.PDFont;
3234

@@ -74,6 +76,32 @@ static Color htmlColorToColor(String color) {
7476
return new Color(Integer.parseInt(color.substring(1), 16));
7577
} catch (NumberFormatException e) {
7678
}
79+
} else if (color.startsWith("rgb")) {
80+
Pattern pattern = Pattern.compile("rgb\\( *([0-9]+), *([0-9]+), *([0-9]+)\\)");
81+
Color colorObject = null;
82+
Matcher matcher = pattern.matcher(color);
83+
if (matcher.matches()) {
84+
colorObject =
85+
new Color(
86+
Integer.parseInt(matcher.group(1)),
87+
Integer.parseInt(matcher.group(2)),
88+
Integer.parseInt(matcher.group(3)));
89+
}
90+
return colorObject;
91+
92+
} else if (color.startsWith("hsl")){
93+
Pattern pattern = Pattern.compile("hsl\\( *([0-9]+), *([0-9]+)%, *([0-9]+)%\\)");
94+
Color colorObject = null;
95+
Matcher matcher = pattern.matcher(color);
96+
97+
if (matcher.matches()) {
98+
colorObject =
99+
ColorUtils.getHSLColor(
100+
Integer.parseInt(matcher.group(1)),
101+
Integer.parseInt(matcher.group(2)),
102+
Integer.parseInt(matcher.group(3)));
103+
}
104+
return colorObject;
77105
} else {
78106
switch (color.trim().toLowerCase()) {
79107
case "black":

src/test/java/com/moebiusgames/pdfbox/table/PDFTableTest.java

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.jsoup.Jsoup;
4747
import org.jsoup.nodes.Document;
4848
import org.jsoup.nodes.Node;
49+
import org.junit.Test;
4950

5051
public class PDFTableTest {
5152

@@ -84,7 +85,7 @@ public void jSoupTest() {
8485
// System.out.println(node0);
8586
}
8687

87-
// @Test
88+
@Test
8889
public void layoutTest3() throws Exception {
8990
File targetFile = new File("D:/Temp/layout3.pdf");
9091
PDDocument doc = new PDDocument();
@@ -130,8 +131,9 @@ public void layoutTest3() throws Exception {
130131
}
131132
}
132133

134+
@Test
133135
public void layoutTest2() throws IOException {
134-
final File targetFile = new File("D:/temp/table_html.pdf");
136+
final File targetFile = new File("D:/Temp/table_html.pdf");
135137
final String simpleText = "Hello this is some <b><i>very</i> Bold</b> text!";
136138
final String complexText = "Jemand musste <font size=\"7\">Jogef K.</font> verleumdet <br/><font size=\"6\">haben</font>, denn <font size=\"5\">ohne</font> dass er etwas <i>Böses</i> getan hätte, wurde eR eines Morgens verhaftet. "
137139
+ "<div><b>»Wie ein Hund!«</b> sagte er, es war,</div> als sollte die <i>Scham</i> ihn überleben. Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte, "
@@ -247,8 +249,34 @@ public void layoutTest2() throws IOException {
247249
+ " </blockquote>\n"
248250
+ "</blockquote>";
249251

250-
251-
PDDocument doc = new PDDocument();
252+
String underlined = "<div><u>some underlined text</u> and here some non underlined text.<br>\n"
253+
+ "<u><strong><i>and here it's underlined, bold and italic!</i></strong></u></div>\n"
254+
+ "<div>here is plain text again</div>\n"
255+
+ "<div><u>First line of underlined text<br>\n"
256+
+ "second line</u></div>";
257+
258+
String colors = "<p><u>Hex Colors</u></p>\n"
259+
+ "<p><span style=\"color:#009688;\">Color</span></p>\n"
260+
+ "<p><u>RGB Format</u></p>\n"
261+
+ "<p><span style=\"color:rgb(255,0,0);\">Color</span></p>\n"
262+
+ "<p>HSL format</p>\n"
263+
+ "<p><span style=\"color:hsl(240,75%,60%);\">Color</span></p>\n"
264+
+ "<p>“Document colors”</p>\n"
265+
+ "<p><span style=\"color:rgb(255,0,0);\">Color</span></p>\n"
266+
+ "<h2><span style=\"color:hsl(60,75%,60%);\">Title and Color</span></h2>\n"
267+
+ "<h3><span style=\"color:hsl(30,75%,60%);\">Title 2 and Color</span></h3>\n"
268+
+ "<ul><li><span style=\"color:#009688;\">c1</span></li>\n"
269+
+ "<li><span style=\"color:rgb(255,0,0);\">c2</span></li>\n"
270+
+ "<li><span style=\"color:hsl(240,75%,60%);\">c3</span></li></ul>\n"
271+
+ "<p>all color points</p>\n"
272+
+ "<ul><li><span style=\"color:hsl(240,75%,60%);\">p1</span></li>\n"
273+
+ "<li><span style=\"color:hsl(240,75%,60%);\">p2</span></li>\n"
274+
+ "<li><span style=\"color:hsl(240,75%,60%);\">p3</span></li></ul>\n"
275+
+ "<p><span style=\"color:hsl(240,75%,60%);\"\n"
276+
+ "<p>&nbsp;</p>\n";
277+
278+
279+
PDDocument doc = new PDDocument();
252280
try {
253281
PDPage firstPage = new PDPage(PDRectangle.A4);
254282
PDFRenderContext context = new PDFRenderContext(doc, firstPage);
@@ -275,7 +303,9 @@ public void layoutTest2() throws IOException {
275303
for (int i = 0; i < 1; ++i) {
276304
PDFTableRow row = reportTable.addRow();
277305
// row.getCell(COL_SOMETHING).setContent(complexText);
278-
row.getCell(COL_DOCUMENTATION).setContent(bulletPoints);
306+
// row.getCell(COL_DOCUMENTATION).setContent(underlined);
307+
// row.getCell(COL_DOCUMENTATION).setContent(html2Text);
308+
row.getCell(COL_DOCUMENTATION).setContent(colors);
279309
row.getCell(COL_DOCUMENTATION).setTextType(TextType.HTML);
280310
}
281311

0 commit comments

Comments
 (0)