added first support for lists

This commit is contained in:
jomu
2016-05-30 14:40:42 +00:00
parent 6262d07196
commit 969b583001
17 changed files with 450 additions and 84 deletions

View File

@ -19,7 +19,6 @@
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -45,27 +44,13 @@
<artifactId>slf4j-log4j12</artifactId> <artifactId>slf4j-log4j12</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<scope>test</scope>
<version>2.44.0</version>
</dependency>
<dependency>
<groupId>com.opera</groupId>
<artifactId>operadriver</artifactId>
<scope>test</scope>
<version>1.5</version>
<exclusions>
<exclusion>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-remote-driver</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>commons-io</groupId> <groupId>commons-io</groupId>
<artifactId>commons-io</artifactId> <artifactId>commons-io</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -0,0 +1,71 @@
package de.muehlencord.shared.pdf;
import com.google.gson.annotations.Expose;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author joern.muehlencord
*/
public class DefaultTableRow extends TableRow {
@Expose
private final List<Text> row;
@Expose
private Boolean isList;
@Expose
private String listName;
@Expose
private String varName;
public DefaultTableRow() {
this.row = new ArrayList<>();
this.isList = false;
this.listName = null;
this.varName = null;
}
public void add(Text text) {
row.add(text);
}
/* *** TableRow methods *** */
@Override
public int getColumnCount() {
return row.size();
}
@Override
public Text getColumnValue(int columnPos) {
return row.get(columnPos);
}
@Override
public boolean isList() {
return isList;
}
@Override
public void createList(String listName, String varName) {
this.listName = listName;
this.varName = varName;
this.isList = true;
}
@Override
public String getListName() {
return listName;
}
@Override
public String getVarName() {
return varName;
}
}

View File

