org.odftoolkit.simple.text.Paragraph Maven / Gradle / Ivy
Show all versions of simple-odf Show documentation
/*
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.
*/
package org.odftoolkit.simple.text;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.odftoolkit.odfdom.dom.OdfContentDom;
import org.odftoolkit.odfdom.dom.OdfDocumentNamespace;
import org.odftoolkit.odfdom.dom.OdfStylesDom;
import org.odftoolkit.odfdom.dom.element.OdfStyleBase;
import org.odftoolkit.odfdom.dom.element.dc.DcCreatorElement;
import org.odftoolkit.odfdom.dom.element.dc.DcDateElement;
import org.odftoolkit.odfdom.dom.element.office.OfficeAnnotationElement;
import org.odftoolkit.odfdom.dom.element.style.StyleTextPropertiesElement;
import org.odftoolkit.odfdom.dom.element.text.TextAElement;
import org.odftoolkit.odfdom.dom.element.text.TextHElement;
import org.odftoolkit.odfdom.dom.element.text.TextLineBreakElement;
import org.odftoolkit.odfdom.dom.element.text.TextPElement;
import org.odftoolkit.odfdom.dom.element.text.TextParagraphElementBase;
import org.odftoolkit.odfdom.dom.element.text.TextSElement;
import org.odftoolkit.odfdom.dom.element.text.TextSpanElement;
import org.odftoolkit.odfdom.dom.element.text.TextTabElement;
import org.odftoolkit.odfdom.dom.style.OdfStyleFamily;
import org.odftoolkit.odfdom.incubator.doc.office.OdfOfficeAutomaticStyles;
import org.odftoolkit.odfdom.incubator.doc.style.OdfStyle;
import org.odftoolkit.odfdom.pkg.OdfElement;
import org.odftoolkit.odfdom.pkg.OdfFileDom;
import org.odftoolkit.odfdom.pkg.OdfName;
import org.odftoolkit.odfdom.pkg.OdfXMLFactory;
import org.odftoolkit.simple.Component;
import org.odftoolkit.simple.Document;
import org.odftoolkit.simple.PresentationDocument;
import org.odftoolkit.simple.draw.AbstractTextboxContainer;
import org.odftoolkit.simple.draw.Control;
import org.odftoolkit.simple.draw.ControlContainer;
import org.odftoolkit.simple.draw.FrameRectangle;
import org.odftoolkit.simple.draw.Textbox;
import org.odftoolkit.simple.draw.TextboxContainer;
import org.odftoolkit.simple.style.Font;
import org.odftoolkit.simple.style.StyleTypeDefinitions.HorizontalAlignmentType;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
/**
* This class presents paragraph element in ODF document. It provides methods to
* manipulate text content, and other child component under the paragraph.
* Headings and body text paragraphs are collectively referred to as paragraph
* elements.
*
* @since 0.5
*/
public class Paragraph extends Component implements TextboxContainer,
TextHyperlinkContainer, ControlContainer {
private TextPElement mParagraphElement;
private TextHElement mHeadingElement;
private Document mOwnerDocument;
private ParagraphStyleHandler mStyleHandler;
private TextboxContainerImpl mTextboxContainerImpl;
private TextHyperlinkContainerImpl mHyperlinkContainerImpl;
private Paragraph(TextParagraphElementBase paragraphElement) {
if (paragraphElement instanceof TextPElement) {
mParagraphElement = (TextPElement) paragraphElement;
mHeadingElement = null;
}
if (paragraphElement instanceof TextHElement) {
mHeadingElement = (TextHElement) paragraphElement;
mParagraphElement = null;
}
mOwnerDocument = (Document) ((OdfFileDom) paragraphElement.getOwnerDocument()).getDocument();
mStyleHandler = new ParagraphStyleHandler(this);
}
/**
* Gets a paragraph instance by an instance of
* TextParagraphElementBase
.
*
* @param paragraphElement
* the instance of TextParagraphElementBase.
* @return an instance of paragraph.
*/
public static Paragraph getInstanceof(TextParagraphElementBase paragraphElement) {
if (paragraphElement == null)
return null;
Paragraph para = null;
para = (Paragraph) Component.getComponentByElement(paragraphElement);
if (para != null)
return para;
para = new Paragraph(paragraphElement);
Component.registerComponent(para, paragraphElement);
return para;
}
/**
* Create an instance of paragraph
*
* The paragrah will be added at the end of this container.
*
* @param container
* the paragraph container that contains this paragraph.
*/
public static Paragraph newParagraph(ParagraphContainer container) {
Paragraph para = null;
OdfElement parent = container.getParagraphContainerElement();
OdfFileDom ownerDom = (OdfFileDom) parent.getOwnerDocument();
TextPElement pEle = ownerDom.newOdfElement(TextPElement.class);
parent.appendChild(pEle);
para = new Paragraph(pEle);
Component.registerComponent(para, pEle);
return para;
}
/**
* Set the text content of this paragraph.
*
* All the existing text content of this paragraph would be removed, and
* then new text content would be set. The style of the last character will
* be inherited.
*
* The white space characters in the content would be collapsed by default.
* For example, tab character would be replaced with , break line
* character would be replaced with .
*
* @param content
* - the text content
* @see #setTextContentNotCollapsed(String content)
*/
public void setTextContent(String content) {
removeTextContentImpl(getOdfElement());
Node lastNode = getOdfElement().getLastChild();
if (lastNode != null && lastNode.getNodeName() != null
&& (lastNode.getNodeName().equals("text:a") || lastNode.getNodeName().equals("text:span"))) {
if (content != null && !content.equals(""))
appendTextElements((TextAElement) lastNode, content, true);
} else {
if (content != null && !content.equals(""))
appendTextElements(getOdfElement(), content, true);
}
// remove empty hyperlink
removeEmptyHyperlink(getOdfElement());
}
static void removeEmptyHyperlink(OdfElement element) {
// remove empty hyperlink
NodeList nodeList = element.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node;
node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
String nodename = node.getNodeName();
if (nodename.equals("text:a") && node.hasChildNodes() == false)
element.removeChild(node);
}
}
}
/**
* Remove the text content of this paragraph. The empty hyperlink element
* will be removed.
*
* The other child elements except text content will not be removed.
*
*/
public void removeTextContent() {
removeTextContentImpl(getOdfElement());
removeEmptyHyperlink(getOdfElement());
}
static void removeTextContentImpl(OdfElement ownerElement) {
NodeList nodeList = ownerElement.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node;
node = nodeList.item(i);
if (node.getNodeType() == Node.TEXT_NODE) {
ownerElement.removeChild(node);
// element removed need reset index.
i--;
} else if (node.getNodeType() == Node.ELEMENT_NODE) {
String nodename = node.getNodeName();
if (nodename.equals("text:s") || nodename.equals("text:tab") || nodename.equals("text:line-break")) {
ownerElement.removeChild(node);
// element removed need reset index.
i--;
} else if (nodename.equals("text:a"))
removeTextContentImpl((OdfElement) node);
}
}
}
/**
* Return the text content of this paragraph.
*
* The other child elements except text content will not be returned.
*
* @return - the text content of this paragraph
*/
public String getTextContent() {
return getTextContent(getOdfElement());
}
static String getTextContent(OdfElement ownerEle) {
StringBuilder buffer = new StringBuilder();
NodeList nodeList = ownerEle.getChildNodes();
int i;
for (i = 0; i < nodeList.getLength(); i++) {
Node node;
node = nodeList.item(i);
if (node.getNodeType() == Node.TEXT_NODE)
buffer.append(node.getNodeValue());
else if (node.getNodeType() == Node.ELEMENT_NODE) {
if (node instanceof TextSpanElement) {
buffer.append((getTextContent((TextSpanElement) node)));
}
else if (node.getNodeName().equals("text:s")) {
Integer count = ((TextSElement) node).getTextCAttribute();
for (int j = 0; j < (count != null ? count : 1); j++)
buffer.append(' ');
} else if (node.getNodeName().equals("text:tab"))
buffer.append('\t');
else if (node.getNodeName().equals("text:line-break")) {
String lineseperator = System.getProperty("line.separator");
buffer.append(lineseperator);
} else if (node.getNodeName().equals("text:a"))
buffer.append(TextHyperlink.getInstanceof((TextAElement) node).getTextContent());
}
}
return buffer.toString();
}
/**
* Set the text content of this paragraph.
*
* All the existing text content of this paragraph would be removed, and
* then new text content would be set.
*
* The white space characters in the content would not be collapsed.
*
* @param content
* - the text content
* @see #setTextContent(String content)
*/
public void setTextContentNotCollapsed(String content) {
removeTextContent();
if (content != null && !content.equals(""))
appendTextElements(getOdfElement(), content, false);
}
/**
* Append the text content at the end of this paragraph. The appended text
* would follow the style of the last character.
*
* The white space characters in the content would be collapsed by default.
* For example, tab character would be replaced with , break line
* character would be replaced with .
*
* @param content
* - the text content
* @see #appendTextContentNotCollapsed(String content)
*/
public void appendTextContent(String content) {
appendTextContent(content, true);
}
/**
* Append the text content at the end of this paragraph. The appended text
* would follow the style of the last character if the second parameter is
* set to true; Or else, the appended text would follow the default style of
* this paragraph.
*
* The white space characters in the content would be collapsed by default.
* For example, tab character would be replaced with , break line
* character would be replaced with .
*
* @param content
* - the text content
* @param isStyleInherited
* - whether the style would be inherited by the appended text
* @see #appendTextContentNotCollapsed(String content)
*/
public void appendTextContent(String content, boolean isStyleInherited) {
boolean canInherited = false;
Node lastNode = getOdfElement().getLastChild();
if (lastNode != null && lastNode.getNodeName() != null
&& (lastNode.getNodeName().equals("text:a") || lastNode.getNodeName().equals("text:span")))
canInherited = true;
if (isStyleInherited && canInherited) {
if (content != null && !content.equals(""))
appendTextElements((OdfElement) lastNode, content, true);
} else {
if (content != null && !content.equals(""))
appendTextElements(getOdfElement(), content, true);
}
}
/**
* Append the text content at the end of this paragraph. The appended text
* would follow the style of the last character.
*
* The white space characters in the content would not be collapsed.
*
* @param content
* - the text content
* @see #appendTextContent(String content)
*/
public void appendTextContentNotCollapsed(String content) {
Node lastNode = getOdfElement().getLastChild();
boolean canInherited = false;
if (lastNode != null && lastNode.getNodeName() != null
&& (lastNode.getNodeName().equals("text:a") || lastNode.getNodeName().equals("text:span")))
canInherited = true;
if (canInherited) {
if (content != null && !content.equals(""))
appendTextElements((OdfElement) lastNode, content, false);
} else {
if (content != null && !content.equals(""))
appendTextElements(getOdfElement(), content, false);
}
}
/**
* Set the style name of this paragraph
*
* @param styleName
* - the style name
*/
public void setStyleName(String styleName) {
mStyleHandler.getStyleElementForWrite().setStyleNameAttribute(styleName);
}
/**
* Get the style name of this paragraph
*
* @return - the style name
*/
public String getStyleName() {
OdfStyleBase style = getStyleHandler().getStyleElementForRead();
if (style == null) {
return "";
}
if (style instanceof OdfStyle)
return ((OdfStyle) style).getStyleNameAttribute();
else
return "";
}
/**
* Get the owner document of this paragraph.
*
* @return the document who owns this paragraph.
*/
public Document getOwnerDocument() {
return mOwnerDocument;
}
/**
* Remove this paragraph from its container.
*
* @since 0.6.5
*/
public void remove(){
Component.unregisterComponent(getOdfElement());
getOdfElement().getParentNode().removeChild(getOdfElement());
mParagraphElement=null;
mHeadingElement=null;
mOwnerDocument=null;
mStyleHandler=null;
mTextboxContainerImpl=null;
mHyperlinkContainerImpl=null;
}
/**
* Get the style handler of this paragraph.
*
* The style handler is an instance of ParagraphStyleHandler
*
* @return an instance of ParagraphStyleHandler
* @see ParagraphStyleHandler
*/
public ParagraphStyleHandler getStyleHandler() {
if (mStyleHandler != null)
return mStyleHandler;
else {
mStyleHandler = new ParagraphStyleHandler(this);
return mStyleHandler;
}
}
/**
* Return the TextParagraphElementBase
of this paragraph.
* Headings and body text paragraphs are collectively referred to as
* paragraph elements, so the TextParagraphElementBase
can be
* TextHElement
element or TextPElement
element.
*
* @return the TextParagraphElementBase
of this paragraph.
*/
@Override
public TextParagraphElementBase getOdfElement() {
if (isHeading()) {
return mHeadingElement;
} else {
return mParagraphElement;
}
}
/**
* Creates a comment in the front of this paragraph.
*
* @param content
* the content of this comment.
* @param creator
* the creator of this comment, if creator
is null,
* the value of System.getProperty("user.name")
will
* be used.
* @since 0.6.5
*/
public void addComment(String content, String creator) {
// create annotation element.
OdfFileDom dom = (OdfFileDom) getOdfElement().getOwnerDocument();
OfficeAnnotationElement annotationElement = (OfficeAnnotationElement) OdfXMLFactory.newOdfElement(dom, OdfName
.newName(OdfDocumentNamespace.OFFICE, "annotation"));
getOdfElement().insertBefore(annotationElement, getOdfElement().getFirstChild());
// set creator
DcCreatorElement dcCreatorElement = annotationElement.newDcCreatorElement();
if (creator == null) {
creator = System.getProperty("user.name");
}
dcCreatorElement.setTextContent(creator);
// set date
String dcDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(new Date());
DcDateElement dcDateElement = annotationElement.newDcDateElement();
dcDateElement.setTextContent(dcDate);
TextPElement notePElement = annotationElement.newTextPElement();
TextSpanElement noteSpanElement = notePElement.newTextSpanElement();
// set comment style
OdfOfficeAutomaticStyles styles = null;
if (dom instanceof OdfContentDom) {
styles = ((OdfContentDom) dom).getAutomaticStyles();
} else if (dom instanceof OdfStylesDom) {
styles = ((OdfStylesDom) dom).getAutomaticStyles();
}
OdfStyle textStyle = styles.newStyle(OdfStyleFamily.Text);
StyleTextPropertiesElement styleTextPropertiesElement = textStyle.newStyleTextPropertiesElement(null);
styleTextPropertiesElement.setStyleFontNameAttribute("Tahoma");
styleTextPropertiesElement.setFoFontSizeAttribute("10pt");
styleTextPropertiesElement.setStyleFontNameAsianAttribute("Lucida Sans Unicode");
styleTextPropertiesElement.setStyleFontSizeAsianAttribute("12pt");
noteSpanElement.setStyleName(textStyle.getStyleNameAttribute());
// set comment content
noteSpanElement.setTextContent(content);
}
/**
* Returns the paragraph type, heading or body text paragraph.
*
* @return the paragraph type, if this paragraph is heading, returns
* true
, otherwise return false
.
*
* @since 0.6.5
*/
public boolean isHeading() {
if (mHeadingElement != null) {
return true;
} else {
return false;
}
}
/**
* Returns outline level of this paragraph.
*
* @return outline level, if this paragraph is a body text paragraph, 0 will
* be returned.
*
* @since 0.6.5
*/
public int getHeadingLevel() {
if (isHeading()) {
return mHeadingElement.getTextOutlineLevelAttribute();
}
return 0;
}
/**
* Sets the paragraph type, heading or body text paragraph.
*
* @param isHeading
* if true
, this paragraph would be formatted as
* heading, otherwise as a body text paragraph.
* @param level
* the heading outline level of this paragraph, if
* isHeading
is true
.
*
* @since 0.6.5
*/
public void applyHeading(boolean isHeading, int level) {
if (isHeading) {
if (!isHeading()) {
// create new heading element, clone children nodes.
OdfFileDom ownerDocument = (OdfFileDom) getOdfElement().getOwnerDocument();
mHeadingElement = ownerDocument.newOdfElement(TextHElement.class);
Node firstChild = mParagraphElement.getFirstChild();
while (firstChild != null) {
// mHeadingElement.appendChild(firstChild.cloneNode(true));
// firstChild = firstChild.getNextSibling();
Node thisChild = firstChild;
firstChild = firstChild.getNextSibling();
mParagraphElement.removeChild(thisChild);
mHeadingElement.appendChild(thisChild);
}
// update style
mHeadingElement.setStyleName(mParagraphElement.getStyleName());
// unregister component
Component.unregisterComponent(mParagraphElement);
// replace paragraph with heading
OdfElement parentOdfElement = (OdfElement) mParagraphElement.getParentNode();
parentOdfElement.replaceChild(mHeadingElement, mParagraphElement);
mParagraphElement = null;
// re-register component.
Component.registerComponent(this, mHeadingElement);
}
// update outline level.
mHeadingElement.setTextOutlineLevelAttribute(level);
} else {
if (isHeading()) {
// need create new paragraph element and clone content.
OdfFileDom ownerDocument = (OdfFileDom) getOdfElement().getOwnerDocument();
mParagraphElement = ownerDocument.newOdfElement(TextPElement.class);
Node firstChild = mHeadingElement.getFirstChild();
while (firstChild != null) {
Node thisChild = firstChild;
firstChild = firstChild.getNextSibling();
mHeadingElement.removeChild(thisChild);
mParagraphElement.appendChild(thisChild);
}
// update style
mParagraphElement.setStyleName(mHeadingElement.getStyleName());
// unregister component
Component.unregisterComponent(mHeadingElement);
// replace heading with paragraph
OdfElement parentOdfElement = (OdfElement) mHeadingElement.getParentNode();
parentOdfElement.replaceChild(mParagraphElement, mHeadingElement);
mHeadingElement = null;
// re-register component.
Component.registerComponent(this, mParagraphElement);
}
}
}
/**
* Formats the paragraph as heading. Its outline level is 1.
*
* @since 0.6.5
*/
public void applyHeading() {
applyHeading(true, 1);
}
/**
* Returns the font definition for this paragraph.
*
* @return font if there is no style definition for this paragraph,
* null
will be returned.
*
* @since 0.6.5
*/
public Font getFont() {
return getStyleHandler().getFont(Document.ScriptType.WESTERN);
}
/**
* Sets font style for this paragraph.
*
* @param font
* the font definition of this paragraph
*
* @since 0.6.5
*/
public void setFont(Font font) {
getStyleHandler().setFont(font);
}
/**
* Return the horizontal alignment setting of this paragraph.
*
* Null will returned if there is no explicit style definition for this
* paragraph.
*
* Default value will be returned if explicit style definition is found but
* no horizontal alignment is set.
*
* @return the horizontal alignment setting.
* @since 0.6.5
*/
public HorizontalAlignmentType getHorizontalAlignment() {
return getStyleHandler().getHorizontalAlignment();
}
/**
* Set the horizontal alignment setting of this paragraph. If the alignment
* is set as Default, the explicit horizontal alignment setting is removed.
*
* @param alignType
* the horizontal alignment setting.
* @since 0.6.5
*/
public void setHorizontalAlignment(HorizontalAlignmentType alignType) {
getStyleHandler().setHorizontalAlignment(alignType);
}
public Textbox addTextbox() {
return getTextboxContainerImpl().addTextbox();
}
public Iterator getTextboxIterator() {
return getTextboxContainerImpl().getTextboxIterator();
}
public boolean removeTextbox(Textbox box) {
return getTextboxContainerImpl().removeTextbox(box);
}
public OdfElement getFrameContainerElement() {
return getTextboxContainerImpl().getFrameContainerElement();
}
public Textbox addTextbox(FrameRectangle position) {
return getTextboxContainerImpl().addTextbox(position);
}
public Textbox getTextboxByName(String name) {
return getTextboxContainerImpl().getTextboxByName(name);
}
public List getTextboxByUsage(PresentationDocument.PresentationClass usage) {
throw new UnsupportedOperationException("this method is not supported by paragraph.");
}
private class TextboxContainerImpl extends AbstractTextboxContainer {
public OdfElement getFrameContainerElement() {
return getOdfElement();
}
}
private TextboxContainerImpl getTextboxContainerImpl() {
if (mTextboxContainerImpl == null)
mTextboxContainerImpl = new TextboxContainerImpl();
return mTextboxContainerImpl;
}
static void appendTextElements(OdfElement ownerElement, String content, boolean isWhitespaceCollapsed) {
OdfFileDom ownerDocument = (OdfFileDom) ownerElement.getOwnerDocument();
if (isWhitespaceCollapsed) {
int i = 0, length = content.length();
String str = "";
while (i < length) {
char ch = content.charAt(i);
if (ch == ' ') {
int j = 1;
i++;
while ((i < length) && (content.charAt(i) == ' ')) {
j++;
i++;
}
if (j == 1) {
str += ' ';
} else {
str += ' ';
Text textnode = ownerDocument.createTextNode(str);
ownerElement.appendChild(textnode);
str = "";
TextSElement spaceElement = ownerDocument.newOdfElement(TextSElement.class);
ownerElement.appendChild(spaceElement);
spaceElement.setTextCAttribute(j - 1);
}
} else if (ch == '\n') {
if (str.length() > 0) {
Text textnode = ownerDocument.createTextNode(str);
ownerElement.appendChild(textnode);
str = "";
}
TextLineBreakElement lineBreakElement = ownerDocument.newOdfElement(TextLineBreakElement.class);
ownerElement.appendChild(lineBreakElement);
i++;
} else if (ch == '\t') {
if (str.length() > 0) {
Text textnode = ownerElement.getOwnerDocument().createTextNode(str);
ownerElement.appendChild(textnode);
str = "";
}
TextTabElement tabElement = ownerDocument.newOdfElement(TextTabElement.class);
ownerElement.appendChild(tabElement);
i++;
} else if (ch == '\r') {
i++;
} else {
str += ch;
i++;
}
}
if (str.length() > 0) {
Text textnode = ownerDocument.createTextNode(str);
ownerElement.appendChild(textnode);
}
} else {
Text textnode = ownerDocument.createTextNode(content);
ownerElement.appendChild(textnode);
}
}
/************ Hyperlink support ************/
public TextHyperlink applyHyperlink(URI linkto) {
return getTextHyperlinkContainerImpl().applyHyperlink(linkto);
}
public Iterator getHyperlinkIterator() {
return getTextHyperlinkContainerImpl().getHyperlinkIterator();
}
public void removeHyperlinks() {
getTextHyperlinkContainerImpl().removeHyperlinks();
}
public TextHyperlink appendHyperlink(String text, URI linkto) {
return getTextHyperlinkContainerImpl().appendHyperlink(text, linkto);
}
private class TextHyperlinkContainerImpl extends AbstractTextHyperlinkContainer {
public TextHyperlinkContainerImpl(OdfElement parent) {
super(parent);
}
}
private TextHyperlinkContainerImpl getTextHyperlinkContainerImpl() {
if (mHyperlinkContainerImpl == null)
mHyperlinkContainerImpl = new TextHyperlinkContainerImpl(getOdfElement());
return mHyperlinkContainerImpl;
}
/************ End of Hyperlink support ************/
// @Override
public Control createDrawControl() {
return Control.newDrawControl(this);
}
// @Override
public OdfElement getDrawControlContainerElement() {
return this.getOdfElement();
}
}