fr.opensagres.poi.xwpf.converter.pdf.internal.FastPdfMapper.java Source code

Java tutorial

Introduction

Here is the source code for fr.opensagres.poi.xwpf.converter.pdf.internal.FastPdfMapper.java

Source

/**
 * Copyright (C) 2011-2015 The XDocReport Team <xdocreport@googlegroups.com>
 *
 * 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 fr.opensagres.poi.xwpf.converter.pdf.internal;

import static fr.opensagres.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.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.UnderlinePatterns;
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.CTBr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPTab;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTabs;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText;

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.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;

import fr.opensagres.poi.xwpf.converter.core.Color;
import fr.opensagres.poi.xwpf.converter.core.ListItemContext;
import fr.opensagres.poi.xwpf.converter.core.ParagraphLineSpacing;
import fr.opensagres.poi.xwpf.converter.core.TableWidth;
import fr.opensagres.poi.xwpf.converter.core.openxmlformats.IOpenXMLFormatsPartProvider;
import fr.opensagres.poi.xwpf.converter.core.openxmlformats.OpenXMlFormatsVisitor;
import fr.opensagres.poi.xwpf.converter.core.utils.DxaUtil;
import fr.opensagres.poi.xwpf.converter.core.utils.StringUtils;
import fr.opensagres.poi.xwpf.converter.pdf.PdfOptions;
import fr.opensagres.poi.xwpf.converter.pdf.internal.elements.StylableAnchor;
import fr.opensagres.poi.xwpf.converter.pdf.internal.elements.StylableDocument;
import fr.opensagres.poi.xwpf.converter.pdf.internal.elements.StylableHeaderFooter;
import fr.opensagres.poi.xwpf.converter.pdf.internal.elements.StylableMasterPage;
import fr.opensagres.poi.xwpf.converter.pdf.internal.elements.StylableParagraph;
import fr.opensagres.poi.xwpf.converter.pdf.internal.elements.StylableTable;
import fr.opensagres.poi.xwpf.converter.pdf.internal.elements.StylableTableCell;
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 FastPdfMapper extends OpenXMlFormatsVisitor<IITextContainer, PdfOptions, StylableMasterPage> {

    private static final String TAB = "\t";

    /**
     * Logger for this class
     */
    private static final Logger LOGGER = Logger.getLogger(FastPdfMapper.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;

    public FastPdfMapper(IOpenXMLFormatsPartProvider provider, OutputStream out, PdfOptions options)
            throws Exception {
        super(provider, options != null ? options : PdfOptions.getDefault());
        this.out = out;
    }

    // ------------------------- 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();
    }

    public void setActiveMasterPage(StylableMasterPage masterPage) {
        pdfDocument.setActiveMasterPage(masterPage);
    }

    public StylableMasterPage createMasterPage(CTSectPr sectPr) {
        return new StylableMasterPage(sectPr);
    }

    @Override
    protected void pageBreak() throws Exception {
        pdfDocument.pageBreak();
    }

    @Override
    protected IITextContainer startVisitParagraph(CTP paragraph, ListItemContext itemContext,
            IITextContainer pdfParentContainer) throws Exception {
        // create PDF paragraph
        StylableParagraph pdfParagraph = pdfDocument.createParagraph(pdfParentContainer);

        CTTbl parentTable = super.getParentTable();
        // indentation left
        Float indentationLeft = stylesDocument.getIndentationLeft(paragraph, parentTable);
        if (indentationLeft != null) {
            pdfParagraph.setIndentationLeft(indentationLeft);
        }
        // indentation right
        Float indentationRight = stylesDocument.getIndentationRight(paragraph, parentTable);
        if (indentationRight != null) {
            pdfParagraph.setIndentationRight(indentationRight);
        }
        // indentation first line
        Float indentationFirstLine = stylesDocument.getIndentationFirstLine(paragraph, parentTable);
        if (indentationFirstLine != null) {
            pdfParagraph.setFirstLineIndent(indentationFirstLine);
        }
        // indentation hanging (remove first line)
        Float indentationHanging = stylesDocument.getIndentationHanging(paragraph, parentTable);
        if (indentationHanging != null) {
            pdfParagraph.setFirstLineIndent(-indentationHanging);
        }

        // // spacing before
        Float spacingBefore = stylesDocument.getSpacingBefore(paragraph, parentTable);
        if (spacingBefore != null) {
            pdfParagraph.setSpacingBefore(spacingBefore);
        }

        // spacing after
        // one more pargraph, spacing after should be applied.
        Float spacingAfter = stylesDocument.getSpacingAfter(paragraph, parentTable);
        if (spacingAfter != null) {
            pdfParagraph.setSpacingAfter(spacingAfter);
        }

        ParagraphLineSpacing lineSpacing = stylesDocument.getParagraphSpacing(paragraph, parentTable);
        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(paragraph, parentTable);
        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;
            }
        }
        return pdfParagraph;
    }

    @Override
    protected void endVisitParagraph(CTP paragraph, IITextContainer pdfParentContainer,
            IITextContainer pdfParagraphContainer) throws Exception {
        // add the iText paragraph in the current parent container.
        ExtendedParagraph pdfParagraph = (ExtendedParagraph) pdfParagraphContainer;
        pdfParentContainer.addElement(pdfParagraph.getElement());
    }

    @Override
    protected void visitRun(CTR run, CTP paragraph, boolean pageNumber, String url,
            IITextContainer pdfParagraphContainer) throws Exception {
        // Font family
        String fontFamilyAscii = stylesDocument.getFontFamilyAscii(run, paragraph);
        String fontFamilyEastAsia = stylesDocument.getFontFamilyEastAsia(run, paragraph);
        String fontFamilyHAnsi = stylesDocument.getFontFamilyHAnsi(run, paragraph);

        // Get font size
        Float fontSize = stylesDocument.getFontSize(run, paragraph);
        if (fontSize == null) {
            fontSize = -1f;
        }

        // Get font style
        int fontStyle = Font.NORMAL;
        Boolean bold = stylesDocument.getFontStyleBold(run, paragraph);
        if (bold != null && bold) {
            fontStyle |= Font.BOLD;
        }
        Boolean italic = stylesDocument.getFontStyleItalic(run, paragraph);
        if (italic != null && italic) {
            fontStyle |= Font.ITALIC;
        }
        Boolean strike = stylesDocument.getFontStyleStrike(run, paragraph);
        if (strike != null && strike) {
            fontStyle |= Font.STRIKETHRU;
        }

        // Font color
        Color fontColor = stylesDocument.getFontColor(run, paragraph);

        // 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(run, paragraph);

        // background color
        this.currentRunBackgroundColor = stylesDocument.getBackgroundColor(run, paragraph);

        // highlight
        if (currentRunBackgroundColor == null) {
            this.currentRunBackgroundColor = stylesDocument.getTextHighlighting(run, paragraph);
        }

        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(run, paragraph, pageNumber, url, pdfParagraphContainer);

        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<String> 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);

        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 o, IITextContainer paragraphContainer) throws Exception {
        // TODO Auto-generated method stub

    }

    @Override
    protected void visitTabs(CTTabs tabs, IITextContainer paragraphContainer) throws Exception {
        // TODO Auto-generated method stub

    }

    @Override
    protected void visitBookmark(CTBookmark bookmark, CTP paragraph, IITextContainer paragraphContainer)
            throws Exception {
        // TODO Auto-generated method stub

    }

    @Override
    protected void addNewLine(CTBr br, IITextContainer paragraphContainer) throws Exception {
        paragraphContainer.addElement(Chunk.NEWLINE);
    }

    @Override
    protected void visitHeader(CTHdrFtr currentHeader, 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;
        StylableHeaderFooter pdfHeader = new StylableHeaderFooter(pdfDocument, headerY, false);
        StylableTableCell tableCell = pdfHeader.getTableCell();
        visitBodyElements(currentHeader, tableCell);
        masterPage.setHeader(pdfHeader);
        this.currentPageWidth = null;
    }

    @Override
    protected void visitFooter(CTHdrFtr currentFooter, 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;
        StylableHeaderFooter pdfFooter = new StylableHeaderFooter(pdfDocument, footerY, false);
        StylableTableCell tableCell = pdfFooter.getTableCell();
        visitBodyElements(currentFooter, tableCell);
        masterPage.setFooter(pdfFooter);
        this.currentPageWidth = null;

    }

    // ----------------- Table

    @Override
    protected IITextContainer startVisitTable(CTTbl table, float[] colWidths, IITextContainer pdfParentContainer)
            throws Exception {
        StylableTable pdfPTable = createPDFTable(table, colWidths, pdfParentContainer);
        return pdfPTable;
    }

    private StylableTable createPDFTable(CTTbl 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(CTTbl table, IITextContainer parentContainer, IITextContainer tableContainer)
            throws Exception {

        parentContainer.addElement(((ExtendedPdfPTable) tableContainer).getElement());

    }

    // ------------------------- Table Row

    @Override
    protected void startVisitTableRow(CTRow row, IITextContainer tableContainer, boolean headerRow)
            throws Exception {
        if (headerRow) {
            PdfPTable table = (PdfPTable) tableContainer;
            table.setHeaderRows(table.getHeaderRows() + 1);
        }
    }

    @Override
    protected IITextContainer startVisitTableCell(CTTc cell, IITextContainer tableContainer) throws Exception {
        StylableTable pdfPTable = (StylableTable) tableContainer;
        StylableTableCell pdfPCell = pdfDocument.createTableCell(pdfPTable);

        // Background Color
        Color backgroundColor = stylesDocument.getTableCellBackgroundColor(cell);
        if (backgroundColor != null) {
            pdfPCell.setBackgroundColor(Converter.toAwtColor(backgroundColor));
        }

        return pdfPCell;
    }

    @Override
    protected void endVisitTableCell(CTTc cell, IITextContainer tableContainer, IITextContainer tableCellContainer)
            throws Exception {
        ExtendedPdfPTable pdfPTable = (ExtendedPdfPTable) tableContainer;
        ExtendedPdfPCell pdfPCell = (ExtendedPdfPCell) tableCellContainer;
        pdfPTable.addCell(pdfPCell);
    }

    // ------------------------- Image

    @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();

        byte[] pictureData = super.getPictureBytes(picture);
        if (pictureData != null) {
            try {
                Image img = Image.getInstance(pictureData);
                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());
            }

        }
    }
}