@ -9,11 +9,12 @@ import com.google.gson.GsonBuilder;
*/ */
public class GsonUtil { public class GsonUtil {
public final static Gson getGsonInstance() { protected final static Gson getGsonInstance() {
return new GsonBuilder() return new GsonBuilder()
.setPrettyPrinting() .setPrettyPrinting()
.excludeFieldsWithoutExposeAnnotation() .excludeFieldsWithoutExposeAnnotation()
.registerTypeAdapter(Content.class, new InterfaceAdapter<>()) .registerTypeAdapter(Content.class, new InterfaceAdapter<>())
.registerTypeAdapter(TableRow.class, new InterfaceAdapter<>())
.create(); .create();
} }

View File

@ -0,0 +1,17 @@
package de.muehlencord.shared.pdf;
/**
*
* @author joern.muehlencord
*/
public interface ListTemplate {
public void createList (String listName, String varName);
public boolean isList();
public String getListName();
public String getVarName();
}

View File

@ -5,6 +5,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import org.apache.commons.lang3.text.StrBuilder;
import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font; import org.apache.pdfbox.pdmodel.font.PDType1Font;
@ -32,6 +34,85 @@ public class PDFDocument {
this.fontMap = null; this.fontMap = null;
} }
public String toJson() {
return GsonUtil.getGsonInstance().toJson(this);
}
public String fromJson() {
return GsonUtil.getGsonInstance().toJson(this);
}
public String getTemplateString() throws TemplateException {
ConcurrentLinkedDeque<Integer> bracketStack = new ConcurrentLinkedDeque<>();
int currentPosInTemplate = 0;
int currentStartOfSubelement = 0;
String templateString = GsonUtil.getGsonInstance().toJson(this);
String typeSearchString = " \"type\": \"de.muehlencord.shared.pdf.DefaultTableRow\"";
while (templateString.indexOf(typeSearchString, currentPosInTemplate) > 0) {
// get next element
currentPosInTemplate = templateString.indexOf(typeSearchString, currentPosInTemplate);
int posOpenBracket = templateString.substring(currentStartOfSubelement, currentPosInTemplate).lastIndexOf("{") + currentStartOfSubelement;
int posCloseBracket;
// store position of 1st open bracket
bracketStack.push(posOpenBracket);
// work until closing bracket for this element
while ((!bracketStack.isEmpty()) && (currentPosInTemplate < templateString.length())) {
currentPosInTemplate += 1;
char currentChar = templateString.charAt(currentPosInTemplate);
if (currentChar == '{') {
// new open bracket found
posOpenBracket = currentPosInTemplate;
bracketStack.push(posOpenBracket);
} else if (currentChar == '}') {
if (bracketStack.isEmpty()) {
throw new TemplateException("Found closing bracket, but missing open bracket");
}
// new open bracket found
posCloseBracket = currentPosInTemplate;
posOpenBracket = bracketStack.pop();
if (bracketStack.isEmpty()) {
// next element starts behing the closing bracket earliests
currentStartOfSubelement = posCloseBracket + 1;
String jsonSubString = templateString.substring(posOpenBracket, posCloseBracket + 1);
System.out.println(jsonSubString);
// insert the list values into the gson string
TableRow element = GsonUtil.getGsonInstance().fromJson(jsonSubString, TableRow.class);
if (element.isList()) {
String listStartString = "<#list ";
listStartString += element.getListName();
listStartString += " as ";
listStartString += element.getVarName();
listStartString += ">\n";
String listEndString = "<#if ("+element.getVarName()+"?has_next)>,</#if>";
listEndString += "</#list>\n";
String newString = templateString.substring(0, posOpenBracket);
newString += listStartString;
newString += templateString.substring(posOpenBracket, posCloseBracket+1);
newString += listEndString;
newString += templateString.substring(posCloseBracket+1, templateString.length());
templateString = newString;
currentPosInTemplate += listStartString.length();
currentPosInTemplate += listEndString.length();
System.out.println(templateString.substring(posOpenBracket-10, posCloseBracket + listStartString.length() + listEndString.length()+10));
}
}
}
}
if (!bracketStack.isEmpty()) {
throw new TemplateException("Exception - stack not empty but end of string reached");
}
} // for all types
return templateString;
}
public void addFont(String name, Font font) { public void addFont(String name, Font font) {
if (fontMap == null) { if (fontMap == null) {
fontMap = new ConcurrentHashMap<>(); fontMap = new ConcurrentHashMap<>();
@ -57,8 +138,6 @@ public class PDFDocument {
} }
} }
public PDFDocument addContent(Content content) { public PDFDocument addContent(Content content) {
contentList.add(content); contentList.add(content);
return this; return this;

View File

@ -0,0 +1,9 @@
package de.muehlencord.shared.pdf;
/**
*
* @author joern.muehlencord
*/
public class PDFElement {
}

View File

@ -3,10 +3,12 @@ package de.muehlencord.shared.pdf;
import com.google.gson.Gson; import com.google.gson.Gson;
import freemarker.template.Template; import freemarker.template.Template;
import freemarker.template.TemplateException; import freemarker.template.TemplateException;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.io.FileUtils;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
@ -45,7 +47,7 @@ public class PDFTemplate {
throw new IOException("Error while processing template", ex); throw new IOException("Error while processing template", ex);
} }
String json = out.toString(); String json = out.toString();
LOGGER.info(json); LOGGER.debug(json);
Gson gson = GsonUtil.getGsonInstance(); Gson gson = GsonUtil.getGsonInstance();
PDFDocument pdfDoc = gson.fromJson(json, PDFDocument.class); PDFDocument pdfDoc = gson.fromJson(json, PDFDocument.class);

View File

@ -19,10 +19,10 @@ public class TableContent extends Content {
private final Font headerFont; private final Font headerFont;
@Expose @Expose
private TableHeader header; private final TableHeader header;
@Expose @Expose
private List<List<Text>> data = null; private List<TableRow> data = null;
public TableContent(PDFDocument doc, Font hf) { public TableContent(PDFDocument doc, Font hf) {
super(doc); super(doc);
@ -39,16 +39,25 @@ public class TableContent extends Content {
this.data = new ArrayList<>(); this.data = new ArrayList<>();
} }
public void addLine(String... values) { public DefaultTableRow addLine(String... values) {
addLine(Arrays.asList(values)); return addLine(Arrays.asList(values));
} }
public void addLine(List<String> values) { public DefaultTableRow addLine(List<String> values) {
List<Text> newLine = new ArrayList<>(); DefaultTableRow newLine = new DefaultTableRow();
values.stream().forEach((cellText) -> { values.stream().forEach((cellText) -> {
newLine.add(new Text(cellText)); newLine.add(new Text(cellText));
}); });
data.add(newLine); data.add(newLine);
return newLine;
}
protected TableRow getRow (int no) {
return data.get(no);
}
protected int getRowCount() {
return data.size();
} }
@Override @Override
@ -71,20 +80,21 @@ public class TableContent extends Content {
cos.showText(header.getHeader(i).getText()); cos.showText(header.getHeader(i).getText());
cos.newLineAtOffset(header.getColumnSize(i), 0); cos.newLineAtOffset(header.getColumnSize(i), 0);
} }
if (data.size() == 0) { if (data.isEmpty()) {
currentY -= headerFont.getFontSize() - headerFont.getPadding(); currentY -= headerFont.getFontSize() - headerFont.getPadding();
} }
cos.setFont(standardFont, document.getStandardFont().getFontSize()); cos.setFont(standardFont, document.getStandardFont().getFontSize());
for (int lineNo = 0; lineNo < data.size(); lineNo++) { for (int lineNo = 0; lineNo < data.size(); lineNo++) {
List<Text> currentRow = data.get(lineNo); TableRow currentRow = data.get(lineNo);
cos.newLineAtOffset(xOffSet, yOffset); cos.newLineAtOffset(xOffSet, yOffset);
currentY += yOffset; currentY += yOffset;
for (int colNo = 0; colNo < currentRow.size(); colNo++) { for (int colNo = 0; colNo < currentRow.getColumnCount(); colNo++) {
cos.showText(currentRow.get(colNo).getText()); cos.showText(currentRow.getColumnValue(colNo).getText());
cos.newLineAtOffset(header.getColumnSize(colNo), 0); cos.newLineAtOffset(header.getColumnSize(colNo), 0);
} }
} }
currentY += yOffset;
cos.endText(); cos.endText();
return new Coordinate(currentX, currentY); return new Coordinate(currentX, currentY);

View File

@ -0,0 +1,21 @@
package de.muehlencord.shared.pdf;
/**
*
* @author joern.muehlencord
*/
public abstract class TableRow {
public abstract int getColumnCount();
public abstract void createList(String listName, String varName);
public abstract boolean isList();
public abstract Text getColumnValue(int columnPos);
public abstract String getListName();
public abstract String getVarName();
}

View File

@ -0,0 +1,25 @@
package de.muehlencord.shared.pdf;
/**
*
* @author joern.muehlencord
*/
public class TemplateException extends Exception {
/**
* Creates a new instance of <code>TemplateException</code> without detail
* message.
*/
public TemplateException() {
}
/**
* Constructs an instance of <code>TemplateException</code> with the
* specified detail message.
*
* @param msg the detail message.
*/
public TemplateException(String msg) {
super(msg);
}
}

View File

@ -6,7 +6,7 @@ import com.google.gson.annotations.Expose;
* *
* @author joern.muehlencord * @author joern.muehlencord
*/ */
public class Text { public class Text extends PDFElement {
@Expose @Expose
private final String text; private final String text;

View File

@ -0,0 +1,35 @@
package de.muehlencord.shared.pdf;
import org.junit.Test;
import static org.junit.Assert.*;
/**
*
* @author joern.muehlencord
*/
public class DefaultTableRowTest {
@Test
public void testFromJson() {
String jsonString = "{\n" +
" \"type\": \"de.muehlencord.shared.pdf.DefaultTableRow\",\n" +
" \"data\": {\n" +
" \"row\": [\n" +
" {\n" +
" \"text\": \"Rechnungs-Nr.:\"\n" +
" },\n" +
" {\n" +
" \"text\": \"${invoiceNumber}\"\n" +
" }\n" +
" ],\n" +
" \"isList\": false\n" +
" }\n" +
" }";
TableRow tableRow = GsonUtil.getGsonInstance().fromJson(jsonString, TableRow.class);
assertNotNull ("tableRowObject", tableRow);
assertEquals ("column count", 2, tableRow.getColumnCount());
assertFalse ("isList", tableRow.isList());
}
}

View File

@ -0,0 +1,25 @@
package de.muehlencord.shared.pdf;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author joern.muehlencord
*/
public class Invoice {
private final List<InvoiceLine> invoiceLines;
public Invoice() {
this.invoiceLines = new ArrayList<>();
}
public void addInvoiceLine(InvoiceLine il) {
this.invoiceLines.add(il);
}
public List<InvoiceLine> getInvoiceLines() {
return invoiceLines;
}
}

View File

@ -0,0 +1,58 @@
package de.muehlencord.shared.pdf;
/**
*
* @author joern.muehlencord
*/
public class InvoiceLine {
private String description;
private String price;
private String amount;
private String total;
public InvoiceLine(String description, String price, String amount, String total) {
this.description = description;
this.price = price;
this.amount = amount;
this.total = total;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getAmount() {
return amount;
}
public void setAmount(String amount) {
this.amount = amount;
}
public String getTotal() {
return total;
}
public void setTotal(String total) {
this.total = total;
}
}

View File

@ -1,17 +1,13 @@
package de.muehlencord.shared.pdf; package de.muehlencord.shared.pdf;
import com.google.gson.Gson;
import freemarker.template.Configuration; import freemarker.template.Configuration;
import freemarker.template.Template; import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler; import freemarker.template.TemplateExceptionHandler;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder; import org.junit.FixMethodOrder;
import org.junit.Test; import org.junit.Test;
@ -22,17 +18,10 @@ import org.junit.Test;
@FixMethodOrder @FixMethodOrder
public class PDFDocumentTest { public class PDFDocumentTest {
private static Gson gson;
private String jsonString = null; private String jsonString = null;
@BeforeClass
public static void setUpClass() {
gson = GsonUtil.getGsonInstance();
}
@Test @Test
public void testToJson() throws FileNotFoundException, IOException, ConfigurationException { public void testToJson() throws FileNotFoundException, IOException, ConfigurationException, TemplateException {
System.out.println("testToJson"); System.out.println("testToJson");
PDFDocument doc = new PDFDocument(); PDFDocument doc = new PDFDocument();
doc.addFont("bold", new Font("Helvetica-Bold", 12, 2)); doc.addFont("bold", new Font("Helvetica-Bold", 12, 2));
@ -72,18 +61,19 @@ public class PDFDocumentTest {
TableContent invoiceLines = new TableContent(doc, doc.getFontByAlias("bold")); TableContent invoiceLines = new TableContent(doc, doc.getFontByAlias("bold"));
invoiceLines.getHeaders() invoiceLines.getHeaders()
.add ("Menge", 100) .add ("Menge", 100)
.add ("Beschreibung", 100) .add ("Beschreibung", 300)
.add ("Einzelpreis", 100) .add ("Einzelpreis", 100)
.add ("Summe", 100); .add ("Summe", 100);
invoiceLines.addLine("1","Anzeige Hövelhofer Rundschau", "10", "10");
invoiceLines.addLine ("${invoiceline.amount}", "${invoiceline.description}", "${invoiceline.price}", "${invoiceline.total}").createList("invoiceLines", "invoiceline");
invoiceLines.addLine("2","Anzeige Hövelhofer Rundschau", "10", "20");
doc.addContent(invoiceLines); doc.addContent(invoiceLines);
TextContent test = new TextContent (doc) TextContent test = new TextContent (doc)
.addLine("Das ist ein Test"); .addLine("Das ist ein Test");
doc.addContent (test); doc.addContent (test);
jsonString = doc.getTemplateString();
jsonString = gson.toJson(doc);
System.out.println(jsonString);
File file = new File("c:/temp/test.ftlh"); File file = new File("c:/temp/test.ftlh");
FileUtils.writeStringToFile(file, jsonString, "UTF-8"); FileUtils.writeStringToFile(file, jsonString, "UTF-8");
@ -97,9 +87,16 @@ public class PDFDocumentTest {
Template template = cfg.getTemplate("test.ftlh"); Template template = cfg.getTemplate("test.ftlh");
PDFTemplate pdfDoc = new PDFTemplate(template); PDFTemplate pdfDoc = new PDFTemplate(template);
Invoice invoice = new Invoice();
invoice.addInvoiceLine(new InvoiceLine ("Product 1", "10", "1", "10"));
invoice.addInvoiceLine(new InvoiceLine ("Product 2", "5", "10", "50"));
invoice.addInvoiceLine(new InvoiceLine ("Product 3", "100", "20", "2000"));
pdfDoc.addToDatamodel("invoiceDate", new Date()); pdfDoc.addToDatamodel("invoiceDate", new Date());
pdfDoc.addToDatamodel("customerNumber", "8755"); pdfDoc.addToDatamodel("customerNumber", "8755");
pdfDoc.addToDatamodel("invoiceNumber", "1234567"); pdfDoc.addToDatamodel("invoiceNumber", "1234567");
pdfDoc.addToDatamodel("invoiceLines", invoice.getInvoiceLines());
pdfDoc.create("c:/temp/test.pdf"); pdfDoc.create("c:/temp/test.pdf");
} }

View File

@ -0,0 +1,27 @@
package de.muehlencord.shared.pdf;
import org.junit.Test;
import static org.junit.Assert.*;
/**
*
* @author joern.muehlencord
*/
public class TextTest {
public TextTest() {
}
@Test
public void testFromJson() {
String jsonString = "{\n"
+ "\"text\": \"Rechnungs-Nr.:\"\n"
+ "}";
Text text = GsonUtil.getGsonInstance().fromJson(jsonString, Text.class);
assertNotNull ("text object", text);
assertEquals ("text value", "Rechnungs-Nr.:", text.getText());
}
}

View File

@ -13,6 +13,10 @@
<level value="warn" /> <level value="warn" />
</logger> </logger>
<logger name="org.gson">
<level value="DEBUG" />
</logger>
<category name="de.muehlencord"> <category name="de.muehlencord">
<priority value="DEBUG"/> <priority value="DEBUG"/>
</category> </category>
@ -26,7 +30,7 @@
</category> </category>
<root> <root>
<level value="INFO" /> <level value="DEBUG" />
<appender-ref ref="consoleAppender" /> <appender-ref ref="consoleAppender" />
</root> </root>