fr.opensagres.poi.xwpf.converter.pdf.internal.elements.StylableDocumentSection.java Source code

Java tutorial

Introduction

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

Source

/**
 * Copyright (C) 2011-2015 The XDocReport Team <xdocreport@googlegroups.com>
 *
 * All rights reserved.
 *
 * Permission is hereby granted, free  of charge, to any person obtaining
 * a  copy  of this  software  and  associated  documentation files  (the
 * "Software"), to  deal in  the Software without  restriction, including
 * without limitation  the rights to  use, copy, modify,  merge, publish,
 * distribute,  sublicense, and/or sell  copies of  the Software,  and to
 * permit persons to whom the Software  is furnished to do so, subject to
 * the following conditions:
 *
 * The  above  copyright  notice  and  this permission  notice  shall  be
 * included in all copies or substantial portions of the Software.
 *
 * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
 * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
 * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package fr.opensagres.poi.xwpf.converter.pdf.internal.elements;

import java.util.ArrayList;
import java.util.List;

import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Table;
import com.lowagie.text.pdf.ColumnText;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPRow;
import com.lowagie.text.pdf.PdfPTable;

import fr.opensagres.poi.xwpf.converter.core.XWPFConverterException;
import fr.opensagres.xdocreport.itext.extension.IITextContainer;

//
// this amazing and awesome algorithm
// which lay out content in columns and do content balancing
// was written by Leszek Piotrowicz <leszekp@safe-mail.net>
//
public class StylableDocumentSection
// implements IITextContainer, IBreakHandlingContainer
{
    private IITextContainer parent;

    private IBoundsLimitContainer sectionParent;

    private IBreakHandlingContainer breakHandlingParent;

    private PdfPTable layoutTable;

    private boolean balanceText = true;

    private List<ColumnText> texts;

    private int colIdx;

    public StylableDocumentSection(StylableDocument ownerDocument, IITextContainer parent, boolean inHeaderFooter) {
        super();
        this.parent = parent;

        // find "real" parent which is first non section element in parent hierarchy
        // this section will be added to that parent, not to direct parent
        if (parent instanceof StylableDocumentSection) {
            // special case - directly nested section
            StylableDocumentSection dc = (StylableDocumentSection) parent;
            // flush parent section to update vertical position on page
            dc.flushTable();
            this.sectionParent = dc.sectionParent;
        } else {
            this.sectionParent = (IBoundsLimitContainer) parent;
        }

        // find parent container which can handle breaks
        // if null, it means that page breaks are forbidden (ie in header/footer)
        this.breakHandlingParent = getIBreakHandlingContainer(parent);
    }

    // public void applyStyles( Style style )
    // {
    // this.lastStyleApplied = style;
    //
    // StyleSectionProperties sectionProperties = style.getSectionProperties();
    // if ( sectionProperties != null )
    // {
    // Boolean dontBalanceTextColumns = sectionProperties.getDontBalanceTextColumns();
    // if ( Boolean.TRUE.equals( dontBalanceTextColumns ) )
    // {
    // balanceText = false;
    // }
    // }
    //
    // // initialize layout table
    // float width = sectionParent.getWidthLimit();
    // float height = sectionParent.getHeightLimit();
    // layoutTable = createLayoutTable( width, height, style );
    //
    // // initialize simulated text
    // texts = new ArrayList<ColumnText>();
    // setColIdx( 0 );
    // }

    public IITextContainer getParent() {
        return parent;
    }

    public Element getElement() {
        // force calculate height because it may be zero
        // and nothing will be flushed
        layoutTable.calculateHeights(true);
        return layoutTable;
    }

    public void addElement(Element element) {
        if (element instanceof SectionPdfPTable) {
            // section is a specific container
            // it is not added to direct parent container
            // but to section parent container which may be
            // StylableDocument or StylableTableCell or StylableHeaderFooter table cell
            sectionParent.addElement(element);
        } else {
            // ordinary element
            texts.get(colIdx).addElement(element);
            simulateText();
        }
    }

    public void columnBreak() {
        if (colIdx + 1 < layoutTable.getNumberOfColumns()) {
            setColIdx(colIdx + 1);
        } else {
            pageBreak();
        }
    }

    public void pageBreak() {
        if (breakHandlingParent != null) {
            if (sectionParent.getHeightLimit() >= 0.0f) {
                // fill remaining space on page break
                fillTable(sectionParent.getHeightLimit());
            }
            // flush
            flushTable();
            breakHandlingParent.columnBreak();
        } else {
            // more column breaks than available columns
            // but we cannot break to a new page
            // don't really know what to do in such situation
            // anyway it is logical error made by document creator
            setColIdx(colIdx + 1);
        }
    }

    private void flushTable() {
        sectionParent.addElement(getElement());

        // initialize layout table
        layoutTable = cloneAndClearTable(layoutTable, true);

        // initialize simulated text
        texts = new ArrayList<ColumnText>();
        setColIdx(0);
    }

    private void simulateText() {
        float maxHeight = sectionParent.getHeightLimit();
        List<ColumnText> splittedTexts = fillTable(maxHeight);
        if (splittedTexts == null) {
            // fits in current column on page
            if (balanceText) {
                float minHeight = 0f;
                if (maxHeight < 0.0f) {
                    maxHeight = layoutTable.calculateHeights(true);
                }
                float currHeight = 0.0f;
                while (Math.abs(maxHeight - minHeight) > 0.1f) {
                    currHeight = (minHeight + maxHeight) / 2;
                    if (fillTable(currHeight) == null) {
                        // try to lower height
                        maxHeight = currHeight;
                    } else {
                        // have to raise height
                        minHeight = currHeight;
                    }
                }
                // populate table with last height
                if (currHeight != maxHeight) {
                    fillTable(maxHeight);
                }
            } else {
                if (maxHeight >= 0.0f) {
                    // fill minimum space only
                    fillTable(-1.0f);
                }
            }
        } else {
            // overflow
            if (breakHandlingParent != null) {
                flushTable();
                breakHandlingParent.columnBreak();
                //
                texts = splittedTexts;
                colIdx = texts.size() - 1;
                simulateText();
            }
        }
    }

    private List<ColumnText> fillTable(float height) {
        // copy text for simulation
        List<ColumnText> tt = null;
        if (breakHandlingParent == null && colIdx >= layoutTable.getNumberOfColumns()) {
            // more column breaks than available column
            // we try not to lose content
            // but results may be different than in open office
            // anyway it is logical error made by document creator
            tt = new ArrayList<ColumnText>();
            ColumnText t = createColumnText();
            tt.add(t);
            for (int i = 0; i < texts.size(); i++) {
                PdfPTable table = new PdfPTable(1);
                table.setWidthPercentage(100.0f);
                PdfPCell cell = new PdfPCell();
                cell.setBorder(Table.NO_BORDER);
                cell.setPadding(0.0f);
                cell.setColumn(ColumnText.duplicate(texts.get(i)));
                table.addCell(cell);
                t.addElement(table);
            }
        } else {
            tt = new ArrayList<ColumnText>(texts);
            for (int i = 0; i < tt.size(); i++) {
                tt.set(i, ColumnText.duplicate(tt.get(i)));
            }
        }
        // clear layout table
        clearTable(layoutTable, true);
        setWidthIfNecessary();

        // try to fill cells with text
        ColumnText t = tt.get(0);
        for (PdfPCell cell : layoutTable.getRow(0).getCells()) {
            cell.setFixedHeight(height >= 0.0f ? height : -1.0f);
            cell.setColumn(ColumnText.duplicate(t));
            //
            t.setSimpleColumn(cell.getLeft() + cell.getPaddingLeft(),
                    height >= 0.0f ? -height : PdfPRow.BOTTOM_LIMIT, cell.getRight() - cell.getPaddingRight(), 0);
            int res = 0;
            try {
                res = t.go(true);
            } catch (DocumentException e) {
                throw new XWPFConverterException(e);
            }
            if (!ColumnText.hasMoreText(res)) {
                // no overflow in current column
                if (tt.size() == 1) {
                    // no more text
                    return null;
                } else {
                    // some text waiting for new column
                    tt.remove(0);
                    t = tt.get(0);
                }
            }
        }
        return tt;
    }

    private void setColIdx(int idx) {
        colIdx = idx;
        for (int i = texts.size(); i <= idx; i++) {
            ColumnText text = createColumnText();
            texts.add(text);
        }
    }

    private void setWidthIfNecessary() {
        if (layoutTable.getTotalWidth() != sectionParent.getWidthLimit()) {
            layoutTable.setTotalWidth(sectionParent.getWidthLimit());
        }
    }

    //
    // static helper functions
    //

    public static IBreakHandlingContainer getIBreakHandlingContainer(IITextContainer c) {
        // while ( c != null )
        // {
        // if ( c instanceof IBreakHandlingContainer )
        // {
        // return (IBreakHandlingContainer) c;
        // }
        // c = c.getParent();
        // }
        return null;
    }

    public static SectionPdfPTable createLayoutTable(float width, float height) {
        // create one row table which will layout section text
        // if ( style != null )
        // {
        // List<StyleColumnProperties> columnPropertiesList = style.getColumnPropertiesList();
        // StyleColumnsProperties columnsProperties = style.getColumnsProperties();
        // if ( columnPropertiesList != null && !columnPropertiesList.isEmpty() )
        // {
        // // explicit column list
        // return createLayoutTable( width, height, columnPropertiesList );
        // }
        // else if ( columnsProperties != null )
        // {
        // // we have columns properties
        // // make table with columns of equal width
        // columnPropertiesList = new ArrayList<StyleColumnProperties>();
        //
        // Integer columnCount = columnsProperties.getColumnCount();
        // int colCount = columnCount != null ? columnCount : 1;
        //
        // Float columnGap = columnsProperties.getColumnGap();
        // float halfGap = columnGap != null ? columnGap / 2.0f : 0.0f;
        //
        // for ( int i = 0; i < colCount; i++ )
        // {
        // StyleColumnProperties columnProperties = new StyleColumnProperties();
        // columnProperties.setRelWidth( 100 );
        // if ( halfGap > 0.0f )
        // {
        // columnProperties.setStartIndent( i == 0 ? null : halfGap );
        // columnProperties.setEndIndent( i == colCount - 1 ? null : halfGap );
        // }
        // columnPropertiesList.add( columnProperties );
        // }
        // return createLayoutTable( width, height, columnPropertiesList );
        // }
        // }
        // default is one column and no gap
        List<StyleColumnProperties> columnPropertiesList = new ArrayList<StyleColumnProperties>();
        StyleColumnProperties columnProperties = new StyleColumnProperties();
        columnProperties.setRelWidth(100);
        columnPropertiesList.add(columnProperties);
        return createLayoutTable(width, height, columnPropertiesList);
    }

    public static SectionPdfPTable createLayoutTable(float width, float height,
            List<StyleColumnProperties> columnPropertiesList) {
        // create one row table which will layout section text
        int colCount = columnPropertiesList.size();
        int relativeWidths[] = new int[colCount];
        SectionPdfPTable table = new SectionPdfPTable(colCount);
        // add cells
        for (int i = 0; i < colCount; i++) {
            PdfPCell cell = new PdfPCell();
            cell.setBorder(Table.NO_BORDER);
            cell.setPadding(0.0f);
            cell.setColumn(createColumnText());
            cell.setFixedHeight(height >= 0.0f ? height : -1.0f);
            // apply styles to cell
            StyleColumnProperties columnProperties = columnPropertiesList.get(i);
            relativeWidths[i] = columnProperties.getRelWidth();
            cell.setPaddingLeft(
                    columnProperties.getStartIndent() != null ? columnProperties.getStartIndent() : 0.0f);
            cell.setPaddingRight(columnProperties.getEndIndent() != null ? columnProperties.getEndIndent() : 0.0f);
            table.addCell(cell);
        }
        replaceTableCells(table);
        // set width
        try {
            table.setWidths(relativeWidths);
        } catch (DocumentException e) {
            throw new XWPFConverterException(e);
        }
        table.setTotalWidth(width);
        table.setLockedWidth(true);
        return table;
    }

    public static ColumnText createColumnText() {
        ColumnText text = new ColumnText(null);
        // make iText first line alignment compatible with open office
        text.setAdjustFirstLine(false);
        return text;
    }

    public static void replaceTableCells(PdfPTable table) {
        // replace default table cells
        // by fixed height cells
        PdfPCell[] cells = table.getRow(0).getCells();
        for (int i = 0; i < cells.length; i++) {
            cells[i] = new FixedHeightPdfPCell(cells[i]);
        }
    }

    public static PdfPCell getCell(PdfPTable table, int idx) {
        return table.getRow(0).getCells()[idx];
    }

    public static SectionPdfPTable cloneAndClearTable(PdfPTable table, boolean resetFixedHeight) {
        SectionPdfPTable clonedTable = new SectionPdfPTable(table);
        clearTable(clonedTable, resetFixedHeight);
        replaceTableCells(clonedTable);
        return clonedTable;
    }

    public static void clearTable(PdfPTable table, boolean resetFixedHeight) {
        for (PdfPCell cell : table.getRow(0).getCells()) {
            cell.setColumn(createColumnText());
            if (resetFixedHeight) {
                cell.setFixedHeight(-1.0f);
            }
        }
    }

    public static class SectionPdfPTable extends PdfPTable {
        public SectionPdfPTable(int numColumns) {
            super(numColumns);
        }

        public SectionPdfPTable(PdfPTable table) {
            super(table);
        }
    }

    public static class FixedHeightPdfPCell extends PdfPCell {

        public FixedHeightPdfPCell(PdfPCell cell) {
            super(cell);
        }

        @Override
        public float getMaxHeight() {
            // sometimes max height of a table cell is higher than desired height
            // such a situation occurs if paragraphs have space before or after
            // it is hard to say if it is a bug or a feature in iText
            // we want fixed height to avoid breaking table to a new page
            return getFixedHeight() >= 0.0f ? getFixedHeight() : super.getMaxHeight();
        }
    }

    //
    // probably not used
    //

    public IITextContainer getITextContainer() {
        return null;
    }

    public void setITextContainer(IITextContainer container) {
    }
}