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>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
@ -45,27 +44,13 @@
<artifactId>slf4j-log4j12</artifactId>
<scope>test</scope>
</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>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
</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 final static Gson getGsonInstance() {
protected final static Gson getGsonInstance() {
return new GsonBuilder()
.setPrettyPrinting()
.excludeFieldsWithoutExposeAnnotation()
.registerTypeAdapter(Content.class, new InterfaceAdapter<>())
.registerTypeAdapter(TableRow.class, new InterfaceAdapter<>())
.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.Map;
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.PDType1Font;
@ -32,6 +34,85 @@ public class PDFDocument {
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) {
if (fontMap == null) {
fontMap = new ConcurrentHashMap<>();
@ -57,8 +138,6 @@ public class PDFDocument {
}
}
public PDFDocument addContent(Content content) {
contentList.add(content);
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 freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.io.FileUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
@ -45,7 +47,7 @@ public class PDFTemplate {
throw new IOException("Error while processing template", ex);
}
String json = out.toString();
LOGGER.info(json);
LOGGER.debug(json);
Gson gson = GsonUtil.getGsonInstance();
PDFDocument pdfDoc = gson.fromJson(json, PDFDocument.class);

View File

@ -19,10 +19,10 @@ public class TableContent extends Content {
private final Font headerFont;
@Expose
private TableHeader header;
private final TableHeader header;
@Expose
private List<List<Text>> data = null;
private List<TableRow> data = null;
public TableContent(PDFDocument doc, Font hf) {
super(doc);
@ -39,16 +39,25 @@ public class TableContent extends Content {
this.data = new ArrayList<>();
}
public void addLine(String... values) {
addLine(Arrays.asList(values));
public DefaultTableRow addLine(String... values) {
return addLine(Arrays.asList(values));
}
public void addLine(List<String> values) {
List<Text> newLine = new ArrayList<>();
public DefaultTableRow addLine(List<String> values) {
DefaultTableRow newLine = new DefaultTableRow();
values.stream().forEach((cellText) -> {
newLine.add(new Text(cellText));
});
data.add(newLine);
return newLine;
}
protected TableRow getRow (int no) {
return data.get(no);
}
protected int getRowCount() {
return data.size();
}
@Override
@ -71,20 +80,21 @@ public class TableContent extends Content {
cos.showText(header.getHeader(i).getText());
cos.newLineAtOffset(header.getColumnSize(i), 0);
}
if (data.size() == 0) {
if (data.isEmpty()) {
currentY -= headerFont.getFontSize() - headerFont.getPadding();
}
cos.setFont(standardFont, document.getStandardFont().getFontSize());
for (int lineNo = 0; lineNo < data.size(); lineNo++) {
List<Text> currentRow = data.get(lineNo);
TableRow currentRow = data.get(lineNo);
cos.newLineAtOffset(xOffSet, yOffset);
currentY += yOffset;
for (int colNo = 0; colNo < currentRow.size(); colNo++) {
cos.showText(currentRow.get(colNo).getText());
for (int colNo = 0; colNo < currentRow.getColumnCount(); colNo++) {
cos.showText(currentRow.getColumnValue(colNo).getText());
cos.newLineAtOffset(header.getColumnSize(colNo), 0);
}
}
currentY += yOffset;
cos.endText();
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
*/
public class Text {
public class Text extends PDFElement {
@Expose
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;
import com.google.gson.Gson;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
@ -22,17 +18,10 @@ import org.junit.Test;
@FixMethodOrder
public class PDFDocumentTest {
private static Gson gson;
private String jsonString = null;
@BeforeClass
public static void setUpClass() {
gson = GsonUtil.getGsonInstance();
}
@Test
public void testToJson() throws FileNotFoundException, IOException, ConfigurationException {
public void testToJson() throws FileNotFoundException, IOException, ConfigurationException, TemplateException {
System.out.println("testToJson");
PDFDocument doc = new PDFDocument();
doc.addFont("bold", new Font("Helvetica-Bold", 12, 2));
@ -72,18 +61,19 @@ public class PDFDocumentTest {
TableContent invoiceLines = new TableContent(doc, doc.getFontByAlias("bold"));
invoiceLines.getHeaders()
.add ("Menge", 100)
.add ("Beschreibung", 100)
.add ("Beschreibung", 300)
.add ("Einzelpreis", 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);
TextContent test = new TextContent (doc)
.addLine("Das ist ein Test");
doc.addContent (test);
jsonString = gson.toJson(doc);
System.out.println(jsonString);
jsonString = doc.getTemplateString();
File file = new File("c:/temp/test.ftlh");
FileUtils.writeStringToFile(file, jsonString, "UTF-8");
@ -97,9 +87,16 @@ public class PDFDocumentTest {
Template template = cfg.getTemplate("test.ftlh");
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("customerNumber", "8755");
pdfDoc.addToDatamodel("invoiceNumber", "1234567");
pdfDoc.addToDatamodel("invoiceLines", invoice.getInvoiceLines());
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" />
</logger>
<logger name="org.gson">
<level value="DEBUG" />
</logger>
<category name="de.muehlencord">
<priority value="DEBUG"/>
</category>
@ -26,7 +30,7 @@
</category>
<root>
<level value="INFO" />
<level value="DEBUG" />
<appender-ref ref="consoleAppender" />
</root>