All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.craigburke.document.builder.PdfDocumentBuilder.groovy Maven / Gradle / Ivy
package com.craigburke.document.builder
import com.craigburke.document.builder.render.ParagraphRenderer
import com.craigburke.document.builder.render.TableRenderer
import com.craigburke.document.core.EmbeddedFont
import com.craigburke.document.core.HeaderFooterOptions
import com.craigburke.document.core.PageBreak
import com.craigburke.document.core.builder.RenderState
import groovy.transform.InheritConstructors
import groovy.xml.MarkupBuilder
import com.craigburke.document.core.builder.DocumentBuilder
import com.craigburke.document.core.Document
import com.craigburke.document.core.TextBlock
import com.craigburke.document.core.Table
import com.craigburke.document.core.Image
import org.apache.pdfbox.pdmodel.common.PDMetadata
/**
* Builder for PDF documents
* @author Craig Burke
*/
@InheritConstructors
class PdfDocumentBuilder extends DocumentBuilder {
PdfDocument pdfDocument
void initializeDocument(Document document, OutputStream out) {
pdfDocument = new PdfDocument(document)
pdfDocument.x = document.margin.left
pdfDocument.y = document.margin.top
document.element = pdfDocument
}
def addEmbeddedFont = { EmbeddedFont embeddedFont ->
PdfFont.addFont(pdfDocument.pdDocument, embeddedFont)
}
def addPageBreakToDocument = { PageBreak pageBreak, Document document ->
pdfDocument.addPage()
}
def onTextBlockComplete = { TextBlock paragraph ->
if (renderState == RenderState.PAGE && paragraph.parent instanceof Document) {
int pageWidth = document.width - document.margin.left - document.margin.right
int maxLineWidth = pageWidth - paragraph.margin.left - paragraph.margin.right
int renderStartX = document.margin.left + paragraph.margin.left
pdfDocument.x = renderStartX
pdfDocument.scrollDownPage(paragraph.margin.top)
ParagraphRenderer paragraphRenderer =
new ParagraphRenderer(paragraph, pdfDocument, renderStartX, maxLineWidth)
while (!paragraphRenderer.fullyParsed) {
paragraphRenderer.parse(pdfDocument.remainingPageHeight)
paragraphRenderer.render(pdfDocument.y)
if (paragraphRenderer.fullyParsed) {
pdfDocument.scrollDownPage(paragraphRenderer.renderedHeight)
} else {
pdfDocument.addPage()
}
}
pdfDocument.scrollDownPage(paragraph.margin.bottom)
}
}
def onTableComplete = { Table table ->
if (renderState == RenderState.PAGE) {
pdfDocument.x = table.margin.left + document.margin.left
pdfDocument.scrollDownPage(table.margin.top)
TableRenderer tableRenderer = new TableRenderer(table, pdfDocument, pdfDocument.x)
while (!tableRenderer.fullyParsed) {
tableRenderer.parse(pdfDocument.remainingPageHeight)
tableRenderer.render(pdfDocument.y)
if (tableRenderer.fullyParsed) {
pdfDocument.scrollDownPage(tableRenderer.renderedHeight)
} else {
pdfDocument.addPage()
}
}
pdfDocument.scrollDownPage(table.margin.bottom)
}
}
void writeDocument(Document document, OutputStream out) {
addHeaderFooter()
addMetadata()
pdfDocument.contentStream?.close()
pdfDocument.pdDocument.save(out)
pdfDocument.pdDocument.close()
}
private void addHeaderFooter() {
int pageCount = pdfDocument.pages.size()
def options = new HeaderFooterOptions(pageCount: pageCount, dateGenerated: new Date())
(1..pageCount).each { int pageNumber ->
pdfDocument.pageNumber = pageNumber
options.pageNumber = pageNumber
if (document.header) {
renderState = RenderState.HEADER
def header = document.header(options)
renderHeaderFooter(header)
}
if (document.footer) {
renderState = RenderState.FOOTER
def footer = document.footer(options)
renderHeaderFooter(footer)
}
}
renderState = RenderState.PAGE
}
private void renderHeaderFooter(headerFooter) {
float startX = document.margin.left + headerFooter.margin.left
float startY
if (renderState == RenderState.HEADER) {
startY = headerFooter.margin.top
} else {
float pageBottom = pdfDocument.pageBottomY + document.margin.bottom
startY = pageBottom - getElementHeight(headerFooter) - headerFooter.margin.bottom
}
def renderer
if (headerFooter instanceof TextBlock) {
renderer = new ParagraphRenderer(headerFooter, pdfDocument, startX, document.width)
} else {
renderer = new TableRenderer(headerFooter as Table, pdfDocument, startX)
}
renderer.parse(document.height)
renderer.render(startY)
}
private float getElementHeight(element) {
float width = document.width - document.margin.top - document.margin.bottom
if (element instanceof TextBlock) {
new ParagraphRenderer(element, pdfDocument, 0, width).totalHeight
} else if (element instanceof Table) {
new TableRenderer(element, pdfDocument, 0).totalHeight
} else {
0
}
}
private void addMetadata() {
ByteArrayOutputStream xmpOut = new ByteArrayOutputStream()
def xml = new MarkupBuilder(xmpOut.newWriter())
xml.document(marginTop: "${document.margin.top}", marginBottom: "${document.margin.bottom}",
marginLeft: "${document.margin.left}", marginRight: "${document.margin.right}") {
delegate = xml
resolveStrategy = Closure.DELEGATE_FIRST
document.children.each { child ->
switch (child.getClass()) {
case TextBlock:
addParagraphToMetadata(delegate, child)
break
case Table:
addTableToMetadata(delegate, child)
break
}
}
}
def catalog = pdfDocument.pdDocument.documentCatalog
InputStream inputStream = new ByteArrayInputStream(xmpOut.toByteArray())
PDMetadata metadata = new PDMetadata(pdfDocument.pdDocument, inputStream, false)
catalog.metadata = metadata
}
private void addParagraphToMetadata(builder, TextBlock paragraphNode) {
builder.paragraph(marginTop: "${paragraphNode.margin.top}",
marginBottom: "${paragraphNode.margin.bottom}",
marginLeft: "${paragraphNode.margin.left}",
marginRight: "${paragraphNode.margin.right}") {
paragraphNode.children?.findAll { it.getClass() == Image }.each {
builder.image()
}
}
}
private void addTableToMetadata(builder, Table tableNode) {
builder.table(columns: tableNode.columnCount, width: tableNode.width, borderSize: tableNode.border.size) {
delegate = builder
resolveStrategy = Closure.DELEGATE_FIRST
tableNode.children.each {
def cells = it.children
row {
cells.each {
cell(width: "${it.width ?: 0}")
}
}
}
}
}
}