summaryrefslogtreecommitdiff
path: root/xmerge/source/wordsmith
diff options
context:
space:
mode:
Diffstat (limited to 'xmerge/source/wordsmith')
-rw-r--r--xmerge/source/wordsmith/build.xml76
-rw-r--r--xmerge/source/wordsmith/converter.xml17
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/ConverterCapabilitiesImpl.java93
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DOCConstants.java61
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DocumentDeserializerImpl.java555
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DocumentMergerImpl.java99
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DocumentSerializerImpl.java536
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/PluginFactoryImpl.java149
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WSDecoder.java352
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WSEncoder.java212
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/Wse.java100
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseColorTable.java247
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseFontTable.java218
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseHeader.java145
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WsePara.java282
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseTextRun.java324
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/textRecord.java115
-rw-r--r--xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/util.java68
-rw-r--r--xmerge/source/wordsmith/makefile.mk33
19 files changed, 3682 insertions, 0 deletions
diff --git a/xmerge/source/wordsmith/build.xml b/xmerge/source/wordsmith/build.xml
new file mode 100644
index 000000000000..9a0c1aef7563
--- /dev/null
+++ b/xmerge/source/wordsmith/build.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ #*************************************************************************
+ #
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ Copyright 2000, 2010 Oracle and/or its affiliates.
+
+ OpenOffice.org - a multi-platform office productivity suite
+
+ This file is part of OpenOffice.org.
+
+ OpenOffice.org is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 3
+ only, as published by the Free Software Foundation.
+
+ OpenOffice.org is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License version 3 for more details
+ (a copy is included in the LICENSE file that accompanied this code).
+
+ You should have received a copy of the GNU Lesser General Public License
+ version 3 along with OpenOffice.org. If not, see
+ <http://www.openoffice.org/license.html>
+ for a copy of the LGPLv3 License.
+
+ #*************************************************************************
+ -->
+
+<project name="wordsmith" default="all" basedir=".">
+
+ <property file="../inc/antbuild.properties"/>
+
+ <path id="classpath">
+ <pathelement location="${build.dir}/xmerge.jar"/>
+ </path>
+
+
+ <target name="init" >
+ <mkdir dir="${target.dir}"/>
+ </target>
+
+ <!-- compile java sources in ${package} -->
+ <target name="compile" depends="init">
+ <javac srcdir="${src.dir}"
+ destdir="${target.dir}"
+ debug="${debug}"
+ deprecation="${deprecation}"
+ optimize="${optimize}">
+ <classpath refid="classpath"/>
+ </javac>
+ </target>
+
+ <!-- package to jar -->
+ <target name="jar" depends="compile">
+ <jar destfile="${target.jar}">
+ <fileset dir="${target.dir}"
+ includes="**/*.class" />
+ <metainf dir="${basedir}">
+ <filename name="converter.xml"/>
+ </metainf>
+ </jar>
+ </target>
+
+ <!-- clean up -->
+ <target name="clean">
+ <delete dir="${class.dir}"/>
+ <delete file="${target.jar}"/>
+ </target>
+
+ <target name="all" depends="jar">
+ </target>
+
+</project>
+
diff --git a/xmerge/source/wordsmith/converter.xml b/xmerge/source/wordsmith/converter.xml
new file mode 100644
index 000000000000..9285730569db
--- /dev/null
+++ b/xmerge/source/wordsmith/converter.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<converters>
+ <converter type="staroffice/sxw" version="1.0">
+ <converter-display-name>
+ WordSmith
+ </converter-display-name>
+ <converter-description>
+ StarWriter XML to/from WordSmith conversion
+ </converter-description>
+ <converter-vendor>OpenOffice.org</converter-vendor>
+ <converter-class-impl>
+ org.openoffice.xmerge.converter.xml.sxw.wordsmith.PluginFactoryImpl
+ </converter-class-impl>
+ <converter-target type="application/x-wordsmith" />
+ </converter>
+</converters>
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/ConverterCapabilitiesImpl.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/ConverterCapabilitiesImpl.java
new file mode 100644
index 000000000000..11e264a07cfb
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/ConverterCapabilitiesImpl.java
@@ -0,0 +1,93 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.converter.xml.OfficeConstants;
+
+/**
+ * <p>WordSmith implementation of <code>ConverterCapabilities</code> for
+ * the {@link
+ * org.openoffice.xmerge.converter.xml.sxw.wordsmith.PluginFactoryImpl
+ * PluginFactoryImpl}.</p>
+ *
+ * <p>Used with StarWriter XML to/from WordSmith conversions.
+ * The <code>ConverterCapibilies</code> specify which &quot;Office&quot;
+ * <code>Document</code> tags and attributes are supported on the
+ * &quot;Device&quot; <code>Document</code> format.</p>
+ */
+public final class ConverterCapabilitiesImpl
+ implements ConverterCapabilities {
+
+ public boolean canConvertTag(String tag) {
+
+ if (OfficeConstants.TAG_OFFICE_DOCUMENT.equals(tag))
+ return true;
+ else if (OfficeConstants.TAG_OFFICE_DOCUMENT_CONTENT.equals(tag))
+ return true;
+ else if (OfficeConstants.TAG_OFFICE_BODY.equals(tag))
+ return true;
+ else if (OfficeConstants.TAG_PARAGRAPH.equals(tag))
+ return true;
+ else if (OfficeConstants.TAG_HEADING.equals(tag))
+ return true;
+ else if (OfficeConstants.TAG_ORDERED_LIST.equals(tag))
+ return true;
+ else if (OfficeConstants.TAG_UNORDERED_LIST.equals(tag))
+ return true;
+ else if (OfficeConstants.TAG_LIST_ITEM.equals(tag))
+ return true;
+ else if (OfficeConstants.TAG_LIST_HEADER.equals(tag))
+ return true;
+ else if (OfficeConstants.TAG_SPAN.equals(tag))
+ return true;
+ else if (OfficeConstants.TAG_HYPERLINK.equals(tag))
+ return true;
+ else if (OfficeConstants.TAG_LINE_BREAK.equals(tag))
+ return true;
+ else if (OfficeConstants.TAG_SPACE.equals(tag))
+ return true;
+ else if (OfficeConstants.TAG_TAB_STOP.equals(tag))
+ return true;
+
+ return false;
+ }
+
+ public boolean canConvertAttribute(String tag,
+ String attribute) {
+
+ if (OfficeConstants.TAG_SPACE.equals(tag)) {
+
+ if (OfficeConstants.ATTRIBUTE_SPACE_COUNT.equals(attribute))
+ return true;
+ }
+
+ return false;
+ }
+}
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DOCConstants.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DOCConstants.java
new file mode 100644
index 000000000000..415392933c2c
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DOCConstants.java
@@ -0,0 +1,61 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+/**
+ * Constants used for encoding and decoding the WordSmith format.
+ *
+ * @author Herbie Ong, David Proulx
+ */
+interface DOCConstants {
+
+ /** Constant for uncompressed version. */
+ public static final short UNCOMPRESSED = 1;
+
+ /** Constant for compressed version. */
+ public static final short COMPRESSED = 2;
+
+ /** Constant used for spare fields. */
+ public static final int SPARE = 0;
+
+ /** WordSmith record size. */
+ public static final short TEXT_RECORD_SIZE = 4096;
+
+ /** Constant for encoding scheme. */
+ public static final String ENCODING = "8859_1";
+
+ /** Constant for TAB character. */
+ public final static char TAB_CHAR = '\t';
+
+ /** Constant for EOL character. */
+ public final static char EOL_CHAR = '\n';
+
+ /** Constant for SPACE character. */
+ public final static char SPACE_CHAR = ' ';
+}
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DocumentDeserializerImpl.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DocumentDeserializerImpl.java
new file mode 100644
index 000000000000..91c0074f4698
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DocumentDeserializerImpl.java
@@ -0,0 +1,555 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+import org.w3c.dom.*;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.openoffice.xmerge.Document;
+import org.openoffice.xmerge.ConvertData;
+import org.openoffice.xmerge.ConvertException;
+import org.openoffice.xmerge.DocumentDeserializer;
+import org.openoffice.xmerge.converter.xml.OfficeConstants;
+import org.openoffice.xmerge.converter.palm.PalmDB;
+import org.openoffice.xmerge.converter.palm.Record;
+import org.openoffice.xmerge.converter.palm.PdbDecoder;
+import org.openoffice.xmerge.converter.palm.PalmDocument;
+import org.openoffice.xmerge.converter.xml.sxw.SxwDocument;
+
+import java.util.Vector;
+import java.io.ByteArrayInputStream;
+
+import org.openoffice.xmerge.converter.xml.*;
+import org.openoffice.xmerge.util.Debug;
+import org.openoffice.xmerge.util.XmlUtil;
+
+/**
+ * <p>WordSmith implementation of
+ * org.openoffice.xmerge.DocumentDeserializer
+ * for the {@link
+ * org.openoffice.xmerge.converter.xml.sxw.wordsmith.PluginFactoryImpl
+ * PluginFactoryImpl}.</p>
+ *
+ * The <code>deserialize</code> method uses a
+ * <code>DocDecoder</code> to read the WordSmith format into a
+ * <code>String</code> object, then it calls <code>buildDocument</code>
+ * to create a <code>SxwDocument</code> object from it.
+ *
+ * @author Herbie Ong, David Proulx
+ */
+public final class DocumentDeserializerImpl
+implements DOCConstants, OfficeConstants, DocumentDeserializer {
+
+ /** A Decoder object for decoding WordSmith format. */
+ private WSDecoder decoder = null;
+
+ WseFontTable fontTable = null;
+ WseColorTable colorTable = null;
+ StyleCatalog styleCat = null;
+ StyleCatalog oldStyleCat = null;
+
+ /** A <code>ConvertData</code> object assigned to this object. */
+ private ConvertData cd = null;
+
+
+ /**
+ * Constructor that assigns the given <code>ConvertData</code>
+ * to the object.
+ *
+ * @param cd A <code>ConvertData</code> object to read data for
+ * the conversion process by the deserialize method.
+ */
+ public DocumentDeserializerImpl(ConvertData cd) {
+ this.cd = cd;
+ }
+
+
+ /**
+ * Convert the given <code>ConvertData</code> into a
+ * <code>SxwDocument</code> object.
+ *
+ * @return Resulting <code>Document</code> object.
+ *
+ * @throws ConvertException If any conversion error occurs.
+ * @throws IOException If any I/O error occurs.
+ */
+ public Document deserialize() throws ConvertException,
+ IOException {
+ return deserialize(null, cd);
+ }
+
+
+ public Document deserialize(Document origDoc, ConvertData cd)
+ throws IOException {
+
+ Document doc = null;
+ PalmDocument palmDoc = null;
+ Enumeration e = cd.getDocumentEnumeration();
+
+ while(e.hasMoreElements()) {
+ palmDoc = (PalmDocument) e.nextElement();
+ PalmDB pdb = palmDoc.getPdb();
+ Record[] recs = pdb.getRecords();
+ decoder = new WSDecoder();
+ Wse[] b = decoder.parseDocument(recs);
+ String docName = palmDoc.getName();
+ doc = buildDocument(docName, b, origDoc);
+ }
+ return doc;
+ }
+
+
+ /**
+ * Temporary method to read existing <code>StyleCatalog</code>
+ * as a starting point.
+ *
+ * @param parentDoc The parent <code>Document</code>.
+ */
+ private void readStyleCatalog(Document parentDoc) {
+ Element rootNode = null;
+ try {
+ java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
+ parentDoc.write(bos);
+ SxwDocument sxwDoc = new SxwDocument("old");
+ sxwDoc.read(new ByteArrayInputStream(bos.toByteArray()));
+ org.w3c.dom.Document domDoc = sxwDoc.getContentDOM();
+
+ String families[] = new String[3];
+ families[0] = "text";
+ families[1] = "paragraph";
+ families[2] = "paragraph";
+ Class classes[] = new Class[3];
+ classes[0] = TextStyle.class;
+ classes[1] = ParaStyle.class;
+ classes[2] = TextStyle.class;
+
+ NodeList nl = domDoc.getElementsByTagName(TAG_OFFICE_STYLES);
+ oldStyleCat.add(nl.item(0), families, classes, null, false);
+ nl = domDoc.getElementsByTagName(TAG_OFFICE_AUTOMATIC_STYLES);
+ oldStyleCat.add(nl.item(0), families, classes, null, false);
+ nl = domDoc.getElementsByTagName(TAG_OFFICE_MASTER_STYLES);
+ oldStyleCat.add(nl.item(0), families, classes, null, false);
+
+ } catch (Exception e) {
+ Debug.log(Debug.ERROR, "", e);
+ }
+
+ }
+
+
+ /**
+ * Given an array of paragraph <code>Style</code> objects, see if
+ * there is exactly one which matches the text formatting
+ * <code>Style</code> of <code>tStyle</code>.
+ *
+ * @param paraStyles An array of paragraph <code>Style</code>
+ * objects.
+ * @param tStyle Text <code>Style</code> to match.
+ *
+ * @return The paragraph <code>Style</code> that matches.
+ */
+ private ParaStyle matchParaByText(Style paraStyles[], TextStyle tStyle) {
+ int matchIndex = -1;
+ int matchCount = 0;
+ Style txtMatches[] = (Style[]) oldStyleCat.getMatching(tStyle);
+ if (txtMatches.length >= 1) {
+ for (int j = 0; j < txtMatches.length; j++) {
+ TextStyle t = (TextStyle)txtMatches[j];
+
+ if (!t.getFamily().equals("paragraph"))
+ continue;
+
+ for (int k = 0; k < paraStyles.length; k++) {
+ if (t.getName().equals(paraStyles[k].getName())) {
+ matchCount++;
+ matchIndex = k;
+ }
+ }
+ }
+ }
+ if (matchCount == 1)
+ return (ParaStyle)paraStyles[matchIndex];
+ else return null;
+ }
+
+
+ /**
+ * Take a <code>String</code> of text and turn it into a sequence
+ * of <code>Node</code> objects.
+ *
+ * @param text <code>String</code> of text.
+ * @param parentDoc Parent <code>Document</code>.
+ *
+ * @return Array of <code>Node</code> objects.
+ */
+ private Node[] parseText(String text, org.w3c.dom.Document parentDoc) {
+ Vector nodeVec = new Vector();
+
+ // Break up the text from the WordSmith text run into Open
+ // Office text runs. There may be more runs in OO because
+ // runs of 2 or more spaces map to nodes.
+ while ((text.indexOf(" ") != -1) || (text.indexOf("\t") != 1)) {
+
+ // Find the indices of tabs and multiple spaces, and
+ // figure out which of them occurs first in the string.
+ int spaceIndex = text.indexOf(" ");
+ int tabIndex = text.indexOf("\t");
+ if ((spaceIndex == -1) && (tabIndex == -1))
+ break; // DJP This should not be necessary. What is wrong
+ // with the while() stmt up above?
+ int closerIndex; // Index of the first of these
+ if (spaceIndex == -1)
+ closerIndex = tabIndex;
+ else if (tabIndex == -1)
+ closerIndex = spaceIndex;
+ else
+ closerIndex = (spaceIndex > tabIndex) ? tabIndex : spaceIndex;
+
+ // If there is any text prior to the first occurrence of a
+ // tab or spaces, create a text node from it, then chop it
+ // off the string we're working with.
+ if (closerIndex > 0) {
+ String beginningText = text.substring(0, closerIndex);
+ Text textNode = parentDoc.createTextNode(beginningText);
+ nodeVec.addElement(textNode);
+ log("<TEXT>");
+ log(beginningText);
+ log("</TEXT>");
+ }
+ text = text.substring(closerIndex);
+
+ // Handle either tab character or space sequence by creating
+ // an element for it, and then chopping out the text that
+ // represented it in "text".
+ if (closerIndex == tabIndex) {
+ Element tabNode = parentDoc.createElement(TAG_TAB_STOP);
+ nodeVec.add(tabNode);
+ text = text.substring(1); // tab is always a single character
+ log("<TAB/>");
+ } else {
+ // Compute length of space sequence.
+ int nrSpaces = 2;
+ while ((nrSpaces < text.length())
+ && text.substring(nrSpaces, nrSpaces + 1).equals(" "))
+ nrSpaces++;
+
+ Element spaceNode = parentDoc.createElement(TAG_SPACE);
+ spaceNode.setAttribute(ATTRIBUTE_SPACE_COUNT, new Integer(nrSpaces).toString());
+ nodeVec.add(spaceNode);
+ text = text.substring(nrSpaces);
+ log("<SPACE count=\"" + nrSpaces + "\" />");
+ }
+ }
+
+ // No more tabs or space sequences. If there's any remaining
+ // text create a text node for it.
+ if (text.length() > 0) {
+ Text textNode = parentDoc.createTextNode(text);
+ nodeVec.add(textNode);
+ log("<TEXT>");
+ log(text);
+ log("</TEXT>");
+ }
+
+ // Now create and populate an array to return the nodes in.
+ Node nodes[] = new Node[nodeVec.size()];
+ for (int i = 0; i < nodeVec.size(); i++)
+ nodes[i] = (Node)nodeVec.elementAt(i);
+ return nodes;
+ }
+
+
+ /**
+ * Parses the text content of a WordSmith format and builds a
+ * <code>SXWDocument</code>.
+ *
+ * @param docName <code>Document</code> name
+ * @param str Text content of WordSmith format
+ *
+ * @return Resulting <code>SXWDocument</code> object.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ private SxwDocument buildDocument(String docName, Wse[] data, Document origDoc)
+ throws IOException {
+
+ // create minimum office xml document.
+ SxwDocument sxwDoc = new SxwDocument(docName);
+ sxwDoc.initContentDOM();
+
+ org.w3c.dom.Document doc = sxwDoc.getContentDOM();
+
+ // Grab hold of the office:body tag,
+ // Assume there should be one.
+ // This is where top level paragraphs will append to.
+ NodeList list = doc.getElementsByTagName(TAG_OFFICE_BODY);
+ Node bodyNode = list.item(0);
+
+ styleCat = new StyleCatalog(50);
+ oldStyleCat = new StyleCatalog(50);
+ if (origDoc != null)
+ readStyleCatalog(origDoc);
+
+ Element currPara = null;
+ ParaStyle currParaStyle = null;
+ int newTextStyleNr = 0;
+ int newParaStyleNr = 0;
+
+ // Now write out the document body by running through
+ // the list of WordSmith elements and processing each one
+ // in turn.
+ for (int i = 0; i < data.length; i++) {
+
+ if (data[i].getClass() == WsePara.class) {
+
+ currPara = doc.createElement(TAG_PARAGRAPH);
+ log("</PARA>");
+ log("<PARA>");
+
+ WsePara p = (WsePara)data[i];
+
+ // Save info about the first text run, if there is one.
+ WseTextRun firstTextRun = null;
+
+ if ((data.length >= i + 2)
+ && (data[i+1].getClass() == WseTextRun.class))
+ firstTextRun = (WseTextRun)data[i+1];
+
+ Style matches[] = oldStyleCat.getMatching(p.makeStyle());
+
+ // See if we can find a unique match in the catalog
+ // of existing styles from the original document.
+ ParaStyle pStyle = null;
+ if (matches.length == 1) {
+ pStyle = (ParaStyle)matches[0];
+ log("using an existing style");
+ } else if ((matches.length > 1) && (firstTextRun != null)) {
+ pStyle = matchParaByText(matches, firstTextRun.makeStyle());
+ log("resolved a para by looking @ text");
+ }
+
+ // If nothing found so far, try looking in the catalog
+ // of newly-created styles.
+ // DJP FIXME: if we need to add two para styles with the
+ // same para formatting info but different default text
+ // styles, this won't work!
+ if (pStyle == null) {
+ log("had " + matches.length + " matches in old catalog");
+ matches = styleCat.getMatching(p.makeStyle());
+ if (matches.length == 0) {
+ pStyle = p.makeStyle();
+ String newName = new String("PPP" + ++newParaStyleNr);
+ pStyle.setName(newName);
+ styleCat.add(pStyle);
+ // DJP: write in the text format info here
+ log("created a new style");
+ } else if (matches.length == 1) {
+ pStyle = (ParaStyle)matches[0];
+ log("re-using a new style");
+ } else if (firstTextRun != null) {
+ pStyle = matchParaByText(matches, firstTextRun.makeStyle());
+ if (pStyle != null) {
+ log("resolved a (new) para by looking @ text");
+ } else
+ log("Hey this shouldn't happen! - nr of matches is "
+ + matches.length);
+ }
+ }
+
+ if (pStyle == null)
+ log("Unable to figure out a para style");
+
+ // Figured out a style to use. Specify the style in this
+ // paragraph's attributes.
+ currPara.setAttribute(ATTRIBUTE_TEXT_STYLE_NAME, pStyle.getName());
+
+ bodyNode.appendChild(currPara);
+ currParaStyle = pStyle;
+ } else if (data[i].getClass() == WseTextRun.class) {
+ WseTextRun tr = (WseTextRun)data[i];
+ TextStyle trStyle = null;
+ Node trNodes[] = parseText(tr.getText(), doc);
+
+ // First see if the formatting of this text run matches
+ // the default text formatting for this paragraph. If
+ // it does, then just make the text node(s) children of
+ // the current paragraph.
+ Style[] cps = new Style[1];
+ cps[0] = currParaStyle;
+ if (matchParaByText(cps, tr.makeStyle()) != null) {
+ for (int ii = 0; ii < trNodes.length; ii++) {
+ currPara.appendChild(trNodes[ii]);
+ }
+ continue;
+ }
+
+ // Check for existing, matching styles in the old style
+ // catalog. If exactly one is found, use it. Otherwise,
+ // check the new style catalog, and either use the style
+ // found or add this new one to it.
+ Style matches[] = oldStyleCat.getMatching(tr.makeStyle());
+ if (matches.length == 1)
+ trStyle = (TextStyle)matches[0];
+ else {
+ matches = styleCat.getMatching(tr.makeStyle());
+ if (matches.length == 0) {
+ trStyle = tr.makeStyle();
+ String newName = new String("TTT" + ++newTextStyleNr);
+ trStyle.setName(newName);
+ styleCat.add(trStyle);
+ } else if (matches.length == 1)
+ trStyle = (TextStyle)matches[0];
+ else
+ log("multiple text style matches from new catalog");
+ }
+
+ // Create a text span node, set the style attribute, make the
+ // text node(s) its children, and append it to current paragraph's
+ // list of children.
+ Element textSpanNode = doc.createElement(TAG_SPAN);
+ textSpanNode.setAttribute(ATTRIBUTE_TEXT_STYLE_NAME, trStyle.getName());
+ for (int ii = 0; ii < trNodes.length; ii++) {
+ textSpanNode.appendChild(trNodes[ii]);
+ }
+ currPara.appendChild(textSpanNode);
+ log("</SPAN>");
+ }
+
+ else if (data[i].getClass() == WseFontTable.class) {
+ fontTable = (WseFontTable)data[i];
+ }
+
+ else if (data[i].getClass() == WseColorTable.class) {
+ colorTable = (WseColorTable)data[i];
+ }
+ }
+
+
+ //NodeList r = doc.getElementsByTagName(TAG_OFFICE_DOCUMENT);
+ NodeList r = doc.getElementsByTagName(TAG_OFFICE_DOCUMENT_CONTENT);
+ Node rootNode = r.item(0);
+
+ // read the original document
+ org.w3c.dom.NodeList nl;
+ if (origDoc != null) {
+ java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
+ origDoc.write(bos);
+ SxwDocument origSxwDoc = new SxwDocument("old");
+ origSxwDoc.read(new ByteArrayInputStream(bos.toByteArray()));
+ org.w3c.dom.Document origDomDoc = origSxwDoc.getContentDOM();
+
+ XmlUtil xu = new XmlUtil();
+ org.w3c.dom.DocumentFragment df;
+ org.w3c.dom.Node newNode;
+
+ // copy font declarations from original document to the new document
+ nl = origDomDoc.getElementsByTagName(TAG_OFFICE_FONT_DECLS);
+ df = doc.createDocumentFragment();
+ newNode = xu.deepClone(df, nl.item(0));
+ rootNode.insertBefore(newNode, bodyNode);
+
+ // copy style catalog from original document to the new document
+ nl = origDomDoc.getElementsByTagName(TAG_OFFICE_STYLES);
+ df = doc.createDocumentFragment();
+ newNode = xu.deepClone(df, nl.item(0));
+ rootNode.insertBefore(newNode, bodyNode);
+
+ nl = origDomDoc.getElementsByTagName(TAG_OFFICE_AUTOMATIC_STYLES);
+ df = doc.createDocumentFragment();
+ newNode = xu.deepClone(df, nl.item(0));
+ rootNode.insertBefore(newNode, bodyNode);
+
+ nl = origDomDoc.getElementsByTagName(TAG_OFFICE_MASTER_STYLES);
+ df = doc.createDocumentFragment();
+ newNode = xu.deepClone(df, nl.item(0));
+ rootNode.insertBefore(newNode, bodyNode);
+ }
+
+ // Original document not specified. We need to add font declarations.
+ // DJP: this might just be for debugging. Merger will probably put
+ // the "real" ones in.
+ // DJP: if really doing it this way, do it right: gather font names
+ // from style catalog(s).
+ else {
+ org.w3c.dom.Node declNode;
+
+ log("<FONT-DECLS/>");
+
+ declNode = doc.createElement(TAG_OFFICE_FONT_DECLS);
+ rootNode.insertBefore(declNode, bodyNode);
+ org.w3c.dom.Element fontNode;
+
+ fontNode = doc.createElement(TAG_STYLE_FONT_DECL);
+ fontNode.setAttribute(ATTRIBUTE_STYLE_NAME, "Arial");
+ fontNode.setAttribute(ATTRIBUTE_FO_FONT_FAMILY, "Arial");
+ fontNode.setAttribute(ATTRIBUTE_STYLE_FONT_PITCH, "variable");
+ declNode.appendChild(fontNode);
+
+ fontNode = doc.createElement(TAG_STYLE_FONT_DECL);
+ fontNode.setAttribute(ATTRIBUTE_STYLE_NAME, "Arioso");
+ fontNode.setAttribute(ATTRIBUTE_FO_FONT_FAMILY, "Arioso");
+ fontNode.setAttribute(ATTRIBUTE_STYLE_FONT_PITCH, "variable");
+ declNode.appendChild(fontNode);
+ }
+
+
+ // Now add any new styles we have created in this document.
+ nl = doc.getElementsByTagName(TAG_OFFICE_AUTOMATIC_STYLES);
+ Node autoStylesNode = nl.item(0);
+ if (autoStylesNode == null) {
+ autoStylesNode = doc.createElement(TAG_OFFICE_AUTOMATIC_STYLES);
+ log("<OFFICE-AUTOMATIC-STYLES/>");
+ rootNode.insertBefore(autoStylesNode, bodyNode);
+ }
+
+ Node newStyleCatNode = styleCat.writeNode(doc, "dummy");
+ nl = newStyleCatNode.getChildNodes();
+ int nNodes = nl.getLength();
+ for (int i = 0; i < nNodes; i++) {
+ autoStylesNode.appendChild(nl.item(0));
+ }
+
+ oldStyleCat.dumpCSV(true);
+ styleCat.dumpCSV(true);
+ return sxwDoc;
+ }
+
+
+ /**
+ * Sends message to the log object.
+ *
+ * @param str Debug message.
+ */
+ private void log(String str) {
+
+ Debug.log(Debug.TRACE, str);
+ }
+}
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DocumentMergerImpl.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DocumentMergerImpl.java
new file mode 100644
index 000000000000..b338c9adb6ff
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DocumentMergerImpl.java
@@ -0,0 +1,99 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.openoffice.xmerge.DocumentMerger;
+import org.openoffice.xmerge.MergeException;
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.converter.xml.sxw.SxwDocument;
+import org.openoffice.xmerge.merger.DiffAlgorithm;
+import org.openoffice.xmerge.merger.Difference;
+import org.openoffice.xmerge.merger.NodeMergeAlgorithm;
+import org.openoffice.xmerge.merger.Iterator;
+import org.openoffice.xmerge.merger.DiffAlgorithm;
+import org.openoffice.xmerge.merger.diff.ParaNodeIterator;
+import org.openoffice.xmerge.merger.diff.IteratorLCSAlgorithm;
+import org.openoffice.xmerge.merger.merge.DocumentMerge;
+import org.openoffice.xmerge.merger.merge.CharacterBaseParagraphMerge;
+import org.openoffice.xmerge.util.Debug;
+
+
+/**
+ * Wordsmith implementation of <code>DocumentMerger</code>
+ * for the {@link
+ * org.openoffice.xmerge.converter.xml.sxw.wordsmith.PluginFactoryImpl
+ * PluginFactoryImpl}.</p>
+ */
+public class DocumentMergerImpl implements DocumentMerger {
+
+ private ConverterCapabilities cc_;
+ private org.openoffice.xmerge.Document orig = null;
+
+ public DocumentMergerImpl(org.openoffice.xmerge.Document doc, ConverterCapabilities cc) {
+ cc_ = cc;
+ this.orig = doc;
+ }
+
+ public void merge(org.openoffice.xmerge.Document modifiedDoc) throws MergeException {
+
+ SxwDocument wdoc1 = (SxwDocument) orig;
+ SxwDocument wdoc2 = (SxwDocument) modifiedDoc;
+
+ Document doc1 = wdoc1.getContentDOM();
+ Document doc2 = wdoc2.getContentDOM();
+
+ Iterator i1 = new ParaNodeIterator(cc_, doc1.getDocumentElement());
+ Iterator i2 = new ParaNodeIterator(cc_, doc2.getDocumentElement());
+
+ DiffAlgorithm diffAlgo = new IteratorLCSAlgorithm();
+
+ // find out the paragrah level diffs
+ Difference[] diffTable = diffAlgo.computeDiffs(i1, i2);
+
+ if (Debug.isFlagSet(Debug.INFO)) {
+ Debug.log(Debug.INFO, "Diff Result: ");
+
+ for (int i = 0; i < diffTable.length; i++) {
+ Debug.log(Debug.INFO, diffTable[i].debug());
+ }
+ }
+
+ // merge the paragraphs
+ NodeMergeAlgorithm charMerge = new CharacterBaseParagraphMerge();
+ DocumentMerge docMerge = new DocumentMerge(cc_, charMerge);
+
+ Iterator result = null;
+
+ docMerge.applyDifference(i1, i2, diffTable);
+ }
+}
+
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DocumentSerializerImpl.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DocumentSerializerImpl.java
new file mode 100644
index 000000000000..bca91fa53415
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DocumentSerializerImpl.java
@@ -0,0 +1,536 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Node;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Element;
+
+import java.io.IOException;
+
+import org.openoffice.xmerge.Document;
+import org.openoffice.xmerge.ConvertData;
+import org.openoffice.xmerge.ConvertException;
+import org.openoffice.xmerge.DocumentSerializer;
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.converter.xml.OfficeConstants;
+import org.openoffice.xmerge.converter.xml.sxw.SxwDocument;
+import org.openoffice.xmerge.converter.palm.PalmDB;
+import org.openoffice.xmerge.converter.palm.PdbEncoder;
+import org.openoffice.xmerge.converter.palm.Record;
+import org.openoffice.xmerge.converter.palm.PdbUtil;
+import org.openoffice.xmerge.converter.palm.PalmDocument;
+import org.openoffice.xmerge.converter.xml.OfficeDocument;
+import org.openoffice.xmerge.util.*;
+import org.openoffice.xmerge.converter.xml.*;
+
+/**
+ * <p>WordSmith implementation of
+ * org.openoffice.xmerge.DocumentSerializer
+ * for the {@link
+ * org.openoffice.xmerge.converter.xml.sxw.wordsmith.PluginFactoryImpl
+ * PluginFactoryImpl}.</p>
+ *
+ * <p>The <code>serialize</code> method traverses the DOM
+ * document from the given <code>Document</code> object. It uses a
+ * <code>DocEncoder</code> object for the actual conversion of
+ * contents to the WordSmith format.</p>
+ *
+ * @author Herbie Ong, David Proulx
+ */
+
+// DJP: take out "implements OfficeConstants"
+public final class DocumentSerializerImpl
+implements OfficeConstants, DocumentSerializer {
+
+ /** A WSEncoder object for encoding to WordSmith. */
+ private WSEncoder encoder = null;
+
+ /** The <code>StyleCatalog</code>. */
+ private StyleCatalog styleCat = null;
+
+ private WseFontTable fontTable = new WseFontTable();
+ private WseColorTable colorTable = new WseColorTable();
+
+ /**
+ * The <code>SxwDocument</code> object that this converter
+ * processes.
+ */
+ private SxwDocument sxwDoc = null;
+
+ /**
+ * Constructor.
+ *
+ * @param doc The <code>Document</code> to convert.
+ */
+ public DocumentSerializerImpl(Document doc) {
+ sxwDoc = (SxwDocument) doc;
+ }
+
+
+ /**
+ * <p>Method to convert a <code>Document</code> into a
+ * <code>PalmDocument</code>.</p>
+ *
+ * <p>This method is not thread safe for performance reasons.
+ * This method should not be called from within two threads.
+ * It would be best to call this method only once per object
+ * instance.</p>
+ *
+ * <p>Note that the doc parameter needs to be an XML
+ * <code>Document</code>, else this method will throw a
+ * <code>ClassCastException</code>. I think this is a hack,
+ * but this is the only way to not modify most of the existing
+ * code right now.</p>
+ *
+ * @param doc Input should be an XML <code>Document</code>
+ * object
+ * @param os Output of <code>PalmDB</code> object
+ *
+ * @throws ConvertException If any conversion error occurs.
+ * @throws IOException If any I/O error occurs.
+ */
+ public ConvertData serialize()
+ throws IOException {
+
+
+ // get the server document name
+ String docName = sxwDoc.getName();
+
+ // get DOM document
+ org.w3c.dom.Document domDoc = sxwDoc.getContentDOM();
+
+ // Create WordSmith encoder object. Add WordSmith header,
+ // empty font table to it.
+ encoder = new WSEncoder();
+ encoder.addElement(fontTable);
+ encoder.addElement(colorTable);
+
+ // Read the styles into the style catalog
+ String families[] = new String[3];
+ families[0] = "text";
+ families[1] = "paragraph";
+ families[2] = "paragraph";
+ Class classes[] = new Class[3];
+ classes[0] = TextStyle.class;
+ classes[1] = ParaStyle.class;
+ classes[2] = TextStyle.class;
+ styleCat = new StyleCatalog(25);
+
+ // Parse the input document
+ // DJP todo: eliminate multiple calls to add() when it can
+ // recurse properly.
+ NodeList nl = domDoc.getElementsByTagName(TAG_OFFICE_STYLES);
+ styleCat.add(nl.item(0), families, classes, null, false);
+ nl = domDoc.getElementsByTagName(TAG_OFFICE_AUTOMATIC_STYLES);
+ styleCat.add(nl.item(0), families, classes, null, false);
+ nl = domDoc.getElementsByTagName(TAG_OFFICE_MASTER_STYLES);
+ styleCat.add(nl.item(0), families, classes, null, false);
+
+ // Traverse to the office:body element.
+ // There should only be one.
+ NodeList list = domDoc.getElementsByTagName(TAG_OFFICE_BODY);
+ int len = list.getLength();
+ if (len > 0) {
+ Node node = list.item(0);
+ traverseBody(node);
+ }
+
+ // create a PalmDB object and ConvertData object.
+ //
+ Record records[] = encoder.getRecords();
+
+ ConvertData cd = new ConvertData();
+ PalmDocument palmDoc = new PalmDocument(docName,
+ PdbUtil.intID("WrdS"), PdbUtil.intID("BDOC"), 0,
+ PalmDB.PDB_HEADER_ATTR_BACKUP, records);
+ cd.addDocument(palmDoc);
+ return cd;
+ }
+
+
+ /**
+ * This method traverses <i>office:body</i> element.
+ *
+ * @param node <i>office:body</i> <code>Node</code>.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ private void traverseBody(Node node) throws IOException {
+
+ if (node.hasChildNodes()) {
+
+ NodeList nodeList = node.getChildNodes();
+ int len = nodeList.getLength();
+
+ for (int i = 0; i < len; i++) {
+
+ Node child = nodeList.item(i);
+
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ String nodeName = child.getNodeName();
+
+ if (nodeName.equals(TAG_PARAGRAPH) ||
+ nodeName.equals(TAG_HEADING)) {
+
+ traverseParagraph(child);
+
+ } else if (nodeName.equals(TAG_UNORDERED_LIST)) {
+
+ traverseList(child);
+
+ } else if (nodeName.equals(TAG_ORDERED_LIST)) {
+
+ traverseList(child);
+
+ } else {
+
+ Debug.log(Debug.INFO, "<OTHERS " /* + XmlDebug.nodeInfo(child) */ + " />");
+ }
+ }
+ }
+ }
+
+ }
+
+
+ /**
+ * This method traverses the <i>text:p</i> and <i>text:h</i>
+ * element <code>Node</code> objects.
+ *
+ * @param node A <i>text:p</i> or <i>text:h</i> <code>Node</code>.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ private void traverseParagraph(Node node) throws IOException {
+
+ String styleName = findAttribute(node, "text:style-name");
+ ParaStyle pstyle = (ParaStyle)styleCat.lookup(styleName, "paragraph",
+ null, ParaStyle.class);
+
+ // If the style does not exist in the style catalog for some reason,
+ // make up a default style and use it. We'll have to add this default
+ // style to the style catalog the first time it is used.
+ if (pstyle == null) {
+ styleName = "CONVERTER-DEFAULT";
+ pstyle = (ParaStyle)styleCat.lookup(styleName, "paragraph", null,
+ ParaStyle.class);
+ if (pstyle == null) {
+ pstyle = new ParaStyle(styleName, "paragraph", null,
+ (String [])null, null, styleCat);
+ styleCat.add(pstyle);
+ styleCat.add(new TextStyle(styleName, "paragraph", null,
+ 0, 0, 12, "Times-Roman", styleCat));
+ }
+ }
+
+ pstyle = (ParaStyle)pstyle.getResolved();
+ encoder.addElement(new WsePara(pstyle, styleCat));
+ TextStyle defParaTextStyle = (TextStyle)
+ styleCat.lookup(styleName, "paragraph", null, TextStyle.class);
+
+ traverseParaContents(node, defParaTextStyle);
+ }
+
+
+ /**
+ * This method traverses a paragraph content. Note that this
+ * method may recurse to call itself.
+ *
+ * @param node A paragraph or content <code>Node</code>
+ */
+ private void traverseParaContents(Node node, TextStyle defTextStyle) {
+
+ String styleName = findAttribute(node, "text:style-name");
+ TextStyle style = (TextStyle)
+ styleCat.lookup(styleName, "text", null, TextStyle.class);
+
+ if (node.hasChildNodes()) {
+ NodeList nodeList = node.getChildNodes();
+ int nChildren = nodeList.getLength();
+
+ for (int i = 0; i < nChildren; i++) {
+ Node child = nodeList.item(i);
+
+ if (child.getNodeType() == Node.TEXT_NODE) {
+
+ // this is for grabbing text nodes.
+ String s = child.getNodeValue();
+
+ if (s.length() > 0) {
+ if (style != null)
+ encoder.addElement(new WseTextRun(s, style, styleCat,
+ fontTable, colorTable));
+ else
+ encoder.addElement(new WseTextRun(s, defTextStyle,
+ styleCat, fontTable, colorTable));
+ }
+
+ } else if (child.getNodeType() == Node.ELEMENT_NODE) {
+
+ String childNodeName = child.getNodeName();
+
+ if (childNodeName.equals(TAG_SPACE)) {
+
+ // this is for text:s tags.
+ NamedNodeMap map = child.getAttributes();
+ Node attr = map.getNamedItem(ATTRIBUTE_SPACE_COUNT);
+ StringBuffer space = new StringBuffer(" ");
+ int count = 1;
+
+ if (attr != null) {
+ try {
+ String countStr = attr.getNodeValue();
+ count = Integer.parseInt(countStr.trim());
+ } catch (NumberFormatException e) {
+ Debug.log(Debug.ERROR, "Problem parsing space tag", e);
+ }
+ }
+
+ for (int j = 1; j < count; j++)
+ space.append(" ");
+
+ encoder.addElement(new WseTextRun(space.toString(),
+ defTextStyle,
+ styleCat, fontTable, colorTable));
+ Debug.log(Debug.INFO, "<SPACE count=\"" + count + "\" />");
+
+ } else if (childNodeName.equals(TAG_TAB_STOP)) {
+
+ // this is for text:tab-stop
+ encoder.addElement(new WseTextRun("\t", defTextStyle, styleCat,
+ fontTable, colorTable));
+
+ Debug.log(Debug.INFO, "<TAB/>");
+
+ } else if (childNodeName.equals(TAG_LINE_BREAK)) {
+
+ // this is for text:line-break
+ encoder.addElement(new WseTextRun("\n", defTextStyle,
+ styleCat, fontTable, colorTable));
+
+ Debug.log(Debug.INFO, "<LINE-BREAK/>");
+
+ } else if (childNodeName.equals(TAG_SPAN)) {
+
+ // this is for text:span
+ Debug.log(Debug.INFO, "<SPAN>");
+ traverseParaContents(child, defTextStyle);
+ Debug.log(Debug.INFO, "</SPAN>");
+
+ } else if (childNodeName.equals(TAG_HYPERLINK)) {
+
+ // this is for text:a
+ Debug.log(Debug.INFO, "<HYPERLINK>");
+ traverseParaContents(child, defTextStyle);
+ Debug.log(Debug.INFO, "<HYPERLINK/>");
+
+ } else if (childNodeName.equals(TAG_BOOKMARK) ||
+ childNodeName.equals(TAG_BOOKMARK_START)) {
+
+ Debug.log(Debug.INFO, "<BOOKMARK/>");
+
+ } else {
+
+ Debug.log(Debug.INFO, "<OTHERS " /* + XmlDebug.nodeInfo(child) */ + " />");
+ }
+
+ }
+
+ }
+ }
+ }
+
+
+ /**
+ * This method traverses list tags <i>text:unordered-list</i> and
+ * <i>text:ordered-list</i>. A list can only contain one optional
+ * <i>text:list-header</i> and one or more <i>text:list-item</i>
+ * elements.
+ *
+ * @param node A list <code>Node</code>.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ private void traverseList(Node node) throws IOException {
+
+ Debug.log(Debug.TRACE, "<LIST>");
+
+ if (node.hasChildNodes()) {
+
+ NodeList nodeList = node.getChildNodes();
+ int len = nodeList.getLength();
+
+ for (int i = 0; i < len; i++) {
+
+ Node child = nodeList.item(i);
+
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+
+ String nodeName = child.getNodeName();
+
+ if (nodeName.equals(TAG_LIST_ITEM)) {
+
+ traverseListItem(child);
+
+ } else if (nodeName.equals(TAG_LIST_HEADER)) {
+
+ traverseListHeader(child);
+
+ } else {
+
+ Debug.log(Debug.ERROR, "<INVALID-XML-BUG " + " />");
+ }
+ }
+ }
+ }
+
+ Debug.log(Debug.TRACE, "</LIST>");
+ }
+
+
+ /**
+ * This method traverses a <i>text:list-header</i> element.
+ * It contains one or more <i>text:p</i> elements.
+ *
+ * @param node A list header <code>Node</code>.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ private void traverseListHeader(Node node) throws IOException {
+
+ Debug.log(Debug.TRACE, "<LIST-HEADER>");
+
+ if (node.hasChildNodes()) {
+
+ NodeList nodeList = node.getChildNodes();
+ int len = nodeList.getLength();
+
+ for (int i = 0; i < len; i++) {
+
+ Node child = nodeList.item(i);
+
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+
+ String nodeName = child.getNodeName();
+
+ if (nodeName.equals(TAG_PARAGRAPH)) {
+
+ traverseParagraph(child);
+
+ } else {
+
+ Debug.log(Debug.TRACE, "<INVALID-XML-BUG " + " />");
+ }
+ }
+ }
+ }
+
+ Debug.log(Debug.TRACE, "</LIST-HEADER>");
+ }
+
+
+ /**
+ * This method will traverse a <i>text:list-item</i>.
+ * A list item may contain one or more of <i>text:p</i>,
+ * <i>text:h</i>, <i>text:section</i>,
+ * <i>text:ordered-list</i> and <i>text:unordered-list</i>.
+ *
+ * This method currently only implements grabbing <i>text:p</i>,
+ * <i>text:h</i>, <i>text:unordered-list</i> and
+ * <i>text:ordered-list</i>.
+ *
+ * @param Node <code>Node</code> to traverse.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ private void traverseListItem(Node node) throws IOException {
+
+ Debug.log(Debug.TRACE, "<LIST-ITEM>");
+
+ if (node.hasChildNodes()) {
+
+ NodeList nodeList = node.getChildNodes();
+ int len = nodeList.getLength();
+
+ for (int i = 0; i < len; i++) {
+
+ Node child = nodeList.item(i);
+
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+
+ String nodeName = child.getNodeName();
+
+ if (nodeName.equals(TAG_PARAGRAPH)) {
+
+ traverseParagraph(child);
+
+ } else if (nodeName.equals(TAG_UNORDERED_LIST)) {
+
+ traverseList(child);
+
+ } else if (nodeName.equals(TAG_ORDERED_LIST)) {
+
+ traverseList(child);
+
+ } else {
+
+ Debug.log(Debug.ERROR, "<INVALID-XML-BUG " + " />");
+ }
+ }
+ }
+ }
+
+ Debug.log(Debug.TRACE, "</LIST-ITEM>");
+ }
+
+
+ /**
+ * Look up a <code>Node</code> object's named attribute and return
+ * its value
+ *
+ * @param node The <code>Node</code>.
+ * @param name The attribute name.
+ *
+ * @return The value of the named attribute
+ */
+ private String findAttribute(Node node, String name) {
+ NamedNodeMap attrNodes = node.getAttributes();
+ if (attrNodes != null) {
+ int len = attrNodes.getLength();
+ for (int i = 0; i < len; i++) {
+ Node attr = attrNodes.item(i);
+ if (attr.getNodeName().equals(name))
+ return attr.getNodeValue();
+ }
+ }
+ return null;
+ }
+}
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/PluginFactoryImpl.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/PluginFactoryImpl.java
new file mode 100644
index 000000000000..d7e84d69f34c
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/PluginFactoryImpl.java
@@ -0,0 +1,149 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+import org.openoffice.xmerge.Document;
+import org.openoffice.xmerge.ConvertData;
+import org.openoffice.xmerge.DocumentMerger;
+import org.openoffice.xmerge.DocumentMergerFactory;
+import org.openoffice.xmerge.DocumentSerializer;
+import org.openoffice.xmerge.DocumentSerializerFactory;
+import org.openoffice.xmerge.DocumentDeserializer;
+import org.openoffice.xmerge.DocumentDeserializerFactory;
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.converter.xml.sxw.SxwPluginFactory;
+import org.openoffice.xmerge.converter.palm.PalmDocument;
+import org.openoffice.xmerge.util.registry.ConverterInfo;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+
+/**
+ * <p>WordSmith implementation of a <code>PluginFactory</code> that
+ * encapsulates conversion of StarWriter XML format to and from
+ * WordSmith format.</p>
+ *
+ * The superclass produces a particular
+ * {@link org.openoffice.xmerge.Document Document}
+ * object, i.e.
+ * {@link org.openoffice.xmerge.converter.xml.sxw.SxwDocument
+ * SxwDocument} that the converters in this class works with. Thus,
+ * this class only implements the methods that produces the converters,
+ * i.e. {@link
+ * org.openoffice.xmerge.DocumentSerializer
+ * DocumentSerializer} and {@link
+ * org.openoffice.xmerge.DocumentDeserializer
+ * DocumentDeserializer};
+ * as well as the {@link
+ * org.openoffice.xmerge.ConverterCapabilities
+ * ConverterCapabilities} object that is specific to this format
+ * conversion. That superclass also produces a {@link
+ * org.openoffice.xmerge.DocumentMerger DocumentMerger}
+ * object, i.e. {@link
+ * org.openoffice.xmerge.converter.xml.sxw.wordsmith.DocumentMergerImpl
+ * DocumentMergerImpl} which this class derives the functionality.</p>
+ *
+ * @author Herbie Ong, Dave Proulx
+ */
+public final class PluginFactoryImpl extends SxwPluginFactory
+ implements DocumentDeserializerFactory, DocumentSerializerFactory,
+ DocumentMergerFactory {
+
+ public PluginFactoryImpl(ConverterInfo ci) {
+ super(ci);
+ }
+
+ /** ConverterCapabilities object for this type of conversion. */
+ private final static ConverterCapabilities converterCap =
+ new ConverterCapabilitiesImpl();
+
+
+ /**
+ * Returns an instance of <code>DocumentSerializerImpl</code>, which is
+ * an implementation of <code>DocumentSerializer</code> interface.
+ *
+ * @param doc <code>Document</code> object to be converted/serialized.
+ *
+ * @return A <code>DocumentSerializerImpl</code> object.
+ */
+ public DocumentSerializer createDocumentSerializer(Document doc) {
+
+ return new DocumentSerializerImpl(doc);
+ }
+
+
+ /**
+ * Returns an instance of <code>DocumentDeserializerImpl</code>,
+ * which is an implementation of <code>DocumentDeserializer</code>
+ * interface.
+ *
+ * @param cd <code>ConvertData</code> object for reading data
+ * which will be converted back to a
+ * <code>Document</code> object.
+ *
+ * @return A <code>DocumentDeserializerImpl</code> object.
+ */
+ public DocumentDeserializer createDocumentDeserializer(ConvertData cd) {
+
+ return new DocumentDeserializerImpl(cd);
+ }
+
+ /**
+ * Returns an instance of <code>DocumentMergerImpl</code>,
+ * which is an implementation of the <code>DocumentMerger</code>
+ * interface.
+ *
+ * @param doc <code>Document</code> to merge.
+ *
+ * @return A DocumentMergerImpl object.
+ */
+ public DocumentMerger createDocumentMerger(Document doc) {
+
+ ConverterCapabilities cc = converterCap;
+ DocumentMergerImpl merger = new DocumentMergerImpl(doc, cc);
+ return merger;
+ }
+
+ /**
+ * Returns an instance of the DeviceDocument
+ * which is an implementation of the <code>DocumentMerger</code>
+ * interface.
+ *
+ * @param doc <code>Document</code> to merge.
+ *
+ * @return A Device Document object
+ */
+ public Document createDeviceDocument(String name, InputStream is)
+ throws IOException {
+
+ PalmDocument palmDoc = new PalmDocument(is);
+ return palmDoc;
+ }
+}
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WSDecoder.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WSDecoder.java
new file mode 100644
index 000000000000..5ac9bc01c725
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WSDecoder.java
@@ -0,0 +1,352 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.io.UnsupportedEncodingException;
+import org.openoffice.xmerge.util.Debug;
+
+import org.openoffice.xmerge.converter.palm.*;
+import org.openoffice.xmerge.util.Resources;
+
+/**
+ * This class is used by {@link
+ * org.openoffice.xmerge.converter.xml.sxw.wordsmith.DocumentDeserializerImpl
+ * DocumentDeserializerImpl} to decode a WordSmith format. It currently
+ * decodes the text content into a single <code>String</code> object.
+ *
+ * @author Herbie Ong, David Proulx
+ */
+final class WSDecoder implements DOCConstants {
+
+ /** For decoding purposes. */
+ private final static int COUNT_BITS = 3;
+
+ /** Resources object for I18N. */
+ private Resources res = null;
+
+ /**
+ * Default constructor creates a header and
+ * a text buffer for holding all the text in
+ * the DOC db.
+ */
+ WSDecoder() {
+ res = Resources.getInstance();
+ }
+
+ /**
+ * Decode the text records into a single <code>byte</code> array.
+ *
+ * @param Record <code>Record</code> array holding WordSmith
+ * contents.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ byte[] parseRecords(Record[] recs) throws IOException {
+
+ // read the header record
+ HeaderInfo header = readHeader(recs[0].getBytes());
+ dumpHeader(header);
+ byte[][] byteArrays = new byte[recs.length - 1][];
+ for (int i = 0; i < recs.length - 1; i++) byteArrays[i] = null;
+
+ switch (header.version & ~4) { // DJP: "4" indicates OOB data is present.
+ // Add a constant to handle this, might also need code to handle it.
+
+ case COMPRESSED:
+ case 3: // DJP: determined this empirically. Are Herbie's constants wrong?
+ for (int i = 1; i < recs.length; i++) {
+ byteArrays[i-1] = decompress(recs[i].getBytes(),
+ header.textRecordSize);
+ Debug.log(Debug.INFO, "processing " + byteArrays[i-1].length + " bytes");
+ }
+
+ break;
+
+ case UNCOMPRESSED:
+ for (int i = 1; i < recs.length; i++) {
+ byteArrays[i-1] = recs[i].getBytes();
+ Debug.log(Debug.INFO, "processing " + byteArrays[i-1].length + " bytes");
+ }
+
+ break;
+
+ default:
+ throw new IOException(res.getString("UNKNOWN_DOC_VERSION"));
+
+ }
+
+ // Concatenate byteArrays[][] into a single byte array.
+ int length = 0;
+ for (int i = 0; i < recs.length - 1; i++)
+ length += byteArrays[i].length;
+ byte bigArray[] = new byte[length];
+ int offset = 0;
+ for (int i = 0; i < recs.length - 1; i++) {
+ System.arraycopy(byteArrays[i], 0, bigArray, offset,
+ byteArrays[i].length);
+ offset += byteArrays[i].length;
+ }
+ return bigArray;
+ }
+
+
+ /**
+ * Decode the text records into a <code>Wse</code> array.
+ *
+ * @param Record[] <code>Record</code> array holding DOC
+ * contents.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ Wse[] parseDocument(Record[] recs) throws IOException {
+
+ java.util.Vector v = new java.util.Vector(20, 20);
+ WseFontTable fontTable = null;
+ WseColorTable colorTable = null;
+
+ // rawData is the document data to be parsed.
+ byte rawData[] = parseRecords(recs);
+
+ // beginning of document has some header information, including
+ // optional font and color tables.
+ // DJP: maybe should add a new WSelement (docHeader) to hold
+ // header info.
+ // DJP: finish code here to parse header
+ if (rawData[0] != 2) throw new IOException();
+ int nParagraphs = util.intFrom4bytes(rawData, 2);
+ int nAtoms = util.intFrom4bytes(rawData, 6);
+ int nChars = util.intFrom4bytes(rawData, 10);
+ int miscSize = util.intFrom4bytes(rawData, 14);
+ int curIndex = 18;
+
+ while (curIndex < rawData.length) {
+ if (WsePara.isValid(rawData, curIndex)) {
+ v.add(new WsePara(rawData, curIndex));
+ curIndex = WsePara.computeNewIndex(rawData, curIndex);
+ } else if (WseTextRun.isValid(rawData, curIndex)) {
+ v.add(new WseTextRun(rawData, curIndex, fontTable, colorTable));
+ curIndex = WseTextRun.computeNewIndex(rawData, curIndex);
+ } else if (WseFontTable.isValid(rawData, curIndex)) {
+ fontTable = new WseFontTable(rawData, curIndex);
+ v.add(fontTable);
+ curIndex = WseFontTable.computeNewIndex(rawData, curIndex);
+ } else if (WseColorTable.isValid(rawData, curIndex)) {
+ colorTable = new WseColorTable(rawData, curIndex);
+ v.add(colorTable);
+ curIndex = WseColorTable.computeNewIndex(rawData, curIndex);
+ } else {
+ Debug.log(Debug.ERROR, "Unknown code " + rawData[curIndex]);
+ throw new IOException();
+ }
+ }
+
+ return (Wse[])v.toArray(new Wse[2]);
+ }
+
+
+ /**
+ * <p>Decompress the <code>byte</code> array.</p>
+ *
+ * <p>The resulting uncompressed <code>byte</code> array
+ * should be within <code>textRecordSize</code> length,
+ * definitely within twice the size it claims, else treat
+ * it as a problem with the encoding of that PDB and
+ * throw <code>IOException</code>.</p>
+ *
+ * @param bytes Compressed <code>byte</code> array
+ * @param textRecordSize Size of uncompressed <code>byte</code>
+ * array
+ *
+ * @throws IOException If <code>textRecordSize</codeL &lt;
+ * <code>cBytes.length</code>.
+ */
+ private byte[] decompress(byte[] cBytes, int textRecordSize)
+ throws IOException {
+
+ // create byte array for storing uncompressed bytes
+ // it should be within textRecordSize range, definitely
+ // within twice of textRecordSize! if not, then
+ // an ArrayIndexOutOfBoundsException will get thrown,
+ // and it should be converted into an IOException, and
+ // treat it as a conversion error.
+ byte[] uBytes = new byte[textRecordSize*2];
+
+ int up = 0;
+ int cp = 0;
+
+ try {
+
+ while (cp < cBytes.length) {
+
+ int c = cBytes[cp++] & 0xff;
+
+ // codes 1...8 mean copy that many bytes
+ if (c > 0 && c < 9) {
+
+ while (c-- > 0)
+ uBytes[up++] = cBytes[cp++];
+ }
+
+ // codes 0, 9...0x7F represent themselves
+ else if (c < 0x80) {
+ uBytes[up++] = (byte) c;
+ }
+
+ // codes 0xC0...0xFF represent "space + ascii char"
+ else if (c >= 0xC0) {
+ uBytes[up++] = (byte) ' ';
+ uBytes[up++] = (byte) (c ^ 0x80);
+ }
+
+ // codes 0x80...0xBf represent sequences
+ else {
+ c <<= 8;
+ c += cBytes[cp++] & 0xff;
+ int m = (c & 0x3fff) >> COUNT_BITS;
+ int n = c & ((1 << COUNT_BITS) - 1);
+ n += COUNT_BITS;
+ while (n-- > 0) {
+ uBytes[up] = uBytes[up - m];
+ up++;
+ }
+ }
+ }
+
+ } catch (ArrayIndexOutOfBoundsException e) {
+
+ throw new IOException(
+ res.getString("DOC_TEXT_RECORD_SIZE_EXCEEDED"));
+ }
+
+ // note that ubytes may be larger that the amount of
+ // uncompressed bytes, so trim it to another byte array
+ // with the exact size.
+ byte[] textBytes = new byte[up];
+ System.arraycopy(uBytes, 0, textBytes, 0, up);
+
+ return textBytes;
+ }
+
+
+ /**
+ * Read the header <code>byte</code> array.
+ *
+ * @param bytes <code>byte</code> array containing header
+ * record data.
+ *
+ * @return <code>HeaderInfo</code> object.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ private HeaderInfo readHeader(byte[] bytes) throws IOException {
+
+ HeaderInfo header = new HeaderInfo();
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+ DataInputStream dis = new DataInputStream(bis);
+
+ // Normally the first 2 bytes comprised of the version
+ // which should either be COMPRESSED or UNCOMPRESSED
+ // SmartDoc/Quickword would add a 0x01 to the first
+ // byte, thus their version would be 0x0101 for UNCOMPRESSED
+ // instead of 0x0001 and 0x0102 for UNCOMPRESSED instead of
+ // 0x0002.
+
+ dis.readByte();
+ header.version = dis.readByte();
+
+ // read extra 2 unused bytes
+ dis.readShort();
+
+ // Read the text length, this should be unsigned 4 bytes.
+ // We could store the read value into a long, but then
+ // our current buffer limit is the max positive of an int.
+ // That is a large enough limit, thus we shall stay with
+ // storing the value in an int. If it exceeds, then
+ // an IOException should be thrown.
+ header.textLen = dis.readInt();
+ if (header.textLen < 0) {
+ throw new IOException(res.getString("DOC_TEXT_LENGTH_EXCEEDED"));
+ }
+
+ // read the number of records - unsigned 2 bytes
+ header.textRecordCount = ((int) dis.readShort()) & 0x0000ffff;
+
+ // read the record size - unsigned 2 bytes
+ header.textRecordSize = ((int) dis.readShort()) & 0x0000ffff;
+
+ // read extra 4 unused bytes
+ dis.readInt();
+
+ return header;
+ }
+
+
+ /**
+ * Prints out header info into log.
+ * Used for debugging purposes only.
+ *
+ * @param header <code>HeaderInfo</code> structure.
+ */
+ private void dumpHeader(HeaderInfo header) {
+ /*
+ log("<DOC_INFO ");
+ log("version=\"" + header.version + "\" ");
+ log("text-length=\"" + header.textLen + "\" ");
+ log("number-of-records=\"" + header.textRecordCount + "\" ");
+ log("record-size=\"" + header.textRecordSize + "\" />\n");
+ */
+ }
+
+
+ /**
+ * Inner class to store DOC header information.
+ */
+ private class HeaderInfo {
+
+ /** length of text section */
+ int textLen = 0;
+
+ /** number of text records */
+ int textRecordCount = 0;
+
+ /**
+ * size of a text record. This is normally the same as
+ * TEXT_RECORD_SIZE, but some applications may modify this.
+ */
+ int textRecordSize = 0;
+
+ /** compression type */
+ int version = 0;
+ }
+}
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WSEncoder.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WSEncoder.java
new file mode 100644
index 000000000000..15cf4ed36544
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WSEncoder.java
@@ -0,0 +1,212 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.*;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Vector;
+
+import org.openoffice.xmerge.converter.palm.*;
+
+/**
+ * This class is used by {@link
+ * org.openoffice.xmerge.converter.xml.sxw.wordsmith.DocumentDeserializerImpl
+ * DocumentDeserializerImpl} to encode the WordSmith format.
+ *
+ * @author David Proulx
+ */
+
+// DJP: replace 4096 w/ a defined constant
+
+final class WSEncoder {
+
+ /* DJP: These should probably go somewhere else! */
+ /** Constant for uncompressed version. */
+ public static final short UNCOMPRESSED = 1;
+
+ /** Constant for compressed version. */
+ public static final short COMPRESSED = 2;
+
+ /** Constant used for spare fields. */
+ public static final int SPARE = 0;
+
+ /* WordSmith Header information. */
+ private short version;
+ private int textLen;
+ private short maxRecSize;
+ private int textRecCount = 0;
+
+
+ /* WordSmith document elements. */
+ WseHeader header = null;
+ WseFontTable ft = null;
+ WseColorTable ct = null;
+ private Vector elements; // paragraphs & text runs
+
+ /* Totals for the WordSmith document. */
+ int nrParagraphs = 0;
+ int nrAtoms = 0;
+ int nrChars = 0;
+
+
+ /**
+ * Default constructor creates a header and
+ * a text buffer for holding all the text in
+ * the WordSmith database.
+ */
+ WSEncoder() {
+ version = 1;
+ textLen = 0;
+ maxRecSize = 4096;
+ elements = new Vector();
+ }
+
+
+ /**
+ * This method adds a new element to the WordSmith document.
+ *
+ * @param elem WordSmith document element to add
+ */
+ void addElement(Wse elem) {
+ if (elem.getClass() == WseHeader.class)
+ header = (WseHeader)elem;
+ else if (elem.getClass() == WseFontTable.class)
+ ft = (WseFontTable)elem;
+ else if (elem.getClass() == WseColorTable.class)
+ ct = (WseColorTable)elem;
+ else
+ elements.addElement(elem);
+ }
+
+
+ /**
+ * This method encodes the information given to
+ * an array of palm Records in the WordSmith database format.
+ *
+ * @return <code>Record</code> array holding WordSmith contents.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ Record[] getRecords() throws IOException {
+
+ Vector allRecs = new Vector();
+ int nElements = elements.size();
+
+ // Count up the number of paragraphs, atoms, and characters.
+ int currElement = 0;
+ while (currElement < nElements) {
+ Wse e = (Wse)elements.elementAt(currElement++);
+ if (e.getClass() == WsePara.class)
+ nrParagraphs++;
+ if (e.getClass() == WseTextRun.class) {
+ nrAtoms++;
+ nrChars += ((WseTextRun)e).getText().length();
+ }
+ }
+
+ byte[] currRec = new byte[4096];
+ int currRecLen = 0;
+
+ // This code assumes that the WordSmith header, font table,
+ // and color table total less than 4096 bytes.
+ header = new WseHeader(nrParagraphs, nrAtoms, nrChars, ft, ct);
+ System.arraycopy(header.getBytes(), 0,
+ currRec, currRecLen, header.getByteCount());
+ currRecLen += header.getByteCount();
+
+ if (ft != null) {
+ System.arraycopy(ft.getBytes(), 0, currRec, currRecLen,
+ ft.getByteCount());
+ currRecLen += ft.getByteCount();
+ }
+ if (ct != null) {
+ System.arraycopy(ct.getBytes(), 0, currRec, currRecLen,
+ ct.getByteCount());
+ currRecLen += ct.getByteCount();
+ }
+
+ currElement = 0;
+ while (currElement < nElements) {
+ Wse e = (Wse)elements.elementAt(currElement++);
+ int length = e.getByteCount();
+ if ((length + currRecLen) <= 4096) {
+ System.arraycopy(e.getBytes(), 0, currRec, currRecLen, length);
+ currRecLen += length;
+ } else {
+ // Copy in enough to get to full size, then create a
+ // new Record and add it to the Vector.
+ int firstPartLen = 4096 - currRecLen;
+ System.arraycopy(e.getBytes(), 0, currRec, currRecLen,
+ firstPartLen);
+ Record r = new Record(currRec);
+ allRecs.addElement(r);
+
+ // Put the remainder at the beginning of the next record
+ currRecLen = 0;
+ System.arraycopy(e.getBytes(), firstPartLen, currRec,
+ currRecLen, length - firstPartLen);
+ currRecLen += length - firstPartLen;
+ }
+ }
+
+ // Processed all the elements. Write out any remaining partial record.
+ if (currRecLen > 0) {
+ byte[] partial = new byte[currRecLen];
+ System.arraycopy(currRec, 0, partial, 0, currRecLen);
+ Record rr = new Record(partial);
+ allRecs.addElement(rr);
+ }
+
+
+ // Record 0 is the WordSmith header. Do it last since it
+ // contains totals for the entire document. It goes
+ // before everything else.
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(bos);
+ dos.writeShort(version);
+ dos.writeShort(0);
+ dos.writeInt(textLen);
+ dos.writeShort(allRecs.size());
+ dos.writeShort(maxRecSize);
+ dos.writeInt(0);
+ allRecs.insertElementAt(new Record(bos.toByteArray()), 0);
+
+ // Convert Vector of Records to an array and return it.
+ int nRecs = allRecs.size();
+ Record recs[] = new Record[nRecs];
+ for (int i = 0; i < nRecs; i++)
+ recs[i] = (Record)allRecs.elementAt(i);
+ return recs;
+ }
+
+}
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/Wse.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/Wse.java
new file mode 100644
index 000000000000..4f1a7141f348
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/Wse.java
@@ -0,0 +1,100 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+import java.io.IOException;
+
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Node;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Element;
+
+import org.openoffice.xmerge.Document;
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.converter.xml.OfficeDocument;
+import org.openoffice.xmerge.converter.xml.sxw.SxwDocument;
+import org.openoffice.xmerge.converter.xml.*;
+
+
+/**
+ * This is the superclass for all elements in a WordSmith document.
+ * Elements can be paragraphs, text runs, font tables, or color tables.
+ *
+ * @author David Proulx
+ */
+abstract class Wse {
+
+ /**
+ * Return true if <code>dataArray[startIndex]</code> is the start
+ * of a valid element of this type.
+ *
+ * @param dataArray <code>byte</code> array.
+ * @param startIndex The start index.
+ *
+ * @return true if <code>dataArray[startIndex]</code> is the
+ * start of a valid element of this type, false otherwise.
+ */
+ static boolean isValid(byte dataArray[], int startIndex) {
+ return false;
+ }
+
+
+ /**
+ * Compute and return the index of the first <code>byte</code>
+ * following this element. It is assumed that the element
+ * starting at <code>dataArray[startIndex]</code> is valid.
+ *
+ * @param dataArray <code>byte</code> array.
+ * @param startIndex The start index.
+ *
+ * @return The index of the first <code>byte</code> following
+ * this element.
+ */
+ static int computeNewIndex(byte dataArray[], int startIndex) {
+ return 0;
+ }
+
+
+ /**
+ * Return the total number of bytes needed to represent this
+ * object.
+ *
+ * @return The total number of bytes needed to represent this
+ * object.
+ */
+ abstract int getByteCount();
+
+
+ /**
+ * Return an <code>byte</code> array representing this element.
+ *
+ * @return An <code>bytes</code> array representing this element.
+ */
+ abstract byte[] getBytes();
+}
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseColorTable.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseColorTable.java
new file mode 100644
index 000000000000..bbb22952a24a
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseColorTable.java
@@ -0,0 +1,247 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+import java.io.IOException;
+import java.awt.Color;
+
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Node;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Element;
+
+import org.openoffice.xmerge.Document;
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.converter.xml.OfficeDocument;
+import org.openoffice.xmerge.converter.xml.sxw.SxwDocument;
+import org.openoffice.xmerge.converter.xml.*;
+
+/**
+ * This class represents a color table in a WordSmith document.
+ *
+ * @author David Proulx
+ */
+class WseColorTable extends Wse {
+
+ private Color fgColors[];
+ private Color bgColors[];
+
+ /**
+ * Constructor to use when going from DOM to WordSmith
+ */
+ public WseColorTable() {
+ fgColors = new Color[16];
+ bgColors = new Color[16];
+
+ // Always need these two!
+ fgColors[0] = Color.black;
+ bgColors[0] = Color.white;
+
+ }
+
+ /**
+ * Constructor to use when going from WordSmith to DOM.
+ *
+ * @param dataArray <code>byte</code> array.
+ * @param i The index.
+ */
+ public WseColorTable(byte dataArray[], int i) {
+ fgColors = new Color[16];
+ bgColors = new Color[16];
+
+ i += 2; // Skip leading "64" and table length field.
+ for (int k = 0; k < 16; k++) {
+ fgColors[k] = new Color(((int)dataArray[i+1]) & 0xFF,
+ ((int)dataArray[i+2]) & 0xFF,
+ ((int)dataArray[i+3]) & 0xFF);
+ i += 4;
+ }
+ for (int k = 0; k < 16; k++) {
+ bgColors[k] = new Color(((int)dataArray[i+1]) & 0xFF,
+ ((int)dataArray[i+2]) & 0xFF,
+ ((int)dataArray[i+3]) & 0xFF);
+ i += 4;
+ }
+
+ }
+
+
+ /**
+ * Compute the index of the first <code>byte</code> following the
+ * paragraph descriptor, assuming that
+ * <code>dataArray[startIndex]</code> is the beginning of a valid
+ * paragraph descriptor.
+ *
+ * @param dataArray <code>byte</code array.
+ * @param startIndex The start index.
+ *
+ * @return The index of the first <code>byte</code> following the
+ * paragraph description.
+ */
+ static int computeNewIndex(byte dataArray[], int startIndex) {
+ int tableLen = dataArray[startIndex + 1];
+ tableLen &= 0xFF; // eliminate problems with sign-extension
+ return startIndex + tableLen + 2;
+ }
+
+
+ /**
+ * Return true if <code>dataArray[startIndex]</code> is the start
+ * of a valid paragraph descriptor.
+ *
+ * @param dataArray <code>byte</code> array.
+ * @param startIndex Start index.
+ *
+ * @return true if <code>dataArray[startIndex]</code> is the start
+ * of a valid paragraph descriptor, false otherwise.
+ */
+ static boolean isValid(byte dataArray[], int startIndex) {
+ try {
+ if (dataArray[startIndex] != 64)
+ return false;
+ int len = dataArray[startIndex + 1];
+ len &= 0xFF; // eliminate problems with sign-extension
+ int temp = dataArray[startIndex + (int)len + 2]; // probe end of table
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * Return the number of bytes needed to represent this color table.
+ *
+ * @return The byte count.
+ */
+ int getByteCount() {
+ return (32 * 4) + 1 + 1;
+ }
+
+
+ /**
+ * Return a <code>byte</code> array representing this color table.
+ *
+ * @return <code>bytes</code> array representing this color table.
+ */
+ byte[] getBytes() {
+ byte[] b = new byte[(32 * 4) + 1 + 1];
+ b[0] = 0x40;
+ b[1] = (byte)128;
+ int i = 2;
+ // int indVal = 0xd8;
+ int indVal = 0;
+
+ for (int j = 0; j < 16; j++) {
+ b[i++] = (byte)indVal++;
+ if (fgColors[j] != null) {
+ b[i++] = (byte)fgColors[j].getRed();
+ b[i++] = (byte)fgColors[j].getGreen();
+ b[i++] = (byte)fgColors[j].getBlue();
+ } else {
+ b[i++] = (byte)0;
+ b[i++] = (byte)0;
+ b[i++] = (byte)0;
+ }
+ }
+
+ for (int j = 0; j < 16; j++) {
+ b[i++] = (byte)indVal++;
+ if (bgColors[j] != null) {
+ b[i++] = (byte)bgColors[j].getRed();
+ b[i++] = (byte)bgColors[j].getGreen();
+ b[i++] = (byte)bgColors[j].getBlue();
+ } else {
+ b[i++] = (byte)0xFF;
+ b[i++] = (byte)0xFF;
+ b[i++] = (byte)0xFF;
+ }
+ }
+
+ return b;
+ }
+
+
+ /**
+ * Return the index of the specified foreground or background
+ * <code>Color</code>. (If the color is not already in the table,
+ * it will be added.)
+ *
+ * Note that the implementation of this may include a "margin of
+ * error" to prevent the color table from being filled up too
+ * quickly.
+ *
+ * @param c The <code>Color</code>.
+ * @param foreground true if foreground color, false if background
+ * color
+ *
+ * @return The index of the specified foreground or background
+ * <code>Color</code>.
+ *
+ * DJP: how to handle table overflow?
+ */
+ int findColor(Color c, boolean foreground) {
+
+ Color colorArray[] = foreground ? fgColors : bgColors;
+
+ for (int i = 0; i < 16; i++) {
+ if (colorArray[i] != null) {
+ if (colorArray[i].equals(c))
+ return i;
+ }
+ else
+ break; // hit a null entry - no more colors in table!
+ }
+
+ // Color was not found in the table. Add it.
+ for (int i = 0; i < 16; i++) {
+ if (colorArray[i] == null) {
+ colorArray[i] = c;
+ return i;
+ }
+ }
+ return 0; // Default - we should never get here though.
+ }
+
+
+ /**
+ * Given an index, return the <code>Color</code> from the table.
+ *
+ * @param index The index
+ * @param foreground true if foreground color, false if background
+ * color
+ *
+ * @return The <code>Color</code> at the specified index.
+ */
+ Color getColor(int index, boolean foreground) {
+
+ Color colorArray[] = foreground ? fgColors : bgColors;
+ return colorArray[index];
+ }
+}
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseFontTable.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseFontTable.java
new file mode 100644
index 000000000000..d7bc6edd8bc2
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseFontTable.java
@@ -0,0 +1,218 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+import java.io.IOException;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Element;
+
+import org.openoffice.xmerge.Document;
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.converter.xml.OfficeDocument;
+import org.openoffice.xmerge.converter.xml.sxw.SxwDocument;
+import org.openoffice.xmerge.converter.xml.*;
+
+/**
+ * <p>This class represents a font table in a WordSmith document.
+ * A font table is represented as follows:</p>
+ *
+ * <p><blockquote>
+ * binary "3"<br>
+ * two-byte length of the table of strings which follows<br>
+ * string table (null-terminated strings) representing font names
+ * </blockquote></p>
+ *
+ * @author David Proulx
+ */
+class WseFontTable extends Wse {
+
+ java.util.Vector fontNames = new java.util.Vector(10);
+
+
+ /**
+ * Constructor for use when going from DOM to WordSmith.
+ */
+ public WseFontTable() {
+ }
+
+
+ /**
+ * Constructor for use when going from WordSmith to DOM.
+ *
+ * @param dataArray <code>byte</code> array.
+ * @param i The index.
+ */
+ public WseFontTable(byte dataArray[], int i) {
+ i++;
+ int tableLen = ((dataArray[i] << 8) | (dataArray[i+1] & 0xFF));
+ i += 2;
+ while (tableLen > 0) {
+ int j = 0;
+ while (dataArray[i + j] != 0) j++;
+ fontNames.add(new String(dataArray, i, j));
+ tableLen -= (j + 1);
+ i += (j + 1);
+ }
+ }
+
+
+ /**
+ * Add a new font to the table.
+ *
+ * @param newFontName The new font name.
+ */
+ public void add(String newFontName) {
+ if (newFontName != null)
+ fontNames.add(newFontName);
+ }
+
+
+ /**
+ * Return a font name from the table, or null if invalid index.
+ *
+ * @param index The font name index.
+ *
+ * @return The font name.
+ */
+ public String getFontName(int index) {
+ try {
+ return (String)fontNames.elementAt(index);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Return the index of a font name in the table, or -1 if not found.
+ *
+ * @param fontName The font name.
+ *
+ * @return The index of the font name, or -1 if not found.
+ */
+ public int getFontIndex(String fontName) {
+ int len = fontNames.size();
+ for (int i = 0; i < len; i++) {
+ String name = (String) fontNames.elementAt(i);
+ if (name.equals(fontName))
+ return i;
+ }
+ return -1;
+ }
+
+
+ /**
+ * Compute the index of the first <code>byte</code> following the
+ * paragraph descriptor, assuming that
+ * <code>dataArray[startIndex]</code> is the beginning of a valid
+ * paragraph descriptor.
+ *
+ * @param dataArray <code>byte</code> array.
+ * @param startIndex The start index.
+ *
+ * @return The index of the first <code>byte</code> following the
+ * paragraph description.
+ */
+ static int computeNewIndex(byte dataArray[], int startIndex) {
+ startIndex++; // Skip the leading "3"
+ int tableLen = ((dataArray[startIndex] << 8) | (dataArray[startIndex+1] & 0xFF));
+ tableLen &= 0xFFFF; // eliminate problems with sign-extension
+ return startIndex + tableLen + 2;
+ }
+
+
+ /**
+ * Return true if <code>dataArray[startIndex]</code> is the start of a
+ * valid paragraph descriptor.
+ *
+ * @param dataArray <code>byte</code> string.
+ * @param startIndex Start index.
+ *
+ * @return true if <code>dataArray[startIndex]</code> is the start
+ * of a valid paragraph descriptor, false otherwise.
+ */
+ static boolean isValid(byte dataArray[], int startIndex) {
+ try {
+ if (dataArray[startIndex] != 3)
+ return false;
+ int len = ((dataArray[startIndex+1] << 8)
+ | (dataArray[startIndex+2] & 0xFF));
+ len &= 0xFFFF; // eliminate problems with sign-extension
+
+ if (dataArray[startIndex + len + 2] != 0)
+ return false;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * Return the number of bytes needed to represent this font table.
+ *
+ * @return The number of bytes needed to represent this font table.
+ */
+ int getByteCount() {
+
+ int length = 3; // leading "3" plus 2 bytes for length.
+ int nFonts = fontNames.size();
+ for (int i = 0; i < nFonts; i++) {
+ String name = (String)fontNames.elementAt(i);
+ length += name.length() + 1; // extra byte is for trailing "0"
+ }
+ return length;
+ }
+
+ /**
+ * Return a <code>byte</code> array representing this font table.
+ *
+ * @return An <code>byte</code> array representing this font table.
+ */
+ byte[] getBytes() {
+
+ int length = getByteCount();
+ int nFonts = fontNames.size();
+ byte b[] = new byte[length];
+ b[0] = 3;
+ length -= 3;
+ b[1] = (byte)(length >> 8);
+ b[2] = (byte)(length & 0xFF);
+ int indx = 3;
+ for (int i = 0; i < nFonts; i++) {
+ String name = (String)fontNames.elementAt(i);
+ byte bname[] = name.getBytes();
+ System.arraycopy(bname, 0, b, indx, bname.length);
+ indx += bname.length;
+ b[indx++] = 0;
+ }
+ return b;
+ }
+}
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseHeader.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseHeader.java
new file mode 100644
index 000000000000..c80e03eca043
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseHeader.java
@@ -0,0 +1,145 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+import java.io.IOException;
+import java.io.DataOutputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * This class represents a WordSmith document header.
+ *
+ * @author David Proulx
+ */
+class WseHeader extends Wse {
+
+ private int nParagraphs = 0;
+ private int nAtoms = 0;
+ private int nChars = 0;
+ private int miscSize = 0;
+
+ /**
+ * Constructor for use when going from DOM to WordSmith.
+ *
+ * @param nPara The number of paragraphs.
+ * @param nAtoms The number of atoms.
+ * @param nChars The number of characters.
+ * @param ft The font table.
+ * @param ct The color table.
+ */
+ public WseHeader(int nPara, int nAtoms, int nChars, WseFontTable ft,
+ WseColorTable ct) {
+ nParagraphs = nPara;
+ this.nAtoms = nAtoms;
+ this.nChars = nChars;
+ if (ft != null) miscSize += ft.getByteCount();
+ if (ct != null) miscSize += ct.getByteCount();
+ }
+
+
+ /**
+ * Constructor for use when going from WordSmith to DOM.
+ *
+ * @param dataArray <code>byte</code> array.
+ * @param i Index.
+ */
+ public WseHeader(byte dataArray[], int i) {
+ // DJP: write this!
+ }
+
+ /**
+ * Return true if <code>dataArray[startIndex]</code> is the start
+ * of a document header.
+ *
+ * @param dataArray <code>byte</code> array.
+ * @param startIndex The index.
+ *
+ * @return true if <code>dataArray[startIndex]</code> is the start
+ * of a document header, false otherwise.
+ */
+ static boolean isValid(byte dataArray[], int startIndex) {
+ return ((dataArray[startIndex] == 2)
+ && (dataArray[startIndex + 1] == 4));
+ }
+
+
+ /**
+ * Compute and return the index of the first <code>byte</code>
+ * following this element. It is assumed that the element
+ * starting at <code>dataArray[startIndex]</code> is valid.
+ *
+ * @param dataArray <code>byte</code> array.
+ * @param startIndex The start index.
+ *
+ * @return The first <code>byte</code> following this element.
+ */
+ static int computeNewIndex(byte dataArray[], int startIndex) {
+ return startIndex + 18;
+ }
+
+
+ /**
+ * Return the total number of bytes needed to represent this.
+ *
+ * @return The total number of bytes needed to represent this.
+ */
+ int getByteCount() {
+ return 18;
+ }
+
+
+ /**
+ * Return a <code>byte</code> array representing this element.
+ *
+ * @return A <code>byte</code> array representing this element.
+ */
+ byte[] getBytes() {
+ DataOutputStream os; // Used for storing the data
+ ByteArrayOutputStream bs = null; // Used for storing the data
+
+ try {
+ bs = new ByteArrayOutputStream();
+ os = new DataOutputStream(bs);
+ os.write(2); // binary doc indicator
+ os.write(4); // binary header indicator
+
+ os.writeInt(nParagraphs);
+ os.writeInt(nAtoms);
+ os.writeInt(nChars);
+ os.writeInt(miscSize);
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ if (bs != null) {
+ return bs.toByteArray();
+ } else return null;
+ }
+}
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WsePara.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WsePara.java
new file mode 100644
index 000000000000..bbd105a2097f
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WsePara.java
@@ -0,0 +1,282 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+import java.io.IOException;
+
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Node;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Element;
+
+import org.openoffice.xmerge.Document;
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.converter.xml.OfficeDocument;
+import org.openoffice.xmerge.converter.xml.sxw.SxwDocument;
+import org.openoffice.xmerge.converter.xml.*;
+
+
+/**
+ * This class represents a paragraph in a WordSmith document.
+ * (A paragraph is "5" followed by 12 bytes of attributes.)
+ *
+ * @author David Proulx
+ */
+class WsePara extends Wse {
+
+ private byte spaceBefore = 0;
+ private byte spaceAfter = 0;
+ private byte leftIndent = 0;
+ private byte firstIndent = 0;
+ private byte rightIndent = 0;
+ private byte misc = 0;
+ private byte style = 0;
+ private byte lineSpace = 0;
+ private byte outline = 0;
+ private byte reserved = 0;
+
+ private static final byte LS_EXACTLY = (byte)0xC0;
+ private static final byte LS_ATLEAST = (byte)0x80;
+ private static final byte LS_MULTIPLE = (byte)0x40;
+ private static final byte LS_VALUEMASK = (byte)0x3F;
+
+ private static final byte ALIGN_RIGHT = (byte)2;
+ private static final byte ALIGN_LEFT = (byte)0;
+ private static final byte ALIGN_CENTER = (byte)1;
+ private static final byte ALIGN_JUST = (byte)3;
+
+ private StyleCatalog sc = null;
+
+
+ /**
+ * Constructor for use when going from DOM to WordSmith.
+ *
+ * @param p The paragraph style.
+ * @param sc The <code>StyleCatalog</code>.
+ */
+ public WsePara(ParaStyle p, StyleCatalog sc) {
+ this.sc = sc;
+ ParaStyle ps = (ParaStyle)p.getResolved();
+
+ if (ps.isAttributeSet(ParaStyle.MARGIN_LEFT)) {
+ double temp = ps.getAttribute(ParaStyle.MARGIN_LEFT) * 1.6 / 100;
+ leftIndent = (byte) temp;
+ if ((temp - leftIndent) > 0.5) leftIndent++;
+ }
+
+ if (ps.isAttributeSet(ParaStyle.MARGIN_RIGHT)) {
+ double temp = ps.getAttribute(ParaStyle.MARGIN_RIGHT) * 1.6 / 100;
+ rightIndent = (byte) temp;
+ if ((temp - rightIndent) > 0.5) rightIndent++;
+ }
+
+ if (ps.isAttributeSet(ParaStyle.TEXT_INDENT)) {
+ double temp = ps.getAttribute(ParaStyle.TEXT_INDENT) * 1.6 / 100;
+ firstIndent = (byte) temp;
+ if ((temp - firstIndent) > 0.5) firstIndent++;
+ }
+
+ if (ps.isAttributeSet(ParaStyle.MARGIN_TOP)) {
+ double temp = ps.getAttribute(ParaStyle.MARGIN_TOP) * 1.6 / 100;
+ spaceBefore = (byte) temp;
+ if ((temp - spaceBefore) > 0.5) spaceBefore++;
+ }
+
+ if (ps.isAttributeSet(ParaStyle.MARGIN_BOTTOM)) {
+ double temp = ps.getAttribute(ParaStyle.MARGIN_BOTTOM) * 1.6 / 100;
+ spaceAfter = (byte) temp;
+ if ((temp - spaceAfter) > 0.5) spaceAfter++;
+ }
+
+ if (ps.isAttributeSet(ParaStyle.LINE_HEIGHT)) {
+ int lh = ps.getAttribute(ParaStyle.LINE_HEIGHT);
+ if ((lh & ~ParaStyle.LH_VALUEMASK) == 0)
+ lineSpace = (byte)(LS_MULTIPLE | (lh * 2));
+ else if ((lh & ParaStyle.LH_PCT) != 0) {
+ lh = (lh & ParaStyle.LH_VALUEMASK) / 100;
+ lineSpace = (byte)(LS_MULTIPLE | (lh * 2));
+ }
+ // DJP: handle other cases....
+ }
+
+ if (ps.isAttributeSet(ParaStyle.TEXT_ALIGN)) {
+
+ int val = ps.getAttribute(ParaStyle.TEXT_ALIGN);
+
+ switch (val) {
+ case ParaStyle.ALIGN_RIGHT:
+ misc = ALIGN_RIGHT;
+ break;
+ case ParaStyle.ALIGN_LEFT:
+ misc = ALIGN_LEFT;
+ break;
+ case ParaStyle.ALIGN_CENTER:
+ misc = ALIGN_CENTER;
+ break;
+ case ParaStyle.ALIGN_JUST:
+ misc = ALIGN_JUST;
+ break;
+ }
+ }
+
+ }
+
+
+ /**
+ * Constructor for use when going from WordSmith to DOM.
+ * Assumes <code>dataArray[startIndex]</code> is the first
+ * <code>byte</code> of a valid WordSmith paragraph descriptor.
+ *
+ * @param dataArray <code>byte</code> array.
+ * @param startIndex The start index.
+ */
+ public WsePara(byte dataArray[], int startIndex) {
+ spaceBefore = dataArray[startIndex + 1];
+ spaceAfter = dataArray[startIndex + 2];
+ leftIndent = dataArray[startIndex + 3];
+ firstIndent = dataArray[startIndex + 4];
+ rightIndent = dataArray[startIndex + 5];
+ misc = dataArray[startIndex + 6];
+ style = dataArray[startIndex + 7];
+ lineSpace = dataArray[startIndex + 8];
+ outline = dataArray[startIndex + 9];
+ }
+
+
+ /**
+ * Compute the index of the first <code>byte</code> following the
+ * paragraph descriptor, assuming that
+ * <code>dataArray[startIndex]</code> is the beginning of a valid
+ * paragraph descriptor.
+ *
+ * @param dataArray <code>byte</code> array.
+ * @param startIndex The start index.
+ *
+ * @return The index of the first <code>byte</code> following the
+ * paragraph description.
+ */
+ static int computeNewIndex(byte dataArray[], int startIndex) {
+ return startIndex + 13;
+ }
+
+
+ /**
+ * Return true if <code>dataArray[startIndex]</code> is the start
+ * of a valid paragraph descriptor.
+ *
+ * @param dataArray <code>byte</code> array.
+ * @param startIndex The start index.
+ *
+ * @return true if <code>dataArray[startIndex]</code> is the start
+ * of a valid paragraph descriptor, false otherwise.
+ */
+ static boolean isValid(byte dataArray[], int startIndex) {
+ return (dataArray[startIndex] == 5);
+ }
+
+ /**
+ * Return the number of bytes needed to represent this paragraph.
+ *
+ * @return The number of bytes needed to represent this paragraph.
+ */
+ int getByteCount() {
+ return 13;
+ }
+
+ /**
+ * Return an <code>byte</code> array representing this paragraph.
+ *
+ * @return An <code>byte</code> array representing this paragraph.
+ */
+ byte[] getBytes() {
+ byte b[] = new byte[13];
+
+ b[0] = 5;
+ b[1] = spaceBefore;
+ b[2] = spaceAfter;
+ b[3] = leftIndent;
+ b[4] = firstIndent;
+ b[5] = rightIndent;
+ b[6] = misc;
+ b[7] = style;
+ b[8] = lineSpace;
+ b[9] = outline;
+ b[10] = reserved;
+ b[11] = 0;
+ b[12] = 0;
+
+ return b;
+ }
+
+ /**
+ * Return a <code>ParaStyle</code> that reflects the formatting of
+ * this run.
+ *
+ * @return A <code>ParaStyle</code> that reflects the formatting
+ * of this run.
+ */
+ ParaStyle makeStyle() {
+ /* Csaba: Commented out the LINE_HEIGHT syle, because there was no
+ incoming data for that style. It was resulting a zero line
+ height in the xml document, ie. the doc looked empty.
+ */
+ int attrs[] = { ParaStyle.MARGIN_LEFT, ParaStyle.MARGIN_RIGHT,
+ ParaStyle.TEXT_INDENT, //ParaStyle.LINE_HEIGHT,
+ ParaStyle.MARGIN_TOP, ParaStyle.MARGIN_BOTTOM,
+ ParaStyle.TEXT_ALIGN };
+ String values[] = new String[attrs.length];
+ double temp;
+
+ temp = leftIndent / 1.6;
+ values[0] = (new Double(temp)).toString() + "mm";
+
+ temp = rightIndent / 1.6;
+ values[1] = (new Double(temp)).toString() + "mm";
+
+ temp = firstIndent / 1.6;
+ values[2] = (new Double(temp)).toString() + "mm";
+
+ temp = spaceBefore / 1.6;
+ values[3] = (new Double(temp)).toString() + "mm";
+
+ temp = spaceAfter / 1.6;
+ values[4] = (new Double(temp)).toString() + "mm";
+
+ switch (misc) {
+ case ALIGN_RIGHT: values[5] = "right"; break;
+ case ALIGN_LEFT: values[5] = "left"; break;
+ case ALIGN_CENTER:values[5] = "center"; break;
+ case ALIGN_JUST: values[5] = "justified"; break;
+ }
+ ParaStyle x = new ParaStyle(null, "paragraph", null, attrs,
+ values, sc);
+
+ return x;
+ }
+}
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseTextRun.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseTextRun.java
new file mode 100644
index 000000000000..7a4bb325e051
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WseTextRun.java
@@ -0,0 +1,324 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+import org.openoffice.xmerge.converter.xml.TextStyle;
+import org.openoffice.xmerge.converter.xml.StyleCatalog;
+import java.awt.Color;
+
+/**
+ * <p>This class represents a text run (aka text atom) in a WordSmith
+ * document.</p>
+ *
+ * <p>WordSmith represents a text run as follows:</p>
+ *
+ * <p><ul><li>
+ * 1 byte Value of "1", indicating beginning of a text atom
+ * </li><li>
+ * 2 bytes Length of text (does not include attributes, this length field,
+ * etc)
+ * </li><li>
+ * 1 byte Font index - Index in the font table of font to be used
+ * </li><li>
+ * 1 byte Font size (DJP: get details of representation)
+ * </li><li>
+ * 1 byte Color index - Index in the color table of font color to be used
+ * </li><li>
+ * 1 byte Modifiers - bit flags for bold, italic, etc
+ * </li><li>
+ * n bytes Text - the actual text
+ * </li></ul></p>
+ *
+ * @author David Proulx
+ */
+class WseTextRun extends Wse {
+
+ /** Font specifier. This is an index into the font table. */
+ private byte fontIndex = 0;
+ private String fontName = null;
+
+ /** Size of the font. */
+ private byte fontSize = 0;
+
+ /**
+ * Color of the font. This is an index into the color table.
+ * High nibble is background color index, low nibble is font color
+ * index.
+ */
+ private byte colorIndex = 0;
+
+ /**
+ * Reference to color table for color lookups.
+ */
+ private WseColorTable ct;
+
+ /**
+ * The modifiers for the text run. (Mostly) Bitwise flags. The "_TOKEN"
+ * values are not yet implemented in this converter. They may not even
+ * be implemented in WordSmith yet.
+ */
+ private byte modifiers = 0;
+ final public static int BOLD = 0x01;
+ final public static int ITALIC = 0x02;
+ final public static int UNDERLINE = 0x04;
+ final public static int STRIKETHRU = 0x08;
+ final public static int SUPERSCRIPT = 0x10;
+ final public static int SUBSCRIPT = 0x20;
+ final public static int LINK = 0x40;
+ final public static int CUSTOM_TOKEN = 0x80;
+ final public static int IMAGE_TOKEN = 0x80;
+ final public static int BOOKMARK_TOKEN = 0x81;
+ final public static int ANNOTATION_TOKEN = 0x82;
+ final public static int LINK_TOKEN = 0x83;
+
+ /** The actual text. */
+ private String text;
+
+ StyleCatalog sc;
+
+
+ /**
+ * Constructor for use when going from DOM to WordSmith.
+ *
+ * @param txt The text.
+ * @param t The text style.
+ * @param sc The <code>StyleCatalog</code>.
+ * @param ft The font table.
+ * @param ct The color Table.
+ */
+ public WseTextRun(String txt, TextStyle t, StyleCatalog sc,
+ WseFontTable ft, WseColorTable ct) {
+
+ this.sc = sc;
+ this.ct = ct;
+
+ TextStyle ts = (TextStyle)t.getResolved();
+
+ if (ts.isSet(TextStyle.BOLD) && ts.getAttribute(TextStyle.BOLD))
+ modifiers |= BOLD;
+ if (ts.isSet(TextStyle.ITALIC) && ts.getAttribute(TextStyle.ITALIC))
+ modifiers |= ITALIC;
+ if (ts.isSet(TextStyle.UNDERLINE) && ts.getAttribute(TextStyle.UNDERLINE))
+ modifiers |= UNDERLINE;
+ if (ts.isSet(TextStyle.STRIKETHRU) && ts.getAttribute(TextStyle.STRIKETHRU))
+ modifiers |= STRIKETHRU;
+ if (ts.isSet(TextStyle.SUPERSCRIPT) && ts.getAttribute(TextStyle.SUPERSCRIPT))
+ modifiers |= SUPERSCRIPT;
+ if (ts.isSet(TextStyle.SUBSCRIPT) && ts.getAttribute(TextStyle.SUBSCRIPT))
+ modifiers |= SUBSCRIPT;
+
+ fontSize = (byte)(ts.getFontSize() * 2);
+ fontName = ts.getFontName();
+ fontIndex = (byte)ft.getFontIndex(fontName);
+ if (fontIndex == -1) {
+ ft.add(fontName);
+ fontIndex = (byte)ft.getFontIndex(fontName);
+ }
+
+ // Figure out the color index.
+ Color c = t.getFontColor();
+ if (c == null)
+ c = Color.black;
+ colorIndex = (byte)ct.findColor(c, true);
+ c = t.getBackgroundColor();
+ if (c == null)
+ c = Color.white;
+ colorIndex |= (byte)(ct.findColor(c, false) << 4);
+
+ text = txt;
+ }
+
+
+ /**
+ * Standard constructor for use when going from WordSmith to DOM.
+ *
+ * @param dataArray <code>byte</code> array.
+ * @param startIndex The start index.
+ * @param ft The font table.
+ * @param ct The color table.
+ */
+ public WseTextRun(byte dataArray[], int startIndex, WseFontTable ft,
+ WseColorTable ct) {
+
+ this.ct = ct;
+
+ startIndex++; // Skip the leading "1"
+
+ int textLen = ((dataArray[startIndex] << 8)
+ | (dataArray[startIndex+1] & 0xFF));
+ startIndex += 2;
+
+ fontIndex = dataArray[startIndex++];
+ if (ft != null)
+ fontName = ft.getFontName(fontIndex);
+
+ fontSize = dataArray[startIndex++];
+
+ colorIndex = dataArray[startIndex++];
+ modifiers = dataArray[startIndex++];
+
+ text = new String(dataArray, startIndex, textLen);
+ startIndex += textLen; // skip the text
+ }
+
+
+ /**
+ * Given a <code>byte</code> sequence, assumed to be a text run,
+ * compute the index of the first byte past the text run.
+ *
+ * @param dataArray <code>byte</code> array.
+ * @param startIndex The start index
+ *
+ * @return The index of the first <code>byte</code> past the
+ * text run.
+ */
+ public static int computeNewIndex(byte dataArray[], int startIndex) {
+
+ startIndex++; // Skip the leading "1"
+
+ int textLen = ((dataArray[startIndex] << 8)
+ | (dataArray[startIndex+1] & 0xFF));
+ startIndex += 2;
+
+ startIndex += 4; // skip attributes
+ // text = new String(dataArray, startIndex, textLen);
+ startIndex += textLen; // skip the text
+ return startIndex;
+ }
+
+
+ /**
+ * Return true if the sequence starting at
+ * <code>dataArray[startIndex]</code> is a valid text run.
+ *
+ * @param dataArray <code>byte</code> array.
+ * @param startIndex The start index.
+ *
+ * @return true if the sequence starting at
+ * <code>dataArray[startIndex]</code> is a valid
+ * text run, false otherwise.
+ */
+ public static boolean isValid(byte dataArray[], int startIndex) {
+ return (dataArray[startIndex] == 1);
+ }
+
+ /**
+ * Return the number of bytes needed to represent this text run.
+ *
+ * @return The number of bytes needed to represent this text run.
+ */
+ int getByteCount() {
+ return text.length() + 7;
+ }
+
+
+ /**
+ * Return an <code>byte</code> array representing this text run.
+ *
+ * @return An <code>byte</code> array representing this text run.
+ */
+ byte[] getBytes() {
+ short textLen = (short)text.length();
+ byte b[] = new byte[textLen + 7];
+ b[0] = 1;
+ b[1] = (byte)(textLen >> 8);
+ b[2] = (byte)(textLen & 0xFF);
+ b[3] = fontIndex;
+ b[4] = fontSize;
+ b[5] = colorIndex;
+ b[6] = modifiers;
+ byte[] txtBytes = text.getBytes();
+ System.arraycopy(txtBytes, 0, b, 7, textLen);
+ return b;
+ }
+
+
+ /**
+ * Return the text of this run.
+ *
+ * @return The text of this run.
+ */
+ public String getText() {
+ return text;
+ }
+
+
+ /**
+ * Return a <code>TextStyle</code> that reflects the formatting
+ * of this run.
+ *
+ * @return A <code>TextStyle</code> that reflects the formatting
+ * of this run.
+ */
+ public TextStyle makeStyle() {
+ int mod = 0;
+ if ((modifiers & BOLD) != 0) mod |= TextStyle.BOLD;
+ if ((modifiers & ITALIC) != 0) mod |= TextStyle.ITALIC;
+ if ((modifiers & UNDERLINE) != 0) mod |= TextStyle.UNDERLINE;
+ if ((modifiers & STRIKETHRU) != 0)
+ mod |= TextStyle.STRIKETHRU;
+ if ((modifiers & SUPERSCRIPT) != 0) mod |= TextStyle.SUPERSCRIPT;
+ if ((modifiers & SUBSCRIPT) != 0) mod |= TextStyle.SUBSCRIPT;
+
+ int mask = TextStyle.BOLD | TextStyle.ITALIC
+ | TextStyle.UNDERLINE
+ | TextStyle.STRIKETHRU | TextStyle.SUPERSCRIPT
+ | TextStyle.SUBSCRIPT;
+
+ TextStyle x = new TextStyle(null, "text", null, mask,
+ mod, (int)(fontSize/2), fontName, sc);
+
+ // If color table is available, set the colors.
+ if (ct != null) {
+ Color fc = ct.getColor(colorIndex & 0xF, true);
+ Color bc = ct.getColor(colorIndex >> 4, false);
+ x.setColors(fc, bc);
+ }
+
+ return x;
+ }
+
+
+ /**
+ * Display debug information.
+ */
+ public void dump() {
+ System.out.print("TEXT RUN: fontIndex = " + fontIndex
+ + " fontsize = " + fontSize
+ + " colorIndex = " + colorIndex
+ + " ");
+ if ((modifiers & BOLD) != 0) System.out.print("BOLD,");
+ if ((modifiers & ITALIC) != 0) System.out.print("ITALIC,");
+ if ((modifiers & UNDERLINE) != 0) System.out.print("UNDERLINE,");
+ if ((modifiers & STRIKETHRU) != 0) System.out.print("STRIKETHRU,");
+ if ((modifiers & SUPERSCRIPT) != 0) System.out.print("SUPERSCRIPT,");
+ if ((modifiers & SUBSCRIPT) != 0) System.out.print("SUBSCRIPT,");
+ System.out.println("\n" + text);
+ }
+}
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/textRecord.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/textRecord.java
new file mode 100644
index 000000000000..01276ce79a94
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/textRecord.java
@@ -0,0 +1,115 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+import org.openoffice.xmerge.util.Debug;
+import java.io.IOException;
+import java.io.DataOutputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * This class represents a single text record in a WordSmith document.
+ * A record is composed of one or more "WordSmith elements", which
+ * include: WordSmith header, font table, color table, paragraphs,
+ * and text runs.
+ *
+ * @author David Proulx
+ */
+
+class textRecord {
+
+ java.util.Vector elements;
+
+
+ /**
+ * Default constructor
+ */
+ textRecord() {
+ elements = new java.util.Vector(10);
+ }
+
+
+ /**
+ * Add an element
+ *
+ * @param elem The element to add
+ */
+ void addElement(Wse elem) {
+ elements.add(elem);
+ }
+
+
+ /**
+ * Return the number of bytes needed to represent the current
+ * contents of this text record.
+ *
+ * @return The number of bytes needed to represent the current
+ * contents of this text record.
+ */
+ int getByteCount() {
+ int totalBytes = 0;
+ int nElements = elements.size();
+ for (int i = 0; i < nElements; i++) {
+ Wse e = (Wse)elements.elementAt(i);
+ totalBytes += e.getByteCount();
+ }
+ return totalBytes;
+ }
+
+
+ /**
+ * Return the contents of this record as a <code>byte</code> array.
+ *
+ * @return the contents of this record as a <code>byte</code> array.
+ */
+ byte[] getBytes() {
+ DataOutputStream os = null; // Used for storing the data
+ ByteArrayOutputStream bs = null; // Used for storing the data
+ byte ftBytes[] = null;
+ byte ctBytes[] = null;
+
+ try {
+ bs = new ByteArrayOutputStream();
+ os = new DataOutputStream(bs);
+ int nElements = elements.size();
+ for (int i = 0; i < nElements; i++) {
+ Wse e = (Wse)elements.get(i);
+ os.write(e.getBytes());
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ if (bs != null)
+ return bs.toByteArray();
+ else
+ return null;
+ }
+}
+
diff --git a/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/util.java b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/util.java
new file mode 100644
index 000000000000..9dcb178197da
--- /dev/null
+++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/util.java
@@ -0,0 +1,68 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxw.wordsmith;
+
+/**
+ * WordSmith utility class.
+ *
+ * @author David Proulx
+ */
+class util {
+
+ /**
+ * Convert 2 bytes to an integer.
+ *
+ * @param data <code>byte</code> data to convert.
+ * @param index Index to convert.
+ *
+ * @return Converted integer.
+ */
+ static int intFrom2bytes(byte[] data, int index) {
+ return (((data[index] & 0xFF) << 8)
+ | (data[index+1] & 0xFF));
+
+ }
+
+
+ /**
+ * Convert 4 bytes to an integer.
+ *
+ * @param data <code>byte</code> data to convert.
+ * @param index Index to convert.
+ *
+ * @return Converted integer.
+ */
+ static int intFrom4bytes(byte[] data, int index) {
+ return (((data[index] & 0xFF) << 24)
+ | ((data[index + 1] & 0xFF) << 16)
+ | ((data[index + 2] & 0xFF) << 8)
+ | (data[index+3] & 0xFF));
+
+ }
+}
+
diff --git a/xmerge/source/wordsmith/makefile.mk b/xmerge/source/wordsmith/makefile.mk
new file mode 100644
index 000000000000..9edb4e758878
--- /dev/null
+++ b/xmerge/source/wordsmith/makefile.mk
@@ -0,0 +1,33 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+TARGET=wordsmith
+PRJ=../..
+PRJNAME=xmerge
+
+.INCLUDE : ant.mk
+ALLTAR: ANTBUILD