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

it.discovery.jasperreports.jasper2word.J2WDocx4jHelper Maven / Gradle / Ivy

There is a newer version: 1.1.5
Show newest version
package it.discovery.jasperreports.jasper2word;

import it.discovery.jasperreports.jasper2word.J2WAbstractJRExporter.PageOrder;
import it.discovery.jasperreports.jasper2word.J2WAbstractPrintElementVisitorContext.DocxDocumentPart;
import it.discovery.jasperreports.jasper2word.J2WAbstractPrintElementVisitorContext.LastDocxElement;
import it.discovery.jasperreports.jasper2word.J2WGridPageLayout.ComponentPosition;
import it.discovery.jasperreports.jasper2word.J2WGridPageLayout.ComponentPositionInTable;
import it.discovery.jasperreports.jasper2word.J2WGridPageLayout.ComponentTableInfo;
import it.discovery.jasperreports.jasper2word.J2WGridPageLayout.HeaderFooterPageInfo;
import it.discovery.jasperreports.jasper2word.J2WReportConfiguration.ESpacingPolicy;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.base.JRBaseStyle;
import net.sf.jasperreports.engine.base.JRBoxPen;
import net.sf.jasperreports.engine.fill.JRTemplatePrintElement;
import net.sf.jasperreports.engine.type.*;
import net.sf.jasperreports.engine.util.JRStyledText;
import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.xmlgraphics.image.loader.ImageSize;
import org.docx4j.XmlUtils;
import org.docx4j.dml.*;
import org.docx4j.dml.picture.Pic;
import org.docx4j.dml.wordprocessingDrawing.*;
import org.docx4j.jaxb.Context;
import org.docx4j.model.structure.SectionWrapper;
import org.docx4j.openpackaging.contenttype.ContentTypes;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.PartName;
import org.docx4j.openpackaging.parts.WordprocessingML.*;
import org.docx4j.openpackaging.parts.relationships.Namespaces;
import org.docx4j.relationships.Relationship;
import org.docx4j.vml.CTLine;
import org.docx4j.vml.CTOval;
import org.docx4j.vml.CTRect;
import org.docx4j.vml.STTrueFalse;
import org.docx4j.vml.officedrawing.STConnectorType;
import org.docx4j.vml.officedrawing.STHrAlign;
import org.docx4j.vml.officedrawing.STInsetMode;
import org.docx4j.wml.*;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.PPrBase.Ind;
import org.docx4j.wml.PPrBase.PBdr;
import org.docx4j.wml.PPrBase.PStyle;
import org.docx4j.wml.PPrBase.Spacing;
import org.docx4j.wml.R.LastRenderedPageBreak;
import org.docx4j.wml.SectPr.PgMar;
import org.docx4j.wml.SectPr.PgSz;
import org.docx4j.wml.Style.Aliases;
import org.docx4j.wml.Style.BasedOn;
import org.docx4j.wml.Style.Name;
import org.docx4j.wml.TcPrInner.GridSpan;
import org.docx4j.wml.TcPrInner.TcBorders;
import org.docx4j.wml.TcPrInner.VMerge;

import javax.imageio.ImageIO;
import javax.xml.bind.JAXBElement;
import java.awt.Color;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.font.TextAttribute;
import java.awt.geom.Dimension2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.text.AttributedCharacterIterator;
import java.text.AttributedCharacterIterator.Attribute;
import java.text.MessageFormat;
import java.util.*;
import java.util.List;

/**
 * Helper class to prodoce docx document with docx4j library.
 * @author discovery
 * @date 13/08/15 10.07
 */
class J2WDocx4jHelper {
    /** Macro for automatic color */
    private static final String COLOR_AUTO = "auto";

    /**
     * Hide the constructor.
     */
    private J2WDocx4jHelper() {
    }

