/** * 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.odfdom.converter.pdf.internal; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; 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.itextpdf.text.DocumentException; import com.itextpdf.text.Font; import com.itextpdf.text.Image; import fr.opensagres.odfdom.converter.core.Color; 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.Converter; import fr.opensagres.odfdom.converter.pdf.PdfAOptions; 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 <leszekp@safe-mail.net> */ 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<Integer> 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 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 ElementVisitorForIText( OdfDocument odfDocument, OutputStream out, StyleEngineForIText styleEngine, PdfAOptions options, Integer forcedPageCount ) { super( odfDocument, options.getExtractor(), out, null ); this.styleEngine = styleEngine; this.forcedPageCount = forcedPageCount; try { document = new StylableDocument( out, options.getPdfAConfiguration(), 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<Integer>(); } 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<Integer>( currentHeadingNumbering ) ); } // ---------------------- visit //text:p @Override public void visit( TextPElement ele ) { processParagraphOrHeading( ele, null ); } private void processParagraphOrHeading( TextParagraphElementBase ele, List<Integer> 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( Converter.toBaseColor(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 : <table:table // table:name="Tableau1" table:style-name="Tableau1">) @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 ( 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 = StylableImage.getImage( imageStream ); 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<StylableChunk> 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; } }