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.WordDocumentBuilder.groovy Maven / Gradle / Ivy
package com.craigburke.document.builder
import static com.craigburke.document.core.UnitUtil.pointToEigthPoint
import static com.craigburke.document.core.UnitUtil.pointToEmu
import static com.craigburke.document.core.UnitUtil.pointToTwip
import static com.craigburke.document.core.UnitUtil.pointToHalfPoint
import com.craigburke.document.core.HeaderFooterOptions
import com.craigburke.document.core.builder.RenderState
import com.craigburke.document.core.BlockNode
import com.craigburke.document.core.Cell
import com.craigburke.document.core.Row
import com.craigburke.document.core.Font
import com.craigburke.document.core.Image
import com.craigburke.document.core.LineBreak
import com.craigburke.document.core.PageBreak
import com.craigburke.document.core.TextBlock
import com.craigburke.document.core.Table
import com.craigburke.document.core.Text
import groovy.transform.InheritConstructors
import com.craigburke.document.core.builder.DocumentBuilder
import com.craigburke.document.core.Document
/**
* Builder for Word documents
* @author Craig Burke
*/
@InheritConstructors
class WordDocumentBuilder extends DocumentBuilder {
private static final String PAGE_NUMBER_PLACEHOLDER = '##pageNumber##'
private static final Map RUN_TEXT_OPTIONS = ['xml:space':'preserve']
void initializeDocument(Document document, OutputStream out) {
document.element = new WordDocument(out)
}
WordDocument getWordDocument() {
document.element
}
void writeDocument(Document document, OutputStream out) {
def headerFooterOptions = new HeaderFooterOptions(
pageNumber:PAGE_NUMBER_PLACEHOLDER,
pageCount:document.pageCount,
dateGenerated:new Date()
)
def header = renderHeader(headerFooterOptions)
def footer = renderFooter(headerFooterOptions)
renderState = RenderState.PAGE
wordDocument.generateDocument { builder ->
w.document {
w.body {
document.children.each { child ->
if (child instanceof TextBlock) {
addParagraph(builder, child)
}
else if (child instanceof PageBreak) {
addPageBreak(builder)
}
else if (child instanceof Table) {
addTable(builder, child)
}
}
w.sectPr {
w.pgSz( 'w:h':pointToTwip(document.height),
'w:w':pointToTwip(document.width),
'w:orient':'portrait'
)
w.pgMar('w:bottom':pointToTwip(document.margin.bottom),
'w:top':pointToTwip(document.margin.top),
'w:right':pointToTwip(document.margin.right),
'w:left':pointToTwip(document.margin.left),
'w:footer':pointToTwip(footer ? footer.node.margin.bottom : 0),
'w:header':pointToTwip(header ? header.node.margin.top : 0)
)
if (header) {
w.headerReference('r:id':header.id, 'w:type':'default')
}
if (footer) {
w.footerReference('r:id':footer.id, 'w:type':'default')
}
}
}
}
}
document.element.write()
}
def renderHeader(HeaderFooterOptions options) {
def header = [:]
if (document.header) {
renderState = RenderState.HEADER
header.node = document.header(options)
header.id = wordDocument.generateHeader { builder ->
w.hdr {
renderHeaderFooterNode(builder, header.node as BlockNode)
}
}
}
header
}
def renderFooter(HeaderFooterOptions options) {
def footer = [:]
if (document.footer) {
renderState = RenderState.FOOTER
footer.node = document.footer(options)
footer.id = wordDocument.generateFooter { builder ->
w.hdr {
renderHeaderFooterNode(builder, footer.node as BlockNode)
}
}
}
footer
}
void renderHeaderFooterNode(builder, BlockNode node) {
if (node instanceof TextBlock) {
addParagraph(builder, node)
}
else {
addTable(builder, node)
}
}
void addPageBreak(builder) {
builder.w.p {
w.r {
w.br('w:type':'page')
}
}
}
int calculateSpacingAfter(BlockNode node) {
int totalSpacing
switch (renderState) {
case RenderState.PAGE:
totalSpacing = node.margin.bottom
def items = node.parent.children
int index = items.findIndexOf { it == node }
if (index != items.size() - 1) {
def nextSibling = items[index + 1]
if (nextSibling instanceof BlockNode) {
totalSpacing += nextSibling.margin.top
}
}
break
case RenderState.HEADER:
totalSpacing = node.margin.bottom
break
case RenderState.FOOTER:
totalSpacing = 0
}
pointToTwip(totalSpacing)
}
int calculatedSpacingBefore(BlockNode node) {
int totalSpacing
switch (renderState) {
case RenderState.PAGE:
totalSpacing = node.margin.top
def items = node.parent.children
int index = items.findIndexOf { it == node }
if (index > 0) {
def previousSibling = items[index - 1]
if (previousSibling instanceof Table) {
totalSpacing += previousSibling.margin.bottom
}
}
break
case RenderState.HEADER:
totalSpacing = 0
break
case RenderState.FOOTER:
totalSpacing = node.margin.top
break
}
pointToTwip(totalSpacing)
}
void addParagraph(builder, TextBlock paragraph) {
builder.w.p {
w.pPr {
String lineRule = (paragraph.lineSpacing) ? 'exact' : 'auto'
BigDecimal lineValue = (paragraph.lineSpacing) ?
pointToTwip(paragraph.lineSpacing) : (paragraph.lineSpacingMultiplier * 240)
w.spacing(
'w:before':calculatedSpacingBefore(paragraph),
'w:after':calculateSpacingAfter(paragraph),
'w:lineRule':lineRule,
'w:line':lineValue
)
w.ind(
'w:start':pointToTwip(paragraph.margin.left),
'w:left':pointToTwip(paragraph.margin.left),
'w:right':pointToTwip(paragraph.margin.right),
'w:end':pointToTwip(paragraph.margin.right)
)
w.jc('w:val':paragraph.align.value)
}
paragraph.children.each { child ->
switch (child.getClass()) {
case Text:
addTextRun(builder, child.font as Font, child.value as String)
break
case Image:
addImageRun(builder, child)
break
case LineBreak:
addLineBreakRun(builder)
break
}
}
}
}
void addLineBreakRun(builder) {
builder.w.r {
w.br()
}
}
DocumentPartType getCurrentDocumentPart() {
switch (renderState) {
case RenderState.PAGE:
DocumentPartType.DOCUMENT
break
case RenderState.HEADER:
DocumentPartType.HEADER
break
case RenderState.FOOTER:
DocumentPartType.FOOTER
break
}
}
void addImageRun(builder, Image image) {
String blipId = document.element.addImage(image.name, image.data, currentDocumentPart)
int widthInEmu = pointToEmu(image.width)
int heightInEmu = pointToEmu(image.height)
String imageDescription = "Image: ${image.name}"
builder.w.r {
w.drawing {
wp.inline(distT:0, distR:0, distB:0, distL:0) {
wp.extent(cx:widthInEmu, cy:heightInEmu)
wp.docPr(id:1, name:imageDescription, descr:image.name)
a.graphic {
a.graphicData(uri:'http://schemas.openxmlformats.org/drawingml/2006/picture') {
pic.pic {
pic.nvPicPr {
pic.cNvPr(id:0, name:imageDescription, descr:image.name)
pic.cNvPicPr {
a.picLocks(noChangeAspect:'true')
}
}
pic.blipFill {
a.blip('r:embed':blipId)
a.stretch {
a.fillRect()
}
}
pic.spPr {
a.xfrm {
a.off(x:0, y:0)
a.ext(cx:widthInEmu, cy:heightInEmu)
}
a.prstGeom(prst:'rect') {
a.avLst()
}
}
}
}
}
}
}
}
}
void addTable(builder, Table table) {
builder.w.tbl {
w.tblPr {
w.tblW('w:w':pointToTwip(table.width))
w.tblBorders {
def properties = ['top', 'right', 'bottom', 'left', 'insideH', 'insideV']
properties.each { String property ->
w."${property}"(
'w:sz':pointToEigthPoint(table.border.size),
'w:color':table.border.color.hex,
'w:val':(table.border.size == 0 ? 'none' : 'single')
)
}
}
}
table.children.each { Row row ->
w.tr {
row.children.each { Cell column ->
if (column.rowsSpanned == 0) {
addColumn(builder, column)
}
else {
addMergeColumn(builder)
}
column.rowsSpanned++
}
}
}
}
}
void addColumn(builder, Cell column) {
Table table = column.parent.parent
builder.w.tc {
w.tcPr {
w.vAlign('w:val':'center')
w.tcW('w:w':pointToTwip(column.width - (table.padding * 2)))
w.tcMar {
w.top('w:w':pointToTwip(table.padding))
w.bottom('w:w':pointToTwip(table.padding))
w.left('w:w':pointToTwip(table.padding))
w.right('w:w':pointToTwip(table.padding))
}
if (column.background) {
w.shd('w:val':'clear', 'w:color':'auto', 'w:fill':column.background.hex)
}
if (column.colspan > 1) {
w.gridSpan('w:val':column.colspan)
}
if (column.rowspan > 1) {
w.vMerge('w:val':'restart')
}
}
column.children.each {
if (it instanceof TextBlock) {
addParagraph(builder, it)
}
else {
addTable(builder, it)
w.p()
}
}
if (!column.children) {
w.p()
}
}
}
void addMergeColumn(builder) {
builder.w.tc {
w.tcPr {
w.vMerge()
}
w.p()
}
}
void addTextRun(builder, Font font, String text) {
builder.w.r {
w.rPr {
w.rFonts('w:ascii':font.family)
if (font.bold) {
w.b()
}
if (font.italic) {
w.i()
}
w.color('w:val':font.color.hex)
w.sz('w:val':pointToHalfPoint(font.size))
}
if (renderState == RenderState.PAGE) {
w.t(text, RUN_TEXT_OPTIONS)
}
else {
parseHeaderFooterText(builder, text)
}
}
}
void parseHeaderFooterText(builder, String text) {
def textParts = text.split(PAGE_NUMBER_PLACEHOLDER)
textParts.eachWithIndex { String part, int index ->
if (index != 0) {
builder.w.pgNum()
}
builder.w.t(part, RUN_TEXT_OPTIONS)
}
}
}