    /**
     * Append, if necessary, a new table in the doc.
     * @param tablePos The position of an element in a table.
     * @param context The exporter visitor context.
     * @return The metadata of the built table.
     */
    static TableDocInfo buildTables(ComponentPositionInTable tablePos, J2WDocx4jPrintElementVisitorContext context) {
        Map tables = context.tables;

        ComponentTableInfo table = tablePos.getTableInfo();
        TableDocInfo res = tables.get(table.getTableId());
        // Check for pre-built table
        if (res == null) {
            // if the previous element in document was a table or table must be moved more down, a new space paragraph is appended
            if (context.getLastDocxElement() == LastDocxElement.TABLE || context.getLastY(tablePos.getDocumentPart()) < tablePos.getTableInfo().getY()) {
                R run = context.objectFactory.createR();

                P paragraph = context.objectFactory.createP();
                paragraph.setPPr(context.objectFactory.createPPr());
                PPr pPr = paragraph.getPPr();

                pPr.setPStyle(context.objectFactory.createPPrBasePStyle());
                pPr.getPStyle().setVal("Normal");

                run.setRPr(context.objectFactory.createRPr());

                pPr.setSpacing(context.objectFactory.createPPrBaseSpacing());
                Spacing spacing = pPr.getSpacing();

                spacing.setBefore(BigInteger.ZERO);
                spacing.setBeforeLines(BigInteger.ZERO);
                spacing.setAfterLines(BigInteger.ZERO);

                setParagraphMargins(paragraph, context.getLastParagraphX(), context.getLastParagraphWidth(), context);

                if (context.getLastY(tablePos.getDocumentPart()) < tablePos.getTableInfo().getY()) {
                    BigInteger gap = EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, tablePos.getTableInfo().getY() - context.getLastY(tablePos.getDocumentPart())).divide(BigInteger.valueOf(2));
                    BigInteger check = BigInteger.valueOf(240);
                    if (gap.min(check).equals(check)) {
                        spacing.setAfter(BigInteger.ZERO);
                        spacing.setLine(gap.add(gap));
                    }
                    else {
                        spacing.setAfter(gap);
                        spacing.setLine(gap);
                    }
                    spacing.setLineRule(STLineSpacingRule.EXACT);
                }
                else {
                    spacing.setAfter(BigInteger.ZERO);
                    spacing.setLineRule(STLineSpacingRule.EXACT);
                    spacing.setLine(BigInteger.ONE);
                }


                paragraph.getContent().add(run);
                if (tablePos.getDocumentPart() == DocxDocumentPart.HEADER)
                    context.headerContent.getContent().add(paragraph);
                else if (tablePos.getDocumentPart() == DocxDocumentPart.FOOTER)
                    context.footerContent.getContent().add(paragraph);
                else
                    context.getDocument().getMainDocumentPart().getContent().add(paragraph);
            }

            TableDocInfo parent = null;
            if (table.getParent() instanceof ComponentPositionInTable)
                parent = buildTables((ComponentPositionInTable) table.getParent(), context);

            // Building the table
            int[] rowsHeight = table.getRows();
            int[] columnsWidth = table.getColumns();
            BigInteger columnsX[] = new BigInteger[columnsWidth.length + 1];
            columnsX[0] = BigInteger.ZERO;

            Tbl xwpfTable = context.objectFactory.createTbl();
            for (int aRowsHeight : rowsHeight) {
                Tr tr = context.objectFactory.createTr();
                TrPr trPr = tr.getTrPr();
                if (trPr == null) {
                    trPr = context.objectFactory.createTrPr();
                    tr.setTrPr(trPr);
                }
                // Rows height
                CTHeight ctHeight = context.objectFactory.createCTHeight();
                if (table.isForceRowHeight())
                    ctHeight.setHRule(STHeightRule.EXACT);
                else
                    ctHeight.setHRule(STHeightRule.AUTO);
                ctHeight.setVal(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, aRowsHeight));
                trPr.getCnfStyleOrDivIdOrGridBefore().add(context.objectFactory.createCTTrPrBaseTrHeight(ctHeight));
                // Columns width (paragraph)
                for (int aColumnsWidth : columnsWidth) {
                    Tc tc = context.objectFactory.createTc();
                    tr.getContent().add(tc);
                    tc.getContent().add(context.objectFactory.createP());

                    TcPr tcPr = tc.getTcPr();
                    if (tcPr == null) {
                        tcPr = context.objectFactory.createTcPr();
                        tc.setTcPr(tcPr);
                    }
                    TblWidth w = context.objectFactory.createTblWidth();
                    w.setType(TblWidth.TYPE_DXA);
                    w.setW(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, aColumnsWidth));
                    tcPr.setTcW(w);

                }
                xwpfTable.getContent().add(tr);
            }

            // Table width
            TblWidth tblWidth = context.objectFactory.createTblWidth();
            tblWidth.setW(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, table.getWidth()));
            tblWidth.setType(TblWidth.TYPE_DXA);

            // Table right margin
            TblWidth tblInd = context.objectFactory.createTblWidth();
            tblInd.setW(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, table.getX()));
            tblInd.setType(TblWidth.TYPE_DXA);


            TblPr tblPr = context.objectFactory.createTblPr();
            tblPr.setTblW(tblWidth);
            tblPr.setTblInd(tblInd);

            // Table borders
            TblBorders tblBorders = context.objectFactory.createTblBorders();
            if (table.isBordered()) {
                tblBorders.setInsideH(createBorder(context.objectFactory, 1, 2, "000000", STBorder.SINGLE));
                tblBorders.setInsideV(createBorder(context.objectFactory, 1, 2, "000000", STBorder.SINGLE));
                tblBorders.setBottom(createBorder(context.objectFactory, 1, 2, "000000", STBorder.SINGLE));
                tblBorders.setLeft(createBorder(context.objectFactory, 1, 2, "000000", STBorder.SINGLE));
                tblBorders.setRight(createBorder(context.objectFactory, 1, 2, "000000", STBorder.SINGLE));
                tblBorders.setTop(createBorder(context.objectFactory, 1, 2, "000000", STBorder.SINGLE));
            }
            else {
                tblBorders.setInsideH(createBorder(context.objectFactory, 0, 0, "000000", STBorder.NONE));
                tblBorders.setInsideV(createBorder(context.objectFactory, 0, 0, "000000", STBorder.NONE));
                tblBorders.setBottom(createBorder(context.objectFactory, 0, 0, "000000", STBorder.NONE));
                tblBorders.setLeft(createBorder(context.objectFactory, 0, 0, "000000", STBorder.NONE));
                tblBorders.setRight(createBorder(context.objectFactory, 0, 0, "000000", STBorder.NONE));
                tblBorders.setTop(createBorder(context.objectFactory, 0, 0, "000000", STBorder.NONE));
            }
            tblPr.setTblBorders(tblBorders);

            // Table grid column definition
            TblGrid tblGrid = context.objectFactory.createTblGrid();
            for (int i = 0; i < columnsWidth.length; i++) {
                int colWidth = columnsWidth[i];
                TblGridCol col = context.objectFactory.createTblGridCol();
                tblGrid.getGridCol().add(col);
                BigInteger width = EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, colWidth);
                col.setW(width);
                columnsX[i + 1] = width;
            }

            xwpfTable.setTblPr(tblPr);
            xwpfTable.setTblGrid(tblGrid);

            if (parent == null) {
                if (tablePos.getDocumentPart() == DocxDocumentPart.HEADER)
                    context.headerContent.getContent().add(xwpfTable);
                else if (tablePos.getDocumentPart() == DocxDocumentPart.FOOTER)
                    context.footerContent.getContent().add(xwpfTable);
                else
                    context.getDocument().getMainDocumentPart().getContent().add(xwpfTable);

            }
            else {
                Tr row = (Tr) parent.table.getContent().get(tablePos.getRow());
                Tc cell = (Tc) row.getContent().get(tablePos.getCol());

                cell.getContent().clear();
                cell.getContent().add(xwpfTable);
            }


            // Build merged cell
            res = new TableDocInfo(xwpfTable);
            SortedSet cellToRemove = new TreeSet<>(new CellToRemovePointComparator());
            for (Rectangle mergedRegion : table.getMergedRegions()) {
                Tr row = (Tr) xwpfTable.getContent().get(mergedRegion.y);
                Tc cell = (Tc) row.getContent().get(mergedRegion.x);

                TcPr tcPr = cell.getTcPr();
                if (tcPr == null) {
                    tcPr = context.objectFactory.createTcPr();
                    cell.setTcPr(tcPr);
                }

                if (mergedRegion.width > 1) {
                    GridSpan gridSpan = context.objectFactory.createTcPrInnerGridSpan();
                    gridSpan.setVal(BigInteger.valueOf((long) mergedRegion.width));
                    tcPr.setGridSpan(gridSpan);
                }
                if (mergedRegion.height > 1) {
                    VMerge vMerge = context.objectFactory.createTcPrInnerVMerge();
                    vMerge.setVal("restart");
                    tcPr.setVMerge(vMerge);
                }

                TblWidth tcW = context.objectFactory.createTblWidth();
                tcW.setW(columnsX[mergedRegion.x + 1].subtract(columnsX[mergedRegion.x]));
                tcW.setType(TblWidth.TYPE_DXA);
                tcPr.setTcW(tcW);

                for (int rowIndex = mergedRegion.y; rowIndex < mergedRegion.height + mergedRegion.y; rowIndex++) {
                    Tr rowSpan = (Tr) xwpfTable.getContent().get(rowIndex);
                    for (int columnIndex = mergedRegion.x; columnIndex < mergedRegion.x + mergedRegion.width ; columnIndex++) {
                        if (columnIndex > mergedRegion.x)
                            cellToRemove.add(new Point(columnIndex, rowIndex));
                        if (rowIndex > mergedRegion.y && columnIndex == mergedRegion.x) {
                            cell = (Tc) rowSpan.getContent().get(columnIndex);
                            tcPr = cell.getTcPr();
                            if (tcPr == null) {
                                tcPr = context.objectFactory.createTcPr();
                                cell.setTcPr(tcPr);
                            }

                            if (mergedRegion.width > 1) {
                                GridSpan gridSpan = context.objectFactory.createTcPrInnerGridSpan();
                                gridSpan.setVal(BigInteger.valueOf((long) mergedRegion.width));
                                tcPr.setGridSpan(gridSpan);
                            }
                            if (mergedRegion.height > 1) {
                                VMerge vMerge = context.objectFactory.createTcPrInnerVMerge();
                                vMerge.setVal("continue");
                                tcPr.setVMerge(vMerge);
                            }
                            tcPr.setTcW(tcW);
                        }
                    }
                }
            }

            for (Point cellCoord : cellToRemove) {
                Tr rowSpan = (Tr) xwpfTable.getContent().get(cellCoord.y);
                rowSpan.getContent().remove(cellCoord.x);
            }

            tables.put(table.getTableId(), res);

            context.setLastY(tablePos.getDocumentPart(), table.getY() + table.getHeight());
        }

        context.setLastDocxElement(LastDocxElement.TABLE);

        return res;
    }

    /**
     * Build a table border.
     * @param factory Jaxb object factory.
     * @param size The border size in points.
     * @param space The space between border and cell content in points.
     * @param color The border color.
     * @param type The border style.
     * @return The built border.
     */
    private static CTBorder createBorder(org.docx4j.wml.ObjectFactory factory, int size, int space, String color, STBorder type) {
        CTBorder ctBorder = factory.createCTBorder();
        ctBorder.setColor(color);
        ctBorder.setVal(type);
        ctBorder.setSz(BigInteger.valueOf(size));
        ctBorder.setSpace(BigInteger.valueOf(space));
        return ctBorder;
    }

    /**
     * Format a {@link Color color} in a hexadecimal string (RRGGBB).
     * @param color The java awt Color.
     * @return The formatted string color.
     */
    static String colorToStringFormat(Color color) {
        Color base = new Color(255, 255, 255, 0);
        return String.format("%6s", Integer.toHexString(color.getRGB() & base.getRGB())).replaceAll(" ", "0");
    }

    /**
     * Parse string (or byte[]) color into a java awt Color.
     * @param color It can be a hexadecimal {@link String String} like RRGGBB or a {@code byte[]} like byte[] {R, G, B}.
     * @return The java awt Color.
     */
    static Color stringToColorParse(Object color) {
        if (color == null)
            return null;
        if (color instanceof String) {
            String s_color = (String) color;
            if (s_color.equals(COLOR_AUTO))
                return null;
            String tokens[] = s_color.split("(?<=\\G\\d{2})", 3);
            return new Color(Integer.parseInt(tokens[0], 16), Integer.parseInt(tokens[1], 16), Integer.parseInt(tokens[2], 16), 0);
        }
        else if (color instanceof byte[]) {
            byte c[] = (byte[]) color;
            return new Color(c[0], c[1], c[2]);
        }
        else
            throw new JRRuntimeException("Unknown color type: " + color.getClass().getName());
    }

    /**
     * Reset document status variables and build document styles.
     * @param firstReport {@code true} if first report, {@code} false otherwise.
     * @param context The visitor context.
     * @param report Thre report to export.
     */
    static void prepareNewDocument(boolean firstReport, J2WDocx4jPrintElementVisitorContext context, JasperPrint report) {
        context.resetDocumentStatus();
        context.setReport(report);
        if (firstReport) {
            context.reportCount = 0;
            context.currentSection = null;
            context.sectionParagraph = null;
        }
        else
            context.reportCount++;
        buildStyles(context, report.getStylesList());
    }

    /**
     * Prepare new page (build new section if required), set page size and (optiona) header and footer.
     * @param context The visitor context.
     * @param jrPage The jasper page.
     * @param pageNumber The relative page number.
     * @param pageOrder The absolute page position.
     */
    static void prepareNewPage(J2WDocx4jPrintElementVisitorContext context, JRPrintPage jrPage, int pageNumber, PageOrder pageOrder) {
        if (!pageOrder.isFirstestPage() && context.currentSection == null) {
            WordprocessingMLPackage document = context.getDocument();
            ObjectFactory factory = context.objectFactory;
            Br breakObj = context.objectFactory.createBr();
            breakObj.setType(STBrType.PAGE);
            breakObj.setClear(STBrClear.ALL);

            P paragraph = factory.createP();
            R run = factory.createR();
            run.getContent().add(breakObj);
            paragraph.getContent().add(run);
            document.getMainDocumentPart().getContent().add(paragraph);
        }
        context.currentSection = null;
        context.sectionParagraph = null;
        setPageSizeAndMargins(context, pageOrder);
        buildHeaderFooter(context, pageNumber, pageOrder);
    }

    /**
     * Finalize the page (add the page section, if present).
     * @param context The visitor context.
     * @param jrPage The jasper page.
     * @param pageNumber The relative page number.
     * @param pageOrder The absolute page position.
     */
    static void finalizePage(J2WDocx4jPrintElementVisitorContext context, JRPrintPage jrPage, int pageNumber, PageOrder pageOrder) {
        WordprocessingMLPackage document = context.getDocument();
        if (context.sectionParagraph != null)
            document.getMainDocumentPart().addObject(context.sectionParagraph);
    }

    /**
     * Return the current section if exits, or create a new one.
     * @param context The visitor context.
     * @param pageOrder The absolute page position.
     * @return The current section or new one.
     */
    private static SectPr getOrCreateCurrentSection(J2WDocx4jPrintElementVisitorContext context, PageOrder pageOrder) {
        WordprocessingMLPackage document = context.getDocument();
        List sections = document.getDocumentModel().getSections();

        // Get last section SectPr and create a new one if it doesn't exist
        SectPr sectPr;
        if (pageOrder.isLastestPage()) {
            sectPr = sections.get(sections.size() - 1).getSectPr();
            if (sectPr == null) {
                sectPr = context.objectFactory.createSectPr();
                sections.get(sections.size() - 1).setSectPr(sectPr);
            }
        }
        else {
            sectPr = context.currentSection;
            if (sectPr == null) {
                sectPr = context.objectFactory.createSectPr();
                context.sectionParagraph = context.objectFactory.createP();

                PPr pPr = context.objectFactory.createPPr();
                pPr.setSectPr(sectPr);
                context.sectionParagraph.setPPr(pPr);

                LastRenderedPageBreak breakObj = context.objectFactory.createRLastRenderedPageBreak();

                R run = context.objectFactory.createR();
                run.getContent().add(breakObj);
                context.sectionParagraph.getContent().add(run);
            }
        }

        context.currentSection = sectPr;
        return sectPr;
    }

    /**
     * Set the page size and margins. If the report has not margins, default margins are created with 5pt size.
     * @param context The visitor context.
     * @param pageOrder The absolute page position.
     */
    private static void setPageSizeAndMargins(J2WDocx4jPrintElementVisitorContext context, PageOrder pageOrder) {
        JasperPrint report = context.getReport();

        HeaderFooterPageInfo pageInfo = context.getLayout().getPageInfo();
        if (context.getLastPageInfo() == null || context.getLastPageInfo().containsHeaderFooter() || pageInfo.containsHeaderFooter() || pageOrder.isLastestPage()) {
            PgSz ctPageSz = context.objectFactory.createSectPrPgSz();
            ctPageSz.setH(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, report.getPageHeight()));
            ctPageSz.setW(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, report.getPageWidth()));

            SectPr sectPr = getOrCreateCurrentSection(context, pageOrder);

            sectPr.setPgSz(ctPageSz);

            int top;
            int bottom;
            int right = 5;
            int left = 5;
            int header = 0;
            int footer = 0;

            if (pageInfo.getRightMargin() > 0)
                right = pageInfo.getRightMargin();
            if (pageInfo.getLeftMargin() > 0)
                left = pageInfo.getLeftMargin();

            if (pageInfo.getMinHeader() != null) {
                top = pageInfo.getTopMargin() + pageInfo.getMinHeader();
                header = pageInfo.getMinHeader();
            }
            else if (pageInfo.getTopMargin() > 0)
                top = pageInfo.getTopMargin();
            else
                top = 5;

            if (pageInfo.getMaxFooter() != null) {
                bottom = pageInfo.getMaxFooter() + pageInfo.getBottomMargin();
                footer = pageInfo.getMaxFooter();
            }
            else if (pageInfo.getBottomMargin() > 0)
                bottom = pageInfo.getBottomMargin();
            else
                bottom = 5;

            PgMar ctPageMar = context.objectFactory.createSectPrPgMar();
            ctPageMar.setBottom(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, bottom));
            ctPageMar.setTop(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, top));
            ctPageMar.setLeft(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, left));
            ctPageMar.setRight(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, right));
            ctPageMar.setHeader(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, header));
            ctPageMar.setFooter(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, footer));

            ctPageMar.setGutter(BigInteger.ZERO);

            sectPr.setPgMar(ctPageMar);
        }
    }

    /**
     * Build the header and footer, if exist.
     * @param context The visitor context.
     * @param pageNumber The relative page number.
     * @param pageOrder The absolute page position.
     */
    static void buildHeaderFooter(J2WDocx4jPrintElementVisitorContext context, int pageNumber, PageOrder pageOrder) {
        try {
            HeaderFooterPageInfo pageInfo = context.getLayout().getPageInfo();
            if (context.getLastPageInfo() == null || context.getLastPageInfo().containsHeaderFooter() || context.getLayout().getPageInfo().containsHeaderFooter()) {
                WordprocessingMLPackage document = context.getDocument();
                MainDocumentPart mainPart = document.getMainDocumentPart();

                // Get last section SectPr and create a new one if it doesn't exist
                SectPr sectPr = getOrCreateCurrentSection(context, pageOrder);

                ObjectFactory factory = context.objectFactory;

                Relationship content_hdr_rel = null;
                Relationship content_ftr_rel = null;

                if (pageInfo.getHeaderHeight() != null) {
                    HeaderPart headerContent = new HeaderPart(new PartName(MessageFormat.format("/word/header_{0,number,0}_{1,number,0}.xml", context.reportCount, pageNumber)));

                    document.getParts().put(headerContent);

                    context.headerContent = factory.createHdr();

                    // Bind the header JAXB element as representing its header part
                    headerContent.setJaxbElement(context.headerContent);

                    // Add the reference to header part to the Main Document Part
                    content_hdr_rel = mainPart.addTargetPart(headerContent);
                }
                else {
                    context.headerContent = null;
                }

                if (pageInfo.getFooterHeight() != null) {
                    FooterPart footerContent = new FooterPart(new PartName(MessageFormat.format("/word/footer_{0,number,0}_{1,number,0}.xml", context.reportCount, pageNumber)));

                    document.getParts().put(footerContent);

                    context.footerContent = factory.createFtr();


                    // Bind the footer JAXB element as representing its footer part
                    footerContent.setJaxbElement(context.footerContent);

                    // Add the reference to footer part to the Main Document Part
                    content_ftr_rel = mainPart.addTargetPart(footerContent);
                }
                else {
                    context.footerContent = null;
                }

                BooleanDefaultTrue boolanDefaultTrue = new BooleanDefaultTrue();
                boolanDefaultTrue.setVal(false);
                sectPr.setTitlePg(boolanDefaultTrue);

                // link content headers
                if (content_hdr_rel != null) {
                    HeaderReference hdr_ref = factory.createHeaderReference();
                    hdr_ref.setId(content_hdr_rel.getId());
                    hdr_ref.setType(HdrFtrRef.DEFAULT);
                    sectPr.getEGHdrFtrReferences().add(hdr_ref);
                }


                // link content footers
                if (content_ftr_rel != null) {
                    FooterReference ftr_ref = factory.createFooterReference();
                    ftr_ref.setId(content_ftr_rel.getId());
                    ftr_ref.setType(HdrFtrRef.DEFAULT);
                    sectPr.getEGHdrFtrReferences().add(ftr_ref);
                }
            }
        } catch (InvalidFormatException e) {
            throw new JRRuntimeException(e);
        }
    }

    /**
     * Return the Style Document part (styles container) if exists, or create a new one if {@code createIfNull} is {@code true}.
     * @param context The visitor context.
     * @param createIfNull {@code true} create a new style document part if not exits.
     * @return The style document part or {@code null} if not exists and {@code createIfNull} is {@code false}.
     */
    private static StyleDefinitionsPart buildStylePart(J2WDocx4jPrintElementVisitorContext context, boolean createIfNull) {
        MainDocumentPart wordDocumentPart = context.getDocument().getMainDocumentPart();

        try {
            StyleDefinitionsPart stylesPart = new StyleDefinitionsPart();
            Relationship rel = wordDocumentPart.getRelationshipsPart().getRel(stylesPart.getPartName());
            if (rel != null)
                stylesPart = (StyleDefinitionsPart) wordDocumentPart.getRelationshipsPart().getPart(rel);
            else if (createIfNull)
                wordDocumentPart.addTargetPart(stylesPart);
            else
                stylesPart = null;

            if (stylesPart != null) {
                Styles styles = stylesPart.getContents();
                if (styles == null && createIfNull) {
                    styles = (Styles) stylesPart.unmarshalDefaultStyles();
                    stylesPart.setContents(styles);
                }
            }

            return stylesPart;
        } catch (Exception e) {
            throw new JRRuntimeException(e);
        }
    }

    /**
     * Update doc style with jasper style attributes.
     * @param ctStyle The doc style.
     * @param jrStyle The jasper style.
     * @param context The visito context.
     * @param update {@code true} to update doc style, {@code false} to create a new one doc style with jasper style attributes.
     * @return The updated doc style.
     */
    private static String updateStyle(Style ctStyle, JRStyle jrStyle, J2WDocx4jPrintElementVisitorContext context, boolean update) {
        JRPen linePen = jrStyle.getLinePen();

        BooleanDefaultTrue ctOnOff_ON = context.objectFactory.createBooleanDefaultTrue();
        BooleanDefaultTrue ctOnOff_OFF = context.objectFactory.createBooleanDefaultTrue();
        ctOnOff_ON.setVal(Boolean.TRUE);
        ctOnOff_OFF.setVal(Boolean.FALSE);

        String styleName = jrStyle.getName();
        if (!update) {
            int styleCount = 1;
            while (context.allStyles.contains(styleName)) {
                styleName = jrStyle.getName() + styleCount;
                styleCount++;
            }
            context.allStyles.add(styleName);

            Name name = context.objectFactory.createStyleName();
            name.setVal(styleName);
            ctStyle.setName(name);
            ctStyle.setStyleId(styleName);
            if (!styleName.equals(jrStyle.getName())) {
                Aliases aliases = context.objectFactory.createStyleAliases();
                aliases.setVal(jrStyle.getName());
                ctStyle.setAliases(aliases);
            }
            ctStyle.setType("paragraph");
            ctStyle.setQFormat(ctOnOff_ON);
        }

        // Default run settings
        RPr ctrPr = ctStyle.getRPr();
        if (ctrPr == null) {
            ctrPr = context.objectFactory.createRPr();
            ctStyle.setRPr(ctrPr);
        }
        // Default paragraph settings
        PPr ctpPr = ctStyle.getPPr();
        if (ctpPr == null) {
            ctpPr = context.objectFactory.createPPr();
            ctStyle.setPPr(ctpPr);
        }

        // Bold
        if (jrStyle.isOwnBold() != null)
            ctrPr.setB(jrStyle.isOwnBold() ? ctOnOff_ON : ctOnOff_OFF);

        // Italic
        if (jrStyle.isOwnItalic() != null)
            ctrPr.setI(jrStyle.isOwnItalic() ? ctOnOff_ON : ctOnOff_OFF);

        // Underline
        if (jrStyle.isOwnUnderline() != null && jrStyle.isOwnUnderline()) {
            U ctUnderline = context.objectFactory.createU();
            if (linePen == null) {
                ctUnderline.setVal(UnderlineEnumeration.SINGLE);
                if (jrStyle.getForecolor() != null)
                    ctUnderline.setColor(colorToStringFormat(jrStyle.getForecolor()));
                else
                    ctUnderline.setColor(COLOR_AUTO);
            }
            else {
                if (linePen.getOwnLineStyleValue() != null) {
                    switch (linePen.getOwnLineStyleValue()) {
                        case SOLID:
                            ctUnderline.setVal(UnderlineEnumeration.SINGLE);
                            break;
                        case DASHED:
                            ctUnderline.setVal(UnderlineEnumeration.DASH);
                            break;
                        case DOTTED:
                            ctUnderline.setVal(UnderlineEnumeration.DOTTED);
                            break;
                        case DOUBLE:
                            ctUnderline.setVal(UnderlineEnumeration.DOUBLE);
                            break;
                        default:
                            ctUnderline.setVal(UnderlineEnumeration.SINGLE);
                            break;
                    }
                }
                else
                    ctUnderline.setVal(UnderlineEnumeration.SINGLE);
                if (linePen.getLineColor() != null)
                    ctUnderline.setColor(colorToStringFormat(linePen.getLineColor()));
                else if (jrStyle.getForecolor() != null)
                    ctUnderline.setColor(colorToStringFormat(jrStyle.getForecolor()));
                else
                    ctUnderline.setColor(colorToStringFormat(Color.black));
            }
            ctrPr.setU(ctUnderline);
        }

        // Strike Through
        if (jrStyle.isOwnStrikeThrough() != null)
            ctrPr.setStrike(jrStyle.isOwnStrikeThrough()? ctOnOff_ON : ctOnOff_OFF);

        // Fore color
        org.docx4j.wml.Color foreColor = context.objectFactory.createColor();
        if (jrStyle.getOwnForecolor() != null)
            foreColor.setVal(colorToStringFormat(jrStyle.getOwnForecolor()));
        else
            foreColor.setVal(COLOR_AUTO);
        ctrPr.setColor(foreColor);


        // Background / Highlight
        CTShd shdp = context.objectFactory.createCTShd();
        shdp.setVal(STShd.CLEAR);
        if (jrStyle.getOwnBackcolor() != null)
            shdp.setFill(colorToStringFormat(jrStyle.getOwnBackcolor()));
        else
            shdp.setFill(COLOR_AUTO);
        if (jrStyle.getOwnBackcolor() != null || !update)
            ctpPr.setShd(shdp);

        // Text alignment
        org.docx4j.wml.PPrBase.TextAlignment alignment = context.objectFactory.createPPrBaseTextAlignment();
        if (jrStyle.getOwnVerticalAlignmentValue() != null) {
            switch (jrStyle.getOwnVerticalAlignmentValue()) {
                case TOP:
                    alignment.setVal("top");
                    break;
                case MIDDLE:
                    alignment.setVal("center");
                    break;
                case BOTTOM:
                    alignment.setVal("bottom");
                    break;
                case JUSTIFIED:
                    alignment.setVal("baseline");
                    break;
                default:
                    alignment.setVal("auto");
                    break;
            }
        }
        else
            alignment.setVal("auto");
        ctpPr.setTextAlignment(alignment);
        if (jrStyle.getOwnHorizontalAlignmentValue() != null) {
            Jc ctJc = context.objectFactory.createJc();
            ctpPr.setJc(ctJc);
            switch (jrStyle.getOwnHorizontalAlignmentValue()) {
                case LEFT:
                    ctJc.setVal(JcEnumeration.LEFT);
                    break;
                case CENTER:
                    ctJc.setVal(JcEnumeration.CENTER);
                    break;
                case RIGHT:
                    ctJc.setVal(JcEnumeration.RIGHT);
                    break;
                case JUSTIFIED:
                    ctJc.setVal(JcEnumeration.BOTH);
                    break;
                default:
                    ctJc.setVal(JcEnumeration.LEFT);
                    break;
            }
        }

        // Font
        if (jrStyle.getOwnFontName() != null) {
            RFonts ctFonts = context.objectFactory.createRFonts();
            ctrPr.setRFonts(ctFonts);
            ctFonts.setAscii(jrStyle.getOwnFontName());
            ctFonts.setHAnsi(jrStyle.getOwnFontName());
        }
        if (jrStyle.getOwnFontsize() != null) {
            HpsMeasure ctHpsMeasure = context.objectFactory.createHpsMeasure();
            ctHpsMeasure.setVal(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.HALF_POINTS, jrStyle.getOwnFontsize()));
            ctrPr.setSz(ctHpsMeasure);
        }

        // Paragraph advanced settings
        JRParagraph paragraph = jrStyle.getParagraph();
        if (paragraph != null) {
            Ind ctInd = ctpPr.getInd();
            if (ctInd == null) {
                ctInd = context.objectFactory.createPPrBaseInd();
                ctpPr.setInd(ctInd);
            }
            if (paragraph.getOwnFirstLineIndent() != null)
                ctInd.setFirstLine(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, paragraph.getOwnFirstLineIndent()));
            if (paragraph.getOwnLeftIndent() != null)
                ctInd.setLeft(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, paragraph.getOwnLeftIndent()));
            if (paragraph.getOwnRightIndent() != null)
                ctInd.setRight(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, paragraph.getOwnRightIndent()));


            ctpPr.setSpacing(buildParagraphSpacing(paragraph, ctpPr.getSpacing(), context, update));
        }

        if (jrStyle.getStyle() != null && ctStyle.getBasedOn() == null) {
            BasedOn basedOn = context.objectFactory.createStyleBasedOn();
            basedOn.setVal(jrStyle.getStyle().getName());
            ctStyle.setBasedOn(basedOn);
        }

        return styleName;
    }

    /**
     * Build all the jaspser styles.
     * @param context The visitor context.
     * @param stylesList The jasper styles.
     */
    private static void buildStyles(J2WDocx4jPrintElementVisitorContext context, java.util.List stylesList) {
        if (!stylesList.isEmpty()) {

            StyleDefinitionsPart stylesPart = buildStylePart(context, true);
            Styles styles;
            try {
                styles = stylesPart.getContents();
            } catch (Docx4JException e) {
                throw new JRRuntimeException(e);
            }


            for (JRStyle jrStyle : stylesList) {

                Style ctStyle = context.objectFactory.createStyle();
                String styleName = updateStyle(ctStyle, jrStyle, context, false);

                styles.getStyle().add(ctStyle);
                context.remapStylesNameByJrStyle.put(jrStyle.getName(), styleName);
            }
        }
    }

    /**
     * Utility method: return {@code valOverride} if {@code useOverriding} is {@code true} and {@code valOverride} is not {@code null};
     * return {@code vale} otherwise.
     * @param val The default value.
     * @param valOverride The overriding value.
     * @param useOverriding {@code} true to use {@code valOverride}, {@code false} to use {@code val}.
     * @param  Type of value.
     * @return {@code valOverride} if {@code useOverriding} is {@code true} and {@code valOverride} is not {@code null};
     * return {@code vale} otherwise.
     */
    static  T getOverridingValue(T val, T valOverride, boolean useOverriding) {
        if (useOverriding)
            return valOverride != null? valOverride : val;
        else
            return val;
    }

    /**
     * Set paragraph spacing: before, after and line spacing.
     * @param paragraph The jasper paragraph.
     * @param from The input/output spacing object (can be {@code null}).
     * @param context The visitor context.
     * @param useOverriding {@code true} to use paragraph own spacing, {@code false} otherwise.
     * @return The spacing object.
     * @see #getOverridingValue(Object, Object, boolean)
     */
    static Spacing buildParagraphSpacing(JRParagraph paragraph, Spacing from, J2WDocx4jPrintElementVisitorContext context, boolean useOverriding) {
        boolean setSpacing = false;
        Spacing ctSpacing = from;
        if (ctSpacing == null)
            ctSpacing = context.objectFactory.createPPrBaseSpacing();
        else
            setSpacing = true;

        Integer spacingAfter = getOverridingValue(paragraph.getSpacingAfter(), paragraph.getOwnSpacingAfter(), useOverriding);
        if (spacingAfter != null) {
            ctSpacing.setAfter(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, spacingAfter));
            setSpacing = true;
        }
        else if (paragraph.getStyle() == null && useOverriding) {
            ctSpacing.setAfter(BigInteger.ZERO);
            ctSpacing.setAfterLines(BigInteger.ZERO);
            setSpacing = true;
        }

        Integer spacingBefore = getOverridingValue(paragraph.getSpacingBefore(), paragraph.getOwnSpacingBefore(), useOverriding);
        if (spacingBefore != null) {
            ctSpacing.setBefore(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, spacingBefore));
            setSpacing = true;
        }
        else if (paragraph.getStyle() == null && useOverriding) {
            ctSpacing.setBefore(BigInteger.ZERO);
            ctSpacing.setBeforeLines(BigInteger.ZERO);
            setSpacing = true;
        }

        LineSpacingEnum lineSpacingEnum = getOverridingValue(paragraph.getLineSpacing(), paragraph.getOwnLineSpacing(), useOverriding);
        if (lineSpacingEnum != null) {
            Float lineSpacingSize = getOverridingValue(paragraph.getLineSpacingSize(), paragraph.getOwnLineSpacingSize(), useOverriding);
            switch (lineSpacingEnum) {
                case SINGLE:
                    ctSpacing.setLineRule(STLineSpacingRule.AUTO);
                    ctSpacing.setLine(EConvertMisure.toLineSpacing(null, 1D));
                    break;
                case ONE_AND_HALF:
                    ctSpacing.setLineRule(STLineSpacingRule.AUTO);
                    ctSpacing.setLine(EConvertMisure.toLineSpacing(null, 1.5D));
                    break;
                case DOUBLE:
                    ctSpacing.setLineRule(STLineSpacingRule.AUTO);
                    ctSpacing.setLine(EConvertMisure.toLineSpacing(null, 2D));
                    break;
                case AT_LEAST:
                    ctSpacing.setLineRule(STLineSpacingRule.AT_LEAST);
                    if (lineSpacingSize != null)
                        ctSpacing.setLine(EConvertMisure.toLineSpacing(lineSpacingSize.doubleValue(), 1D));
                    else
                        ctSpacing.setLine(EConvertMisure.toLineSpacing(null, 1D));
                    break;
                case FIXED:
                    ctSpacing.setLineRule(STLineSpacingRule.EXACT);
                    if (lineSpacingSize != null)
                        ctSpacing.setLine(EConvertMisure.toLineSpacing(lineSpacingSize.doubleValue(), 1D));
                    else
                        ctSpacing.setLine(EConvertMisure.toLineSpacing(null, 1D));
                    break;
                case PROPORTIONAL:
                    ctSpacing.setLineRule(STLineSpacingRule.AUTO);
                    if (lineSpacingSize != null)
                        ctSpacing.setLine(EConvertMisure.toLineSpacing(lineSpacingSize.doubleValue(), 1D));
                    else
                        ctSpacing.setLine(EConvertMisure.toLineSpacing(null, 1D));
                    break;
            }
            setSpacing = true;
        }

        if (setSpacing)
            return ctSpacing;
        else
            return null;
    }

    /**
     * Set paragraph margins (left and right).
     * @param paragraph The doc paragraph.
     * @param x The left margin.
     * @param width The paragraph width.
     * @param context The visitor context.
     */
    static void setParagraphMargins(P paragraph, Integer x, Integer width, J2WDocx4jPrintElementVisitorContext context) {
        if (x != null && width != null) {
            PPr pPr = paragraph.getPPr();
            if (pPr == null) {
                pPr = context.objectFactory.createPPr();
                paragraph.setPPr(pPr);
            }
            Ind ind = pPr.getInd();
            if (ind == null) {
                ind = context.objectFactory.createPPrBaseInd();
                pPr.setInd(ind);
            }
            HeaderFooterPageInfo pageInfo = context.getLayout().getPageInfo();
            ind.setLeft(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, x));
            ind.setRight(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, pageInfo.getPageSize().width - pageInfo.getRightMargin() - pageInfo.getLeftMargin() - x - width));

            context.setLastParagraphX(x);
            context.setLastParagraphWidth(width);
        }
    }

    /**
     * Set the paragraph borders.
     * @param paragraph The doc baragraph.
     * @param element The box element container with border infos.
     * @param context The visitor context.
     */
    static void setParagraphBorders(P paragraph, JRBoxContainer element, J2WDocx4jPrintElementVisitorContext context) {
        if (element != null) {
            JRLineBox lineBox = element.getLineBox();
            if (lineBox != null) {
                PPr pPr = paragraph.getPPr();
                if (pPr == null) {
                    pPr = context.objectFactory.createPPr();
                    paragraph.setPPr(pPr);
                }
                PBdr pBdr = pPr.getPBdr();
                if (pBdr == null) {
                    pBdr = context.objectFactory.createPPrBasePBdr();
                    pPr.setPBdr(pBdr);
                }

                pBdr.setBottom(buildBorder(lineBox.getBottomPen(), context));
                pBdr.setTop(buildBorder(lineBox.getTopPen(), context));
                pBdr.setRight(buildBorder(lineBox.getRightPen(), context));
                pBdr.setLeft(buildBorder(lineBox.getLeftPen(), context));
            }
        }
    }

    /**
     * Set table cell borders.
     * @param cell A doc table cell.
     * @param element The box element container with border infos.
     * @param context The visitor context.
     */
    static void setTableCellBorders(Tc cell, JRBoxContainer element, J2WDocx4jPrintElementVisitorContext context) {
        if (element != null) {
            JRLineBox lineBox = element.getLineBox();
            if (lineBox != null) {
                lineBox.getBottomPen();

                TcPr tcPr = cell.getTcPr();
                if (tcPr == null) {
                    tcPr = context.objectFactory.createTcPr();
                    cell.setTcPr(tcPr);
                }
                TcBorders tcBorders = tcPr.getTcBorders();
                if (tcBorders == null) {
                    tcBorders = context.objectFactory.createTcPrInnerTcBorders();
                    tcPr.setTcBorders(tcBorders);
                }

                tcBorders.setBottom(buildBorder(lineBox.getBottomPen(), context));
                tcBorders.setTop(buildBorder(lineBox.getTopPen(), context));
                tcBorders.setRight(buildBorder(lineBox.getRightPen(), context));
                tcBorders.setLeft(buildBorder(lineBox.getLeftPen(), context));
            }
        }
    }

    /**
     * Build a single edge for a cell.
     * @param pen The border style.
     * @param context The visitor context.
     * @return The border for cell.
     */
    static CTBorder buildBorder(JRBoxPen pen, J2WDocx4jPrintElementVisitorContext context) {
        if (pen == null || pen.getLineWidth() == 0f)
            return null;
        else {
            CTBorder ctBorder = context.objectFactory.createCTBorder();
            ctBorder.setColor(colorToStringFormat(pen.getPen(pen.getBox()).getLineColor()));
            ctBorder.setSz(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.POINTS, pen.getLineWidth()));
            switch (pen.getLineStyleValue()) {
                case SOLID:
                    ctBorder.setVal(STBorder.SINGLE);
                    break;
                case DASHED:
                    ctBorder.setVal(STBorder.DASHED);
                    break;
                case DOTTED:
                    ctBorder.setVal(STBorder.DOTTED);
                    break;
                case DOUBLE:
                    ctBorder.setVal(STBorder.DOUBLE);
                    break;
                default:
                    ctBorder.setVal(STBorder.SINGLE);
                    break;
            }
            ctBorder.setColor(colorToStringFormat(pen.getLineColor()));

            return ctBorder;
        }
    }

    /**
     * Print visitor context. This class improves the base visitor context with extra information useful to build the output document,
     * like jaxb object factories, table infos, styles, etc...
     */
    static class J2WDocx4jPrintElementVisitorContext extends J2WAbstractPrintElementVisitorContext {
        /** Reports build till now */
        private int reportCount;
        /** Table infos */
        private Map tables;
        /** Remapped style name in document */
        private Map remapStylesNameByJrStyle;
        /** Built style's name */
        private final Set allStyles;
        /** The current header */
        private Hdr headerContent;
        /** The current footer */
        private Ftr footerContent;
        /** The current section */
        private SectPr currentSection;
        /** The paragraph of the current section */
        private P sectionParagraph;

        /** General jaxb object factory */
        private final org.docx4j.wml.ObjectFactory objectFactory;
        /** Pic jaxb object factory */
        private final org.docx4j.vml.ObjectFactory picObjectFactory;
        /** Drawing jaxb object factory */
        private final org.docx4j.dml.ObjectFactory drawingObjectFactory;

        /**
         * Construct the context.
         */
        public J2WDocx4jPrintElementVisitorContext() {
            this.reportCount = 0;
            this.allStyles = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
            this.objectFactory = Context.getWmlObjectFactory();
            this.picObjectFactory = new org.docx4j.vml.ObjectFactory();
            this.drawingObjectFactory = new org.docx4j.dml.ObjectFactory();
        }

        /**
         * Reset the document status (reset remapped style's name).
         */
        private void resetDocumentStatus() {
            this.remapStylesNameByJrStyle = new TreeMap<>();
        }

        /**
         * Reset the page status (tables end last docx element).
         */
        public void resetPageStatus() {
            this.tables = new TreeMap<>();
            this.setLastDocxElement(null);
        }
    }

    /**
     * A table informations.
     */
    private static class TableDocInfo {
        /** Docx table */
        private final Tbl table;

        /**
         * Construct a table info with a docx table,
         * @param table The docx table.
         */
        public TableDocInfo(Tbl table) {
            this.table = table;
        }
    }

    /**
     * Comparator that sorts Point from bottom to top and from right to left.
     */
    private static class CellToRemovePointComparator implements Comparator {
        /** Constructor */
        private CellToRemovePointComparator() {
        }

        @Override
        public int compare(Point o1, Point o2) {
            if (o1.y > o2.y)
                return 1;
            else if (o1.y < o2.y)
                return -1;
            else if (o1.x > o2.x)
                return -1;
            else if (o1.x < o2.x)
                return 1;
            else
                return 0;
        }
    }

    /**
     * Docx4j print element visitor. This class can "visit" every jasper report element and draw it in the
     * output document.
     */
    protected static class J2WDocxPrintElementVisitor implements PrintElementVisitor {
        /** Attribute styled text selector (all attributes) */
        protected JRStyledTextAttributeSelector allSelector;
        /** Jasper context */
        protected JasperReportsContext jasperReportsContext;

        /**
         * Construct a visitor context.
         * @param allSelector The attribute selector.
         * @param jasperReportsContext The jasper context.
         */
        public J2WDocxPrintElementVisitor(JRStyledTextAttributeSelector allSelector, JasperReportsContext jasperReportsContext) {
            this.allSelector = allSelector;
            this.jasperReportsContext = jasperReportsContext;
        }

        /**
         * Search or create the output paragraph for the current element to visit.
         * @param compPosition The element position.
         * @param container The element container.
         * @param arg The visitor context.
         * @param createParagraph {@code true} to create the missing paragraph.
         * @return The paragraph for the element (both free paragraphs and table cell paragraphs).
         */
        protected FoundDocParagraph findParagraph(ComponentPosition compPosition, JRBoxContainer container, J2WDocx4jPrintElementVisitorContext arg, boolean createParagraph) {
            P paragraph = null;

            FoundDocParagraph parent = null;

            if (compPosition.getParent() != null)
                parent = this.findParagraph(compPosition.getParent(), container, arg, false);

            Tc cell = null;
            // For element in table cell
            if (compPosition instanceof ComponentPositionInTable) {
                ComponentPositionInTable position = (ComponentPositionInTable) compPosition;
                TableDocInfo tableDocInfo = buildTables(position, arg);
                Tbl xwpfTable = tableDocInfo.table;

                Tr row = (Tr) xwpfTable.getContent().get(position.getRow());
                cell = (Tc) row.getContent().get(position.getCol());
                if (cell.getContent().isEmpty()) {
                    paragraph = arg.objectFactory.createP();
                    cell.getContent().add(paragraph);
                }
                else
                    paragraph = (P) cell.getContent().get(0);

                TcPr tcPr = cell.getTcPr();
                if (tcPr == null) {
                    tcPr = arg.objectFactory.createTcPr();
                    cell.setTcPr(tcPr);
                }
                // Background
                CTShd ctShd = arg.objectFactory.createCTShd();
                ctShd.setFill(COLOR_AUTO);
                ctShd.setVal(STShd.CLEAR);
                tcPr.setShd(ctShd);

                CTVerticalJc ctVerticalJc = arg.objectFactory.createCTVerticalJc();
                ctVerticalJc.setVal(STVerticalJc.CENTER);
                tcPr.setVAlign(ctVerticalJc);
                if (container != null) {
                    setTableCellBorders(cell, container, arg);
                    JRLineBox lineBox = container.getLineBox();
                    if (lineBox != null) {
                        TcMar ctTcMar = tcPr.getTcMar();
                        if (ctTcMar == null) {
                            ctTcMar = arg.objectFactory.createTcMar();
                            tcPr.setTcMar(ctTcMar);
                        }
                        TblWidth b = arg.objectFactory.createTblWidth();
                        TblWidth t = arg.objectFactory.createTblWidth();
                        TblWidth l = arg.objectFactory.createTblWidth();
                        TblWidth r = arg.objectFactory.createTblWidth();

                        b.setType(TblWidth.TYPE_DXA);
                        t.setType(TblWidth.TYPE_DXA);
                        l.setType(TblWidth.TYPE_DXA);
                        r.setType(TblWidth.TYPE_DXA);

                        if (lineBox.getBottomPadding() != null)
                            b.setW(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, lineBox.getBottomPadding()));
                        else
                            b = null;
                        if (lineBox.getTopPadding() != null)
                            t.setW(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, lineBox.getTopPadding()));
                        else
                            t = null;
                        if (lineBox.getLeftPadding() != null)
                            l.setW(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, lineBox.getLeftPadding()));
                        else
                            l = null;
                        if (lineBox.getRightPadding() != null)
                            r.setW(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, lineBox.getRightPadding()));
                        else
                            r = null;

                        ctTcMar.setBottom(b);
                        ctTcMar.setTop(t);
                        ctTcMar.setLeft(l);
                        ctTcMar.setRight(r);
                    }
                }
            }
            if (paragraph == null && parent != null)
                paragraph = parent.getParagraph();

            if (paragraph == null && createParagraph) {
                // Add the new paragraph in the document
                paragraph = arg.objectFactory.createP();
                if (compPosition.getDocumentPart() == DocxDocumentPart.HEADER)
                    arg.headerContent.getContent().add(paragraph);
                else if (compPosition.getDocumentPart() == DocxDocumentPart.FOOTER)
                    arg.footerContent.getContent().add(paragraph);
                else
                    arg.getDocument().getMainDocumentPart().getContent().add(paragraph);
            }
            if (paragraph != null) {
                if (container != null && cell == null)
                    setParagraphBorders(paragraph, container, arg);

                PPr pPr = paragraph.getPPr();
                if (pPr == null) {
                    pPr = arg.objectFactory.createPPr();
                    paragraph.setPPr(pPr);
                }
                ParaRPr rPr = pPr.getRPr();
                if (rPr == null) {
                    rPr = arg.objectFactory.createParaRPr();
                    pPr.setRPr(rPr);
                }
                if (container != null && container.getStyle() != null) {
                    PStyle pStyle = arg.objectFactory.createPPrBasePStyle();
                    pStyle.setVal(arg.remapStylesNameByJrStyle.get(container.getStyle().getName()));
                    pPr.setPStyle(pStyle);
                }
                if (cell == null) {
                    int leftPadding = 0;
                    int rightPadding = 0;
                    if (container != null && container.getLineBox() != null) {
                        JRLineBox lineBox = container.getLineBox();
                        if (lineBox.getLeftPadding() != null)
                            leftPadding = lineBox.getLeftPadding();
                        if (lineBox.getRightPadding() != null)
                            rightPadding = lineBox.getRightPadding();
                    }
                    setParagraphMargins(paragraph, compPosition.getX(), compPosition.getWidth() - rightPadding - leftPadding, arg);
                }
            }

            int pIndex = -1;
            if (paragraph != null && cell == null) {
                if (compPosition.getDocumentPart() == DocxDocumentPart.HEADER)
                    pIndex = arg.headerContent.getContent().size() - 1;
                else if (compPosition.getDocumentPart() == DocxDocumentPart.FOOTER)
                    pIndex = arg.footerContent.getContent().size() - 1;
                else
                    pIndex = arg.getDocument().getMainDocumentPart().getContent().size() - 1;
            }
            return new FoundDocParagraph(cell, paragraph, pIndex);
        }

        public void visit(JRPrintText textElement, J2WDocx4jPrintElementVisitorContext arg) {
            ComponentPosition compPosition = arg.getLayout().getElementPosition(textElement);
            FoundDocParagraph foundDocParagraph = this.findParagraph(compPosition, textElement, arg, true);
            P paragraph = foundDocParagraph.getParagraph();

            boolean setInd = false;
            boolean setPPr = false;
            int pCount = 0;

            PPr pPr = paragraph.getPPr();
            if (pPr == null)
                pPr = arg.objectFactory.createPPr();

            Ind ind = pPr.getInd();
            if (ind == null)
                ind = arg.objectFactory.createPPrBaseInd();

            Spacing spacing = buildParagraphSpacing(textElement.getParagraph(), pPr.getSpacing(), arg, true);

            if (textElement.getOwnHorizontalAlignmentValue() != null) {
                Jc jc = arg.objectFactory.createJc();
                pPr.setJc(jc);
                switch (textElement.getOwnHorizontalAlignmentValue()) {
                    case LEFT:
                        jc.setVal(JcEnumeration.LEFT);
                        break;
                    case CENTER:
                        jc.setVal(JcEnumeration.CENTER);
                        break;
                    case RIGHT:
                        jc.setVal(JcEnumeration.RIGHT);
                        break;
                    case JUSTIFIED:
                        jc.setVal(JcEnumeration.BOTH);
                        break;
                }
                setPPr = true;
            }

            if (textElement.getOwnVerticalAlignmentValue() != null) {
                PPrBase.TextAlignment alignment = arg.objectFactory.createPPrBaseTextAlignment();
                pPr.setTextAlignment(alignment);
                switch (textElement.getOwnVerticalAlignmentValue()) {
                    case TOP:
                        alignment.setVal("top");
                        break;
                    case MIDDLE:
                        alignment.setVal("center");
                        break;
                    case BOTTOM:
                        alignment.setVal("bottom");
                        break;
                    case JUSTIFIED:
                        alignment.setVal("auto");
                        break;
                }
                setPPr = true;
            }


            JRParagraph jrParagraph = textElement.getParagraph();
            if (jrParagraph.getOwnFirstLineIndent() != null) {
                ind.setFirstLine(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, jrParagraph.getOwnFirstLineIndent()));
                setInd = true;
            }
            if (jrParagraph.getOwnLeftIndent() != null) {
                ind.setLeft(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, jrParagraph.getOwnLeftIndent()));
                setInd = true;
            }
            if (jrParagraph.getOwnRightIndent() != null) {
                ind.setRight(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, jrParagraph.getOwnRightIndent()));
                setInd = true;
            }

            if (textElement.getOwnBackcolor() != null) {
                CTShd shd = pPr.getShd();
                if (shd == null) {
                    shd = arg.objectFactory.createCTShd();
                    pPr.setShd(shd);
                }

                shd.setFill(colorToStringFormat(textElement.getOwnBackcolor()));
                shd.setVal(STShd.CLEAR);
                setPPr = true;
            }


            if (setPPr || setInd || spacing != null)
                paragraph.setPPr(pPr);
            if (setInd)
                pPr.setInd(ind);

            pPr.setSpacing(spacing);

            JRStyledText styledText = textElement.getFullStyledText(allSelector);

            String fullText = styledText.getText();


            AttributedCharacterIterator iterator = styledText.getAttributedString().getIterator();

            int runLimit = 0;
            boolean lastNewLine = false;
            while(runLimit < styledText.length() && (runLimit = iterator.getRunLimit()) <= styledText.length()) {
                R docRun = arg.objectFactory.createR();
                paragraph.getContent().add(docRun);
                this.setRunFontStyle(docRun, textElement, iterator.getAttributes(), arg);
                String value = fullText.substring(iterator.getIndex(), runLimit);

                StringTokenizer tkzer = new StringTokenizer(value, "\n", true);
                while(tkzer.hasMoreTokens()) {
                    String token = tkzer.nextToken();
                    boolean isNewLine = "\n".equals(token);
                    if (lastNewLine || isNewLine) {
                        if (tkzer.hasMoreTokens()) {
                            P newParagraph = arg.objectFactory.createP();
                            pCount++;
                            if (foundDocParagraph.isTableCell())
                                foundDocParagraph.getCell().getContent().add(newParagraph);
                            else {
                                if (compPosition.getDocumentPart() == DocxDocumentPart.HEADER)
                                    arg.headerContent.getContent().add(newParagraph);
                                else if (compPosition.getDocumentPart() == DocxDocumentPart.FOOTER)
                                    arg.footerContent.getContent().add(newParagraph);
                                else
                                    arg.getDocument().getMainDocumentPart().getContent().add(newParagraph);
                            }
                            PPr newPPr = this.cloneParagraphProperties(arg, pPr);
                            if (pPr.getSpacing() != null)
                                pPr.getSpacing().setAfter(BigInteger.ZERO);
                            if (newPPr.getSpacing() != null)
                                newPPr.getSpacing().setBefore(BigInteger.ZERO);

                            newParagraph.setPPr(newPPr);
                            paragraph = newParagraph;
                            docRun = arg.objectFactory.createR();
                            paragraph.getContent().add(docRun);
                            this.setRunFontStyle(docRun, textElement, iterator.getAttributes(), arg);
                            lastNewLine = false;
                        }
                        else
                            lastNewLine = true;
                    }
                    if (!isNewLine) {
                        Text text = arg.objectFactory.createText();
                        text.setValue(token);
                        text.setSpace("preserve");
                        docRun.getContent().add(text);
                    }
                }
                iterator.setIndex(runLimit);
            }

            if (!foundDocParagraph.isTableCell()) {
                float textHeight = textElement.getTextHeight();

                int realHeight = 0;
                int topMargin = 0;
                if (jrParagraph.getSpacingAfter() != null)
                    realHeight += jrParagraph.getSpacingAfter();
                if (jrParagraph.getSpacingBefore() != null) {
                    realHeight += jrParagraph.getSpacingBefore();
                    topMargin = jrParagraph.getSpacingBefore();
                }
                JRLineBox lineBox = textElement.getLineBox();
                if (lineBox != null) {
                    if (lineBox.getBottomPadding() != null)
                        realHeight += lineBox.getBottomPadding();
                    if (lineBox.getTopPadding() != null)
                        realHeight += lineBox.getTopPadding();
                }
                realHeight += textHeight; // EConvertMisure.HALF_POINTS.convertInto(EConvertMisure.POINTS, textHeight);

                StyleDefinitionsPart stylePart = buildStylePart(arg, false);
                Style style = stylePart.getStyleById("Normal");
                int defaultFontSize = 12;
                String defaultFontName = "Times New Roman";
                if (style != null && style.getPPr() != null && style.getPPr().getRPr() != null) {
                    ParaRPr defaultRPr = style.getPPr().getRPr();
                    if (defaultRPr.getRFonts() != null && defaultRPr.getRFonts().getAscii() != null)
                        defaultFontName = defaultRPr.getRFonts().getAscii();
                    if (defaultRPr.getSz() != null && defaultRPr.getSz().getVal() != null)
                        defaultFontSize = (int) EConvertMisure.HALF_POINTS.convertInto(EConvertMisure.POINTS, defaultRPr.getSz().getVal().doubleValue());
                }

                Font newLineFont = new Font(defaultFontName, Font.PLAIN, defaultFontSize);
                LineMetrics lineMetrics = newLineFont.getLineMetrics("\n", new FontRenderContext(null, false, true));

                if (realHeight < compPosition.getHeight()) {
                    int gap = compPosition.getHeight() - realHeight;
                    int newLines;
                    if (arg.getConfiguration().getSpacingPolicy() == ESpacingPolicy.EDITABLE)
                        newLines = (int) (gap / lineMetrics.getHeight());
                    else
                        newLines = 0;
                    gap -= (newLines * defaultFontSize);
                    if (gap > defaultFontSize) {
                        Spacing spacingLast = paragraph.getPPr().getSpacing();
                        if (spacingLast == null) {
                            spacingLast = arg.objectFactory.createPPrBaseSpacing();
                            paragraph.getPPr().setSpacing(spacingLast);
                        }
                        Spacing spacingFirst = foundDocParagraph.getParagraph().getPPr().getSpacing();
                        if (spacingFirst == null) {
                            spacingFirst = arg.objectFactory.createPPrBaseSpacing();
                            foundDocParagraph.getParagraph().getPPr().setSpacing(spacingFirst);
                        }

                        BigInteger bigGap = EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, gap);

                        VerticalAlignEnum alignmentValue = textElement.getVerticalAlignmentValue();
                        if (alignmentValue == null)
                            alignmentValue = VerticalAlignEnum.JUSTIFIED;

                        switch (alignmentValue) {
                            case TOP:
                                spacingLast.setAfter(spacingLast.getAfter() != null ? spacingLast.getAfter().add(bigGap) : bigGap);
                                break;
                            case MIDDLE:
                            case JUSTIFIED: {
                                bigGap = bigGap.divide(BigInteger.valueOf(2));
                                spacingLast.setAfter(spacingLast.getAfter() != null ? spacingLast.getAfter().add(bigGap) : bigGap);
                                spacingFirst.setBefore(spacingFirst.getBefore() != null ? spacingFirst.getBefore().add(bigGap) : bigGap);
                            }
                            break;
                            case BOTTOM:
                                spacingFirst.setBefore(spacingFirst.getBefore() != null ? spacingFirst.getBefore().add(bigGap) : bigGap);
                                break;

                        }
                    }
                    this.createBeforeNewLinesParagraphs(arg, newLines, defaultFontSize, defaultFontName, foundDocParagraph.getParagraphIndexInDoc() + pCount + 1, compPosition.getDocumentPart());
                }
                if (arg.getLastY(compPosition.getDocumentPart()) < compPosition.getY()) {
                    int gap = compPosition.getY() - arg.getLastY(compPosition.getDocumentPart());
                    int newLines;
                    if (arg.getConfiguration().getSpacingPolicy() == ESpacingPolicy.EDITABLE)
                        newLines = (int) (gap / lineMetrics.getHeight());
                    else
                        newLines = 0;
                    gap -= (newLines * defaultFontSize);

                    if (gap > 0) {
                        Spacing spacingFirst = foundDocParagraph.getParagraph().getPPr().getSpacing();
                        if (spacingFirst == null) {
                            spacingFirst = arg.objectFactory.createPPrBaseSpacing();
                            foundDocParagraph.getParagraph().getPPr().setSpacing(spacingFirst);
                        }
                        BigInteger bigGap = EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, gap + topMargin);
                        spacingFirst.setBefore(spacingFirst.getBefore() != null? spacingFirst.getBefore().add(bigGap) : bigGap);
                    }
                    this.createBeforeNewLinesParagraphs(arg, newLines, defaultFontSize, defaultFontName, foundDocParagraph.getParagraphIndexInDoc(), compPosition.getDocumentPart());
                }

                arg.setLastY(compPosition.getDocumentPart(), compPosition.getY() + compPosition.getHeight());

                arg.setLastDocxElement(LastDocxElement.TEXT_PARAGRAPH);
            }

        }

        /**
         * Add many paragraphs those indicated in {@code newLines}.
         * @param arg The visitor context.
         * @param newLines How many lines to add.
         * @param defaultFontSize The font size.
         * @param defaultFontName The font name.
         * @param startIndex The index in the document objects list.
         * @param part The document part.
         */
        protected void createBeforeNewLinesParagraphs(J2WDocx4jPrintElementVisitorContext arg, int newLines, int defaultFontSize,
                                                      String defaultFontName, int startIndex, DocxDocumentPart part) {
            for (int i = 0; i < newLines; i++) {
                P newLineP = arg.objectFactory.createP();
                PPr pPr = arg.objectFactory.createPPr();
                pPr.setRPr(arg.objectFactory.createParaRPr());
                HpsMeasure hpsMeasure = arg.objectFactory.createHpsMeasure();
                hpsMeasure.setVal(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.HALF_POINTS, defaultFontSize));
                pPr.getRPr().setSz(hpsMeasure);
                pPr.getRPr().setRFonts(arg.objectFactory.createRFonts());
                pPr.getRPr().getRFonts().setAscii(defaultFontName);

                Spacing spacing = arg.objectFactory.createPPrBaseSpacing();
                spacing.setAfter(BigInteger.ZERO);
                spacing.setBefore(BigInteger.ZERO);
                spacing.setBeforeLines(BigInteger.ZERO);
                spacing.setLine(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, defaultFontSize));
                spacing.setLineRule(STLineSpacingRule.EXACT);
                pPr.setSpacing(spacing);

                setParagraphMargins(newLineP, arg.getLastParagraphX(), arg.getLastParagraphWidth(), arg);

                newLineP.setPPr(pPr);
                R newLineR = arg.objectFactory.createR();
                newLineR.setRPr(arg.objectFactory.createRPr());
                Text text = arg.objectFactory.createText();
                text.setSpace("preserve");
                text.setValue("");
                newLineR.getContent().add(text);
                newLineP.getContent().add(newLineR);
                List content;
                switch (part) {
                    case HEADER:
                        content = arg.headerContent.getContent();
                        break;
                    case FOOTER:
                        content = arg.footerContent.getContent();
                        break;
                    default:
                    case BODY:
                        content = arg.getDocument().getMainDocumentPart().getContent();
                        break;
                }

                content.add(startIndex, newLineP);
            }
        }

        /**
         * Clone paragraphs properties.
         * @param arg The visitor context.
         * @param pPr The source paragraph properties.
         * @return Return a clone of the {@code pPr} properties.
         */
        protected PPr cloneParagraphProperties(J2WDocx4jPrintElementVisitorContext arg, PPr pPr) {
            Spacing spacing = pPr.getSpacing();

            PPr newPPr = arg.objectFactory.createPPr();
            newPPr.setInd(pPr.getInd());
            newPPr.setJc(pPr.getJc());
            newPPr.setPBdr(pPr.getPBdr());
            newPPr.setPStyle(pPr.getPStyle());
            newPPr.setRPr(pPr.getRPr());
            newPPr.setShd(pPr.getShd());
            if (spacing != null) {
                Spacing newSpacing = arg.objectFactory.createPPrBaseSpacing();
                newSpacing.setAfter(spacing.getAfter());
                newSpacing.setAfterAutospacing(spacing.isAfterAutospacing());
                newSpacing.setAfterLines(spacing.getAfterLines());
                newSpacing.setBefore(spacing.getBefore());
                newSpacing.setBeforeAutospacing(spacing.isBeforeAutospacing());
                newSpacing.setBeforeLines(spacing.getBeforeLines());
                newSpacing.setLine(spacing.getLine());
                newSpacing.setLineRule(spacing.getLineRule());
                newPPr.setSpacing(newSpacing);
            }
            newPPr.setTextAlignment(pPr.getTextAlignment());
            newPPr.setTextDirection(pPr.getTextDirection());

            return newPPr;
        }

        /**
         * Set the run (a text block in a paragraph) properties (font, size, colors, etc.)
         * @param docRun The document run.
         * @param textElement The jasper text element.
         * @param attributes The global jasper text element attributes.
         * @param arg The visitor context.
         */
        protected void setRunFontStyle(R docRun, JRPrintText textElement, Map attributes, J2WDocx4jPrintElementVisitorContext arg) {
            String styleName = textElement.getStyle() != null? textElement.getStyle().getName() : null;
            boolean setRPr = false;
            RPr ctrPr = docRun.getRPr();
            if (ctrPr == null)
                ctrPr = arg.objectFactory.createRPr();

            if (textElement.isOwnBold() != null) {
                BooleanDefaultTrue value = arg.objectFactory.createBooleanDefaultTrue();
                value.setVal(textElement.isOwnBold());
                ctrPr.setB(value);
                setRPr = true;
            }
            if (textElement.getOwnForecolor() != null) {
                org.docx4j.wml.Color color = arg.objectFactory.createColor();
                color.setVal(colorToStringFormat(textElement.getOwnForecolor()));
                ctrPr.setColor(color);
                setRPr = true;
            }
            if (textElement.getOwnFontName() != null) {
                RFonts rFonts = ctrPr.getRFonts();
                if (rFonts == null) {
                    rFonts = arg.objectFactory.createRFonts();
                    ctrPr.setRFonts(rFonts);
                }
                rFonts.setAscii(textElement.getOwnFontName());
                rFonts.setHAnsi(textElement.getOwnFontName());
                setRPr = true;
            }
            if (textElement.getOwnFontsize() != null) {
                HpsMeasure sz = ctrPr.getSz();
                if (sz == null) {
                    sz = arg.objectFactory.createHpsMeasure();
                    ctrPr.setSz(sz);
                }
                sz.setVal(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.HALF_POINTS, textElement.getOwnFontsize()));
                setRPr = true;
            }
            if (textElement.isOwnItalic() != null) {
                BooleanDefaultTrue i = arg.objectFactory.createBooleanDefaultTrue();
                ctrPr.setI(i);
                i.setVal(textElement.isOwnItalic());
                setRPr = true;
            }
            if (textElement.isOwnStrikeThrough() != null) {
                BooleanDefaultTrue strike = arg.objectFactory.createBooleanDefaultTrue();
                ctrPr.setStrike(strike);
                strike.setVal(textElement.isOwnStrikeThrough());
                setRPr = true;
            }
            if (textElement.isOwnUnderline() != null && textElement.isOwnUnderline()) {
                U value = arg.objectFactory.createU();
                ctrPr.setU(value);
                if (textElement.getStyle() != null && textElement.getStyle().getLinePen() != null && textElement.getStyle().getLinePen().getLineStyleValue() != null) {
                    switch (textElement.getStyle().getLinePen().getLineStyleValue()) {
                        case SOLID:
                            value.setVal(UnderlineEnumeration.SINGLE);
                            break;
                        case DASHED:
                            value.setVal(UnderlineEnumeration.DASH);
                            break;
                        case DOTTED:
                            value.setVal(UnderlineEnumeration.DOTTED);
                            break;
                        case DOUBLE:
                            value.setVal(UnderlineEnumeration.DOUBLE);
                            break;
                        default:
                            value.setVal(UnderlineEnumeration.SINGLE);
                            break;
                    }
                    if (textElement.getStyle().getLinePen().getOwnLineColor() != null)
                        value.setColor(colorToStringFormat(textElement.getStyle().getLinePen().getOwnLineColor()));
                }
                else {
                    value.setVal(UnderlineEnumeration.SINGLE);
                }

                setRPr = true;
            }

            Map docRunAttributes = buildDocRunAttributes(docRun, styleName, arg);


            if (attributes != null) {
                String fontFamily = this.readAttribute(attributes, docRunAttributes, TextAttribute.FAMILY);
                if (fontFamily != null) {
                    RFonts rFonts = ctrPr.getRFonts();
                    if (rFonts == null) {
                        rFonts = arg.objectFactory.createRFonts();
                        ctrPr.setRFonts(rFonts);
                    }
                    rFonts.setAscii(fontFamily);
                    rFonts.setHAnsi(fontFamily);
                    setRPr = true;
                }

                Number weight = this.readAttribute(attributes, docRunAttributes, TextAttribute.WEIGHT);
                if (weight != null) {
                    BooleanDefaultTrue value = arg.objectFactory.createBooleanDefaultTrue();
                    value.setVal(weight.floatValue() >= TextAttribute.WEIGHT_REGULAR);
                    ctrPr.setB(value);
                    setRPr = true;
                }

                Paint color = this.readAttribute(attributes, docRunAttributes, TextAttribute.FOREGROUND);
                if (color instanceof Color) {
                    org.docx4j.wml.Color color2 = arg.objectFactory.createColor();
                    color2.setVal(colorToStringFormat((Color) color));
                    ctrPr.setColor(color2);
                    setRPr = true;
                }

                Number size = this.readAttribute(attributes, docRunAttributes, TextAttribute.SIZE);
                if (size != null) {
                    HpsMeasure sz = ctrPr.getSz();
                    if (sz == null) {
                        sz = arg.objectFactory.createHpsMeasure();
                        ctrPr.setSz(sz);
                    }
                    sz.setVal(EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.HALF_POINTS, size.doubleValue()));
                    setRPr = true;
                }

                Number posture = this.readAttribute(attributes, docRunAttributes, TextAttribute.POSTURE);
                if (posture != null) {
                    BooleanDefaultTrue i = arg.objectFactory.createBooleanDefaultTrue();
                    ctrPr.setI(i);
                    i.setVal(posture.floatValue() >= TextAttribute.POSTURE_OBLIQUE);
                    setRPr = true;
                }

                Boolean strikeThrough = this.readAttribute(attributes, docRunAttributes, TextAttribute.STRIKETHROUGH);
                if (strikeThrough != null) {
                    BooleanDefaultTrue strike = arg.objectFactory.createBooleanDefaultTrue();
                    ctrPr.setStrike(strike);
                    strike.setVal(strikeThrough);
                    setRPr = true;
                }

                Integer underline = this.readAttribute(attributes, docRunAttributes, TextAttribute.UNDERLINE);
                if (underline != null) {
                    U value = arg.objectFactory.createU();
                    ctrPr.setU(value);
                    if (underline.equals(TextAttribute.UNDERLINE_ON))
                        value.setVal(UnderlineEnumeration.SINGLE);
                    else if (underline.equals(TextAttribute.UNDERLINE_LOW_ONE_PIXEL))
                        value.setVal(UnderlineEnumeration.THICK);
                    else if (underline.equals(TextAttribute.UNDERLINE_LOW_TWO_PIXEL))
                        value.setVal(UnderlineEnumeration.DOUBLE);
                    else if (underline.equals(TextAttribute.UNDERLINE_LOW_DOTTED))
                        value.setVal(UnderlineEnumeration.DOTTED_HEAVY);
                    else if (underline.equals(TextAttribute.UNDERLINE_LOW_GRAY))
                        value.setVal(UnderlineEnumeration.DOTTED);
                    else if (underline.equals(TextAttribute.UNDERLINE_LOW_DASHED))
                        value.setVal(UnderlineEnumeration.DASH);
                    else
                        value.setVal(UnderlineEnumeration.SINGLE);

                    setRPr = true;
                }

                Paint background = this.readAttribute(attributes, docRunAttributes, TextAttribute.BACKGROUND);
                if (background instanceof Color) {
                    CTShd shd = ctrPr.getShd();
                    if (shd == null) {
                        shd = arg.objectFactory.createCTShd();
                        ctrPr.setShd(shd);
                    }

                    shd.setFill(colorToStringFormat(textElement.getOwnBackcolor()));
                    shd.setVal(STShd.CLEAR);
                    setRPr = true;
                }
            }

            Locale locale = JRStyledTextAttributeSelector.getTextLocale(textElement);
            if (locale != null) {
                CTLanguage language = arg.objectFactory.createCTLanguage();
                language.setVal(locale.toLanguageTag());
                ctrPr.setLang(language);
                setRPr = true;
            }

            if (setRPr)
                docRun.setRPr(ctrPr);
        }

        /**
         * Extract text attributes from document run.
         * @param docRun The document run.
         * @param styleName The style name of the run paragraph.
         * @param arg The visitor context.
         * @return The text attributes of the run.
         */
        protected Map buildDocRunAttributes(R docRun, String styleName, J2WDocx4jPrintElementVisitorContext arg) {
            Map res = new HashMap<>();
            StyleDefinitionsPart stylePart = buildStylePart(arg, false);

            PPr stylePPr = null;
            RPr styleRPr = null;

            RPr rPr = docRun.getRPr();
            if (stylePart != null) {
                Style style = stylePart.getStyleById(styleName);
                if (style != null) {
                    stylePPr = style.getPPr();
                    styleRPr = style.getRPr();
                }
            }

            // Font family
            if (rPr != null && rPr.getRFonts() != null)
                res.put(TextAttribute.FAMILY, rPr.getRFonts().getAscii());
            else if (styleRPr != null && styleRPr.getRFonts() != null && styleRPr.getRFonts().getAscii() != null)
                res.put(TextAttribute.FAMILY, styleRPr.getRFonts().getAscii());

            // Bold
            if (rPr != null && rPr.getB() != null)
                res.put(TextAttribute.WEIGHT, rPr.getB().isVal() ? TextAttribute.WEIGHT_BOLD : TextAttribute.WEIGHT_REGULAR);
            else if (styleRPr != null && styleRPr.getB() != null)
                res.put(TextAttribute.WEIGHT, styleRPr.getB().isVal()? TextAttribute.WEIGHT_BOLD : TextAttribute.WEIGHT_REGULAR);

            // Fore color
            if (rPr != null && rPr.getColor() != null)
                res.put(TextAttribute.FOREGROUND, stringToColorParse(rPr.getColor().getVal()));
            else if (styleRPr != null && styleRPr.getColor() != null)
                res.put(TextAttribute.FOREGROUND, stringToColorParse(styleRPr.getColor().getVal()));

            // Font size
            if (rPr != null && rPr.getSz() != null)
                res.put(TextAttribute.SIZE, EConvertMisure.HALF_POINTS.convertInto(EConvertMisure.POINTS, rPr.getSz().getVal().doubleValue()));
            else if (styleRPr != null && styleRPr.getSz() != null)
                res.put(TextAttribute.SIZE, EConvertMisure.HALF_POINTS.convertInto(EConvertMisure.POINTS, styleRPr.getSz().getVal().doubleValue()));

            // Italic
            if (rPr != null && rPr.getI() != null)
                res.put(TextAttribute.POSTURE, rPr.getI().isVal()? TextAttribute.POSTURE_OBLIQUE : TextAttribute.POSTURE_REGULAR);
            else if (styleRPr != null && styleRPr.getI() != null)
                res.put(TextAttribute.POSTURE, styleRPr.getI().isVal()? TextAttribute.POSTURE_OBLIQUE : TextAttribute.POSTURE_REGULAR);

            // Strike
            if (rPr != null && rPr.getStrike() != null)
                res.put(TextAttribute.STRIKETHROUGH, rPr.getStrike().isVal());
            else if (styleRPr != null && styleRPr.getStrike() != null)
                res.put(TextAttribute.STRIKETHROUGH, styleRPr.getStrike().isVal());

            // Underline
            U ctUnderline = null;
            if (rPr != null && rPr.getU() != null)
                ctUnderline = rPr.getU();
            else if (styleRPr != null && styleRPr.getU() != null)
                ctUnderline = styleRPr.getU();

            if (ctUnderline != null) {
                UnderlineEnumeration val = ctUnderline.getVal();
                switch (val) {
                    case SINGLE:
                        res.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
                        break;
                    case DOUBLE:
                        res.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_LOW_TWO_PIXEL);
                        break;
                    case THICK:
                        res.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_LOW_ONE_PIXEL);
                        break;
                    case DOTTED_HEAVY:
                        res.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_LOW_DOTTED);
                        break;
                    case DOTTED:
                        res.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_LOW_GRAY);
                        break;
                    case DASH:
                        res.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_LOW_DASHED);
                        break;
                    case NONE:
                        break;
                    default:
                        res.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
                        break;
                }
            }

            // Background
            if (rPr != null && rPr.getShd() != null)
                res.put(TextAttribute.BACKGROUND, stringToColorParse(rPr.getShd().getColor()));
            else if (styleRPr != null && styleRPr.getShd() != null)
                res.put(TextAttribute.BACKGROUND, stringToColorParse(styleRPr.getShd().getColor()));
            else if (stylePPr != null && stylePPr.getShd() != null)
                res.put(TextAttribute.BACKGROUND, stringToColorParse(stylePPr.getShd().getColor()));

            return res;
        }

        /**
         * Read the text attribute difference between the two parameters map:
         * 
    *
  • if run has not the attribute, return the jasper attribute
  • *
  • if both run and jasper have not the attribute, return {@code null}
  • *
  • if both attributes are present but they are not equal, return the jasper value
  • *
  • otherwise return {@code null}
  • *
* @param jasperAttributes The jasper attributes. * @param runAttributes The run attributes. * @param attributeName The attribute name. * @param The type of attribute. * @return The different attribute value. */ private V readAttribute(Map jasperAttributes, Map runAttributes, Attribute attributeName) { @SuppressWarnings("unchecked") V jasperValue = (V) jasperAttributes.get(attributeName); @SuppressWarnings("unchecked") V runValue = (V) runAttributes.get(attributeName); if (runValue == null) return jasperValue; else if (jasperValue == null) return null; else if (!jasperValue.equals(runValue)) return jasperValue; else return null; } public void visit(JRPrintImage image, J2WDocx4jPrintElementVisitorContext arg) { Renderable renderable = image.getRenderable(); if (renderable != null) { String imageMimeType; String imageType; switch (renderable.getImageTypeValue()) { case UNKNOWN: throw new JRRuntimeException("Unknown image type: " + image.getOrigin()); case GIF: imageMimeType = ContentTypes.IMAGE_GIF; imageType = "gif"; break; case JPEG: imageMimeType = ContentTypes.IMAGE_JPEG; imageType = "jpeg"; break; case PNG: imageMimeType = ContentTypes.IMAGE_PNG; imageType = "png"; break; case TIFF: imageMimeType = ContentTypes.IMAGE_TIFF; imageType = "tiff"; break; default: throw new JRRuntimeException("Unknown image type: (" + renderable.getImageTypeValue() + ") " + image.getOrigin()); } try { BufferedImage bImage = ImageIO.read(new ByteArrayInputStream(renderable.getImageData(jasperReportsContext))); ComponentPosition position = arg.getLayout().getElementPosition(image); ImageInfo imageInfo = new ImageInfo(null, imageMimeType); ImageSize size = new ImageSize(bImage.getWidth(), bImage.getHeight(), bImage.getColorModel().getPixelSize()); size.calcSizeFromPixels(); imageInfo.setSize(size); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); ImageIO.write(bImage, imageType, bytes); BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(arg.getDocument(), arg.getDocument().getMainDocumentPart(), bytes.toByteArray(), imageMimeType); imagePart.setImageInfo(imageInfo); Inline imageInline = imagePart.createImageInline(image.getRenderable().getId(), image.getKey(), image.getPrintElementId(), image.getSourceElementId(), false); /* START default */ int leftPadding = image.getLineBox().getLeftPadding(); int topPadding = image.getLineBox().getTopPadding(); int rightPadding = image.getLineBox().getRightPadding(); int bottomPadding = image.getLineBox().getBottomPadding(); int availableImageWidth = position.getWidth() - leftPadding - rightPadding; availableImageWidth = availableImageWidth < 0 ? 0 : availableImageWidth; int availableImageHeight = position.getHeight() - topPadding - bottomPadding; availableImageHeight = availableImageHeight < 0 ? 0 : availableImageHeight; Renderable renderer = image.getRenderable(); if ( renderer != null && availableImageWidth > 0 && availableImageHeight > 0 ) { if (renderer.getTypeValue() == RenderableTypeEnum.IMAGE) { // Non-lazy image renderers are all asked for their image data at some point. // Better to test and replace the renderer now, in case of lazy load error. renderer = RenderableUtil.getInstance(jasperReportsContext).getOnErrorRendererForImageData(renderer, image.getOnErrorTypeValue()); } } else { renderer = null; } if (renderer != null) { FoundDocParagraph foundDocParagraph = this.findParagraph(position, image, arg, true); int width = availableImageWidth; int height = availableImageHeight; double normalWidth = availableImageWidth; double normalHeight = availableImageHeight; // Image load might fail. Renderable tmpRenderer = RenderableUtil.getInstance(jasperReportsContext).getOnErrorRendererForDimension(renderer, image.getOnErrorTypeValue()); Dimension2D dimension = tmpRenderer == null ? null : tmpRenderer.getDimension(jasperReportsContext); // If renderer was replaced, ignore image dimension. if (tmpRenderer == renderer && dimension != null) { normalWidth = dimension.getWidth(); normalHeight = dimension.getHeight(); } double cropTop = 0; double cropLeft = 0; double cropBottom = 0; double cropRight = 0; switch (image.getScaleImageValue()) { case FILL_FRAME: { width = availableImageWidth; height = availableImageHeight; break; } case CLIP: { if (normalWidth > availableImageWidth) { switch (image.getHorizontalAlignmentValue()) { case RIGHT: { cropLeft = 65536 * (normalWidth - availableImageWidth) / normalWidth; cropRight = 0; break; } case CENTER: { cropLeft = 65536 * (-availableImageWidth + normalWidth) / normalWidth / 2; cropRight = cropLeft; break; } case LEFT: default: { cropLeft = 0; cropRight = 65536 * (normalWidth - availableImageWidth) / normalWidth; break; } } width = availableImageWidth; cropLeft = cropLeft / 0.75d; cropRight = cropRight / 0.75d; } else { width = (int) normalWidth; } if (normalHeight > availableImageHeight) { switch (image.getVerticalAlignmentValue()) { case TOP: { cropTop = 0; cropBottom = 65536 * (normalHeight - availableImageHeight) / normalHeight; break; } case MIDDLE: { cropTop = 65536 * (normalHeight - availableImageHeight) / normalHeight / 2; cropBottom = cropTop; break; } case BOTTOM: default: { cropTop = 65536 * (normalHeight - availableImageHeight) / normalHeight; cropBottom = 0; break; } } height = availableImageHeight; cropTop = cropTop / 0.75d; cropBottom = cropBottom / 0.75d; } else { height = (int) normalHeight; } break; } case RETAIN_SHAPE: default: { if (availableImageHeight > 0) { double ratio = normalWidth / normalHeight; if (ratio > availableImageWidth / (double) availableImageHeight) { width = availableImageWidth; height = (int) (width / ratio); } else { height = availableImageHeight; width = (int) (ratio * height); } } } } // convert the inline to an anchor (xml contents are essentially the same) String anchorXml = XmlUtils.marshaltoString(imageInline, true, false, Context.jc, Namespaces.NS_WORD12, "anchor", Inline.class); org.docx4j.dml.ObjectFactory dmlFactory = arg.drawingObjectFactory; org.docx4j.dml.wordprocessingDrawing.ObjectFactory wordDmlFactory = new org.docx4j.dml.wordprocessingDrawing.ObjectFactory(); Anchor anchor = (Anchor) XmlUtils.unmarshalString(anchorXml, Context.jc, Anchor.class); anchor.setDistT(0L); anchor.setDistB(0L); anchor.setDistL(0L); anchor.setDistR(0L); anchor.setSimplePosAttr(!foundDocParagraph.isTableCell()); anchor.setRelativeHeight(0); anchor.setBehindDoc(false); anchor.setLocked(true); anchor.setLayoutInCell(true); anchor.setAllowOverlap(true); anchor.setSimplePos(dmlFactory.createCTPoint2D()); if (!foundDocParagraph.isTableCell()) { anchor.getSimplePos().setX((long) EConvertMisure.POINTS.convertInto(EConvertMisure.th20_POINTS, position.getX())); anchor.getSimplePos().setY((long) EConvertMisure.POINTS.convertInto(EConvertMisure.th20_POINTS, position.getY())); } else { anchor.getSimplePos().setX(0); anchor.getSimplePos().setY(0); } anchor.setPositionH(wordDmlFactory.createCTPosH()); anchor.getPositionH().setRelativeFrom(STRelFromH.COLUMN); if (image.getHorizontalAlignmentValue() != null) { switch (image.getHorizontalAlignmentValue()) { case RIGHT: anchor.getPositionH().setAlign(STAlignH.RIGHT); break; case CENTER: anchor.getPositionH().setAlign(STAlignH.CENTER); break; case JUSTIFIED: anchor.getPositionH().setAlign(STAlignH.INSIDE); break; case LEFT: default: anchor.getPositionH().setAlign(STAlignH.LEFT); break; } } anchor.setPositionV(wordDmlFactory.createCTPosV()); anchor.getPositionV().setPosOffset(null); anchor.getPositionV().setRelativeFrom(STRelFromV.LINE); if (image.getVerticalAlignmentValue() != null) { switch (image.getVerticalAlignmentValue()) { case TOP: anchor.getPositionV().setAlign(STAlignV.TOP); break; case BOTTOM: anchor.getPositionV().setAlign(STAlignV.BOTTOM); break; case JUSTIFIED: anchor.getPositionV().setAlign(STAlignV.INSIDE); break; case MIDDLE: default: anchor.getPositionV().setAlign(STAlignV.CENTER); break; } } anchor.setWrapNone(wordDmlFactory.createCTWrapNone()); Pic pic = anchor.getGraphic().getGraphicData().getPic(); CTBlipFillProperties blipFill = pic.getBlipFill(); CTRelativeRect srcRect = dmlFactory.createCTRelativeRect(); srcRect.setB((int) cropBottom); srcRect.setL((int) cropLeft); srcRect.setR((int) cropRight); srcRect.setT((int) cropTop); blipFill.setSrcRect(srcRect); CTStretchInfoProperties stretch = dmlFactory.createCTStretchInfoProperties(); stretch.setFillRect(dmlFactory.createCTRelativeRect()); blipFill.setStretch(stretch); CTPositiveSize2D extent = anchor.getExtent(); CTShapeProperties spPr = pic.getSpPr(); if (spPr == null) { spPr = arg.drawingObjectFactory.createCTShapeProperties(); pic.setSpPr(spPr); } spPr.setBwMode(STBlackWhiteMode.AUTO); CTTransform2D xfrm = spPr.getXfrm(); if (xfrm == null) { xfrm = arg.drawingObjectFactory.createCTTransform2D(); spPr.setXfrm(xfrm); } CTPositiveSize2D ext = xfrm.getExt(); if (ext == null) { ext = arg.drawingObjectFactory.createCTPositiveSize2D(); xfrm.setExt(ext); } extent.setCx((long) EConvertMisure.POINTS.convertInto(EConvertMisure.EMU, width)); extent.setCy((long) EConvertMisure.POINTS.convertInto(EConvertMisure.EMU, height)); ext.setCx(extent.getCx()); ext.setCy(extent.getCy()); CTPoint2D off = dmlFactory.createCTPoint2D(); off.setX(0); off.setY(0); xfrm.setOff(off); CTPresetGeometry2D prstGeom = spPr.getPrstGeom(); if (prstGeom == null) { prstGeom = arg.drawingObjectFactory.createCTPresetGeometry2D(); spPr.setPrstGeom(prstGeom); } prstGeom.setPrst(STShapeType.RECT); if (prstGeom.getAvLst() == null) { prstGeom.setAvLst(arg.drawingObjectFactory.createCTGeomGuideList()); } /* END default */ Drawing drawing = arg.objectFactory.createDrawing(); drawing.getAnchorOrInline().add(anchor); P paragraph = foundDocParagraph.getParagraph(); R run = arg.objectFactory.createR(); run.setRPr(arg.objectFactory.createRPr()); paragraph.getContent().add(run); run.getContent().add(drawing); // if (!foundDocParagraph.isTableCell()) // arg.setLastY(position.getDocumentPart(), position.getY() + position.getHeight()); } } catch (JRException e) { throw new JRRuntimeException("Unreadable image", e); } catch (Exception e) { throw new JRRuntimeException("Write image error", e); } } } /** * Visiti a generic shape. * @param drawingShape The shape jaxb object. * @param paragraph The doc paragraph that contains the shape. * @param position The component position. * @param realHeight The current document y position. * @param arg The visitor context. */ protected void visitDrawingShape(JAXBElement drawingShape, P paragraph, ComponentPosition position, int realHeight, J2WDocx4jPrintElementVisitorContext arg) { R run = arg.objectFactory.createR(); paragraph.getContent().add(run); run.getContent().add(drawingShape); PPr pPr = paragraph.getPPr(); if (pPr == null) { pPr = arg.objectFactory.createPPr(); paragraph.setPPr(pPr); } pPr.setRPr(arg.objectFactory.createParaRPr()); pPr.setPStyle(arg.objectFactory.createPPrBasePStyle()); pPr.getPStyle().setVal("Normal"); pPr.setJc(arg.objectFactory.createJc()); pPr.getJc().setVal(JcEnumeration.CENTER); pPr.setTextAlignment(arg.objectFactory.createPPrBaseTextAlignment()); pPr.getTextAlignment().setVal("center"); Spacing spacing = pPr.getSpacing(); if (spacing == null) { spacing = arg.objectFactory.createPPrBaseSpacing(); pPr.setSpacing(spacing); } if (position.getHeight() > realHeight) { BigInteger gap = EConvertMisure.POINTS.convertIntoBigInteger(EConvertMisure.th20_POINTS, position.getHeight() - realHeight).divide(BigInteger.valueOf(2)); spacing.setAfter(gap); spacing.setBefore(gap); } else { spacing.setAfter(BigInteger.ZERO); spacing.setAfterLines(BigInteger.ZERO); } spacing.setBefore(BigInteger.ZERO); spacing.setBeforeLines(BigInteger.ZERO); spacing.setLineRule(STLineSpacingRule.EXACT); spacing.setLine(BigInteger.ZERO); } public void visit(JRPrintRectangle rectangle, J2WDocx4jPrintElementVisitorContext arg) { if (rectangle.getLinePen() != null && (rectangle.getLinePen().getLineWidth() > 0f || (rectangle.getModeValue() == ModeEnum.OPAQUE && rectangle.getFillValue() == FillEnum.SOLID && rectangle.getBackcolor() != null && !rectangle.getBackcolor().equals(Color.white)))) { ComponentPosition position = arg.getLayout().getElementPosition(rectangle); FoundDocParagraph foundDocParagraph = this.findParagraph(position, rectangle.getStyle(), arg, true); P paragraph = foundDocParagraph.getParagraph(); CTRect ctRect = arg.picObjectFactory.createCTRect(); JAXBElement rect = arg.picObjectFactory.createRect(ctRect); Pict pict = arg.objectFactory.createPict(); pict.getAnyAndAny().add(rect); this.visitDrawingShape(arg.objectFactory.createRPict(pict), paragraph, position, position.getHeight(), arg); if (rectangle.getModeValue() == ModeEnum.OPAQUE && rectangle.getFillValue() == FillEnum.SOLID) { ctRect.setFillcolor(colorToStringFormat(rectangle.getBackcolor())); ctRect.setFilled(STTrueFalse.TRUE); // filltemplate = "\n\t"; } if (rectangle.getForecolor() != null) { String color = colorToStringFormat(rectangle.getForecolor()); ctRect.setBorderbottomcolor(color); ctRect.setBorderleftcolor(color); ctRect.setBorderrightcolor(color); ctRect.setBordertopcolor(color); ctRect.setStrokecolor("#" + color); } Float lineWidth = rectangle.getLinePen().getLineWidth(); ctRect.setStrokeweight(String.valueOf(lineWidth) + "pt"); if (rectangle.getRadius() > 0) { ctRect.setConnectortype(STConnectorType.CURVED); } else ctRect.setConnectortype(STConnectorType.STRAIGHT); ctRect.setVmlId("Rectangle " + rectangle.getKey()); MessageFormat format = new MessageFormat("position:absolute;margin-left:{0,number,0.00}pt;" + "margin-top:{1,number,0.00}pt;width:{2,number,0.00}pt;height:{3,number,0.00}pt;z-index:251661312;visibility:visible;" + "mso-wrap-style:square;mso-wrap-distance-left:0pt;mso-wrap-distance-top:0;mso-wrap-distance-right:0pt;mso-wrap-distance-bottom:0;" + "mso-position-horizontal:absolute;mso-position-horizontal-relative:text;mso-position-vertical:absolute;mso-position-vertical-relative:text;v-text-anchor:middle", Locale.US); ctRect.setStyle(format.format(new Object[]{position.getX(), position.getY(), position.getWidth(), position.getHeight()})); ctRect.setSpid("_x0000_s1026"); ctRect.setInsetmode(STInsetMode.CUSTOM); ctRect.setHralign(STHrAlign.LEFT); if (!foundDocParagraph.isTableCell()) arg.setLastY(position.getDocumentPart(), position.getY() + position.getHeight()); } } public void visit(JRPrintLine line, J2WDocx4jPrintElementVisitorContext arg) { if (line.getLinePen() != null && line.getLinePen().getLineWidth() > 0f) { ComponentPosition position = arg.getLayout().getElementPosition(line); FoundDocParagraph foundDocParagraph = this.findParagraph(position, line.getStyle(), arg, true); P paragraph = foundDocParagraph.getParagraph(); CTLine ctLine = arg.picObjectFactory.createCTLine(); JAXBElement lin = arg.picObjectFactory.createLine(ctLine); Pict pict = arg.objectFactory.createPict(); pict.getAnyAndAny().add(lin); this.visitDrawingShape(arg.objectFactory.createRPict(pict), paragraph, position, position.getHeight(), arg); float lineWidth = line.getLinePen().getLineWidth(); int x1, x2, y1, y2; if (line.getHeight() == 1) { // Horizontal x1 = 0; y1 = (int) (position.getY() - lineWidth / 2); x2 = x1 + position.getWidth(); y2 = y1; } else if (line.getWidth() == 1) { // Vertical x1 = (int) (position.getX() - lineWidth / 2); y1 = position.getY(); x2 = x1; y2 = y1 + position.getHeight(); } else { // Oblique if (line.getDirectionValue() == LineDirectionEnum.TOP_DOWN) { x1 = position.getX(); y1 = position.getY(); x2 = x1 + position.getWidth(); y2 = y1 + position.getHeight(); } else { x2 = position.getX(); x1 = x2 + position.getWidth(); y1 = position.getY(); y2 = y1 + position.getHeight(); } } String color = colorToStringFormat(line.getLinePen().getLineColor()); ctLine.setFilled(STTrueFalse.TRUE); MessageFormat format = new MessageFormat("{0,number,0.00}pt,{1,number,0.00}pt", Locale.US); ctLine.setFrom(format.format(new Object[]{x1, y1})); ctLine.setTo(format.format(new Object[]{x2, y2})); ctLine.setStyle("style=\"position:absolute;z-index:251661312;visibility:visible;mso-wrap-style:square;mso-width-percent:0;mso-height-percent:0;mso-wrap-distance-left:0pt;mso-wrap-distance-top:0;mso-wrap-distance-right:0pt;mso-wrap-distance-bottom:0;mso-position-horizontal:absolute;mso-position-horizontal-relative:text;mso-position-vertical:absolute;mso-position-vertical-relative:text;mso-width-percent:0;mso-height-percent:0;mso-width-relative:margin;mso-height-relative:margin\" "); ctLine.setVmlId("Line " + line.getKey()); ctLine.setSpid("_x0000_s1026"); ctLine.setStrokecolor("#" + color); ctLine.setStroked(STTrueFalse.TRUE); format = new MessageFormat("{0,number,0.00}pt", Locale.US); ctLine.setStrokeweight(format.format(new Object[]{lineWidth})); if (!foundDocParagraph.isTableCell()) arg.setLastY(position.getDocumentPart(), Math.max(y1, y2)); } } public void visit(JRPrintEllipse ellipse, J2WDocx4jPrintElementVisitorContext arg) { if (ellipse.getLinePen() != null && (ellipse.getLinePen().getLineWidth() > 0f || (ellipse.getModeValue() == ModeEnum.OPAQUE && ellipse.getFillValue() == FillEnum.SOLID && ellipse.getBackcolor() != null && !ellipse.getBackcolor().equals(Color.white)))) { ComponentPosition position = arg.getLayout().getElementPosition(ellipse); FoundDocParagraph foundDocParagraph = this.findParagraph(position, ellipse.getStyle(), arg, true); P paragraph = foundDocParagraph.getParagraph(); CTOval ctOval = arg.picObjectFactory.createCTOval(); JAXBElement rect = arg.picObjectFactory.createOval(ctOval); Pict pict = arg.objectFactory.createPict(); pict.getAnyAndAny().add(rect); this.visitDrawingShape(arg.objectFactory.createRPict(pict), paragraph, position, position.getHeight(), arg); String color = colorToStringFormat(ellipse.getLinePen().getLineColor()); ctOval.setStrokecolor(color); MessageFormat format = new MessageFormat("{0,number,0.00}", Locale.US); ctOval.setStrokeweight(format.format(new Object[]{ellipse.getLinePen().getLineWidth()})); if (ellipse.getModeValue() == ModeEnum.OPAQUE && ellipse.getFillValue() == FillEnum.SOLID) { ctOval.setFillcolor(colorToStringFormat(ellipse.getLinePen().getLineColor())); ctOval.setFilled(STTrueFalse.TRUE); } ctOval.setConnectortype(STConnectorType.STRAIGHT); ctOval.setVmlId("Oval " + ellipse.getKey()); format = new MessageFormat("position:absolute;margin-left:{0,number,0.00}pt;margin-top:{1,number,0.00}pt;" + "width:{2,number,0.00}pt;height:{3,number,0.00}pt;z-index:251660288;visibility:visible;mso-wrap-style:square;mso-wrap-distance-left:0pt;mso-wrap-distance-top:0;mso-wrap-distance-right:0pt;mso-wrap-distance-bottom:0;mso-position-horizontal:absolute;mso-position-horizontal-relative:text;mso-position-vertical:absolute;mso-position-vertical-relative:text;v-text-anchor:middle", Locale.US); ctOval.setStyle(format.format(new Object[]{position.getX(), position.getY(), position.getWidth(), position.getHeight()})); ctOval.setSpid("_x0000_s1026"); ctOval.setInsetmode(STInsetMode.CUSTOM); ctOval.setHralign(STHrAlign.LEFT); if (!foundDocParagraph.isTableCell()) arg.setLastY(position.getDocumentPart(), position.getY() + position.getHeight()); } } public void visit(JRPrintFrame frame, J2WDocx4jPrintElementVisitorContext arg) { Collection elements = arg.getLayout().listJRPrintElements(frame); JRStyle frameStyle = frame.getStyle(); for (JRPrintElement element : elements) { JRStyle elementStyle = element.getStyle(); if (elementStyle == null) element.setStyle(frameStyle); else if (frameStyle != null) { if (arg.getReport().getStylesMap().containsKey(elementStyle.getName())) { JRStyle newElementStyle = (JRStyle) frameStyle.clone(); String newStyleName = arg.remapStylesNameByJrStyle.get(elementStyle.getName()) + "BasedOn" + arg.remapStylesNameByJrStyle.get(frameStyle.getName()); if (newElementStyle instanceof JRBaseStyle) { ((JRBaseStyle) newElementStyle).rename(newStyleName); } else throw new JRRuntimeException("Operation not supported"); if (!arg.allStyles.contains(newStyleName)) { buildStyles(arg, Collections.singletonList(newElementStyle)); StyleDefinitionsPart stylePart = buildStylePart(arg, false); Style style = stylePart.getStyleById(newStyleName); BasedOn styleBasedOn = arg.objectFactory.createStyleBasedOn(); styleBasedOn.setVal(arg.remapStylesNameByJrStyle.get(frameStyle.getName())); style.setBasedOn(styleBasedOn); updateStyle(style, elementStyle, arg, true); } if (element instanceof JRTemplatePrintElement) ((JRTemplatePrintElement) element).getTemplate().setStyle(newElementStyle); else element.setStyle(newElementStyle); } } element.accept(this, arg); if (element instanceof JRTemplatePrintElement) ((JRTemplatePrintElement) element).getTemplate().setStyle(elementStyle); else element.setStyle(elementStyle); } } public void visit(JRGenericPrintElement printElement, J2WDocx4jPrintElementVisitorContext arg) { } /** * Found or built paragraph. */ protected static class FoundDocParagraph { /** The doc paragraph */ private final P paragraph; /** The table cell (can be {@code null}) */ private final Tc cell; /** The index in the jaxb document list objects */ private final int paragraphIndexInDoc; /** * Construct the found paragraph. * @param cell The table cell (can be {@code null}) if the paragraph is outside a table. * @param paragraph The doc paragraph. * @param paragraphIndexInDoc The index in the jaxb document list objects. */ public FoundDocParagraph(Tc cell, P paragraph, int paragraphIndexInDoc) { this.paragraph = paragraph; this.cell = cell; this.paragraphIndexInDoc = paragraphIndexInDoc; } /** * Return the paragraph. * @return The paragraph. */ public P getParagraph() { return paragraph; } /** * Return the table cell. * @return The table cell or {@code null}. */ public Tc getCell() { return cell; } /** * Return if the paragraph is inside a table. * @return {@code true} if in table, {@code false} otherwise. */ public boolean isTableCell() { return cell != null; } /** * Return the index in the jaxb document list objects. * @return The index in the jaxb document list objects. */ public int getParagraphIndexInDoc() { return paragraphIndexInDoc; } } } }