org.apache.poi.xwpf.converter.pdf.internal.PdfMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of apache-poi-ooxml Show documentation
Show all versions of apache-poi-ooxml Show documentation
The Apache Commons Codec package contains simple encoder and decoders for
various formats such as Base64 and Hexadecimal. In addition to these
widely used encoders and decoders, the codec package also maintains a
collection of phonetic encoding utilities.
/**
* Copyright (C) 2011-2015 The XDocReport Team
*
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.apache.poi.xwpf.converter.pdf.internal;
import static org.apache.poi.xwpf.converter.core.utils.DxaUtil.emu2points;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.List;
import java.util.logging.Logger;
import org.apache.poi.xwpf.converter.core.BorderSide;
import org.apache.poi.xwpf.converter.core.Color;
import org.apache.poi.xwpf.converter.core.ListItemContext;
import org.apache.poi.xwpf.converter.core.ParagraphLineSpacing;
import org.apache.poi.xwpf.converter.core.TableCellBorder;
import org.apache.poi.xwpf.converter.core.TableHeight;
import org.apache.poi.xwpf.converter.core.TableWidth;
import org.apache.poi.xwpf.converter.core.XWPFDocumentVisitor;
import org.apache.poi.xwpf.converter.core.styles.paragraph.ParagraphIndentationHangingValueProvider;
import org.apache.poi.xwpf.converter.core.styles.paragraph.ParagraphIndentationLeftValueProvider;
import org.apache.poi.xwpf.converter.core.utils.DxaUtil;
import org.apache.poi.xwpf.converter.core.utils.StringUtils;
import org.apache.poi.xwpf.converter.pdf.PdfOptions;
import org.apache.poi.xwpf.converter.pdf.internal.elements.StylableAnchor;
import org.apache.poi.xwpf.converter.pdf.internal.elements.StylableDocument;
import org.apache.poi.xwpf.converter.pdf.internal.elements.StylableHeaderFooter;
import org.apache.poi.xwpf.converter.pdf.internal.elements.StylableMasterPage;
import org.apache.poi.xwpf.converter.pdf.internal.elements.StylableParagraph;
import org.apache.poi.xwpf.converter.pdf.internal.elements.StylableTable;
import org.apache.poi.xwpf.converter.pdf.internal.elements.StylableTableCell;
import org.apache.poi.xwpf.usermodel.IBodyElement;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.UnderlinePatterns;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFFooter;
import org.apache.poi.xwpf.usermodel.XWPFHeader;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFPictureData;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.STRelFromH;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.STRelFromV;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.STWrapText;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBookmark;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBorder;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTLvl;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPTab;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTabStop;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTabs;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTextDirection;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTabJc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTabTlc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTextDirection;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalJc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalJc.Enum;
import com.lowagie.text.Chunk;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.Image;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.draw.DottedLineSeparator;
import com.lowagie.text.pdf.draw.LineSeparator;
import com.lowagie.text.pdf.draw.VerticalPositionMark;
import fr.opensagres.xdocreport.itext.extension.ExtendedChunk;
import fr.opensagres.xdocreport.itext.extension.ExtendedImage;
import fr.opensagres.xdocreport.itext.extension.ExtendedParagraph;
import fr.opensagres.xdocreport.itext.extension.ExtendedPdfPCell;
import fr.opensagres.xdocreport.itext.extension.ExtendedPdfPTable;
import fr.opensagres.xdocreport.itext.extension.IITextContainer;
import fr.opensagres.xdocreport.itext.extension.font.FontGroup;
public class PdfMapper extends
XWPFDocumentVisitor {
private static final String TAB = "\t";
/**
* Logger for this class
*/
private static final Logger LOGGER = Logger.getLogger(PdfMapper.class
.getName());
private final OutputStream out;
// Instance of PDF document
private StylableDocument pdfDocument;
private Font currentRunFontAscii;
private Font currentRunFontEastAsia;
private Font currentRunFontHAnsi;
private UnderlinePatterns currentRunUnderlinePatterns;
private Color currentRunBackgroundColor;
private Float currentRunX;
private Float currentPageWidth;
private StylableHeaderFooter pdfHeader;
private StylableHeaderFooter pdfFooter;
private Integer expectedPageCount;
public PdfMapper(XWPFDocument document, OutputStream out,
PdfOptions options, Integer expectedPageCount) throws Exception {
super(document, options != null ? options : PdfOptions.getDefault());
this.out = out;
this.expectedPageCount = expectedPageCount;
}
// ------------------------- Document
@Override
protected IITextContainer startVisitDocument() throws Exception {
// Create instance of PDF document
this.pdfDocument = new StylableDocument(out, options.getConfiguration());
this.pdfDocument.setMasterPageManager(getMasterPageManager());
return pdfDocument;
}
@Override
protected void endVisitDocument() throws Exception {
pdfDocument.close();
out.close();
}
// ------------------------- Header/Footer
@Override
protected void visitHeader(XWPFHeader header, CTHdrFtrRef headerRef,
CTSectPr sectPr, StylableMasterPage masterPage) throws Exception {
BigInteger headerY = sectPr.getPgMar() != null ? sectPr.getPgMar()
.getHeader() : null;
this.currentPageWidth = sectPr.getPgMar() != null ? DxaUtil
.dxa2points(sectPr.getPgSz().getW()) : null;
this.pdfHeader = new StylableHeaderFooter(pdfDocument, headerY, true);
// List bodyElements = header.getBodyElements();
List bodyElements = super.getBodyElements(header);
StylableTableCell tableCell = getHeaderFooterTableCell(pdfHeader,
bodyElements);
visitBodyElements(bodyElements, tableCell);
masterPage.setHeader(pdfHeader);
this.currentPageWidth = null;
this.pdfHeader = null;
}
@Override
protected void visitFooter(XWPFFooter footer, CTHdrFtrRef footerRef,
CTSectPr sectPr, StylableMasterPage masterPage) throws Exception {
BigInteger footerY = sectPr.getPgMar() != null ? sectPr.getPgMar()
.getFooter() : null;
this.currentPageWidth = sectPr.getPgMar() != null ? DxaUtil
.dxa2points(sectPr.getPgSz().getW()) : null;
this.pdfFooter = new StylableHeaderFooter(pdfDocument, footerY, false);
List bodyElements = super.getBodyElements(footer);
StylableTableCell tableCell = getHeaderFooterTableCell(pdfFooter,
bodyElements);
visitBodyElements(bodyElements, tableCell);
masterPage.setFooter(pdfFooter);
this.currentPageWidth = null;
this.pdfFooter = null;
}
private StylableTableCell getHeaderFooterTableCell(
StylableHeaderFooter pdfHeaderFooter,
List bodyElements) throws DocumentException {
return pdfHeaderFooter.getTableCell();
}
public void setActiveMasterPage(StylableMasterPage masterPage) {
pdfDocument.setActiveMasterPage(masterPage);
}
public StylableMasterPage createMasterPage(CTSectPr sectPr) {
return new StylableMasterPage(sectPr);
}
// ------------------------- Paragraph
@Override
protected IITextContainer startVisitParagraph(XWPFParagraph docxParagraph,
ListItemContext itemContext, IITextContainer pdfParentContainer)
throws Exception {
this.currentRunX = null;
// create PDF paragraph
StylableParagraph pdfParagraph = pdfDocument
.createParagraph(pdfParentContainer);
// indentation left
Float indentationLeft = stylesDocument
.getIndentationLeft(docxParagraph);
if (indentationLeft != null) {
pdfParagraph.setIndentationLeft(indentationLeft);
}
// indentation right
Float indentationRight = stylesDocument
.getIndentationRight(docxParagraph);
if (indentationRight != null) {
pdfParagraph.setIndentationRight(indentationRight);
}
// indentation first line
Float indentationFirstLine = stylesDocument
.getIndentationFirstLine(docxParagraph);
if (indentationFirstLine != null) {
pdfParagraph.setFirstLineIndent(indentationFirstLine);
}
// indentation hanging (remove first line)
Float indentationHanging = stylesDocument
.getIndentationHanging(docxParagraph);
if (indentationHanging != null) {
pdfParagraph.setFirstLineIndent(-indentationHanging);
}
// // spacing before
Float spacingBefore = stylesDocument.getSpacingBefore(docxParagraph);
if (spacingBefore != null) {
pdfParagraph.setSpacingBefore(spacingBefore);
}
// spacing after
Float spacingAfter = stylesDocument.getSpacingAfter(docxParagraph);
if (spacingAfter != null) {
pdfParagraph.setSpacingAfter(spacingAfter);
}
ParagraphLineSpacing lineSpacing = stylesDocument
.getParagraphSpacing(docxParagraph);
if (lineSpacing != null) {
if (lineSpacing.getLeading() != null
&& lineSpacing.getMultipleLeading() != null) {
pdfParagraph.setLeading(lineSpacing.getLeading(),
lineSpacing.getMultipleLeading());
} else {
if (lineSpacing.getLeading() != null) {
pdfParagraph.setLeading(lineSpacing.getLeading());
}
if (lineSpacing.getMultipleLeading() != null) {
pdfParagraph.setMultipliedLeading(lineSpacing
.getMultipleLeading());
}
}
}
// text-align
ParagraphAlignment alignment = stylesDocument
.getParagraphAlignment(docxParagraph);
if (alignment != null) {
switch (alignment) {
case LEFT:
pdfParagraph.setAlignment(Element.ALIGN_LEFT);
break;
case RIGHT:
pdfParagraph.setAlignment(Element.ALIGN_RIGHT);
break;
case CENTER:
pdfParagraph.setAlignment(Element.ALIGN_CENTER);
break;
case BOTH:
pdfParagraph.setAlignment(Element.ALIGN_JUSTIFIED);
break;
default:
break;
}
}
// background-color
Color backgroundColor = stylesDocument
.getBackgroundColor(docxParagraph);
if (backgroundColor != null) {
pdfParagraph.setBackgroundColor(Converter
.toAwtColor(backgroundColor));
}
// border
CTBorder borderTop = stylesDocument.getBorderTop(docxParagraph);
pdfParagraph.setBorder(borderTop, Rectangle.TOP);
CTBorder borderBottom = stylesDocument.getBorderBottom(docxParagraph);
pdfParagraph.setBorder(borderBottom, Rectangle.BOTTOM);
CTBorder borderLeft = stylesDocument.getBorderLeft(docxParagraph);
pdfParagraph.setBorder(borderLeft, Rectangle.LEFT);
CTBorder borderRight = stylesDocument.getBorderRight(docxParagraph);
pdfParagraph.setBorder(borderRight, Rectangle.RIGHT);
if (itemContext != null) {
CTLvl lvl = itemContext.getLvl();
CTPPr lvlPPr = lvl.getPPr();
if (lvlPPr != null) {
if (ParagraphIndentationLeftValueProvider.INSTANCE
.getValue(docxParagraph.getCTP().getPPr()) == null) {
// search the indentation from the level properties only if
// paragraph has not override it
// see
// https://code.google.com/p/xdocreport/issues/detail?id=239
Float indLeft = ParagraphIndentationLeftValueProvider.INSTANCE
.getValue(lvlPPr);
if (indLeft != null) {
pdfParagraph.setIndentationLeft(indLeft);
}
}
if (ParagraphIndentationHangingValueProvider.INSTANCE
.getValue(docxParagraph.getCTP().getPPr()) == null) {
// search the hanging from the level properties only if
// paragraph has not override it
// see
// https://code.google.com/p/xdocreport/issues/detail?id=239
Float hanging = stylesDocument
.getIndentationHanging(lvlPPr);
if (hanging != null) {
pdfParagraph.setFirstLineIndent(-hanging);
}
}
}
CTRPr lvlRPr = lvl.getRPr();
if (lvlRPr != null) {
// Font family
String listItemFontFamily = stylesDocument
.getFontFamilyAscii(lvlRPr);
// Get font size
Float listItemFontSize = stylesDocument.getFontSize(lvlRPr);
// Get font style
int listItemFontStyle = Font.NORMAL;
Boolean bold = stylesDocument.getFontStyleBold(lvlRPr);
if (bold != null && bold) {
listItemFontStyle |= Font.BOLD;
}
Boolean italic = stylesDocument.getFontStyleItalic(lvlRPr);
if (italic != null && italic) {
listItemFontStyle |= Font.ITALIC;
}
Boolean strike = stylesDocument.getFontStyleStrike(lvlRPr);
if (strike != null && strike) {
listItemFontStyle |= Font.STRIKETHRU;
}
// Font color
Color listItemFontColor = stylesDocument.getFontColor(lvlRPr);
pdfParagraph.setListItemFontFamily(listItemFontFamily);
pdfParagraph.setListItemFontSize(listItemFontSize);
pdfParagraph.setListItemFontStyle(listItemFontStyle);
pdfParagraph.setListItemFontColor(Converter
.toAwtColor(listItemFontColor));
}
pdfParagraph.setListItemText(itemContext.getText());
}
return pdfParagraph;
}
@Override
protected void endVisitParagraph(XWPFParagraph docxParagraph,
IITextContainer pdfParentContainer,
IITextContainer pdfParagraphContainer) throws Exception {
// add the iText paragraph in the current parent container.
ExtendedParagraph pdfParagraph = (ExtendedParagraph) pdfParagraphContainer;
pdfParentContainer.addElement(pdfParagraph.getElement());
this.currentRunX = null;
}
// ------------------------- Run
@Override
protected void visitEmptyRun(IITextContainer pdfParagraphContainer)
throws Exception {
StylableParagraph paragraph = (StylableParagraph) pdfParagraphContainer;
IITextContainer parent = paragraph.getParent();
if (parent instanceof StylableTableCell) {
StylableTableCell cell = (StylableTableCell) parent;
if (cell.getRotation() > 0) {
// Run paragraph belongs to Cell which has rotation, ignore the
// empty run.
return;
}
}
// Add new PDF line
pdfParagraphContainer.addElement(Chunk.NEWLINE);
}
@Override
protected void visitRun(XWPFRun docxRun, boolean pageNumber, String url,
IITextContainer pdfParagraphContainer) throws Exception {
// Font family
String fontFamilyAscii = stylesDocument.getFontFamilyAscii(docxRun);
String fontFamilyEastAsia = stylesDocument
.getFontFamilyEastAsia(docxRun);
String fontFamilyHAnsi = stylesDocument.getFontFamilyHAnsi(docxRun);
// Get font size
Float fontSize = stylesDocument.getFontSize(docxRun);
if (fontSize == null) {
fontSize = -1f;
}
// Get font style
int fontStyle = Font.NORMAL;
Boolean bold = stylesDocument.getFontStyleBold(docxRun);
if (bold != null && bold) {
fontStyle |= Font.BOLD;
}
Boolean italic = stylesDocument.getFontStyleItalic(docxRun);
if (italic != null && italic) {
fontStyle |= Font.ITALIC;
}
Boolean strike = stylesDocument.getFontStyleStrike(docxRun);
if (strike != null && strike) {
fontStyle |= Font.STRIKETHRU;
}
// Font color
Color fontColor = stylesDocument.getFontColor(docxRun);
// Font
this.currentRunFontAscii = getFont(fontFamilyAscii, fontSize,
fontStyle, fontColor);
this.currentRunFontEastAsia = getFont(fontFamilyEastAsia, fontSize,
fontStyle, fontColor);
this.currentRunFontHAnsi = getFont(fontFamilyHAnsi, fontSize,
fontStyle, fontColor);
// Underline patterns
this.currentRunUnderlinePatterns = stylesDocument.getUnderline(docxRun);
// background color
this.currentRunBackgroundColor = stylesDocument
.getBackgroundColor(docxRun);
// highlight
if (currentRunBackgroundColor == null) {
this.currentRunBackgroundColor = stylesDocument
.getTextHighlighting(docxRun);
}
StylableParagraph pdfParagraph = (StylableParagraph) pdfParagraphContainer;
pdfParagraph.adjustMultipliedLeading(currentRunFontAscii);
// addd symbol list item chunk if needed.
String listItemText = pdfParagraph.getListItemText();
if (StringUtils.isNotEmpty(listItemText)) {
// FIXME: add some space after the list item
listItemText += " ";
String listItemFontFamily = pdfParagraph.getListItemFontFamily();
Float listItemFontSize = pdfParagraph.getListItemFontSize();
int listItemFontStyle = pdfParagraph.getListItemFontStyle();
java.awt.Color listItemFontColor = pdfParagraph
.getListItemFontColor();
Font listItemFont = options.getFontProvider().getFont(
listItemFontFamily != null ? listItemFontFamily
: fontFamilyAscii,
options.getFontEncoding(),
listItemFontSize != null ? listItemFontSize : fontSize,
listItemFontStyle != Font.NORMAL ? listItemFontStyle
: fontStyle,
listItemFontColor != null ? listItemFontColor : Converter
.toAwtColor(fontColor));
Chunk symbol = createTextChunk(listItemText, false, listItemFont,
currentRunUnderlinePatterns, currentRunBackgroundColor);
pdfParagraph.add(symbol);
pdfParagraph.setListItemText(null);
}
IITextContainer container = pdfParagraphContainer;
if (url != null) {
// URL is not null, generate a PDF hyperlink.
StylableAnchor pdfAnchor = new StylableAnchor();
pdfAnchor.setReference(url);
pdfAnchor.setITextContainer(container);
container = pdfAnchor;
}
super.visitRun(docxRun, pageNumber, url, container);
if (url != null) {
// URL is not null, add the PDF hyperlink in the PDF paragraph
pdfParagraphContainer.addElement((StylableAnchor) container);
}
this.currentRunFontAscii = null;
this.currentRunFontEastAsia = null;
this.currentRunFontHAnsi = null;
this.currentRunUnderlinePatterns = null;
this.currentRunBackgroundColor = null;
}
private Font getFont(String fontFamily, Float fontSize, int fontStyle,
Color fontColor) {
String fontToUse = stylesDocument.getFontNameToUse(fontFamily);
if (StringUtils.isNotEmpty(fontToUse)) {
return options.getFontProvider().getFont(fontToUse,
options.getFontEncoding(), fontSize, fontStyle,
Converter.toAwtColor(fontColor));
}
Font font = options.getFontProvider().getFont(fontFamily,
options.getFontEncoding(), fontSize, fontStyle,
Converter.toAwtColor(fontColor));
if (!isFontExists(font)) {
// font is not found
try {
List altNames = stylesDocument
.getFontsAltName(fontFamily);
if (altNames != null) {
// Loop for each alternative names font (from the
// fontTable.xml) to find the well font.
for (String altName : altNames) {
// check if the current font name is not the same that
// original (o avoid StackOverFlow : see
// https://code.google.com/p/xdocreport/issues/detail?id=393)
if (!fontFamily.equals(altName)) {
font = getFont(altName, fontSize, fontStyle,
fontColor);
if (isFontExists(font)) {
stylesDocument.setFontNameToUse(fontFamily,
altName);
return font;
}
}
}
}
} catch (Exception e) {
LOGGER.severe(e.getMessage());
}
}
return font;
}
/**
* Returns true if the iText font exists and false otherwise.
*
* @param font
* @return
*/
private boolean isFontExists(Font font) {
// FIXME : is it like this to test that font exists?
return font != null && font.getBaseFont() != null;
}
@Override
protected void visitText(CTText docxText, boolean pageNumber,
IITextContainer pdfParagraphContainer) throws Exception {
Font font = currentRunFontAscii;
Font fontAsian = currentRunFontEastAsia;
Font fontComplex = currentRunFontHAnsi;
createAndAddChunks(pdfParagraphContainer, docxText.getStringValue(),
currentRunUnderlinePatterns, currentRunBackgroundColor,
pageNumber, font, fontAsian, fontComplex);
}
private Chunk createTextChunk(String text, boolean pageNumber,
Font currentRunFont, UnderlinePatterns currentRunUnderlinePatterns,
Color currentRunBackgroundColor) {
// Chunk textChunk =
// pageNumber ? new ExtendedChunk( pdfDocument, true, currentRunFont ) :
// new Chunk( text, currentRunFont );
Chunk textChunk = null;
if (processingTotalPageCountField && expectedPageCount != null) {
textChunk = new Chunk(String.valueOf(expectedPageCount),
currentRunFont);
} else {
textChunk = pageNumber ? new ExtendedChunk(pdfDocument, true,
currentRunFont) : new Chunk(text, currentRunFont);
}
if (currentRunUnderlinePatterns != null) {
// underlined
boolean singleUnderlined = false;
switch (currentRunUnderlinePatterns) {
case SINGLE:
singleUnderlined = true;
break;
default:
break;
}
if (singleUnderlined) {
textChunk.setUnderline(1, -2);
}
}
// background color
if (currentRunBackgroundColor != null) {
textChunk.setBackground(Converter
.toAwtColor(currentRunBackgroundColor));
}
if (currentRunX != null) {
this.currentRunX += textChunk.getWidthPoint();
}
return textChunk;
}
private void createAndAddChunks(IITextContainer parent, String textContent,
UnderlinePatterns underlinePatterns, Color backgroundColor,
boolean pageNumber, Font font, Font fontAsian, Font fontComplex) {
StringBuilder sbuf = new StringBuilder();
FontGroup currentGroup = FontGroup.WESTERN;
for (int i = 0; i < textContent.length(); i++) {
char ch = textContent.charAt(i);
FontGroup group = FontGroup.getUnicodeGroup(ch, font, fontAsian,
fontComplex);
if (sbuf.length() == 0 || currentGroup.equals(group)) {
// continue current chunk
sbuf.append(ch);
} else {
// end chunk
Font chunkFont = getFont(font, fontAsian, fontComplex,
currentGroup);
Chunk chunk = createTextChunk(sbuf.toString(), pageNumber,
chunkFont, underlinePatterns, backgroundColor);
parent.addElement(chunk);
// start new chunk
sbuf.setLength(0);
sbuf.append(ch);
}
currentGroup = group;
}
// end chunk
Font chunkFont = getFont(font, fontAsian, fontComplex, currentGroup);
Chunk chunk = createTextChunk(sbuf.toString(), pageNumber, chunkFont,
underlinePatterns, backgroundColor);
parent.addElement(chunk);
}
private Font getFont(Font font, Font fontAsian, Font fontComplex,
FontGroup group) {
switch (group) {
case WESTERN:
return font;
case ASIAN:
return fontAsian;
case COMPLEX:
return fontComplex;
}
return font;
}
@Override
protected void visitTab(CTPTab tab, IITextContainer pdfParagraphContainer)
throws Exception {
// TODO manage this case.
//
// if ( tab == null )
// {
// float defaultTabStop = stylesDocument.getDefaultTabStop();
// Chunk pdfTab = new Chunk( new VerticalPositionMark(), defaultTabStop,
// false );
// pdfParagraphContainer.addElement( pdfTab );
// }
// else
// {
// Chunk pdfTab = new Chunk( new VerticalPositionMark() );
// pdfParagraphContainer.addElement( pdfTab );
// }
}
@Override
protected void visitTabs(CTTabs tabs, IITextContainer pdfParagraphContainer)
throws Exception {
if (currentRunX == null) {
Paragraph paragraph = null;
if (pdfParagraphContainer instanceof Paragraph) {
paragraph = (Paragraph) pdfParagraphContainer;
} else {
paragraph = (Paragraph) ((StylableAnchor) pdfParagraphContainer)
.getITextContainer();
}
currentRunX = paragraph.getFirstLineIndent();
List chunks = paragraph.getChunks();
for (Chunk chunk : chunks) {
currentRunX += chunk.getWidthPoint();
}
} else {
if (currentRunX >= pdfDocument.getPageWidth()) {
currentRunX = 0f;
}
}
Float tabPosition = null;
STTabTlc.Enum tabLeader = null;
STTabJc.Enum tabVal = null;
boolean useDefaultTabStop = false;
if (tabs != null) {
List tabList = tabs.getTabList();
CTTabStop tabStop = getTabStop(tabList);
if (tabStop != null) {
float lastX = DxaUtil.dxa2points(tabStop.getPos().floatValue());
if (lastX > currentRunX) {
tabPosition = lastX;
tabLeader = tabStop.getLeader();
tabVal = tabStop.getVal();
} else {
useDefaultTabStop = true;
}
}
}
if (tabs == null || useDefaultTabStop) {
// default tab
float defaultTabStop = stylesDocument.getDefaultTabStop();
float pageWidth = pdfDocument.getPageWidth();
int nbInterval = (int) (pageWidth / defaultTabStop);
Float lastX = getTabStopPosition(currentRunX, defaultTabStop,
nbInterval);
if (lastX != null) {
tabPosition = lastX;
}
}
if (tabPosition != null) {
currentRunX = tabPosition;
// tab leader : Specifies the character which shall be used to fill
// in the space created by a tab
// which
// ends
// at this custom tab stop. This character shall be repeated as
// required to completely fill the
// tab spacing generated by the tab character.
VerticalPositionMark mark = createVerticalPositionMark(tabLeader);
Chunk pdfTab = null;
if (STTabJc.RIGHT.equals(tabVal)) {
pdfTab = new Chunk(mark);
} else {
pdfTab = new Chunk(mark, currentRunX);
}
pdfParagraphContainer.addElement(pdfTab);
}
}
private Float getTabStopPosition(float currentPosition, float interval,
int nbInterval) {
Float nextPosition = null;
float newPosition = 0f;
for (int i = 1; i < nbInterval; i++) {
newPosition = interval * i;
if (currentPosition < newPosition) {
nextPosition = newPosition;
break;
}
}
return nextPosition;
}
private VerticalPositionMark createVerticalPositionMark(
org.openxmlformats.schemas.wordprocessingml.x2006.main.STTabTlc.Enum leader) {
if (leader != null) {
if (leader == STTabTlc.DOT) {
return new DottedLineSeparator();
} else if (leader == STTabTlc.UNDERSCORE) {
return new LineSeparator();
}
}
return new VerticalPositionMark();
}
private CTTabStop getTabStop(List tabList) {
if (tabList.size() == 1) {
CTTabStop tabStop = tabList.get(0);
if (isClearTab(tabStop)) {
return null;
}
return tabStop;
}
CTTabStop selectedTabStop = null;
for (CTTabStop tabStop : tabList) {
if (isClearTab(tabStop)) {
continue;
}
if (canApplyTabStop(tabStop)) {
return tabStop;
}
//
// if ( selectedTabStop == null )
// {
// selectedTabStop = tabStop;
// }
// else
// {
// if ( tabStop.getPos().floatValue() >
// selectedTabStop.getPos().floatValue() )
// {
// selectedTabStop = tabStop;
// }
// }
}
// TODO : retrieve the well tab stop according the current width of the
// line.
return null;
}
private boolean canApplyTabStop(CTTabStop tabStop) {
if (tabStop.getVal().equals(STTabJc.LEFT)) {
if (currentRunX < DxaUtil.dxa2points(tabStop.getPos().floatValue())) {
return true;
}
} else if (tabStop.getVal().equals(STTabJc.RIGHT)) {
if (isWordDocumentPartParsing()) {
if (pdfDocument.getWidthLimit()
- (currentRunX + DxaUtil.dxa2points(tabStop.getPos()
.floatValue())) <= 0) {
return true;
}
} else {
if (currentPageWidth == null) {
return true;
}
if (currentPageWidth.floatValue()
- (currentRunX + DxaUtil.dxa2points(tabStop.getPos()
.floatValue())) <= 0) {
return true;
}
}
} else if (tabStop.getVal().equals(STTabJc.CENTER)) {
}
return false;
}
private boolean isClearTab(CTTabStop tabStop) {
org.openxmlformats.schemas.wordprocessingml.x2006.main.STTabJc.Enum tabVal = tabStop
.getVal();
if (tabVal != null) {
if (tabVal.equals(STTabJc.CLEAR)) {
// Specifies that the current tab stop is cleared and shall be
// removed and ignored when processing
// the contents of this document
return true;
}
}
return false;
}
@Override
protected void addNewLine(CTBr br, IITextContainer pdfParagraphContainer)
throws Exception {
pdfParagraphContainer.addElement(Chunk.NEWLINE);
}
@Override
protected void visitBR(CTBr br, IITextContainer paragraphContainer)
throws Exception {
currentRunX = 0f;
super.visitBR(br, paragraphContainer);
}
@Override
protected void pageBreak() throws Exception {
pdfDocument.pageBreak();
}
@Override
protected void visitBookmark(CTBookmark bookmark, XWPFParagraph paragraph,
IITextContainer paragraphContainer) throws Exception {
// destination for a local anchor
// chunk with empty text does not work as local anchor
// so we create chunk with invisible but not empty text content
// if bookmark is the last chunk in a paragraph something must be added
// after or it does not work
Chunk chunk = new Chunk(TAB);
chunk.setLocalDestination(bookmark.getName());
paragraphContainer.addElement(chunk);
}
// ----------------- Table
@Override
protected IITextContainer startVisitTable(XWPFTable table,
float[] colWidths, IITextContainer pdfParentContainer)
throws Exception {
StylableTable pdfPTable = createPDFTable(table, colWidths,
pdfParentContainer);
return pdfPTable;
}
private StylableTable createPDFTable(XWPFTable table, float[] colWidths,
IITextContainer pdfParentContainer) throws DocumentException {
// 2) Compute tableWith
TableWidth tableWidth = stylesDocument.getTableWidth(table);
StylableTable pdfPTable = pdfDocument.createTable(pdfParentContainer,
colWidths.length);
pdfPTable.setTotalWidth(colWidths);
if (tableWidth != null && tableWidth.width > 0) {
if (tableWidth.percentUnit) {
pdfPTable.setWidthPercentage(tableWidth.width);
} else {
pdfPTable.setTotalWidth(tableWidth.width);
}
}
pdfPTable.setLockedWidth(true);
// Table alignment
ParagraphAlignment alignment = stylesDocument.getTableAlignment(table);
if (alignment != null) {
switch (alignment) {
case LEFT:
pdfPTable.setHorizontalAlignment(Element.ALIGN_LEFT);
break;
case RIGHT:
pdfPTable.setHorizontalAlignment(Element.ALIGN_RIGHT);
break;
case CENTER:
pdfPTable.setHorizontalAlignment(Element.ALIGN_CENTER);
break;
case BOTH:
pdfPTable.setHorizontalAlignment(Element.ALIGN_JUSTIFIED);
break;
default:
break;
}
}
// Table indentation
Float indentation = stylesDocument.getTableIndentation(table);
if (indentation != null) {
pdfPTable.setPaddingLeft(indentation);
}
return pdfPTable;
}
@Override
protected void endVisitTable(XWPFTable table,
IITextContainer pdfParentContainer,
IITextContainer pdfTableContainer) throws Exception {
pdfParentContainer.addElement(((ExtendedPdfPTable) pdfTableContainer)
.getElement());
}
// ------------------------- Table Row
@Override
protected void startVisitTableRow(XWPFTableRow row,
IITextContainer tableContainer, int rowIndex, boolean headerRow)
throws Exception {
if (headerRow) {
PdfPTable table = (PdfPTable) tableContainer;
table.setHeaderRows(table.getHeaderRows() + 1);
}
super.startVisitTableRow(row, tableContainer, rowIndex, headerRow);
}
// ------------------------- Table Cell
@Override
protected IITextContainer startVisitTableCell(final XWPFTableCell cell,
IITextContainer pdfTableContainer, boolean firstRow,
boolean lastRow, boolean firstCol, boolean lastCol,
List vMergeCells) throws Exception {
XWPFTableRow row = cell.getTableRow();
XWPFTable table = row.getTable();
// 1) store table cell info
stylesDocument.getTableInfo(table).addCellInfo(cell, firstRow, lastRow,
firstCol, lastCol);
// 2) create PDF cell
StylableTable pdfPTable = (StylableTable) pdfTableContainer;
StylableTableCell pdfPCell = pdfDocument.createTableCell(pdfPTable);
// pdfPCell.setUseAscender( true );
// pdfPCell.setUseDescender( true );
XWPFTableCell lastVMergedCell = null;
if (vMergeCells != null) {
pdfPCell.setRowspan(vMergeCells.size());
lastVMergedCell = vMergeCells.get(vMergeCells.size() - 1);
stylesDocument.getTableInfo(table).addCellInfo(lastVMergedCell,
false, lastRow, firstCol, lastCol);
}
// border-top
TableCellBorder borderTop = stylesDocument
.getTableCellBorderWithConflicts(cell, BorderSide.TOP);
if (borderTop != null) {
boolean borderTopInside = stylesDocument.isBorderInside(cell,
BorderSide.TOP);
if (borderTopInside) {
// Manage conflict border with the adjacent border bottom
}
}
pdfPCell.setBorderTop(borderTop, false);
// border-bottom
XWPFTableCell theCell = lastVMergedCell != null ? lastVMergedCell
: cell;
TableCellBorder borderBottom = stylesDocument
.getTableCellBorderWithConflicts(theCell, BorderSide.BOTTOM);
pdfPCell.setBorderBottom(borderBottom,
stylesDocument.isBorderInside(theCell, BorderSide.BOTTOM));
// border-left
TableCellBorder borderLeft = stylesDocument
.getTableCellBorderWithConflicts(cell, BorderSide.LEFT);
pdfPCell.setBorderLeft(borderLeft,
stylesDocument.isBorderInside(cell, BorderSide.LEFT));
// border-right
TableCellBorder borderRight = stylesDocument
.getTableCellBorderWithConflicts(cell, BorderSide.RIGHT);
pdfPCell.setBorderRight(borderRight,
stylesDocument.isBorderInside(cell, BorderSide.RIGHT));
// Text direction 0) {
pdfPCell.setPaddingBottom(marginBottom);
}
Float marginLeft = stylesDocument.getTableCellMarginLeft(cell);
if (marginLeft == null) {
marginLeft = stylesDocument.getTableRowMarginLeft(row);
if (marginLeft == null) {
marginLeft = stylesDocument.getTableMarginLeft(table);
}
}
if (marginLeft != null) {
pdfPCell.setPaddingLeft(marginLeft);
}
Float marginRight = stylesDocument.getTableCellMarginRight(cell);
if (marginRight == null) {
marginRight = stylesDocument.getTableRowMarginRight(row);
if (marginRight == null) {
marginRight = stylesDocument.getTableMarginRight(table);
}
}
if (marginRight != null) {
pdfPCell.setPaddingRight(marginRight);
}
// Row height
TableHeight tableHeight = stylesDocument.getTableRowHeight(row);
if (tableHeight != null) {
if (tableHeight.minimum) {
pdfPCell.setMinimumHeight(tableHeight.height);
} else {
pdfPCell.setFixedHeight(tableHeight.height);
}
}
// No wrap
Boolean noWrap = stylesDocument.getTableCellNoWrap(cell);
if (noWrap != null) {
pdfPCell.setNoWrap(noWrap);
}
return pdfPCell;
}
@Override
protected void endVisitTableCell(XWPFTableCell cell,
IITextContainer tableContainer, IITextContainer tableCellContainer) {
ExtendedPdfPTable pdfPTable = (ExtendedPdfPTable) tableContainer;
ExtendedPdfPCell pdfPCell = (ExtendedPdfPCell) tableCellContainer;
pdfPTable.addCell(pdfPCell);
}
@Override
protected void visitPicture(
CTPicture picture,
Float offsetX,
org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.STRelFromH.Enum relativeFromH,
Float offsetY,
org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.STRelFromV.Enum relativeFromV,
STWrapText.Enum wrapText, IITextContainer pdfParentContainer)
throws Exception {
CTPositiveSize2D ext = picture.getSpPr().getXfrm().getExt();
long x = ext.getCx();
long y = ext.getCy();
XWPFPictureData pictureData = super.getPictureData(picture);
if (pictureData != null) {
try {
Image img = Image.getInstance(pictureData.getData());
img.scaleAbsolute(emu2points(x), emu2points(y));
IITextContainer parentOfParentContainer = pdfParentContainer
.getITextContainer();
if (parentOfParentContainer != null
&& parentOfParentContainer instanceof PdfPCell) {
parentOfParentContainer.addElement(img);
} else {
float chunkOffsetX = 0;
if (offsetX != null) {
if (STRelFromH.CHARACTER.equals(relativeFromH)) {
chunkOffsetX = offsetX;
} else if (STRelFromH.COLUMN.equals(relativeFromH)) {
chunkOffsetX = offsetX;
} else if (STRelFromH.INSIDE_MARGIN
.equals(relativeFromH)) {
chunkOffsetX = offsetX;
} else if (STRelFromH.LEFT_MARGIN.equals(relativeFromH)) {
chunkOffsetX = offsetX;
} else if (STRelFromH.MARGIN.equals(relativeFromH)) {
chunkOffsetX = pdfDocument.left() + offsetX;
} else if (STRelFromH.OUTSIDE_MARGIN
.equals(relativeFromH)) {
chunkOffsetX = offsetX;
} else if (STRelFromH.PAGE.equals(relativeFromH)) {
chunkOffsetX = offsetX - pdfDocument.left();
}
}
float chunkOffsetY = 0;
boolean useExtendedImage = false;
if (STRelFromV.PARAGRAPH.equals(relativeFromV)) {
useExtendedImage = true;
}
if (useExtendedImage) {
ExtendedImage extImg = new ExtendedImage(img, -offsetY);
if (STRelFromV.PARAGRAPH.equals(relativeFromV)) {
chunkOffsetY = -extImg.getScaledHeight();
}
Chunk chunk = new Chunk(extImg, chunkOffsetX,
chunkOffsetY, false);
pdfParentContainer.addElement(chunk);
}
/*
* float chunkOffsetY = 0; if ( wrapText != null ) {
* chunkOffsetY = -img.getScaledHeight(); } boolean
* useExtendedImage = offsetY != null; // if (
* STRelFromV.PARAGRAPH.equals( relativeFromV ) ) // { //
* useExtendedImage = true; // } // if ( useExtendedImage )
* { float imgY = -offsetY; if ( pdfHeader != null ) { float
* headerY = pdfHeader.getY() != null ? pdfHeader.getY() :
* 0; imgY += - img.getScaledHeight() + headerY; }
* ExtendedImage extImg = new ExtendedImage( img, imgY ); //
* if ( STRelFromV.PARAGRAPH.equals( relativeFromV ) ) // {
* // chunkOffsetY = -extImg.getScaledHeight(); // } Chunk
* chunk = new Chunk( extImg, chunkOffsetX, chunkOffsetY,
* false ); pdfParentContainer.addElement( chunk ); }
*/
else {
if (pdfParentContainer instanceof Paragraph) {
// I don't know why but we need add some spacing
// before in the paragraph
// otherwise the image cut the text of the below
// paragraph (see FormattingTests JUnit)?
Paragraph paragraph = (Paragraph) pdfParentContainer;
paragraph.setSpacingBefore(paragraph
.getSpacingBefore() + 5f);
}
pdfParentContainer.addElement(new Chunk(img,
chunkOffsetX, chunkOffsetY, false));
}
}
} catch (Exception e) {
LOGGER.severe(e.getMessage());
}
}
}
public int getPageCount() {
if (pdfDocument.isOpen()) {
return pdfDocument.getPageNumber();
} else {
return pdfDocument.getPageNumber() - 1;
}
}
public boolean useTotalPageField() {
return totalPageFieldUsed;
}
}