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

fr.opensagres.xdocreport.document.docx.textstyling.DocxDocumentHandler Maven / Gradle / Ivy

/**
 * 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.xdocreport.document.docx.textstyling;

import java.io.IOException;
import java.util.Stack;

import fr.opensagres.xdocreport.core.utils.StringUtils;
import fr.opensagres.xdocreport.document.docx.preprocessor.DefaultStyle;
import fr.opensagres.xdocreport.document.docx.preprocessor.sax.hyperlinks.HyperlinkRegistry;
import fr.opensagres.xdocreport.document.docx.preprocessor.sax.hyperlinks.HyperlinkUtils;
import fr.opensagres.xdocreport.document.docx.preprocessor.sax.numbering.NumberingRegistry;
import fr.opensagres.xdocreport.document.docx.template.DocxContextHelper;
import fr.opensagres.xdocreport.document.preprocessor.sax.BufferedElement;
import fr.opensagres.xdocreport.document.textstyling.AbstractDocumentHandler;
import fr.opensagres.xdocreport.document.textstyling.properties.ContainerProperties;
import fr.opensagres.xdocreport.document.textstyling.properties.HeaderProperties;
import fr.opensagres.xdocreport.document.textstyling.properties.ListItemProperties;
import fr.opensagres.xdocreport.document.textstyling.properties.ListProperties;
import fr.opensagres.xdocreport.document.textstyling.properties.ParagraphProperties;
import fr.opensagres.xdocreport.document.textstyling.properties.SpanProperties;
import fr.opensagres.xdocreport.document.textstyling.properties.TableCellProperties;
import fr.opensagres.xdocreport.document.textstyling.properties.TableProperties;
import fr.opensagres.xdocreport.document.textstyling.properties.TableRowProperties;
import fr.opensagres.xdocreport.document.textstyling.properties.TextAlignment;
import fr.opensagres.xdocreport.template.IContext;

/**
 * Document handler implementation to build docx fragment content.
 */
public class DocxDocumentHandler
    extends AbstractDocumentHandler
{

    private boolean bolding;

    private boolean italicsing;

    private boolean underlining;

    private boolean striking;
    
    private boolean subscripting;

    private boolean superscripting;
    
    private Stack paragraphsStack;

    private Stack spansStack;

    private HyperlinkRegistry hyperlinkRegistry;

    protected final IDocxStylesGenerator styleGen;

    private final NumberingRegistry numberingRegistry;

    private DefaultStyle defaultStyle;

    private boolean insideHeader;

    private boolean paragraphWasInserted;

    private int currentNumId;

    private boolean haspPr;

    private int addLineBreak;

    public DocxDocumentHandler( BufferedElement parent, IContext context, String entryName )
    {
        super( parent, context, entryName );
        this.styleGen = DocxContextHelper.getStylesGenerator( context );
        this.defaultStyle = DocxContextHelper.getDefaultStyle( context );
        this.numberingRegistry = getNumberingRegistry( context );
        this.insideHeader = false;
        this.paragraphWasInserted = false;
    }

    public void startDocument()
    {
        this.bolding = false;
        this.italicsing = false;
        this.underlining = false;
        this.striking = false;
        this.subscripting = false;
        this.superscripting = false;
        this.paragraphsStack = new Stack();
        this.spansStack = new Stack();
        this.addLineBreak = 0;
    }

    public void endDocument()
        throws IOException
    {
        endParagraphIfNeeded();
        this.spansStack.clear();
    }

    private void endParagraphIfNeeded()
        throws IOException
    {
        if ( !paragraphsStack.isEmpty() )
        {
            paragraphsStack.size();
            for ( int i = 0; i < paragraphsStack.size(); i++ )
            {
                endParagraph();
            }
            paragraphsStack.clear();
        }
    }

    private void closeCurrentParagraph()
        throws IOException
    {
        if ( !paragraphsStack.isEmpty() )
        {
            endParagraph();
        }
    }

    public void startBold()
    {
        this.bolding = true;
    }

    public void endBold()
    {
        this.bolding = false;
    }

    public void startItalics()
    {
        this.italicsing = true;
    }

    public void endItalics()
    {
        this.italicsing = false;
    }

    public void startUnderline()
        throws IOException
    {
        this.underlining = true;
    }

    public void endUnderline()
        throws IOException
    {
        this.underlining = false;
    }

    public void startStrike()
        throws IOException
    {
        this.striking = true;
    }

    public void endStrike()
        throws IOException
    {
        this.striking = false;
    }

    public void startSubscript()
        throws IOException
    {
    	this.subscripting = true;
    }

    public void endSubscript()
        throws IOException
    {
    	this.subscripting = false;
    }

    public void startSuperscript()
        throws IOException
    {
    	this.superscripting = true;
    }

    public void endSuperscript()
        throws IOException
    {
    	this.superscripting = false;
    }

    @Override
    public void handleString( String content )
        throws IOException
    {
        // Re-escape DOCX special characters, xml parsing removes them.
        content = StringUtils.xmlUnescape( content );
        content = StringUtils.xmlEscape( content );

        if ( insideHeader )
        {
            // Title of the Header.
            super.write( content );
        }
        else
        {
            startParagraphIfNeeded();
            boolean bold = bolding;
            boolean italic = italicsing;
            boolean underline = underlining;
            boolean strike = striking;
            boolean subscript = subscripting;
            boolean superscript = superscripting;
            SpanProperties properties = getCurrentSpanProperties();
            if ( properties != null )
            {
                // ovveride properties declared in the span.
                if ( !bold )
                {
                    bold = properties.isBold();
                }
                if ( !italic )
                {
                    italic = properties.isItalic();
                }
                if ( !underline )
                {
                    underline = properties.isUnderline();
                }
                if ( !strike )
                {
                    strike = properties.isStrike();
                }
                if ( !subscript )
                {
                	subscript = properties.isSubscript();
                }
                if ( !superscript)
                {
                	superscript = properties.isSuperscript();
                }
            }
            super.write( "" );
            // w:RP
            processRunProperties( false, bold, italic, underline, strike, subscript, superscript );
            // w:br
            for ( int i = 0; i < addLineBreak; i++ )
            {
                super.write( "" );
            }
            addLineBreak = 0;
            if(!content.isEmpty()) 
            {
	            // w:t
	            super.write( "" );
	            super.write( content );
	            super.write( "" );
            }
            super.write( "" );
        }
    }

    private void processRunProperties( boolean isInsidePPr, boolean bold, boolean italics, boolean underline,
                                       boolean strike, boolean subscript, boolean superscript )
        throws IOException
    {
        if ( bold || italics || underline || strike || subscript || superscript  )
        {
            if ( isInsidePPr )
            {
                startPPrIfNeeded();
            }
            super.write( "" );
            if ( bold )
            {
                super.write( "" );
            }
            if ( italics )
            {
                super.write( "" );
            }
            if ( underline )
            {
                super.write( "" );
            }
            if ( strike )
            {
                super.write( "" );
            }
            if (subscript) {
            	super.write( "" );
            }
            if (superscript) {
            	super.write( "" );
            }
            super.write( "" );
        }
    }

    private void startParagraphIfNeeded()
        throws IOException
    {

        if ( paragraphWasInserted && paragraphsStack.isEmpty() )
        {
            internalStartParagraph( null );
        }
    }

    private void internalStartParagraph( ContainerProperties properties, String pStyle, boolean isList )
        throws IOException
    {
        paragraphWasInserted = true;
        super.setTextLocation( TextLocation.End );
        super.write( "" );
        processParagraphProperties( properties, pStyle, isList );
        paragraphsStack.push( properties );
    }

    private void internalStartParagraph( ContainerProperties properties )
        throws IOException
    {
        if (properties != null && properties.getStyleName() != null) {
            internalStartParagraph(properties, properties.getStyleName(), false);
        } else {
            internalStartParagraph(properties, null, false);
        }
    }

    public void startParagraph( ParagraphProperties properties )
        throws IOException
    {
        processPageBreakBefore( properties );
        closeCurrentParagraph();
        internalStartParagraph( properties );
    }

    private void processPageBreakBefore( ContainerProperties properties )
        throws IOException
    {
        if ( properties != null )
        {
            if ( properties.isPageBreakBefore() )
            {
                super.setTextLocation( TextLocation.End );
                generatePageBreak();
            }
        }
    }

    private void processPageBreakAfter( ContainerProperties properties )
        throws IOException
    {
        if ( properties != null )
        {
            if ( properties.isPageBreakAfter() )
            {
                super.setTextLocation( TextLocation.End );
                generatePageBreak();
            }
        }
    }

    private void generatePageBreak()
        throws IOException
    {
        super.write( "" );
        super.write( "" );
        super.write( "" );
        super.write( "" );
        super.write( "" );
    }

    public void endParagraph()
        throws IOException
    {
        super.write( "" );
        processPageBreakAfter( paragraphsStack.pop() );
    }

    public void startListItem( ListItemProperties properties )
        throws IOException
    {
        // Close current paragraph
        closeCurrentParagraph();
        internalStartParagraph( properties, null, true );
    }

    /**
     * Generate wpPr docx element.
     * 
     * @param properties
     * @param pStyle
     * @param isList
     * @throws IOException
     */
    private void processParagraphProperties( ContainerProperties properties, String pStyle, boolean isList )
        throws IOException
    {
        haspPr = false;
        if ( pStyle != null )
        {
            startPPrIfNeeded();
            super.write( "" );
        }
        if ( isList )
        {
            startPPrIfNeeded();
            super.write( "" );

            // 
            int ilvlVal = super.getCurrentListIndex();
            super.write( "" );

            // ""
            int numIdVal = getCurrentNumId();
            super.write( "" );

            super.write( "" );
        }
        //
        if ( properties != null )
        {
            // w:jc
            TextAlignment textAlignment = properties.getTextAlignment();
            if ( textAlignment != null )
            {
                switch ( textAlignment )
                {
                    case Left:
                        startPPrIfNeeded();
                        super.write( "" );
                        break;
                    case Center:
                        startPPrIfNeeded();
                        super.write( "" );
                        break;
                    case Right:
                        startPPrIfNeeded();
                        super.write( "" );
                        break;
                    case Justify:
                        startPPrIfNeeded();
                        super.write( "" );
                        break;
                }
            }
            // rPPr
            processRunProperties( true, properties.isBold(), properties.isItalic(), properties.isUnderline(),
                                  properties.isStrike(), properties.isSubscript(), properties.isSuperscript() );
        }

        endPPrIfNeeded();
    }

    private void startPPrIfNeeded()
        throws IOException
    {
        if ( !haspPr )
        {
            super.write( "" );
        }
        haspPr = true;
    }

    private void endPPrIfNeeded()
        throws IOException
    {
        if ( haspPr )
        {
            super.write( "" );
        }
    }

    public void endListItem()
        throws IOException
    {
        // endParagraph();
    }

    public void startHeading( int level, HeaderProperties properties )
        throws IOException
    {
        // Close current paragraph
        closeCurrentParagraph();

        // In docx, title is a paragraph with a style.

        /**
         *   
         *   Titre1  
         */
        String headingStyleName = styleGen.getHeaderStyleId( level, defaultStyle );
        internalStartParagraph( properties, headingStyleName, false );
        super.write( "" );
        insideHeader = true;
    }

    public void endHeading( int level )
        throws IOException
    {
        super.write( "" );
        endParagraph();
        insideHeader = false;
    }

    public void startSpan( SpanProperties properties )
        throws IOException
    {
        spansStack.push( properties );
        // startParagraphIfNeeded();
        // super.write( "" );
        // processRunProperties( false, properties.isBold(), properties.isItalic(), properties.isUnderline(),
        // properties.isStrike() );
        // super.write( "" );
        // super.write( content );
        // super.write( "" );
        // super.write( "" );
    }

    private SpanProperties getCurrentSpanProperties()
    {
        if ( !spansStack.isEmpty() )
        {
            return spansStack.peek();
        }
        return null;
    }

    public void endSpan()
        throws IOException
    {
        spansStack.pop();
    }

    @Override
    protected void doStartOrderedList( ListProperties properties )
        throws IOException
    {
    	if(this.addLineBreak>0) 
    	{
    		handleString("");
    	}
        // if ( numbersStack.isEmpty() )
        // {
        if ( getCurrentListIndex() < 1 )
        {
            int abstractNumId = styleGen.getAbstractNumIdForList( true, defaultStyle );
            int numId = getNumberingRegistry().addNum( abstractNumId, getMaxNumId(), true ).getNumId();
            // numbersStack.push( numId );
            currentNumId = numId;
        }
        // }
    }

    @Override
    protected void doStartUnorderedList( ListProperties properties )
        throws IOException
    {
    	if(this.addLineBreak>0) 
    	{
    		handleString("");
    	}
        // if ( numbersStack.isEmpty() )
        // {
        if ( getCurrentListIndex() < 1 )
        {
            int abstractNumId = styleGen.getAbstractNumIdForList( false, defaultStyle );
            int numId = getNumberingRegistry().addNum( abstractNumId, getMaxNumId(), false ).getNumId();
            // numbersStack.push( numId );
            currentNumId = numId;
        }
        // }
    }

    private Integer getMaxNumId()
    {
        if ( defaultStyle == null )
        {
            return null;
        }
        return defaultStyle.getMaxNumId();
    }

    @Override
    protected void doEndUnorderedList()
        throws IOException
    {
        // Close current paragraph
        closeCurrentParagraph();
    }

    @Override
    protected void doEndOrderedList()
        throws IOException
    {
        // Close current paragraph
        closeCurrentParagraph();
    }

    private int getCurrentNumId()
    {
        return currentNumId;// numbersStack.peek();
    }

    public void handleReference( String ref, String label )
        throws IOException
    {
        if ( ref != null )
        {
            // 1) replace & with &
            // fix issue http://code.google.com/p/xdocreport/issues/detail?id=147
            ref = StringUtils.xmlUnescape( ref );
            ref = StringUtils.xmlEscape( ref );
            // fix issue http://code.google.com/p/xdocreport/issues/detail?id=154
            label = StringUtils.xmlUnescape( label );
            label = StringUtils.xmlEscape( label );

            // 2) Update the hyperlink registry to modifiy the Hyperlink Relationship in the _rels/document.xml.rels
            HyperlinkRegistry registry = getHyperlinkRegistry();
            String rId = registry.registerHyperlink( ref );

            // 3) Generate w:hyperlink
            String hyperlinkStyleName = styleGen.getHyperLinkStyleId( defaultStyle );
            super.write( " " );
            super.write( "" );
            super.write( "" );
            super.write( "" );
            super.write( "" );
            super.write( "" );
            super.write( "" );
            super.write( label != null ? label : ref );
            super.write( "" );
            super.write( "" );
            super.write( "" );
            super.write( "" );
        }
    }

    public void handleImage( String ref, String label )
        throws IOException
    {

    }

    private NumberingRegistry getNumberingRegistry()
    {
        return numberingRegistry;
    }

    private NumberingRegistry getNumberingRegistry( IContext context )
    {

        if ( context == null )
        {
            return new NumberingRegistry();
        }

        NumberingRegistry registry = DocxContextHelper.getNumberingRegistry( context );
        if ( registry == null )
        {
            registry = new NumberingRegistry();
            DocxContextHelper.putNumberingRegistry( context, registry );
        }
        return registry;

    }

    private HyperlinkRegistry getHyperlinkRegistry()
    {
        if ( hyperlinkRegistry != null )
        {
            return hyperlinkRegistry;
        }

        IContext context = getContext();
        if ( context == null )
        {
            hyperlinkRegistry = new HyperlinkRegistry();
            return hyperlinkRegistry;
        }

        String key = HyperlinkUtils.getHyperlinkRegistryKey( getEntryName() );
        hyperlinkRegistry = (HyperlinkRegistry) context.get( key );
        if ( hyperlinkRegistry == null )
        {
            hyperlinkRegistry = new HyperlinkRegistry();
            context.put( key, hyperlinkRegistry );
        }
        return hyperlinkRegistry;
    }

    public void handleLineBreak()
        throws IOException
    {
        // 
        this.addLineBreak++;
    }

    protected void doStartTable( TableProperties properties )
        throws IOException
    {
        closeCurrentParagraph();
        // ooxml table cannot be generated now, because here we don't know the table column count required to generate
        // w:tblGrid
        // that's here temp writer is used.
        pushTempWriter();
    }

    public void doEndTable( TableProperties properties )
        throws IOException
    {
        StringBuilder startTable = new StringBuilder( "" );
        startTable.append( "" );
        int count = properties.getColumnCount();
        for ( int i = 0; i < count; i++ )
        {
            startTable.append( "" );
        }
        startTable.append( "" );
        popTempWriter( startTable.toString() );
        super.write( "" );
        // if table is inside a table cell, a paragraph is required.
        super.write( "" );
    }

    public void doStartTableRow( TableRowProperties properties )
        throws IOException
    {
        super.write( "" );
    }

    public void doEndTableRow()
        throws IOException
    {
        super.write( "" );
    }

    public void doStartTableCell( TableCellProperties properties )
        throws IOException
    {
        super.write( "" );
        /*
         * super.write( "" ); super.write( "" ); super.write( "" );
         */
        internalStartParagraph( null );

    }

    public void doEndTableCell()
        throws IOException
    {
        endParagraph();
        super.write( "" );
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy