All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy