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

Go to download

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.

There is a newer version: 62
Show newest version
/**
 * 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