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

org.odftoolkit.odfdom.converter.pdf.internal.stylable.StylableDocument Maven / Gradle / Ivy

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

import java.io.OutputStream;
import java.util.List;

import org.odftoolkit.odfdom.converter.core.ODFConverterException;
import org.odftoolkit.odfdom.converter.pdf.internal.StyleEngineForIText;
import org.odftoolkit.odfdom.converter.pdf.internal.styles.Style;
import org.odftoolkit.odfdom.converter.pdf.internal.styles.StylePageLayoutProperties;
import org.odftoolkit.odfdom.dom.style.OdfStyleFamily;

import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Image;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.ColumnText;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;

import fr.opensagres.xdocreport.itext.extension.ExtendedDocument;
import fr.opensagres.xdocreport.itext.extension.IMasterPage;
import fr.opensagres.xdocreport.itext.extension.IPdfWriterConfiguration;
import fr.opensagres.xdocreport.itext.extension.PageOrientation;
import fr.opensagres.xdocreport.itext.extension.font.FontGroup;

/**
 * fixes for pdf conversion by Leszek Piotrowicz 
 */
public class StylableDocument
    extends ExtendedDocument
    implements IStylableContainer, IBoundsLimitContainer, IBreakHandlingContainer, IStylableFactory
{
    private final StyleEngineForIText styleEngine;

    private Style lastStyleApplied = null;

    private StylableMasterPage activeMasterPage;

    private boolean masterPageJustChanged;

    private boolean documentEmpty = true;

    private PdfPTable layoutTable;

    private ColumnText text;

    private int colIdx;

    public StylableDocument( OutputStream out, IPdfWriterConfiguration configuration, StyleEngineForIText styleEngine )
        throws DocumentException
    {
        super( out, configuration );
        this.styleEngine = styleEngine;
    }

    //
    // IStylableFactory implementation
    //

    public StylableAnchor createAnchor( IStylableContainer parent )
    {
        return new StylableAnchor( this, parent );
    }

    public StylableChunk createChunk( IStylableContainer parent, String textContent, FontGroup fontGroup )
    {
        return new StylableChunk( this, parent, textContent, fontGroup );
    }

    public StylableDocumentSection createDocumentSection( IStylableContainer parent, boolean inHeaderFooter )
    {
        return new StylableDocumentSection( this, parent, inHeaderFooter );
    }

    public StylableHeaderFooter createHeaderFooter( boolean header )
    {
        return new StylableHeaderFooter( this, header );
    }

    public StylableHeading createHeading( IStylableContainer parent, List headingNumbering )
    {
        return new StylableHeading( this, parent, headingNumbering );
    }

    public StylableImage createImage( IStylableContainer parent, Image image, Float x, Float y, Float width,
                                      Float height )
    {
        return new StylableImage( this, parent, image, x, y, width, height );
    }

    public StylableList createList( IStylableContainer parent, int listLevel )
    {
        return new StylableList( this, parent, listLevel );
    }

    public StylableListItem createListItem( IStylableContainer parent )
    {
        return new StylableListItem( this, parent );
    }

    public StylableParagraph createParagraph( IStylableContainer parent )
    {
        return new StylableParagraph( this, parent );
    }

    public StylablePhrase createPhrase( IStylableContainer parent )
    {
        return new StylablePhrase( this, parent );
    }

    public StylableTab createTab( IStylableContainer parent, boolean inTableOfContent )
    {
        return new StylableTab( this, parent, inTableOfContent );
    }

    public StylableTable createTable( IStylableContainer parent, int numColumns )
    {
        return new StylableTable( this, parent, numColumns );
    }

    public StylableTableCell createTableCell( IStylableContainer parent )
    {
        return new StylableTableCell( this, parent );
    }

    //
    // master page handling
    //

    @Override
    public void setActiveMasterPage( IMasterPage masterPage )
    {
        // flush pending content
        flushTable();
        // activate master page in three steps
        Style style = getStyleMasterPage( (StylableMasterPage) masterPage );
        if ( style != null )
        {
            // step 1 - apply styles like page dimensions and orientation
            this.applyStyles( style );
        }
        // step 2 - set header/footer if any, it needs page dimensions from step 1
        super.setActiveMasterPage( masterPage );
        if ( activeMasterPage != null )
        {
            // set a flag used by addElement/pageBreak
            masterPageJustChanged = true;
        }
        activeMasterPage = (StylableMasterPage) masterPage;
        // step 3 - initialize column layout, it needs page dimensions which may be lowered by header/footer in step 2
        layoutTable = StylableDocumentSection.createLayoutTable( getPageWidth(), getAdjustedPageHeight(), style );
        text = StylableDocumentSection.createColumnText();
        setColIdx( 0 );
    }

    private Style setNextActiveMasterPageIfNecessary()
    {
        // called on page break
        // return new page style if changed
        if ( activeMasterPage != null )
        {
            String nextMasterPageStyleName = activeMasterPage.getNextStyleName();
            if ( nextMasterPageStyleName != null && nextMasterPageStyleName.length() > 0 )
            {
                StylableMasterPage nextMasterPage = getMasterPage( nextMasterPageStyleName );
                if ( nextMasterPage != null )
                {
                    // activate next master page
                    Style style = getStyleMasterPage( nextMasterPage );
                    if ( style != null )
                    {
                        // step 1 - apply styles like page dimensions and orientation
                        this.applyStyles( style );
                    }
                    // step 2 - set header/footer if any, it needs page dimensions from step 1
                    super.setActiveMasterPage( nextMasterPage );
                    //
                    activeMasterPage = nextMasterPage;
                    return style;
                }
            }
        }
        return null;
    }

    public StylableMasterPage getActiveMasterPage()
    {
        return activeMasterPage;
    }

    public Style getStyleMasterPage( StylableMasterPage masterPage )
    {
        Style style = styleEngine.getStyle( OdfStyleFamily.List.getName(), masterPage.getPageLayoutName(), null );
        return style;
    }

    @Override
    public StylableMasterPage getMasterPage( String masterPageName )
    {
        return (StylableMasterPage) super.getMasterPage( masterPageName );
    }

    @Override
    public StylableMasterPage getDefaultMasterPage()
    {
        return (StylableMasterPage) super.getDefaultMasterPage();
    }

    //
    // IStylableContainer implementation
    //

    public void applyStyles( Style style )
    {
        this.lastStyleApplied = style;

        StylePageLayoutProperties pageLayoutProperties = style.getPageLayoutProperties();
        if ( pageLayoutProperties != null )
        {
            // width/height
            Float width = pageLayoutProperties.getWidth();
            Float height = pageLayoutProperties.getHeight();
            if ( width != null && height != null )
            {
                Rectangle pageSize = new Rectangle( width, height );
                super.setPageSize( pageSize );
            }

            // margin
            if ( pageLayoutProperties.getMarginTop() != null )
            {
                originMarginTop = pageLayoutProperties.getMarginTop();
            }
            if ( pageLayoutProperties.getMarginBottom() != null )
            {
                originMarginBottom = pageLayoutProperties.getMarginBottom();
            }
            if ( pageLayoutProperties.getMarginLeft() != null )
            {
                originMarginLeft = pageLayoutProperties.getMarginLeft();
            }
            if ( pageLayoutProperties.getMarginRight() != null )
            {
                originMarginRight = pageLayoutProperties.getMarginRight();
            }
            super.setMargins( originMarginLeft, originMarginRight, originMarginTop, originMarginBottom );

            // orientation
            PageOrientation orientation = pageLayoutProperties.getOrientation();
            if ( orientation != null )
            {
                super.setOrientation( orientation );
            }
        }
    }

    public Style getLastStyleApplied()
    {
        return lastStyleApplied;
    }

    public IStylableContainer getParent()
    {
        return null;
    }

    public Element getElement()
    {
        return null;
    }

    //
    // this amazing and awesome algorithm
    // which lay out content in columns on page
    // was written by Leszek Piotrowicz 
    //

    public void addElement( Element element )
    {
        if ( !super.isOpen() )
        {
            super.open();
        }
        if ( masterPageJustChanged )
        {
            // master page was changed but there was no explicit page break
            pageBreak();
        }
        text.addElement( element );
        StylableDocumentSection.getCell( layoutTable, colIdx ).getColumn().addElement( element );
        simulateText();
        documentEmpty = false;
    }

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

    public void pageBreak()
    {
        if ( documentEmpty )
        {
            // no element was added - ignore page break
        }
        else if ( masterPageJustChanged )
        {
            // we are just after master page change
            // move to a new page but do not initialize column layout
            // because it is already done
            masterPageJustChanged = false;
            super.newPage();
        }
        else
        {
            // flush pending content
            flushTable();
            // check if master page change necessary
            Style nextStyle = setNextActiveMasterPageIfNecessary();
            // document new page
            super.newPage();
            // initialize column layout for new page
            if ( nextStyle == null )
            {
                // ordinary page break
                layoutTable = StylableDocumentSection.cloneAndClearTable( layoutTable, false );
            }
            else
            {
                // page break with new master page activation
                // style changed so recreate table
                layoutTable =
                    StylableDocumentSection.createLayoutTable( getPageWidth(), getAdjustedPageHeight(), nextStyle );
            }
            setColIdx( 0 );
            simulateText();
        }
    }

    @Override
    public boolean newPage()
    {
        throw new ODFConverterException( "internal error - do not call newPage directly" );
    }

    @Override
    public void close()
    {
        flushTable();
        super.close();
    }

    public float getWidthLimit()
    {
        PdfPCell cell = StylableDocumentSection.getCell( layoutTable, colIdx );
        return cell.getRight() - cell.getPaddingRight() - cell.getLeft() - cell.getPaddingLeft();
    }

    public float getHeightLimit()
    {
        // yLine is negative
        return StylableDocumentSection.getCell( layoutTable, colIdx ).getFixedHeight() + text.getYLine();
    }

    public float getPageWidth()
    {
        return right() - left();
    }

    private float getAdjustedPageHeight()
    {
        // subtract small value from height, otherwise table breaks to new page
        return top() - bottom() - 0.001f;
    }

    private void setColIdx( int idx )
    {
        colIdx = idx;
        PdfPCell cell = StylableDocumentSection.getCell( layoutTable, colIdx );
        text.setSimpleColumn( cell.getLeft() + cell.getPaddingLeft(), -getAdjustedPageHeight(),
                              cell.getRight() - cell.getPaddingRight(), 0.0f );
        cell.setColumn( ColumnText.duplicate( text ) );
    }

    private void simulateText()
    {
        int res = 0;
        try
        {
            res = text.go( true );
        }
        catch ( DocumentException e )
        {
            throw new ODFConverterException( e );
        }
        if ( ColumnText.hasMoreText( res ) )
        {
            // text does not fit into current column
            // split it to a new column
            columnBreak();
        }
    }

    private void flushTable()
    {
        if ( layoutTable != null )
        {
            // force calculate height because it may be zero
            // and nothing will be flushed
            layoutTable.calculateHeights( true );
            try
            {
                super.add( layoutTable );
            }
            catch ( DocumentException e )
            {
                throw new ODFConverterException( e );
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy