From 6c5966390eb0fdeb8c4b7cc5231e6244cdd53512 Mon Sep 17 00:00:00 2001 From: Robert Meyer Date: Tue, 22 Oct 2013 10:06:12 +0000 Subject: [PATCH] FOP-2104: RTF renderer barfs when fo:table-row is missing inside fo-table-header git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1534582 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/render/rtf/RTFHandler.java | 42 ++++++++--- .../fop/render/rtf/RTFPlaceHolderHelper.java | 72 +++++++++++++++++++ .../rtf/rtflib/tools/BuilderContext.java | 71 +++++++++++++++--- 3 files changed, 166 insertions(+), 19 deletions(-) create mode 100644 src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java diff --git a/src/java/org/apache/fop/render/rtf/RTFHandler.java b/src/java/org/apache/fop/render/rtf/RTFHandler.java index dfeff65f1..73fa3504c 100644 --- a/src/java/org/apache/fop/render/rtf/RTFHandler.java +++ b/src/java/org/apache/fop/render/rtf/RTFHandler.java @@ -157,6 +157,7 @@ public class RTFHandler extends FOEventHandler { private PercentContext percentManager = new PercentContext(); + /** * Creates a new RTF structure handler. * @param userAgent the FOUserAgent for this process @@ -270,7 +271,7 @@ public class RTFHandler extends FOEventHandler { return; } else { - builderContext.popContainer(); + builderContext.popContainer(RtfSection.class, this); this.pagemaster = null; } } @@ -377,10 +378,10 @@ public class RTFHandler extends FOEventHandler { //just do nothing } else if (regionBefore != null && fl.getFlowName().equals(regionBefore.getRegionName())) { - builderContext.popContainer(); + builderContext.popContainer(RtfBefore.class, this); } else if (regionAfter != null && fl.getFlowName().equals(regionAfter.getRegionName())) { - builderContext.popContainer(); + builderContext.popContainer(RtfAfter.class, this); } } catch (Exception e) { log.error("endFlow: " + e.getMessage()); @@ -571,7 +572,7 @@ public class RTFHandler extends FOEventHandler { nestedTableDepth--; builderContext.popTableContext(); - builderContext.popContainer(); + builderContext.popContainer(RtfTable.class, this); } /** {@inheritDoc} */ @@ -605,21 +606,25 @@ public class RTFHandler extends FOEventHandler { /** {@inheritDoc} */ public void startHeader(TableHeader header) { + builderContext.pushPart(header); startPart(header); } /** {@inheritDoc} */ public void endHeader(TableHeader header) { + builderContext.popPart(header.getClass(), this); endPart(header); } /** {@inheritDoc} */ public void startFooter(TableFooter footer) { + builderContext.pushPart(footer); startPart(footer); } /** {@inheritDoc} */ public void endFooter(TableFooter footer) { + builderContext.popPart(footer.getClass(), this); endPart(footer); } @@ -711,11 +716,13 @@ public class RTFHandler extends FOEventHandler { * {@inheritDoc} */ public void startBody(TableBody body) { + builderContext.pushPart(body); startPart(body); } /** {@inheritDoc} */ public void endBody(TableBody body) { + builderContext.popPart(TableBody.class, this); endPart(body); } @@ -784,7 +791,7 @@ public class RTFHandler extends FOEventHandler { } - builderContext.popContainer(); + builderContext.popContainer(RtfTableRow.class, this); builderContext.getTableContext().decreaseRowSpannings(); } @@ -893,7 +900,7 @@ public class RTFHandler extends FOEventHandler { throw new RuntimeException(e.getMessage()); } - builderContext.popContainer(); + builderContext.popContainer(RtfTableCell.class, this); builderContext.getTableContext().selectNextColumn(); } @@ -929,7 +936,7 @@ public class RTFHandler extends FOEventHandler { return; } - builderContext.popContainer(); + builderContext.popContainer(RtfList.class, this); } /** {@inheritDoc} */ @@ -976,7 +983,7 @@ public class RTFHandler extends FOEventHandler { return; } - builderContext.popContainer(); + builderContext.popContainer(RtfListItem.class, this); } /** {@inheritDoc} */ @@ -1005,7 +1012,7 @@ public class RTFHandler extends FOEventHandler { return; } - builderContext.popContainer(); + builderContext.popContainer(RtfListItemLabel.class, this); } /** {@inheritDoc} */ @@ -1070,7 +1077,7 @@ public class RTFHandler extends FOEventHandler { return; } - builderContext.popContainer(); + builderContext.popContainer(RtfHyperLink.class, this); } /** {@inheritDoc} */ @@ -1306,7 +1313,7 @@ public class RTFHandler extends FOEventHandler { return; } - builderContext.popContainer(); + builderContext.popContainer(RtfFootnote.class, this); } /** {@inheritDoc} */ @@ -1687,6 +1694,19 @@ public class RTFHandler extends FOEventHandler { } } + /** + * Closes any mismatched tags that are detected in the RTF structure. + * @param containerClass The class representing the tag to close. + * @return Determines whether the tag mismatch has been handled. + */ + public boolean endContainer(Class containerClass) { + if (containerClass == RtfTableRow.class) { + endRow(null); + return true; + } + return false; + } + /** * Calls the event handlers for the passed FONode and all its elements. * diff --git a/src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java b/src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java new file mode 100644 index 000000000..c31c68db1 --- /dev/null +++ b/src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.rtf; + +import org.apache.fop.render.rtf.rtflib.exceptions.RtfException; +import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfAttributes; +import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfContainer; +import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfTable; +import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfTableRow; +import org.apache.fop.render.rtf.rtflib.tools.BuilderContext; + +/** + * This class creates objects which are missing from the XSL:FO but are required + * by the RTF format. + */ +public class RTFPlaceHolderHelper { + /** The context object for building the RTF */ + private BuilderContext builderContext; + + /** + * Creates a new instance for the RTF place holder which attempts to resolve + * mismatches in structure between XSL:FO and RTF. + * @param builderContext The builder context + */ + public RTFPlaceHolderHelper(BuilderContext builderContext) { + this.builderContext = builderContext; + } + + /** + * A method to create an object which is missing and required from the + * RTF structure. + * @param containerClass The class which is missing + * @throws Exception + */ + public void createRTFPlaceholder(Class containerClass) throws RtfException { + if (containerClass == RtfTableRow.class) { + createRtfTableRow(); + } + } + + private void createRtfTableRow() throws RtfException { + try { + RtfContainer element = builderContext.getContainer(RtfTable.class, true, null); + if (element != null && element instanceof RtfTable) { + RtfTable table = (RtfTable)element; + RtfAttributes attribs = new RtfAttributes(); + RtfTableRow newRow = table.newTableRow(attribs); + builderContext.pushContainer(newRow); + builderContext.getTableContext().selectFirstColumn(); + } + } catch (Exception ex) { + throw new RtfException(ex.getMessage()); + } + } +} diff --git a/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java b/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java index f0a29a0ab..6dcae5412 100644 --- a/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java +++ b/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java @@ -21,6 +21,12 @@ package org.apache.fop.render.rtf.rtflib.tools; import java.util.Stack; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.fo.FObj; +import org.apache.fop.render.rtf.RTFHandler; +import org.apache.fop.render.rtf.RTFPlaceHolderHelper; import org.apache.fop.render.rtf.rtflib.exceptions.RtfException; import org.apache.fop.render.rtf.rtflib.rtfdoc.IRtfOptions; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfContainer; @@ -38,6 +44,10 @@ import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfContainer; */ public class BuilderContext { + + /** Static logging instance */ + protected static final Log LOG = LogFactory.getLog(BuilderContext.class.getName()); + /** stack of RtfContainers */ private final Stack containers = new Stack(); @@ -96,17 +106,22 @@ public class BuilderContext { * @throws RtfException if not caught */ public RtfContainer getContainer(Class containerClass, boolean required, - Object /*IBuilder*/ forWhichBuilder) throws RtfException { + Object forWhichBuilder) throws RtfException { // TODO what to do if the desired container is not at the top of the stack? // close top-of-stack container? - final RtfContainer result = (RtfContainer)getObjectFromStack(containers, + RtfContainer result = (RtfContainer)getObjectFromStack(containers, containerClass); if (result == null && required) { - throw new RtfException( - "No RtfContainer of class '" + containerClass.getName() - + "' available for '" + forWhichBuilder.getClass().getName() + "' builder" - ); + RTFPlaceHolderHelper placeHolderHelper = new RTFPlaceHolderHelper(this); + placeHolderHelper.createRTFPlaceholder(containerClass); + result = getContainer(containerClass, required, forWhichBuilder); + if (result == null) { + throw new RtfException( + "No RtfContainer of class '" + containerClass.getName() + + "' available for '" + forWhichBuilder.getClass().getName() + "' builder" + ); + } } return result; @@ -120,6 +135,14 @@ public class BuilderContext { containers.push(c); } + /** + * Push a Class representing a non-writeable section of the FO tree + * @param part the part + */ + public void pushPart(FObj part) { + containers.push(part); + } + /** * In some cases an RtfContainer must be replaced by another one on the * stack. This happens when handling nested fo:blocks for example: after @@ -142,8 +165,40 @@ public class BuilderContext { } /** pop the topmost RtfContainer from our stack */ - public void popContainer() { - containers.pop(); + public void popContainer(Class containerClass, RTFHandler handler) { + handlePop(containerClass, handler); + } + + /** pop the topmost part class from our stack */ + public void popPart(Class part, RTFHandler handler) { + handlePop(part, handler); + } + + /** + * This method checks for any tag mismatches between what is being closed + * and what exists on the stack. If a mismatch is found, then it will push + * the object back onto the stack and attempt to close the given section + * before retrying with the current pop task. + * @param aClass The class to be popped from the stack + * @param handler The RTF handler class to fix any mismatches + */ + private void handlePop(Class aClass, RTFHandler handler) { + Object object = containers.pop(); + if (object.getClass() != aClass) { + pushAndClose(aClass, object, handler); + } + } + + private void pushAndClose(Class aClass, Object object, RTFHandler handler) { + containers.push(object); + if (handler.endContainer(object.getClass())) { + popContainer(aClass, handler); + } else { + /* This should never happen unless a placeholder is not catered for + * in the RTFHandler.endContainer method. */ + LOG.warn("Unhandled RTF structure tag mismatch detected between " + + aClass.getSimpleName() + " and "+object.getClass().getSimpleName()); + } } /* push an IBuilder to our stack / -- 2.39.5