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

fr.opensagres.odfdom.converter.pdf.internal.ElementVisitorForIText Maven / Gradle / Ivy

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

import java.awt.Color;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.odftoolkit.odfdom.doc.OdfDocument;
import org.odftoolkit.odfdom.dom.element.OdfStylableElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawCustomShapeElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawFrameElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawImageElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawLineElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawTextBoxElement;
import org.odftoolkit.odfdom.dom.element.office.OfficeTextElement;
import org.odftoolkit.odfdom.dom.element.style.StyleFooterElement;
import org.odftoolkit.odfdom.dom.element.style.StyleFooterLeftElement;
import org.odftoolkit.odfdom.dom.element.style.StyleHeaderElement;
import org.odftoolkit.odfdom.dom.element.style.StyleHeaderLeftElement;
import org.odftoolkit.odfdom.dom.element.style.StyleMasterPageElement;
import org.odftoolkit.odfdom.dom.element.svg.SvgDescElement;
import org.odftoolkit.odfdom.dom.element.svg.SvgTitleElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableCellElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableHeaderRowsElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableRowElement;
import org.odftoolkit.odfdom.dom.element.text.TextAElement;
import org.odftoolkit.odfdom.dom.element.text.TextBookmarkElement;
import org.odftoolkit.odfdom.dom.element.text.TextBookmarkStartElement;
import org.odftoolkit.odfdom.dom.element.text.TextHElement;
import org.odftoolkit.odfdom.dom.element.text.TextIndexBodyElement;
import org.odftoolkit.odfdom.dom.element.text.TextLineBreakElement;
import org.odftoolkit.odfdom.dom.element.text.TextListElement;
import org.odftoolkit.odfdom.dom.element.text.TextListItemElement;
import org.odftoolkit.odfdom.dom.element.text.TextPElement;
import org.odftoolkit.odfdom.dom.element.text.TextPageCountElement;
import org.odftoolkit.odfdom.dom.element.text.TextPageNumberElement;
import org.odftoolkit.odfdom.dom.element.text.TextParagraphElementBase;
import org.odftoolkit.odfdom.dom.element.text.TextSElement;
import org.odftoolkit.odfdom.dom.element.text.TextSectionElement;
import org.odftoolkit.odfdom.dom.element.text.TextSoftPageBreakElement;
import org.odftoolkit.odfdom.dom.element.text.TextSpanElement;
import org.odftoolkit.odfdom.dom.element.text.TextTabElement;
import org.odftoolkit.odfdom.dom.element.text.TextTableOfContentElement;
import org.odftoolkit.odfdom.dom.element.text.TextTableOfContentSourceElement;
import org.odftoolkit.odfdom.dom.style.OdfStyleFamily;
import org.odftoolkit.odfdom.pkg.OdfElement;
import org.w3c.dom.Node;
import org.w3c.dom.Text;

import com.lowagie.text.DocumentException;
import com.lowagie.text.Font;
import com.lowagie.text.Image;

import fr.opensagres.odfdom.converter.core.ElementVisitorConverter;
import fr.opensagres.odfdom.converter.core.ODFConverterException;
import fr.opensagres.odfdom.converter.core.utils.ODFUtils;
import fr.opensagres.odfdom.converter.pdf.PdfOptions;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.IStylableContainer;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.IStylableElement;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylableAnchor;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylableChunk;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylableDocument;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylableDocumentSection;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylableHeaderFooter;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylableHeading;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylableImage;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylableList;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylableListItem;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylableMasterPage;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylableParagraph;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylableParagraphWrapper;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylablePhrase;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylableTab;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylableTable;
import fr.opensagres.odfdom.converter.pdf.internal.stylable.StylableTableCell;
import fr.opensagres.odfdom.converter.pdf.internal.styles.Style;
import fr.opensagres.odfdom.converter.pdf.internal.styles.StyleTextProperties;
import fr.opensagres.xdocreport.utils.StringUtils;

/**
 * fixes for pdf conversion by Leszek Piotrowicz 
 */
public class ElementVisitorForIText
    extends ElementVisitorConverter
{
    private final StyleEngineForIText styleEngine;

    // private final PDFViaITextOptions options;

    private IStylableContainer currentContainer;

    private StylableMasterPage currentMasterPage;

    private StylableDocument document;

    private boolean parseOfficeTextElement = false;

    private boolean inTableOfContent; // tabs processing

    private List currentHeadingNumbering; // heading processing

    private StylableTable currentTable; // table processing

    private int currentListLevel; // list processing

    private StylableList previousList; // list processing

    private Integer forcedPageCount; // page count processing

    private Integer expectedPageCount; // page count processing
    
    // Image Cache
    private Map imageCache = new HashMap();

    public ElementVisitorForIText( OdfDocument odfDocument, OutputStream out, StyleEngineForIText styleEngine,
                                   PdfOptions options, Integer forcedPageCount )
    {
        super( odfDocument, options.getExtractor(), out, null );
        this.styleEngine = styleEngine;
        this.forcedPageCount = forcedPageCount;
        // this.options = options != null ? options : PDFViaITextOptions.create();
        // Create document
        try
        {
            document = new StylableDocument( out, options.getConfiguration(), styleEngine );
        }
        catch ( DocumentException e )
        {
            throw new ODFConverterException( e );
        }
    }

    public Integer getExpectedPageCount()
    {
        return expectedPageCount;
    }

    public int getActualPageCount()
    {
        if ( document.isOpen() )
        {
            return document.getPageNumber();
        }
        else
        {
            return document.getPageNumber() - 1;
        }
    }

    // ---------------------- visit root
    // styles.xml//office:document-styles/office:master-styles/style:master-page

    /**
     * Generate XHTML page footer + header
     */
    @Override
    public void visit( StyleMasterPageElement ele )
    {
        String name = ele.getStyleNameAttribute();
        String pageLayoutName = ele.getStylePageLayoutNameAttribute();
        String nextStyleName = ele.getStyleNextStyleNameAttribute();
        currentMasterPage = new StylableMasterPage( name, pageLayoutName, nextStyleName );
        document.addMasterPage( currentMasterPage );
        super.visit( ele );
        currentMasterPage = null;
    }

    // ---------------------- visit
    // styles.xml//office:document-styles/office:master-styles/style:master-page/style-header

    @Override
    public void visit( StyleHeaderElement ele )
    {
        StylableHeaderFooter header = document.createHeaderFooter( true );
        Style style = document.getStyleMasterPage( currentMasterPage );
        if ( style != null )
        {
            document.applyStyles( style );
            header.applyStyles( style );
        }
        currentMasterPage.setHeader( header );
        StylableTableCell tableCell = header.getTableCell();
        currentContainer = tableCell;
        super.visit( ele );
        header.flush();
        currentContainer = null;
    }

    @Override
    public void visit( StyleHeaderLeftElement ele )
    {
        // TODO : implement it.
    }

    // ---------------------- visit
    // styles.xml//office:document-styles/office:master-styles/style:master-page/style-footer

    @Override
    public void visit( StyleFooterElement ele )
    {
        StylableHeaderFooter footer = document.createHeaderFooter( false );
        Style style = document.getStyleMasterPage( currentMasterPage );
        if ( style != null )
        {
            document.applyStyles( style );
            footer.applyStyles( style );
        }
        currentMasterPage.setFooter( footer );
        StylableTableCell tableCell = footer.getTableCell();
        currentContainer = tableCell;
        super.visit( ele );
        footer.flush();
        currentContainer = null;

    }

    @Override
    public void visit( StyleFooterLeftElement ele )
    {
        // TODO : implement it.
    }

    // ---------------------- visit root //office-body/office-text

    @Override
    public void visit( OfficeTextElement ele )
    {
        this.parseOfficeTextElement = true;
        currentContainer = document;
        super.visit( ele );
        this.parseOfficeTextElement = false;
    }

    @Override
    public void visit( TextTableOfContentElement ele )
    {
        inTableOfContent = true;
        super.visit( ele );
        inTableOfContent = false;
    }

    @Override
    public void visit( TextTableOfContentSourceElement ele )
    {
        // do not visit child nodes
        // they may contain unnecessary text
    }

    @Override
    public void visit( TextIndexBodyElement ele )
    {
        super.visit( ele );
    }

    @Override
    public void visit( TextSectionElement ele )
    {
        StylableDocumentSection documentSection =
            document.createDocumentSection( currentContainer, !parseOfficeTextElement );
        applyStyles( ele, documentSection );
        addITextContainer( ele, documentSection );
    }

    // ---------------------- visit //text:h

    @Override
    public void visit( TextHElement ele )
    {
        // compute heading numbering (ie 1.3.1)
        int outlineLevel = ele.getTextOutlineLevelAttribute() != null ? ele.getTextOutlineLevelAttribute() : 1;
        if ( currentHeadingNumbering == null )
        {
            currentHeadingNumbering = new ArrayList();
        }
        while ( currentHeadingNumbering.size() > outlineLevel )
        {
            currentHeadingNumbering.remove( currentHeadingNumbering.size() - 1 );
        }
        if ( currentHeadingNumbering.size() == outlineLevel )
        {
            currentHeadingNumbering.set( outlineLevel - 1, currentHeadingNumbering.get( outlineLevel - 1 ) + 1 );
        }
        while ( currentHeadingNumbering.size() < outlineLevel )
        {
            currentHeadingNumbering.add( StylableHeading.getFirst( currentContainer.getLastStyleApplied(),
                                                                   currentHeadingNumbering.size() + 1 ) );
        }

        processParagraphOrHeading( ele, new ArrayList( currentHeadingNumbering ) );
    }

    // ---------------------- visit //text:p

    @Override
    public void visit( TextPElement ele )
    {
        processParagraphOrHeading( ele, null );
    }

    private void processParagraphOrHeading( TextParagraphElementBase ele, List headingNumbering )
    {
        StylableParagraphWrapper paragraphWrapper = createParagraphWrapperAndApplyStyles( ele );
        if ( paragraphWrapper.hasBorders() || paragraphWrapper.hasBackgroundColor() )
        {
            // paragraph has borders or background
            // have to wrap it in a table
            boolean joinWithPrevious = joinParagraphWith( paragraphWrapper, ele.getPreviousSibling() );
            boolean joinWithNext = joinParagraphWith( paragraphWrapper, ele.getNextSibling() );
            // start paragraph wrapper
            if ( joinWithPrevious )
            {
                // add this paragraph to existing wrapper
            }
            else
            {
                // start a new wrapper
                currentContainer = paragraphWrapper;
            }
            // process paragraph
            StylableParagraph paragraph =
                headingNumbering != null ? document.createHeading( currentContainer, headingNumbering )
                                : document.createParagraph( currentContainer );
            applyStyles( ele, paragraph );
            // apply top or bottom margin if necessary
            if ( joinWithNext )
            {
                paragraph.setSpacingAfter( paragraphWrapper );
            }
            if ( joinWithPrevious )
            {
                paragraph.setSpacingBefore( paragraphWrapper );
            }
            addITextContainer( ele, paragraph );
            // end paragraph wrapper
            if ( joinWithNext )
            {
                // some paragraph will be added to current wrapper, do nothing
            }
            else
            {
                // end current wrapper
                IStylableContainer oldContainer = currentContainer.getParent();
                oldContainer.addElement( currentContainer.getElement() );
                currentContainer = oldContainer;
            }
        }
        else
        {
            // paragraph has no border and no background
            // don't have to wrap it in a table
            StylableParagraph paragraph =
                headingNumbering != null ? document.createHeading( currentContainer, headingNumbering )
                                : document.createParagraph( currentContainer );
            applyStyles( ele, paragraph );
            // apply margin (left, right, top, bottom) values
            paragraph.setIndentation( paragraphWrapper );
            paragraph.setSpacingBefore( paragraphWrapper );
            paragraph.setSpacingAfter( paragraphWrapper );
            addITextContainer( ele, paragraph );
        }
    }

    private StylableParagraphWrapper createParagraphWrapperAndApplyStyles( TextParagraphElementBase ele )
    {
        StylableParagraphWrapper paragraphWrapper = new StylableParagraphWrapper( document, currentContainer );
        applyStyles( ele, paragraphWrapper );
        return paragraphWrapper;
    }

    private boolean joinParagraphWith( StylableParagraphWrapper paragraphWrapper1, Node node )
    {
        if ( node instanceof TextParagraphElementBase )
        {
            TextParagraphElementBase ele = (TextParagraphElementBase) node;
            StylableParagraphWrapper paragraphWrapper2 = createParagraphWrapperAndApplyStyles( ele );
            Style style1 = paragraphWrapper1.getLastStyleApplied();
            Style style2 = paragraphWrapper2.getLastStyleApplied();
            if ( style1 != null && style2 != null )
            {
                String styleName1 = style1.getStyleName();
                String styleName2 = style2.getStyleName();
                if ( styleName1 != null && styleName1.equals( styleName2 ) )
                {
                    boolean hasBorders = paragraphWrapper1.hasBorders();
                    boolean joinBorder = paragraphWrapper1.joinBorder();
                    if ( hasBorders && joinBorder )
                    {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    // ---------------------- visit //text:span

    @Override
    public void visit( TextSpanElement ele )
    {
        StylablePhrase phrase = document.createPhrase( currentContainer );
        applyStyles( ele, phrase );
        addITextContainer( ele, phrase );
    }

    // ---------------------- visit //text:a

    @Override
    public void visit( TextAElement ele )
    {
        StylableAnchor anchor = document.createAnchor( currentContainer );
        String reference = ele.getXlinkHrefAttribute();
        applyStyles( ele, anchor );

        if ( anchor.getFont().getColor() == null )
        {
            // if no color was applied to the link get the font of the paragraph and set blue color.
            Font linkFont = anchor.getFont();
            Style style = currentContainer.getLastStyleApplied();
            if ( style != null )
            {
                StyleTextProperties textProperties = style.getTextProperties();
                if ( textProperties != null )
                {
                    Font font = textProperties.getFont();
                    if ( font != null )
                    {
                        linkFont = new Font( font );
                        anchor.setFont( linkFont );
                    }
                }
            }
            linkFont.setColor( Color.BLUE );
        }

        // set the link
        if ( reference.endsWith( StylableHeading.IMPLICIT_REFERENCE_SUFFIX ) )
        {
            reference = "#" + StylableHeading.generateImplicitDestination( reference );
        }
        anchor.setReference( reference );
        // Add to current container.
        addITextContainer( ele, anchor );
    }

    @Override
    public void visit( TextBookmarkElement ele )
    {
        // destination for a local anchor
        // chunk with empty text does not work as local anchor
        // so we create chunk with invisible but not empty text content
        // if bookmark is the last chunk in a paragraph something must be added after or it does not work
        createAndAddChunk( ODFUtils.TAB_STR, ele.getTextNameAttribute(), false );
    }

    @Override
    public void visit( TextBookmarkStartElement ele )
    {
        createAndAddChunk( ODFUtils.TAB_STR, ele.getTextNameAttribute(), false );
        super.visit( ele );
    }

    // ---------------------- visit table:table (ex : )

    @Override
    public void visit( TableTableElement ele )
    {
        float[] columnWidth = ODFUtils.getColumnWidths( ele, odfDocument );
        StylableTable table = document.createTable( currentContainer, columnWidth.length );
        try
        {
            table.setTotalWidth( columnWidth );
        }
        catch ( DocumentException e )
        {
            // Do nothing
        }
        applyStyles( ele, table );
        StylableTable oldTable = currentTable;
        currentTable = table;
        addITextContainer( ele, table );
        currentTable = oldTable;
    }

    // ---------------------- visit table:table-header-rows

    @Override
    public void visit( TableTableHeaderRowsElement ele )
    {
        // we want to count table rows nested inside table header rows element
        // to determine how many header rows we have in current table
        currentTable.beginTableHeaderRows();
        super.visit( ele );
        currentTable.endTableHeaderRows();
    }

    // ---------------------- visit table:table-row

    @Override
    public void visit( TableTableRowElement ele )
    {
        Style currentRowStyle = getStyle( ele, null );
        if ( currentRowStyle != null )
        {
            currentTable.applyStyles( currentRowStyle );
        }
        currentTable.beginTableRow( currentRowStyle );
        super.visit( ele );
        currentTable.endTableRow();
    }

    // ---------------------- visit table:table-cell

    @Override
    public void visit( TableTableCellElement ele )
    {
        StylableTableCell tableCell = document.createTableCell( currentContainer );

        // table:number-columns-spanned
        Integer colSpan = ele.getTableNumberColumnsSpannedAttribute();
        if ( colSpan != null )
        {
            tableCell.setColspan( colSpan );
        }
        // table:number-rows-spanned
        Integer rowSpan = ele.getTableNumberRowsSpannedAttribute();
        if ( rowSpan != null )
        {
            tableCell.setRowspan( rowSpan );

        }
        // Apply styles coming from table-row
        if ( currentTable.getCurrentRowStyle() != null )
        {
            tableCell.applyStyles( currentTable.getCurrentRowStyle() );
        }
        // Apply styles coming from table-cell
        applyStyles( ele, tableCell );
        addITextContainer( ele, tableCell );
    }

    // ---------------------- visit text:list

    @Override
    public void visit( TextListElement ele )
    {
        currentListLevel++;
        StylableList list = document.createList( currentContainer, currentListLevel );
        applyStyles( ele, list );
        Boolean continueNumbering = ele.getTextContinueNumberingAttribute();
        if (continueNumbering == null){
        	continueNumbering = ele.getTextStyleNameAttribute() != null;
        }        
        if ( Boolean.TRUE.equals( continueNumbering ) && previousList != null
            && previousList.getLastStyleApplied() != null && list.getLastStyleApplied() != null
            && previousList.getLastStyleApplied().getStyleName() != null
            && previousList.getLastStyleApplied().getStyleName().equals( list.getLastStyleApplied().getStyleName() ) )
        {
            list.setFirst( previousList.getIndex() );
        }
        addITextContainer( ele, list );
        currentListLevel--;
        previousList = list;
    }

    // ---------------------- visit text:listitem

    @Override
    public void visit( TextListItemElement ele )
    {
        StylableListItem listItem = document.createListItem( currentContainer );
        addITextContainer( ele, listItem );
    }

    // ---------------------- visit draw:image

    @Override
    protected void visitImage( DrawImageElement ele, String href, byte[] imageStream )
    {    	
    	// add image in the pdf.
		Image imageObj = imageCache.get(href);
		if (imageObj == null) 
		{
			imageObj = StylableImage.getImage(imageStream);
			imageCache.put(href, imageObj);
		} 
		
		if (imageObj != null) 
		{
            DrawFrameElement frame = null;
            Float x = null;
            Float y = null;
            Float width = null;
            Float height = null;
            // set width, height....image
            Node parentNode = ele.getParentNode();
            if ( parentNode instanceof DrawFrameElement )
            {
                frame = (DrawFrameElement) parentNode;
                String svgX = frame.getSvgXAttribute();
                if ( StringUtils.isNotEmpty( svgX ) )
                {
                    x = ODFUtils.getDimensionAsPoint( svgX );
                }
                String svgY = frame.getSvgYAttribute();
                if ( StringUtils.isNotEmpty( svgY ) )
                {
                    y = ODFUtils.getDimensionAsPoint( svgY );
                }
                String svgWidth = frame.getSvgWidthAttribute();
                if ( StringUtils.isNotEmpty( svgWidth ) )
                {
                    width = ODFUtils.getDimensionAsPoint( svgWidth );
                }
                String svgHeight = frame.getSvgHeightAttribute();
                if ( StringUtils.isNotEmpty( svgHeight ) )
                {
                    height = ODFUtils.getDimensionAsPoint( svgHeight );
                }
            }
            StylableImage image = document.createImage( currentContainer, imageObj, x, y, width, height );
            if ( frame != null )
            {
                applyStyles( frame, image );
            }
            addITextElement( image );
        }
    }

    @Override
    protected boolean isNeedImageStream()
    {
        return true;
    }

    @Override
    public void visit( DrawTextBoxElement ele )
    {
        // do not visit child nodes
        // they may contain unnecessary text
    }

    @Override
    public void visit( DrawLineElement ele )
    {
        // do not visit child nodes
        // they may contain unnecessary text
    }

    @Override
    public void visit( DrawCustomShapeElement ele )
    {
        // do not visit child nodes
        // they may contain unnecessary text
    }

    @Override
    public void visit( SvgTitleElement ele )
    {
        // do not visit child nodes
        // they may contain unnecessary text
    }

    @Override
    public void visit( SvgDescElement ele )
    {
        // do not visit child nodes
        // they may contain unnecessary text
    }

    // ---------------------- visit //text:soft-page-break

    @Override
    public void visit( TextSoftPageBreakElement ele )
    {
    }

    // ---------------------- visit //text:tab

    @Override
    public void visit( TextTabElement ele )
    {
        StylableTab tab = document.createTab( currentContainer, inTableOfContent );
        Style style = currentContainer.getLastStyleApplied();
        if ( style != null )
        {
            tab.applyStyles( style );
        }
        addITextElement( tab );
    }

    // ---------------------- visit text:line-break

    @Override
    public void visit( TextLineBreakElement ele )
    {
        createAndAddChunk( "\n", null, false );
    }

    // ---------------------- visit text:s

    @Override
    public void visit( TextSElement ele )
    {
        String spaceStr = " ";
        Integer spaceCount = ele.getTextCAttribute();
        if ( spaceCount != null && spaceCount > 1 )
        {
            for ( int i = 1; i < spaceCount; i++ )
            {
                spaceStr += " ";
            }
        }
        createAndAddChunk( spaceStr, null, false );
    }

    @Override
    public void visit( TextPageNumberElement ele )
    {
        createAndAddChunk( "#", null, true );
    }

    @Override
    public void visit( TextPageCountElement ele )
    {
        if ( forcedPageCount != null )
        {
            createAndAddChunk( forcedPageCount.toString(), null, false );
        }
        else
        {
            String textContent = ele.getTextContent();
            try
            {
                int pageCount = Integer.parseInt( textContent );
                if ( expectedPageCount == null || expectedPageCount == pageCount )
                {
                    expectedPageCount = pageCount;
                }
                else if ( expectedPageCount != pageCount )
                {
                    expectedPageCount = -1;
                }
            }
            catch ( NumberFormatException e )
            {
                expectedPageCount = -1;
            }
            textContent = expectedPageCount != null & expectedPageCount >= 0 ? expectedPageCount.toString() : "#";
            createAndAddChunk( textContent, null, false );
        }
    }

    @Override
    protected void processTextNode( Text node )
    {
        createAndAddChunk( node.getTextContent(), null, false );
    }

    private void createAndAddChunk( String textContent, String localDestinationName, boolean pageNumberChunk )
    {
        // StylableChunk can replace several ODT elements
        // plain text
        // text:bookmark
        // text:line-break
        // text:s
        // text:page-number
        // text:page-count
        List chunks = StylableChunk.createChunks( document, currentContainer, textContent );
        for ( StylableChunk chunk : chunks )
        {
            Style style = currentContainer.getLastStyleApplied();
            if ( style != null )
            {
                chunk.applyStyles( style );
            }
            if ( localDestinationName != null )
            {
                chunk.setLocalDestination( localDestinationName );
            }
            if ( pageNumberChunk )
            {
                chunk.setPageNumberChunk( pageNumberChunk );
            }
            addITextElement( chunk );
        }
    }

    @Override
    public void save()
        throws IOException
    {
        if ( document != null )
        {
            document.close();
        }
        super.save();
    }

    private void addITextContainer( OdfElement ele, IStylableContainer newContainer )
    {
        addITextContainer( ele, newContainer, true );
    }

    private void addITextContainer( OdfElement ele, IStylableContainer newContainer, boolean add )
    {
        IStylableContainer oldContainer = currentContainer;
        try
        {
            currentContainer = newContainer;
            super.visit( ele );
            if ( add )
            {
                oldContainer.addElement( newContainer.getElement() );
            }
        }
        finally
        {
            currentContainer = oldContainer;
        }
    }

    private void addITextElement( IStylableElement element )
    {
        currentContainer.addElement( element.getElement() );
    }

    private void applyStyles( OdfElement ele, IStylableElement element )
    {
        Style style = getStyle( ele, element );
        if ( style != null )
        {
            if ( parseOfficeTextElement )
            {
                String masterPageName = style.getMasterPageName();
                if ( StringUtils.isNotEmpty( masterPageName ) )
                {
                    // explicit master page activation
                    StylableMasterPage masterPage = document.getMasterPage( masterPageName );
                    if ( masterPage != null && masterPage != document.getActiveMasterPage() )
                    {
                        document.setActiveMasterPage( masterPage );
                    }
                }
                else if ( document.getActiveMasterPage() == null )
                {
                    // no master page was activated yet
                    // activate default
                    document.setActiveMasterPage( document.getDefaultMasterPage() );
                }
            }
            element.applyStyles( style );
        }
    }

    private Style getStyle( OdfElement e, IStylableElement element )
    {
        Style style = null;
        Style parentElementStyle = element != null ? getParentElementStyle( element ) : null;
        if ( e instanceof OdfStylableElement )
        {
            OdfStylableElement ele = (OdfStylableElement) e;

            String styleName = ele.getStyleName();
            String familyName = ele.getStyleFamily() != null ? ele.getStyleFamily().getName() : null;

            style = styleEngine.getStyle( familyName, styleName, parentElementStyle );
        }
        else if ( e instanceof TextListElement )
        {
            TextListElement ele = (TextListElement) e;

            String styleName = ele.getTextStyleNameAttribute();

            style = styleEngine.getStyle( OdfStyleFamily.List.getName(), styleName, parentElementStyle );
        }
        return style;
    }

    private Style getParentElementStyle( IStylableElement element )
    {
        for ( IStylableContainer c = element.getParent(); c != null; c = c.getParent() )
        {
            Style style = c.getLastStyleApplied();
            if ( style != null )
            {
                return style;
            }
        }
        return null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy