diff options
Diffstat (limited to 'xmerge/source/wordsmith')
19 files changed, 3709 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 "Office" + * <code>Document</code> tags and attributes are supported on the + * "Device" <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..29098b72cc17 --- /dev/null +++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/DocumentDeserializerImpl.java @@ -0,0 +1,565 @@ +/************************************************************************* + * + * 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); + } + + + /* + public static void main(String args[]) { + + // DocumentDeserializerImpl d = new DocumentDeserializerImpl(new InputStream()); + + Node nodes[] = parseText("Tab here:\tThen some more text"); + } +*/ +} + 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 < + * <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..36b21e21b931 --- /dev/null +++ b/xmerge/source/wordsmith/java/org/openoffice/xmerge/converter/xml/sxw/wordsmith/WsePara.java @@ -0,0 +1,299 @@ +/************************************************************************* + * + * 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"; + +/* if ((lineSpace & LS_MULTIPLE) != 0) { + temp = (lineSpace & LS_VALUEMASK) / 2; + temp *= 100; + values[3] = (new Double(temp)).toString() + "%"; + } else { + values[3] = (new Double(temp)).toString() + "mm"; + // DJP: handle other cases + } +*/ + temp = spaceBefore / 1.6; +// values[4] = (new Double(temp)).toString() + "mm"; + values[3] = (new Double(temp)).toString() + "mm"; + + temp = spaceAfter / 1.6; +// values[5] = (new Double(temp)).toString() + "mm"; + values[4] = (new Double(temp)).toString() + "mm"; + + switch (misc) { + +// case ALIGN_RIGHT: values[6] = "right"; break; +// case ALIGN_LEFT: values[6] = "left"; break; +// case ALIGN_CENTER:values[6] = "center"; break; +// case ALIGN_JUST: values[6] = "justified"; break; + + 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 |