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.
org.apache.poi.xwpf.converter.pdf.internal.PdfMapper Maven / Gradle / Ivy
/**
* 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;
}
}