From e9d4858c96af42c035ef235f804a6600215c409a Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Mon, 14 Nov 2005 18:02:14 +0000 Subject: [PATCH] Bugzilla #36480: Improved space-before/space-after support for RTF output. Fixes problems with space support in the existing code. Submitted by: Sergey Simonchik git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@344172 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/render/rtf/RTFHandler.java | 16 +- .../rtf/rtflib/rtfdoc/RtfSpaceManager.java | 131 +++++++++++++++ .../rtf/rtflib/rtfdoc/RtfSpaceSplitter.java | 152 ++++++++++++++++++ .../render/rtf/rtflib/rtfdoc/RtfTextrun.java | 109 ++++++++++++- 4 files changed, 396 insertions(+), 12 deletions(-) create mode 100644 src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfSpaceManager.java create mode 100644 src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfSpaceSplitter.java diff --git a/src/java/org/apache/fop/render/rtf/RTFHandler.java b/src/java/org/apache/fop/render/rtf/RTFHandler.java index 3f85094e5..601a24864 100644 --- a/src/java/org/apache/fop/render/rtf/RTFHandler.java +++ b/src/java/org/apache/fop/render/rtf/RTFHandler.java @@ -336,7 +336,7 @@ public class RTFHandler extends FOEventHandler { RtfTextrun textrun = container.getTextrun(); textrun.addParagraphBreak(); - textrun.pushAttributes(rtfAttr); + textrun.pushBlockAttributes(rtfAttr); textrun.addBookmark(bl.getId()); } catch (IOException ioe) { // TODO could we throw Exception in all FOEventHandler events? @@ -367,7 +367,7 @@ public class RTFHandler extends FOEventHandler { RtfTextrun textrun = container.getTextrun(); textrun.addParagraphBreak(); - textrun.popAttributes(); + textrun.popBlockAttributes(); } catch (IOException ioe) { log.error("startBlock:" + ioe.getMessage()); @@ -398,7 +398,7 @@ public class RTFHandler extends FOEventHandler { RtfTextrun textrun = container.getTextrun(); textrun.addParagraphBreak(); - textrun.pushAttributes(rtfAttr); + textrun.pushBlockAttributes(rtfAttr); } catch (IOException ioe) { // TODO could we throw Exception in all FOEventHandler events? log.error("startBlock: " + ioe.getMessage()); @@ -426,7 +426,7 @@ public class RTFHandler extends FOEventHandler { RtfTextrun textrun = container.getTextrun(); textrun.addParagraphBreak(); - textrun.popAttributes(); + textrun.popBlockAttributes(); } catch (IOException ioe) { log.error("startBlock:" + ioe.getMessage()); @@ -552,7 +552,7 @@ public class RTFHandler extends FOEventHandler { IRtfTextrunContainer.class, true, this); RtfTextrun textrun = container.getTextrun(); - textrun.pushAttributes(rtfAttr); + textrun.pushInlineAttributes(rtfAttr); textrun.addBookmark(inl.getId()); } catch (IOException ioe) { log.error("startInline:" + ioe.getMessage()); @@ -581,7 +581,7 @@ public class RTFHandler extends FOEventHandler { IRtfTextrunContainer.class, true, this); RtfTextrun textrun = container.getTextrun(); - textrun.popAttributes(); + textrun.popInlineAttributes(); } catch (IOException ioe) { log.error("startInline:" + ioe.getMessage()); throw new RuntimeException(ioe.getMessage()); @@ -1146,9 +1146,9 @@ public class RTFHandler extends FOEventHandler { RtfAttributes rtfAttr = TextAttributesConverter.convertCharacterAttributes(text); - textrun.pushAttributes(rtfAttr); + textrun.pushInlineAttributes(rtfAttr); textrun.addString(new String(data, start, length - start)); - textrun.popAttributes(); + textrun.popInlineAttributes(); } catch (IOException ioe) { // FIXME could we throw Exception in all FOEventHandler events? log.error("characters: " + ioe.getMessage()); diff --git a/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfSpaceManager.java b/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfSpaceManager.java new file mode 100644 index 000000000..f7340842b --- /dev/null +++ b/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfSpaceManager.java @@ -0,0 +1,131 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed 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.rtflib.rtfdoc; + +import java.util.Iterator; +import java.util.LinkedList; + +/** + * This class is responsible for saving space-before/space-after attributes + * history and adding spacing to established candidates (i.e. attributes) or + * accumulation spacing in case of candidate absence. + */ +public class RtfSpaceManager { + /** Stack for saving rtf block-level attributes. */ + private LinkedList blockAttributes = new LinkedList(); + + /** Stack for saving rtf inline-level attributes. */ + private LinkedList inlineAttributes = new LinkedList(); + + /** + * Keeps value of accumulated space in twips. For example if block has + * nonzero space-before or space-after properties and has no plain text + * inside, then the next block should has increased value of space-before + * property. + */ + private int accumulatedSpace = 0; + + /** + * Construct a newly allocated RtfSpaceManager object. + */ + public RtfSpaceManager() { + } + + /** + * Iterates block-level stack (i.e. all open blocks) and stops updating + * candidate for adding space-before/space-after attribute in case of + * candidate presence. + */ + public void stopUpdatingSpaceBefore() { + for (Iterator it = blockAttributes.iterator(); it.hasNext();) { + RtfSpaceSplitter splitter = (RtfSpaceSplitter) it.next(); + if (splitter.isBeforeCadidateSet()) { + splitter.stopUpdatingSpaceBefore(); + } + } + } + + /** + * Set attributes as candidate for space attributes inheritance. + * + * @param attrs attributes to set + */ + public void setCandidate(RtfAttributes attrs) { + for (Iterator it = blockAttributes.iterator(); it.hasNext();) { + RtfSpaceSplitter splitter = (RtfSpaceSplitter) it.next(); + splitter.setSpaceBeforeCandidate(attrs); + splitter.setSpaceAfterCandidate(attrs); + } + } + + /** + * Builds RtfSpaceSplitter on attrs and adds it to the + * block-level stack. + * + * @param attrs RtfAttribute to add + * @return instance of RtfSpaceSplitter + */ + public RtfSpaceSplitter pushRtfSpaceSplitter(RtfAttributes attrs) { + RtfSpaceSplitter splitter; + splitter = new RtfSpaceSplitter(attrs, accumulatedSpace); + // set accumulatedSpace to 0, because now accumulatedSpace used + // in splitter + accumulatedSpace = 0; + blockAttributes.addLast(splitter); + return splitter; + } + + /** + * Removes RtfSpaceSplitter from top of block-level stack. + */ + public void popRtfSpaceSplitter() { + if (!blockAttributes.isEmpty()) { + RtfSpaceSplitter splitter; + splitter = (RtfSpaceSplitter) blockAttributes.removeLast(); + accumulatedSpace += splitter.flush(); + } + } + + /** + * Pushes inline attributes to inline-level stack. + * + * @param attrs attributes to add + */ + public void pushInlineAttributes(RtfAttributes attrs) { + inlineAttributes.addLast(attrs); + } + + /** + * Pops inline attributes from inline-level stack. + */ + public void popInlineAttributes() { + if (!inlineAttributes.isEmpty()) { + inlineAttributes.removeLast(); + } + } + + /** + * Peeks at inline-level attribute stack. + * + * @return RtfAttributes from top of inline-level stack + */ + public RtfAttributes getLastInlineAttribute() { + return (RtfAttributes) inlineAttributes.getLast(); + } +} diff --git a/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfSpaceSplitter.java b/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfSpaceSplitter.java new file mode 100644 index 000000000..d42d4a5d1 --- /dev/null +++ b/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfSpaceSplitter.java @@ -0,0 +1,152 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed 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.rtflib.rtfdoc; + +/** + * This class splits block attributes into space-before attribute, space-after + * attribute and common attributes. + */ +public class RtfSpaceSplitter { + + /** Common attributes for all text. */ + private RtfAttributes commonAttributes; + + /** Space-before attributes of a block. */ + private int spaceBefore; + + /** Space-after attributes of a block. */ + private int spaceAfter; + + /** Indicate that we can update candidate for space-before. */ + private boolean updatingSpaceBefore; + + /** Candidate for adding space-before. */ + private RtfAttributes spaceBeforeCandidate; + + /** Candidate for adding space-before. */ + private RtfAttributes spaceAfterCandidate; + + /** + * Create RtfSpaceSplitter with given RtfAttributes. + * + * @param attrs RtfAttributes for splitting + * @param previousSpace integer, representing accumulated spacing + */ + public RtfSpaceSplitter(RtfAttributes attrs, int previousSpace) { + commonAttributes = attrs; + updatingSpaceBefore = true; + spaceBeforeCandidate = null; + spaceAfterCandidate = null; + + spaceBefore = split(RtfText.SPACE_BEFORE) + previousSpace; + spaceAfter = split(RtfText.SPACE_AFTER); + } + + /** + * Remove attributes with name key from + * commonAttributes and return it as int. + * + * @param key attributes name to extract + * @return integer, representing value of extracted attributes + */ + public int split(String key) { + Integer i = (Integer) commonAttributes.getValue(key); + if (i == null) { + i = new Integer(0); + } + + commonAttributes.unset(key); + return i.intValue(); + } + + /** @return attributes, applicable to whole block. */ + public RtfAttributes getCommonAttributes() { + return commonAttributes; + } + + /** @return space-before value. */ + public int getSpaceBefore() { + return spaceBefore; + } + + /** + * Sets a candidate for space-before property. + * + * @param candidate instance of RtfAttributes, considered as + * a candidate for space-before adding + */ + public void setSpaceBeforeCandidate(RtfAttributes candidate) { + if (updatingSpaceBefore) { + this.spaceBeforeCandidate = candidate; + } + } + + /** + * Sets a candidate for space-after property. + * + * @param candidate instance of RtfAttributes, considered as + * a candidate for space-after adding + */ + public void setSpaceAfterCandidate(RtfAttributes candidate) { + this.spaceAfterCandidate = candidate; + } + + /** @return true, if candidate for space-before is set. */ + public boolean isBeforeCadidateSet() { + return spaceBeforeCandidate != null; + } + + /** @return true, if candidate for space-after is set. */ + public boolean isAfterCadidateSet() { + return spaceAfterCandidate != null; + } + + /** + * Stops updating candidates for space-before attribute. + */ + public void stopUpdatingSpaceBefore() { + updatingSpaceBefore = false; + } + + /** + * Adds corresponding attributes to their candidates. + * + * @return integer, representing value of space-before/space-after + * attributes, that can't be added anywhere (i.e. these attributes + * hasn't their candidates) + */ + public int flush() { + int accumulatingSpace = 0; + if (!isBeforeCadidateSet()) { + accumulatingSpace += spaceBefore; + } else { + spaceBeforeCandidate.addIntegerValue(spaceBefore, + RtfText.SPACE_BEFORE); + } + + if (!isAfterCadidateSet()) { + accumulatingSpace += spaceAfter; + } else { + spaceAfterCandidate.addIntegerValue(spaceAfter, + RtfText.SPACE_AFTER); + } + + return accumulatingSpace; + } +} diff --git a/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfTextrun.java b/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfTextrun.java index 56316bbc8..a919c555e 100644 --- a/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfTextrun.java +++ b/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfTextrun.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.io.Writer; import java.util.List; import java.util.Iterator; +import java.util.ListIterator; // FOP import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfExternalGraphic; @@ -43,6 +44,9 @@ public class RtfTextrun extends RtfContainer { private boolean bSuppressLastPar = false; private RtfListItem rtfListItem; + /** Manager for handling space-* property. */ + private RtfSpaceManager rtfSpaceManager = new RtfSpaceManager(); + /** Class which represents the opening of a RTF group mark.*/ private class RtfOpenGroupMark extends RtfElement { @@ -121,24 +125,121 @@ public class RtfTextrun extends RtfContainer { super(parent, w, attrs); } - public void pushAttributes(RtfAttributes attrs) throws IOException { + + /** + * Adds instance of OpenGroupMark as a child with attributes. + * + * @param attrs attributes to add + * @throws IOException for I/O problems + */ + public void addOpenGroupMark(RtfAttributes attrs) throws IOException { RtfOpenGroupMark r = new RtfOpenGroupMark(this, writer, attrs); } - - public void popAttributes() throws IOException { + + /** + * Adds instance of CloseGroupMark as a child. + * + * @throws IOException for I/O problems + */ + public void addCloseGroupMark() throws IOException { RtfCloseGroupMark r = new RtfCloseGroupMark(this, writer); } + /** + * Pushes block attributes, notifies all opened blocks about pushing block + * attributes, adds OpenGroupMark as a child. + * + * @param attrs the block attributes to push + * @throws IOException for I/O problems + */ + public void pushBlockAttributes(RtfAttributes attrs) throws IOException { + rtfSpaceManager.stopUpdatingSpaceBefore(); + RtfSpaceSplitter splitter = rtfSpaceManager.pushRtfSpaceSplitter(attrs); + addOpenGroupMark(splitter.getCommonAttributes()); + } + + /** + * Pops block attributes, notifies all opened blocks about pushing block + * attributes, adds CloseGroupMark as a child. + * + * @throws IOException for I/O problems + */ + public void popBlockAttributes() throws IOException { + rtfSpaceManager.popRtfSpaceSplitter(); + rtfSpaceManager.stopUpdatingSpaceBefore(); + addCloseGroupMark(); + } + + /** + * Pushes inline attributes. + * + * @param attrs the inline attributes to push + * @throws IOException for I/O problems + */ + public void pushInlineAttributes(RtfAttributes attrs) throws IOException { + rtfSpaceManager.pushInlineAttributes(attrs); + addOpenGroupMark(attrs); + } + + /** + * Pop inline attributes. + * + * @throws IOException for I/O problems + */ + public void popInlineAttributes() throws IOException { + rtfSpaceManager.popInlineAttributes(); + addCloseGroupMark(); + } + + /** + * Add string to children list. + * + * @param s string to add + * @throws IOException for I/O problems + */ public void addString(String s) throws IOException { + if (s.equals("")) { + return; + } + RtfAttributes attrs = rtfSpaceManager.getLastInlineAttribute(); + //add RtfSpaceSplitter to inherit accumulated space + rtfSpaceManager.pushRtfSpaceSplitter(attrs); + rtfSpaceManager.setCandidate(attrs); RtfString r = new RtfString(this, writer, s); + rtfSpaceManager.popRtfSpaceSplitter(); } public RtfFootnote addFootnote() throws IOException { return new RtfFootnote(this, writer); } + /** + * Inserts paragraph break before all close group marks. + * + * @throws IOException for I/O problems + */ public void addParagraphBreak() throws IOException { - RtfParagraphBreak r = new RtfParagraphBreak(this, writer); + // get copy of children list + List children = getChildren(); + + // delete all previous CloseGroupMark + int deletedCloseGroupCount = 0; + + ListIterator lit = children.listIterator(children.size()); + while (lit.hasPrevious() + && (lit.previous() instanceof RtfCloseGroupMark)) { + lit.remove(); + deletedCloseGroupCount++; + } + + if (children.size() != 0) { + // add paragraph break and restore all deleted close group marks + setChildren(children); + new RtfParagraphBreak(this, writer); + for (int i = 0; i < deletedCloseGroupCount; i++) { + addCloseGroupMark(); + } + } } public void addPageNumber(RtfAttributes attr) throws IOException { -- 2.39.5