org.apache.fop.render.rtf.rtflib.rtfdoc.RtfTextrun Maven / Gradle / Ivy
/*
* 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: RtfTextrun.java 1761020 2016-09-16 11:17:35Z ssteiner $ */
package org.apache.fop.render.rtf.rtflib.rtfdoc;
// Java
import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Stack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Class which contains a linear text run. It has methods to add attributes,
* text, paragraph breaks....
*
* This work was authored by Peter Herweg ([email protected]).
*/
public class RtfTextrun extends RtfContainer {
/** Constant for no page break */
public static final int BREAK_NONE = 0;
/** Constant for a normal page break */
public static final int BREAK_PAGE = 1;
/** Constant for a column break */
public static final int BREAK_COLUMN = 2;
/** Constant for a even page break */
public static final int BREAK_EVEN_PAGE = 3;
/** Constant for a odd page break */
public static final int BREAK_ODD_PAGE = 4;
private boolean bSuppressLastPar;
private RtfListItem rtfListItem;
/**
* logging instance
*/
protected static final Log log = LogFactory.getLog(RtfTextrun.class);
/** 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 {
RtfOpenGroupMark(RtfContainer parent, Writer w, RtfAttributes attr)
throws IOException {
super(parent, w, attr);
}
/**
* @return true if this element would generate no "useful" RTF content
*/
public boolean isEmpty() {
return false;
}
/**
* write RTF code of all our children
* @throws IOException for I/O problems
*/
protected void writeRtfContent() throws IOException {
writeGroupMark(true);
writeAttributes(getRtfAttributes(), null);
}
}
/** Class which represents the closing of a RTF group mark.*/
private class RtfCloseGroupMark extends RtfElement {
private int breakType = BREAK_NONE;
RtfCloseGroupMark(RtfContainer parent, Writer w, int breakType)
throws IOException {
super(parent, w);
this.breakType = breakType;
}
/**
* @return true if this element would generate no "useful" RTF content
*/
public boolean isEmpty() {
return false;
}
/**
* Returns the break type.
* @return the break type (BREAK_* constants)
*/
public int getBreakType() {
return breakType;
}
/**
* Write RTF code of all our children.
* @throws IOException for I/O problems
*/
protected void writeRtfContent() throws IOException {
writeGroupMark(false);
boolean bHasTableCellParent = this.getParentOfClass(RtfTableCell.class) != null;
//Unknown behavior when a table starts a new section,
//Word may crash
if (breakType != BREAK_NONE) {
if (!bHasTableCellParent) {
writeControlWord("sect");
/* The following modifiers don't seem to appear in the right place */
switch (breakType) {
case BREAK_EVEN_PAGE:
writeControlWord("sbkeven");
break;
case BREAK_ODD_PAGE:
writeControlWord("sbkodd");
break;
case BREAK_COLUMN:
writeControlWord("sbkcol");
break;
default:
writeControlWord("sbkpage");
}
} else {
log.warn("Cannot create break-after for a paragraph inside a table.");
}
}
}
}
/** Create an RTF container as a child of given container */
RtfTextrun(RtfContainer parent, Writer w, RtfAttributes attrs) throws IOException {
super(parent, w, attrs);
}
/**
* Adds instance of OpenGroupMark
as a child with attributes.
*
* @param attrs attributes to add
* @throws IOException for I/O problems
*/
private void addOpenGroupMark(RtfAttributes attrs) throws IOException {
RtfOpenGroupMark r = new RtfOpenGroupMark(this, writer, attrs);
}
/**
* Adds instance of CloseGroupMark
as a child.
*
* @throws IOException for I/O problems
*/
private void addCloseGroupMark(int breakType) throws IOException {
RtfCloseGroupMark r = new RtfCloseGroupMark(this, writer, breakType);
}
/**
* Adds instance of CloseGroupMark
as a child, but without a break option.
* Inline attributes do not need that for example
*
* @throws IOException for I/O problems
*/
private void addCloseGroupMark() throws IOException {
RtfCloseGroupMark r = new RtfCloseGroupMark(this, writer, BREAK_NONE);
}
/**
* 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.
* @param breakType the break type
* @throws IOException for I/O problems
*/
public void popBlockAttributes(int breakType) throws IOException {
rtfSpaceManager.popRtfSpaceSplitter();
rtfSpaceManager.stopUpdatingSpaceBefore();
addCloseGroupMark(breakType);
}
/**
* 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);
}
/**
* Inserts a page number citation.
* @param refId the identifier being referenced
* @throws IOException for I/O problems
*/
public void addPageNumberCitation(String refId) throws IOException {
RtfPageNumberCitation r = new RtfPageNumberCitation(this, writer, refId);
}
/**
* 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);
// create a string and add it as a child
new RtfString(this, writer, s);
rtfSpaceManager.popRtfSpaceSplitter();
}
/**
* Inserts a footnote.
*
* @return inserted footnote
* @throws IOException for I/O problems
*/
public RtfFootnote addFootnote() throws IOException {
return new RtfFootnote(this, writer);
}
/**
* Inserts paragraph break before all close group marks.
*
* @throws IOException for I/O problems
* @return The paragraph break element
*/
public RtfParagraphBreak addParagraphBreak() throws IOException {
// get copy of children list
List children = getChildren();
Stack tmp = new Stack();
RtfParagraphBreak par = null;
// delete all previous CloseGroupMark
int deletedCloseGroupCount = 0;
ListIterator lit = children.listIterator(children.size());
while (lit.hasPrevious()
&& (lit.previous() instanceof RtfCloseGroupMark)) {
tmp.push(((RtfCloseGroupMark) lit.next()).getBreakType());
lit.remove();
deletedCloseGroupCount++;
}
if (children.size() != 0) {
// add paragraph break and restore all deleted close group marks
setChildren(children);
par = new RtfParagraphBreak(this, writer);
for (int i = 0; i < deletedCloseGroupCount; i++) {
addCloseGroupMark((Integer) tmp.pop());
}
}
return par;
}
/**
* Inserts a leader.
* @param attrs Attributes for the leader
* @throws IOException for I/O problems
*/
public void addLeader(RtfAttributes attrs) throws IOException {
new RtfLeader(this, writer, attrs);
}
/**
* Inserts a page number.
* @param attr Attributes for the page number to insert.
* @throws IOException for I/O problems
*/
public void addPageNumber(RtfAttributes attr) throws IOException {
RtfPageNumber r = new RtfPageNumber(this, writer, attr);
}
/**
* Inserts a hyperlink.
* @param attr Attributes for the hyperlink to insert.
* @return inserted hyperlink
* @throws IOException for I/O problems
*/
public RtfHyperLink addHyperlink(RtfAttributes attr) throws IOException {
return new RtfHyperLink(this, writer, attr);
}
/**
* Inserts a bookmark.
* @param id Id for the inserted bookmark
* @throws IOException for I/O problems
*/
public void addBookmark(String id) throws IOException {
if (id.length() > 0) {
// if id is not empty, add bookmark
new RtfBookmark(this, writer, id);
}
}
/**
* Inserts an image.
* @return inserted image
* @throws IOException for I/O problems
*/
public RtfExternalGraphic newImage() throws IOException {
return new RtfExternalGraphic(this, writer);
}
/**
* Adds a new RtfTextrun to the given container if necessary, and returns it.
* @param container RtfContainer, which is the parent of the returned RtfTextrun
* @param writer Writer of the given RtfContainer
* @param attrs RtfAttributes which are to write at the beginning of the RtfTextrun
* @return new or existing RtfTextrun object.
* @throws IOException for I/O problems
*/
public static RtfTextrun getTextrun(RtfContainer container, Writer writer, RtfAttributes attrs)
throws IOException {
List list = container.getChildren();
if (list.size() == 0) {
//add a new RtfTextrun
RtfTextrun textrun = new RtfTextrun(container, writer, attrs);
list.add(textrun);
return textrun;
}
Object obj = list.get(list.size() - 1);
if (obj instanceof RtfTextrun) {
//if the last child is a RtfTextrun, return it
return (RtfTextrun) obj;
}
//add a new RtfTextrun as the last child
RtfTextrun textrun = new RtfTextrun(container, writer, attrs);
list.add(textrun);
return textrun;
}
/**
* specify, if the last paragraph control word (\par) should be suppressed.
* @param bSuppress true, if the last \par should be suppressed
*/
public void setSuppressLastPar(boolean bSuppress) {
bSuppressLastPar = bSuppress;
}
/**
* write RTF code of all our children
* @throws IOException for I/O problems
*/
protected void writeRtfContent() throws IOException {
/**
*TODO: The textrun's children are iterated twice:
* 1. To determine the last RtfParagraphBreak
* 2. To write the children
* Maybe this can be done more efficient.
*/
boolean bHasTableCellParent
= this.getParentOfClass(RtfTableCell.class) != null;
RtfAttributes attrBlockLevel = new RtfAttributes();
//determine, if this RtfTextrun is the last child of its parent
boolean bLast = false;
for (Iterator it = parent.getChildren().iterator(); it.hasNext();) {
if (it.next() == this) {
bLast = !it.hasNext();
break;
}
}
//get last RtfParagraphBreak, which is not followed by any visible child
RtfParagraphBreak lastParagraphBreak = null;
if (bLast) {
RtfElement aBefore = null;
for (Object o : getChildren()) {
final RtfElement e = (RtfElement) o;
if (e instanceof RtfParagraphBreak) {
//If the element before was a paragraph break or a bookmark
//they will be hidden and are therefore not considered as visible
if (!(aBefore instanceof RtfParagraphBreak)
&& !(aBefore instanceof RtfBookmark)) {
lastParagraphBreak = (RtfParagraphBreak) e;
}
} else {
if (!(e instanceof RtfOpenGroupMark)
&& !(e instanceof RtfCloseGroupMark)
&& e.isEmpty()) {
lastParagraphBreak = null;
}
}
aBefore = e;
}
}
//may contain for example \intbl
writeAttributes(attrib, null);
if (rtfListItem != null) {
rtfListItem.getRtfListStyle().writeParagraphPrefix(this);
}
//write all children
boolean bPrevPar = false;
boolean bBookmark = false;
boolean bFirst = true;
for (Object o : getChildren()) {
final RtfElement e = (RtfElement) o;
final boolean bRtfParagraphBreak = (e instanceof RtfParagraphBreak);
if (bHasTableCellParent) {
attrBlockLevel.set(e.getRtfAttributes());
}
/**
* -Write RtfParagraphBreak only, if the previous visible child
* was't also a RtfParagraphBreak.
* -Write RtfParagraphBreak only, if it is not the first visible
* child.
* -If the RtfTextrun is the last child of its parent, write a
* RtfParagraphBreak only, if it is not the last child.
* -If the RtfParagraphBreak can not be hidden (e.g. a table cell requires it)
* it is also written
*/
boolean bHide = false;
bHide = bRtfParagraphBreak;
bHide = bHide
&& (bPrevPar
|| bFirst
|| (bSuppressLastPar && bLast && lastParagraphBreak != null
&& e == lastParagraphBreak)
|| bBookmark)
&& ((RtfParagraphBreak) e).canHide();
if (!bHide) {
newLine();
e.writeRtf();
if (rtfListItem != null && e instanceof RtfParagraphBreak) {
rtfListItem.getRtfListStyle().writeParagraphPrefix(this);
}
}
if (e instanceof RtfParagraphBreak) {
bPrevPar = true;
} else if (e instanceof RtfBookmark) {
bBookmark = true;
} else if (e instanceof RtfCloseGroupMark) {
//do nothing
} else if (e instanceof RtfOpenGroupMark) {
//do nothing
} else {
bPrevPar = bPrevPar && e.isEmpty();
bFirst = bFirst && e.isEmpty();
bBookmark = false;
}
} //for (Iterator it = ...)
//
if (bHasTableCellParent) {
writeAttributes(attrBlockLevel, null);
}
}
/**
* Set the parent list-item of the textrun.
*
* @param listItem parent list-item of the textrun
*/
public void setRtfListItem(RtfListItem listItem) {
rtfListItem = listItem;
}
/**
* Gets the parent list-item of the textrun.
*
* @return parent list-item of the textrun
*/
public RtfListItem getRtfListItem() {
return rtfListItem;
}
}