]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
FOP-2104: RTF renderer barfs when fo:table-row is missing inside fo-table-header
authorRobert Meyer <rmeyer@apache.org>
Tue, 22 Oct 2013 10:06:12 +0000 (10:06 +0000)
committerRobert Meyer <rmeyer@apache.org>
Tue, 22 Oct 2013 10:06:12 +0000 (10:06 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1534582 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/fop/render/rtf/RTFHandler.java
src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java [new file with mode: 0644]
src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java

index dfeff65f1bbb0efdb9f1e447d0b40a3945113a87..73fa3504c7fc41cafabe19844e9c24e75efb02a3 100644 (file)
@@ -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 (file)
index 0000000..c31c68d
--- /dev/null
@@ -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());
+        }
+    }
+}
index f0a29a0ab383e7e5a056cf2567447443fcc72f1a..6dcae5412b127e6a29dc4967f125d779af612e1f 100644 (file)
@@ -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 /