Initial import

git-svn-id: svn://svn.code.sf.net/p/writer2latex/code/trunk@5 f0f2a975-2e09-46c8-9428-3b39399b9f3c
This commit is contained in:
henrikjust 2009-02-20 09:37:06 +00:00
parent 75e32b1e8f
commit b0b66fcae9
252 changed files with 49000 additions and 0 deletions

View file

@ -0,0 +1,382 @@
/************************************************************************
*
* Application.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-22)
*
*/
package writer2latex;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
//import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import writer2latex.api.BatchConverter;
import writer2latex.api.Converter;
import writer2latex.api.ConverterFactory;
import writer2latex.api.ConverterResult;
import writer2latex.api.MIMETypes;
//import writer2latex.api.OutputFile;
import writer2latex.util.Misc;
/**
* <p>Commandline utility to convert an OpenOffice.org Writer XML file into XHTML/LaTeX/BibTeX</p>
* <p>The utility is invoked with the following command line:</p>
* <pre>java -jar writer2latex.jar [options] source [target]</pre>
* <p>Where the available options are
* <ul>
* <li><code>-latex</code>, <code>-bibtex</code>, <code>-xhtml</code>,
<code>-xhtml+mathml</code>, <code>-xhtml+mathml+xsl</code>
* <li><code>-recurse</code>
* <li><code>-ultraclean</code>, <code>-clean</code>, <code>-pdfscreen</code>,
* <code>-pdfprint</code>, <code>-cleanxhtml</code>
* <li><code>-config[=]filename</code>
* <li><code>-template[=]filename</code>
* <li><code>-option[=]value</code>
* </ul>
* <p>where <code>option</code> can be any simple option known to Writer2LaTeX
* (see documentation for the configuration file).</p>
*/
public final class Application {
/* Based on command-line parameters. */
private String sTargetMIME = MIMETypes.LATEX;
private boolean bRecurse = false;
private Vector configFileNames = new Vector();
private String sTemplateFileName = null;
private Hashtable options = new Hashtable();
private String sSource = null;
private String sTarget = null;
/**
* Main method
*
* @param args The argument passed on the command line.
*/
public static final void main (String[] args){
try {
Application app = new Application();
app.parseCommandLine(args);
app.doConversion();
} catch (IllegalArgumentException ex) {
String msg = ex.getMessage();
showUsage(msg);
}
}
// Convert the directory or file
private void doConversion() {
// Step 1: Say hello...
String sOutputFormat;
if (MIMETypes.LATEX.equals(sTargetMIME)) { sOutputFormat = "LaTeX"; }
else if (MIMETypes.BIBTEX.equals(sTargetMIME)) { sOutputFormat = "BibTeX"; }
else { sOutputFormat = "xhtml"; }
System.out.println();
System.out.println("This is Writer2" + sOutputFormat +
", Version " + ConverterFactory.getVersion() +
" (" + ConverterFactory.getDate() + ")");
System.out.println();
System.out.println("Starting conversion...");
// Step 2: Examine source
File source = new File(sSource);
if (!source.exists()) {
System.out.println("I'm sorry, I can't find "+sSource);
System.exit(1);
}
if (!source.canRead()) {
System.out.println("I'm sorry, I can't read "+sSource);
System.exit(1);
}
boolean bBatch = source.isDirectory();
// Step 3: Examine target
File target;
if (bBatch) {
if (sTarget==null) {
target=source;
}
else {
target = new File(sTarget);
}
}
else {
if (sTarget==null) {
target = new File(source.getParent(),Misc.removeExtension(source.getName()));
}
else {
target = new File(sTarget);
if (sTarget.endsWith(File.separator)) {
target = new File(target,Misc.removeExtension(source.getName()));
}
}
}
// Step 4: Create converters
Converter converter = ConverterFactory.createConverter(sTargetMIME);
if (converter==null) {
System.out.println("Failed to create converter for "+sTargetMIME);
System.exit(1);
}
BatchConverter batchCv = null;
if (bBatch) {
batchCv = ConverterFactory.createBatchConverter(MIMETypes.XHTML);
if (batchCv==null) {
System.out.println("Failed to create batch converter");
System.exit(1);
}
batchCv.setConverter(converter);
}
// Step 5: Read template
if (sTemplateFileName!=null) {
try {
System.out.println("Reading template "+sTemplateFileName);
byte [] templateBytes = Misc.inputStreamToByteArray(new FileInputStream(sTemplateFileName));
converter.readTemplate(new ByteArrayInputStream(templateBytes));
if (batchCv!=null) {
// Currently we use the same template for the directory and the files
batchCv.readTemplate(new ByteArrayInputStream(templateBytes));
}
}
catch (FileNotFoundException e) {
System.out.println("--> This file does not exist!");
System.out.println(" "+e.getMessage());
}
catch (IOException e) {
System.out.println("--> Failed to read the template file!");
System.out.println(" "+e.getMessage());
}
}
// Step 6: Read config
for (int i=0; i<configFileNames.size(); i++) {
String sConfigFileName = (String) configFileNames.get(i);
if (sConfigFileName.startsWith("*")) {
sConfigFileName = sConfigFileName.substring(1);
System.out.println("Reading default configuration "+sConfigFileName);
try {
converter.getConfig().readDefaultConfig(sConfigFileName);
}
catch (IllegalArgumentException e) {
System.err.println("--> This configuration is unknown!");
System.out.println(" "+e.getMessage());
}
}
else {
System.out.println("Reading configuration file "+sConfigFileName);
try {
byte[] configBytes = Misc.inputStreamToByteArray(new FileInputStream(sConfigFileName));
converter.getConfig().read(new ByteArrayInputStream(configBytes));
if (bBatch) {
// Currently we use the same configuration for the directory and the files
batchCv.getConfig().read(new ByteArrayInputStream(configBytes));
}
}
catch (IOException e) {
System.err.println("--> Failed to read the configuration!");
System.out.println(" "+e.getMessage());
}
}
}
// Step 7: Set options from command line
Enumeration keys = options.keys();
while (keys.hasMoreElements()) {
String sKey = (String) keys.nextElement();
String sValue = (String) options.get(sKey);
converter.getConfig().setOption(sKey,sValue);
if (batchCv!=null) {
batchCv.getConfig().setOption(sKey,sValue);
}
}
// Step 8: Perform conversion
if (bBatch) {
batchCv.convert(source,target,bRecurse, new BatchHandlerImpl());
}
else {
System.out.println("Converting "+source.getPath());
ConverterResult dataOut = null;
try {
dataOut = converter.convert(source,target.getName());
}
catch (FileNotFoundException e) {
System.out.println("--> The file "+source.getPath()+" does not exist!");
System.out.println(" "+e.getMessage());
System.exit(1);
}
catch (IOException e) {
System.out.println("--> Failed to convert the file "+source.getPath()+"!");
System.out.println(" "+e.getMessage());
System.exit(1);
}
// TODO: Should do some further checking on the feasability of writing
// the directory and the files.
File targetDir = target.getParentFile();
if (targetDir!=null && !targetDir.exists()) { targetDir.mkdirs(); }
try {
dataOut.write(targetDir);
}
catch (IOException e) {
System.out.println("--> Error writing out file!");
System.out.println(" "+e.getMessage());
System.exit(1);
}
}
// Step 9: Say goodbye!
System.out.println("Done!");
}
/**
* Display usage.
*/
private static void showUsage(String msg) {
System.out.println();
System.out.println("This is Writer2LaTeX, Version " + ConverterFactory.getVersion()
+ " (" + ConverterFactory.getDate() + ")");
System.out.println();
if (msg != null) System.out.println(msg);
System.out.println();
System.out.println("Usage:");
System.out.println(" java -jar <path>/writer2latex.jar <options> <source file/directory> [<target file/directory>]");
System.out.println("where the available options are:");
System.out.println(" -latex");
System.out.println(" -bibtex");
System.out.println(" -xhtml");
System.out.println(" -xhtml+mathml");
System.out.println(" -xhtml+mathml+xsl");
System.out.println(" -recurse");
System.out.println(" -template[=]<template file>");
System.out.println(" -ultraclean");
System.out.println(" -clean");
System.out.println(" -pdfprint");
System.out.println(" -pdfscreen");
System.out.println(" -cleanxhtml");
System.out.println(" -config[=]<configuration file>");
System.out.println(" -<configuration option>[=]<value>");
System.out.println("See the documentation for the available configuration options");
}
/**
* Parse command-line arguments.
*
* @param args Array of command line arguments.
*
* @throws IllegalArgumentException If an argument is invalid.
*/
private void parseCommandLine(String sArgs[])
throws IllegalArgumentException {
int i = 0;
while (i<sArgs.length) {
String sArg = getArg(i++,sArgs);
if (sArg.startsWith("-")) { // found an option
if ("-latex".equals(sArg)) { sTargetMIME = MIMETypes.LATEX; }
else if ("-bibtex".equals(sArg)) { sTargetMIME = MIMETypes.BIBTEX; }
else if ("-xhtml".equals(sArg)) { sTargetMIME = MIMETypes.XHTML; }
else if ("-xhtml+mathml".equals(sArg)) { sTargetMIME = MIMETypes.XHTML_MATHML; }
else if ("-xhtml+mathml+xsl".equals(sArg)) { sTargetMIME = MIMETypes.XHTML_MATHML_XSL; }
else if ("-recurse".equals(sArg)) { bRecurse = true; }
else if ("-ultraclean".equals(sArg)) { configFileNames.add("*ultraclean.xml"); }
else if ("-clean".equals(sArg)) { configFileNames.add("*clean.xml"); }
else if ("-pdfprint".equals(sArg)) { configFileNames.add("*pdfprint.xml"); }
else if ("-pdfscreen".equals(sArg)) { configFileNames.add("*pdfscreen.xml"); }
else if ("-cleanxhtml".equals(sArg)) { configFileNames.add("*cleanxhtml.xml"); }
else { // option with argument
int j=sArg.indexOf("=");
String sArg2;
if (j>-1) { // argument is separated by =
sArg2 = sArg.substring(j+1);
sArg = sArg.substring(0,j);
}
else { // argument is separated by space
sArg2 = getArg(i++,sArgs);
}
if ("-config".equals(sArg)) { configFileNames.add(sArg2); }
else if ("-template".equals(sArg)) { sTemplateFileName = sArg2; }
else { // configuration option
options.put(sArg.substring(1),sArg2);
}
}
}
else { // not an option, so this must be the source
sSource = sArg;
// Possibly followed by the target
if (i<sArgs.length) {
String sArgument = getArg(i++,sArgs);
if (sArgument.length()>0) { sTarget = sArgument; }
}
// Skip any trailing empty arguments and signal an error if there's more
while (i<sArgs.length) {
String sArgument = getArg(i++,sArgs);
if (sArgument.length()>0) {
throw new IllegalArgumentException("I didn't expect "+sArgument+"?");
}
}
}
}
if (sSource==null) {
throw new IllegalArgumentException("Please specify a source document/directory!");
}
// Parsing of command line ended successfully!
}
/**
* Extract the next argument from the array, while checking to see
* that the array size is not exceeded. Throw a friendly error
* message in case the arg is missing.
*
* @param i Argument index.
* @param args Array of command line arguments.
*
* @return The argument with the specified index.
*
* @throws IllegalArgumentException If an argument is invalid.
*/
private String getArg(int i, String args[])
throws IllegalArgumentException {
if (i < args.length) {
return args[i];
}
else throw new
IllegalArgumentException("I'm sorry, the commandline ended abnormally");
}
}

View file

@ -0,0 +1,94 @@
/************************************************************************
*
* BatchHandlerImpl.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-10-03)
*
*/
package writer2latex;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import writer2latex.api.BatchHandler;
/** This class implements a <code>BatchHandler</code> for command line usage
*/
public class BatchHandlerImpl implements BatchHandler {
private int nIndent = 0;
private void writeMessage(String sMsg) {
for (int i=0; i<nIndent; i++) {
System.out.print(" ");
}
System.out.println(sMsg);
}
public void startConversion() {
System.out.println("Press Enter to cancel the conversion");
}
public void endConversion() {
// No message
}
public void startDirectory(String sName) {
writeMessage("Converting directory "+sName);
nIndent++;
}
public void endDirectory(String sName, boolean bSuccess) {
nIndent--;
if (!bSuccess) {
writeMessage("--> Conversion of the directory "+sName+" failed!");
}
}
public void startFile(String sName) {
writeMessage("Converting file "+sName);
nIndent++;
}
public void endFile(String sName, boolean bSuccess) {
nIndent--;
if (!bSuccess) {
writeMessage("--> Conversion of the file "+sName+" failed!");
}
}
public boolean cancel() {
try {
if (System.in.available()>0) {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
in.readLine();
System.out.print("Do you want to cancel the conversion (y/n)? ");
String s = in.readLine();
if (s!= null && s.toLowerCase().startsWith("y")) { return true; }
}
}
catch (IOException e) {
}
return false;
}
}

View file

@ -0,0 +1,96 @@
/************************************************************************
*
* BatchConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.api;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
/** This is an interface for a converter, which offers conversion of
* all OpenDocument (or OpenOffice.org 1.x) documents in a directory
* (and optionally subdirectories), creating index pages in a specific format.
* Instances of this interface are created using the
* {@link ConverterFactory}
*/
public interface BatchConverter {
/** Get the configuration interface for this batch converter
*
* @return the configuration
*/
public Config getConfig();
/** Define a <code>Converter</code> implementation to use for
* conversion of the individual documents.
* If no converter is given, the <code>convert</code> method cannot
* convert documents (but can still create index pages).
*
* @param converter the <code>Converter</code> to use
*/
public void setConverter(Converter converter);
/** Read a template to use as a base for the index pages.
* The format of the template depends on the <code>BatchConverter</code>
* implementation.
*
* @param is an <code>InputStream</code> from which to read the template
* @throws IOException if some exception occurs while reading the template
*/
public void readTemplate(InputStream is) throws IOException;
/** Read a template to use as a base for the index pages.
* The format of the template depends on the <code>BatchConverter</code>
* implementation.
*
* @param file the file from which to read the template
* @throws IOException if the file does not exist or some exception occurs
* while reading the template
*/
public void readTemplate(File file) throws IOException;
/** Create an index page with specific entries
*
* @param sHeading a heading describing the index page
* @param entries an array of <code>IndexPageEntry</code> objects (null entries
* are allowed, and will be ignored) describing the individual directories
* and documents
*/
public OutputFile createIndexFile(String sHeading, IndexPageEntry[] entries);
/** Convert a directory using the given <code>Converter</code> (if none is given,
* all files will be ignored).
* This method fails silently if you haven't set a converter.
*
* @param source a <code>File</code> representing the directory to convert
* @param target a <code>File</code> representing the directory to contain
* the converted documents
* @param bRecurse determines wether or not to recurse into subdirectories
* @param handler a </code>BatchHandler</code>
*/
public void convert(File source, File target, boolean bRecurse, BatchHandler handler);
}

View file

@ -0,0 +1,77 @@
/************************************************************************
*
* BatchHandler.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.api;
/** This is a call back interface to handle user interaction during a
* batch conversion with a {@link BatchConverter}
*/
public interface BatchHandler {
/** Notification that the conversion is started */
public void startConversion();
/** Notification that the conversion has finished */
public void endConversion();
/** Notification that a directory conversion starts
*
* @param sName the name of the directory to convert
*/
public void startDirectory(String sName);
/** Notification that a directory conversion has finished
*
* @param sName the name of the directory
* @param bSuccess true if the conversion was successful (this only means
* that the index page was created with success, not that the conversion
* of files and subdirectories was successful)
*/
public void endDirectory(String sName, boolean bSuccess);
/** Notification that a file conversion starts
*
* @param sName the name of the file to convert
*/
public void startFile(String sName);
/** Notification that a file conversion has finished
*
* @param sName the name of the file
* @param bSuccess true if the conversion of this file was successful
*/
public void endFile(String sName, boolean bSuccess);
/** Notification that the conversion may be cancelled. The
* {@link BatchConverter} fires this event once per document.
* Cancelling the conversion does not delete files that was already
* converted
*
* @return true if the handler wants to cancel the conversion
*/
public boolean cancel();
}

View file

@ -0,0 +1,99 @@
/************************************************************************
*
* Config.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.api;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.IllegalArgumentException;
/** This is an interface for configuration of a {@link Converter}.
* A configuration always supports simple name/value options.
* In addition, you can read and write configurations using streams
* or abstract file names. The format depends on the {@link Converter}
* implementation, cf. the user's manual.
*/
public interface Config {
/** Read a default configuration: The available configurations depend on the
* {@link Converter} implementation
*
* @param sName the name of the configuration
* @throws IllegalArgumentException if the configuration does not exist
*/
public void readDefaultConfig(String sName) throws IllegalArgumentException;
/** Read a configuration (stream based version)
*
* @param is the <code>InputStream</code> to read from
* @throws IOException if an error occurs reading the stream, or the data
* is not in the right format
*/
public void read(InputStream is) throws IOException;
/** Read a configuration (file based version)
*
* @param file the <code>File</code> to read from
* @throws IOException if the file does not exist, an error occurs reading
* the file, or the data is not in the right format
*/
public void read(File file) throws IOException;
/** Write the configuration (stream based version)
*
* @param os the <code>OutputStream</code> to write to
* @throws IOException if an error occurs writing to the stream
*/
public void write(OutputStream os) throws IOException;
/** Write the configuration (file based version)
*
* @param file the <code>File</code> to write to
* @throws IOException if an error occurs writing to the file
*/
public void write(File file) throws IOException;
/** Set a name/value option. Options that are not defined by the
* {@link Converter} implementation as well as null values are
* silently ignored
*
* @param sName the name of the option
* @param sValue the value of the option
*/
public void setOption(String sName, String sValue);
/** Get a named option
*
* @param sName the name of the option
* @return the value of the option, or <code>null</code> if the option does
* not exist or the given name is null
*/
public String getOption(String sName);
}

View file

@ -0,0 +1,99 @@
/************************************************************************
*
* Converter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.api;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.IOException;
/** This is an interface for a converter, which offers conversion of
* OpenDocument (or OpenOffice.org 1.x) documents into a specific format.
* Instances of this interface are created using the
* <code>ConverterFactory</code>
*/
public interface Converter {
/** Get the interface for the configuration of this converter
*
* @return the configuration
*/
public Config getConfig();
/** Define a <code>GraphicConverter</code> implementation to use for
* conversion of graphic files. If no converter is specified, graphic
* files will not be converted into other formats.
*
* @param gc the <code>GraphicConverter</code> to use
*/
public void setGraphicConverter(GraphicConverter gc);
/** Read a template to use as a base for the converted document.
* The format of the template depends on the <code>Converter</code>
* implementation.
*
* @param is an <code>InputStream</code> from which to read the template
* @throws IOException if some exception occurs while reading the template
*/
public void readTemplate(InputStream is) throws IOException;
/** Read a template to use as a base for the converted document.
* The format of the template depends on the <code>Converter</code>
* implementation.
*
* @param file a file from which to read the template
* @throws IOException if the file does not exist or some exception occurs
* while reading the template
*/
public void readTemplate(File file) throws IOException;
/** Convert a document
*
* @param is an <code>InputStream</code> from which to read the source document.
* @param sTargetFileName the file name to use for the converted document
* (if the converted document is a compound document consisting consisting
* of several files, this name will be used for the master document)
* @return a <code>ConverterResult</code> containing the converted document
* @throws IOException if some exception occurs while reading the document
*/
public ConverterResult convert(InputStream is, String sTargetFileName)
throws IOException;
/** Convert a document
*
* @param source a <code>File</code> from which to read the source document.
* @param sTargetFileName the file name to use for the converted document
* (if the converted document is a compound document consisting consisting
* of several files, this name will be used for the master document)
* @return a <code>ConverterResult</code> containing the converted document
* @throws FileNotFoundException if the file does not exist
* @throws IOException if some exception occurs while reading the document
*/
public ConverterResult convert(File source, String sTargetFileName)
throws FileNotFoundException, IOException;
}

View file

@ -0,0 +1,126 @@
/************************************************************************
*
* ConverterFactory.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2009 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2009-02-18)
*
*/
package writer2latex.api;
/** This is a factory class which provides static methods to create converters
* for documents in OpenDocument (or OpenOffice.org 1.x) format into a specific MIME type
*/
public class ConverterFactory {
// Version information
private static final String VERSION = "0.9.4";
private static final String DATE = "2008-02-19";
/** Return version information
* @return the Writer2LaTeX version in the form
* (major version).(minor version).(development version).(patch level)
*/
public static String getVersion() { return VERSION; }
/** Return date information
* @return the release date for this Writer2LaTeX version
*/
public static String getDate() { return DATE; }
/** <p>Create a <code>Converter</code> implementation which supports
* conversion into the specified MIME type.</p>
* <p>Currently supported MIME types are:</p>
* <ul>
* <li><code>application/x-latex</code> for LaTeX format</li>
* <li><code>application/x-bibtex</code> for BibTeX format</li>
* <li><code>text/html</code> for XHTML 1.0 strict format</li>
* <li><code>application/xhtml+xml</code> for XHTML+MathML</li>
* <li><code>application/xml</code> for XHTML+MathML using stylesheets from w3c's
* math working group</li>
* </ul>
*
* @param sMIME the MIME type of the target format
* @return the required <code>Converter</code> or null if a converter for
* the requested MIME type could not be created
*/
public static Converter createConverter(String sMIME) {
Object converter = null;
if (MIMETypes.LATEX.equals(sMIME)) {
converter = createInstance("writer2latex.latex.ConverterPalette");
}
else if (MIMETypes.BIBTEX.equals(sMIME)) {
converter = createInstance("writer2latex.bibtex.Converter");
}
else if (MIMETypes.XHTML.equals(sMIME)) {
converter = createInstance("writer2latex.xhtml.Xhtml10Converter");
}
else if (MIMETypes.XHTML_MATHML.equals(sMIME)) {
converter = createInstance("writer2latex.xhtml.XhtmlMathMLConverter");
}
else if (MIMETypes.XHTML_MATHML_XSL.equals(sMIME)) {
converter = createInstance("writer2latex.xhtml.XhtmlMathMLXSLConverter");
}
return converter instanceof Converter ? (Converter) converter : null;
}
/** <p>Create a <code>BatchConverter</code> implementation which supports
* conversion into the specified MIME type</p>
* <p>The only currently supported MIME type is <code>text/html</code>
* (XHTML 1.0 strict)</p>
*
* @param sMIME the MIME type of the target format
* @return the required <code>BatchConverter</code> or null if a converter
* for the requested MIME type could not be created
*/
public static BatchConverter createBatchConverter(String sMIME) {
Object converter = null;
if (MIMETypes.XHTML.equals(sMIME)) {
converter = createInstance("writer2latex.xhtml.BatchConverterImpl");
}
return converter instanceof BatchConverter ? (BatchConverter) converter : null;
}
/** Create a <code>StarMathConverter</code> implementation
*
* @return the converter
*/
public static StarMathConverter createStarMathConverter() {
Object converter = createInstance("writer2latex.latex.StarMathConverter");
return converter instanceof StarMathConverter ? (StarMathConverter) converter : null;
}
private static Object createInstance(String sClassName) {
try {
return Class.forName(sClassName).newInstance();
}
catch (java.lang.ClassNotFoundException e) {
return null;
}
catch (java.lang.InstantiationException e) {
return null;
}
catch (java.lang.IllegalAccessException e) {
return null;
}
}
}

View file

@ -0,0 +1,59 @@
/************************************************************************
*
* ConverterResult.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-24)
*
*/
package writer2latex.api;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
/** A <code>ConverterResult</code> represent a document, which is the result
* of a conversion performed by a <code>Converter</code>implementation.
*/
public interface ConverterResult {
/** Get the master document
* @return <code>OutputFile</code> the master document
*/
public OutputFile getMasterDocument();
/** Gets an <code>Iterator</code> to access all files in the
* <code>ConverterResult</code>. This <em>includes</em> the master document.
* @return an <code>Iterator</code> of all files
*/
public Iterator iterator();
/** Write all files of the <code>ConverterResult</code> to a directory.
* Subdirectories are created as required by the individual
* <code>OutputFile</code>s.
* @param dir the directory to write to (this directory must exist).
If the parameter is null, the default directory is used
* @throws IOException if the directory does not exist or one or more files
* could not be written
*/
public void write(File dir) throws IOException;
}

View file

@ -0,0 +1,58 @@
/************************************************************************
*
* GraphicConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.api;
/** A simple interface for a graphic converter which converts between various
* graphics formats
*/
public interface GraphicConverter {
/** Check whether a certain conversion is supported by the converter
*
* @param sSourceMime a string containing the source Mime type
* @param sTargetMime a string containing the target Mime type
* @param bCrop true if the target graphic should be cropped
* @param bResize true if the target graphic should be resized
* (the last two parameters are for future use)
* @return true if the conversion is supported
*/
public boolean supportsConversion(String sSourceMime, String sTargetMime, boolean bCrop, boolean bResize);
/** Convert a graphics file from one format to another
*
* @param source a byte array containing the source graphic
* @param sSourceMime a string containing the Mime type of the source
* @param sTargetMime a string containing the desired Mime type of the target
* @return a byte array containing the converted graphic. Returns null
* if the conversion failed.
*/
public byte[] convert(byte[] source, String sSourceMime, String sTargetMime);
}

View file

@ -0,0 +1,141 @@
/************************************************************************
*
* IndexPageEntry.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2009 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2009-02-08)
*
*/
package writer2latex.api;
/** This class represents a single entry on an index page created by a batch converter
*/
public class IndexPageEntry {
private String sFile;
private String sDisplayName;
private String sDescription = null;
private String sPdfFile = null;
private String sOriginalFile = null;
private boolean bIsDirectory;
/** Construct a new <code>IndexPageEntry</code> based on a file name.
* The file name is also used as display name.
*
* @param sFile the file name for this entry
* @param bIsDirectory true if this is a directory, false if it is a file
*/
public IndexPageEntry(String sFile, boolean bIsDirectory) {
this.sFile = sFile;
this.sDisplayName = sFile;
this.bIsDirectory = bIsDirectory;
}
/** Set the file name
*
* @param sFile the file name
*/
public void setFile(String sFile) {
this.sFile = sFile;
}
/** Set the display name for this entry. The display name is the
* name presented on the index page.
*
* @param sDisplayName the display name
*/
public void setDisplayName(String sDisplayName) {
this.sDisplayName = sDisplayName;
}
/** Set the description of this file (additional information about the file)
*
* @param sDescription the description
*/
public void setDescription(String sDescription) {
this.sDescription = sDescription;
}
/** Set the file name for a pdf file associated with this file
*
* @param sPdfFile the file name
*/
public void setPdfFile(String sPdfFile) {
this.sPdfFile = sPdfFile;
}
/** Set the file name for the original file
*
* @param sOriginalFile the origianl file name
*/
public void setOriginalFile(String sOriginalFile) {
this.sOriginalFile = sOriginalFile;
}
/** Get the file name
*
* @return the file name
*/
public String getFile() {
return sFile;
}
/** Get the display name
*
* @return the display name
*/
public String getDisplayName() {
return sDisplayName;
}
/** Get the description
*
* @return the description, or null if there is no description
*/
public String getDescription() {
return sDescription;
}
/** Get the pdf file name
*
* @return the file name or null if there is none
*/
public String getPdfFile() {
return sPdfFile;
}
/** Get the original file name
*
* @return the file name or null if there is none
*/
public String getOriginalFile() {
return sOriginalFile;
}
/** Check whether this is a file or a directory
*
* @return true for a directory, false for a file
*/
public boolean isDirectory() {
return bIsDirectory;
}
}

View file

@ -0,0 +1,55 @@
/************************************************************************
*
* MIMETypes.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.api;
/* Some helpers to handle the MIME types used by OOo
*/
public class MIMETypes {
// Various graphics formats, see
// http://api.openoffice.org/docs/common/ref/com/sun/star/graphic/MediaProperties.html#MimeType
public static final String PNG="image/png";
public static final String JPEG="image/jpeg";
public static final String GIF="image/gif";
public static final String TIFF="image/tiff";
public static final String BMP="image/bmp";
public static final String WMF="image/x-wmf";
public static final String EPS="image/x-eps";
// MIME type for SVM has changed
//public static final String SVM="image/x-svm";
public static final String SVM="application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"";
public static final String PDF="application/pdf";
// Desitination formats
public static final String XHTML="text/html";
public static final String XHTML_MATHML="application/xhtml+xml";
public static final String XHTML_MATHML_XSL="application/xml";
public static final String LATEX="application/x-latex";
public static final String BIBTEX="application/x-bibtex";
public static final String TEXT="text";
}

View file

@ -0,0 +1,53 @@
/************************************************************************
*
* OutputFile.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.api;
import java.io.OutputStream;
import java.io.IOException;
/** An <code>OutputFile</code> represent a single file in a
* {@link ConverterResult}, which is output from a {@link Converter}
* implementation.
*/
public interface OutputFile {
/** Writes the <code>OutputFile</code> to an <code>OutputStream</code>.
*
* @param os <code>OutputStream</code> to which the content should be written
* @throws IOException if any I/O error occurs
*/
public void write(OutputStream os) throws IOException;
/** Returns the file name of the <code>OutputFile</code>. This includes
* the file extension and may also include a relative path, always using
* / as separator.
*
* @return the file name of this <code>OutputFile</code>
*/
public String getFileName();
}

View file

@ -0,0 +1,13 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>The package writer2latex.api</title>
</head>
<body>
<p>This package contains the api for using Writer2LaTeX.</p>
<p>The api consitst of a factory class and a number of interfaces, and is
used by the command line application as well as by the filters.
</body>
</html>

View file

@ -0,0 +1,61 @@
/************************************************************************
*
* StarMathConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.api;
//import java.io.InputStream;
//import java.io.IOException;
/** This is an interface for a converter, which offers conversion of
* a StarMath formula into LaTeX
* Instances of this interface are created using the
* {@link ConverterFactory}
*/
public interface StarMathConverter {
/** Get the configuration used when converting.
*
* @return the configuration used by this converter
*/
public Config getConfig();
/** Convert a StarMath formula
*
* @param sStarMathFormula is a string containing the StarMath formula
* @return a string containing the converted LaTeX formula
*/
public String convert(String sStarMathFormula);
/** Create a suitable LaTeX preamble to process the formulas converted so far
*
* @return a string containg the entire LaTeX preamble
*/
public String getPreamble();
}

View file

@ -0,0 +1,182 @@
/************************************************************************
*
* BatchConverterBase.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-10-15)
*
*/
package writer2latex.base;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import writer2latex.api.BatchConverter;
import writer2latex.api.BatchHandler;
import writer2latex.api.Converter;
import writer2latex.api.ConverterResult;
import writer2latex.api.IndexPageEntry;
import writer2latex.api.OutputFile;
import writer2latex.util.Misc;
/**
* Abstract base implementation of <code>writer2latex.api.BatchConverter</code>.
* The base implementation handles the traversal of directories and files, and
* leaves the handling of indexpages to the subclass.
*/
public abstract class BatchConverterBase implements BatchConverter {
private Converter converter;
public BatchConverterBase() {
converter = null;
}
// Partial implementation of the interface
public void setConverter(Converter converter) {
this.converter = converter;
}
public void convert(File source, File target, boolean bRecurse, BatchHandler handler) {
handler.startConversion();
convertDirectory(source, target, bRecurse, source.getName(), handler);
handler.endConversion();
}
protected abstract String getIndexFileName();
// Convert files and directories in the directory indir
// (return false if conversion has been cancelled by the BatchHandler)
private boolean convertDirectory(File indir, File outdir, boolean bRecurse, String sHeading, BatchHandler handler) {
handler.startDirectory(indir.getPath());
// Step 1: Get the directory
File[] contents = indir.listFiles();
int nLen = contents.length;
IndexPageEntry[] entries = new IndexPageEntry[nLen];
// Step 2: Traverse subdirectories, if allowed
if (bRecurse) {
String sUplink = getConfig().getOption("uplink");
for (int i=0; i<nLen; i++) {
if (contents[i].isDirectory()) {
getConfig().setOption("uplink","../"+getIndexFileName());
File newOutdir = new File(outdir,contents[i].getName());
String sNewHeading = sHeading + " - " + contents[i].getName();
boolean bResult = convertDirectory(contents[i],newOutdir,bRecurse,sNewHeading,handler);
getConfig().setOption("uplink", sUplink);
if (!bResult) { return false; }
// Create entry for this subdirectory
IndexPageEntry entry = new IndexPageEntry(Misc.makeHref(contents[i].getName()+"/"+getIndexFileName()),true);
entry.setDisplayName(contents[i].getName());
entries[i]=entry;
}
}
}
// Step 3: Traverse documents, if we have a converter
if (converter!=null) {
String sUplink = getConfig().getOption("uplink");
for (int i=0; i<nLen; i++) {
if (contents[i].isFile()) {
getConfig().setOption("uplink",getIndexFileName());
String sLinkFile = convertFile(contents[i],outdir,handler);
getConfig().setOption("uplink", sUplink);
if (sLinkFile!=null) {
// Create entry for this file
IndexPageEntry entry = new IndexPageEntry(Misc.makeHref(sLinkFile),false);
entry.setDisplayName(Misc.removeExtension(sLinkFile));
entries[i]=entry;
if (handler.cancel()) { return false; }
}
}
}
}
// Step 4: Create and write out the index file
OutputFile indexFile = createIndexFile(sHeading, entries);
if (!outdir.exists()) { outdir.mkdirs(); }
boolean bSuccess = true;
File outfile = new File(outdir,indexFile.getFileName());
try {
FileOutputStream fos = new FileOutputStream(outfile);
indexFile.write(fos);
fos.flush();
fos.close();
} catch (Exception writeExcept) {
bSuccess = false;
}
handler.endDirectory(indir.getPath(), bSuccess);
return !handler.cancel();
}
// Convert a single file, returning the name of the master file
// Returns null if conversion fails
private String convertFile(File infile, File outdir, BatchHandler handler) {
handler.startFile(infile.getPath());
// Currently we discriminate based on file extension
if (!(infile.getName().endsWith(".odt") || infile.getName().endsWith(".ods") || infile.getName().endsWith(".odp"))) {
handler.endFile(infile.getPath(),false);
return null;
}
// Do conversion
ConverterResult dataOut = null;
try {
// The target file name is always the same as the source
dataOut = converter.convert(infile,Misc.removeExtension(infile.getName()));
}
catch (FileNotFoundException e) {
handler.endFile(infile.getPath(),false);
return null;
}
catch (IOException e) {
handler.endFile(infile.getPath(),false);
return null;
}
// Write out files
if (!outdir.exists()) { outdir.mkdirs(); }
try {
dataOut.write(outdir);
}
catch (IOException e) {
handler.endFile(infile.getPath(),false);
return null;
}
handler.endFile(infile.getPath(),true);
return dataOut.getMasterDocument().getFileName();
}
}

View file

@ -0,0 +1,44 @@
/************************************************************************
*
* BooleanOption.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-09-08)
*
*/
package writer2latex.base;
// A BooleanOption interprets the values as booleans
public class BooleanOption extends Option {
private boolean bValue;
public boolean getValue() { return bValue; }
public void setString(String sValue) {
super.setString(sValue);
bValue = "true".equals(sValue);
}
public BooleanOption(String sName, String sDefaultValue) {
super(sName,sDefaultValue);
}
}

View file

@ -0,0 +1,170 @@
/************************************************************************
*
* ConfigBase.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-22)
*
*/
package writer2latex.base;
/** Base implementation of writer2latex.api.Config
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.w3c.dom.DOMImplementation;
import writer2latex.xmerge.NewDOMDocument;
public abstract class ConfigBase implements writer2latex.api.Config {
protected abstract int getOptionCount();
protected abstract String getDefaultConfigPath();
protected Option[] options;
public ConfigBase() {
options = new Option[getOptionCount()];
}
public void setOption(String sName,String sValue) {
if (sName!=null && sValue!=null) {
for (int j=0; j<getOptionCount(); j++) {
if (sName.equals(options[j].getName())) {
options[j].setString(sValue);
break;
}
}
}
}
public String getOption(String sName) {
if (sName!=null) {
for (int j=0; j<getOptionCount(); j++) {
if (sName.equals(options[j].getName())) {
return options[j].getString();
}
}
}
return null;
}
public void readDefaultConfig(String sName) throws IllegalArgumentException {
InputStream is = this.getClass().getResourceAsStream(getDefaultConfigPath()+sName);
if (is==null) {
throw new IllegalArgumentException("The internal configuration '"+sName+ "' does not exist");
}
try {
read(is);
}
catch (IOException e) {
// This would imply a bug in the configuration file!
throw new IllegalArgumentException("The internal configuration '"+sName+ "' is invalid");
}
}
/** <p>Read configuration from a specified input stream</p>
* @param is the input stream to read the configuration from
*/
public void read(InputStream is) throws IOException {
NewDOMDocument doc = new NewDOMDocument("config",".xml");
doc.read(is); // may throw an IOException
Document dom = doc.getContentDOM();
if (dom==null) {
throw new IOException("Failed to parse configuration");
}
Node root = dom.getDocumentElement();
Node child = root.getFirstChild();
while (child!=null) {
if (child.getNodeType()==Node.ELEMENT_NODE) {
Element elm = (Element)child;
if (elm.getTagName().equals("option")) {
String sName = elm.getAttribute("name");
String sValue = elm.getAttribute("value");
if (sName!="") { setOption(sName,sValue); }
}
else {
readInner(elm);
}
}
child = child.getNextSibling();
}
}
public void read(File file) throws IOException {
read(new FileInputStream(file));
}
/** Read configuration information from an xml element.
* The subclass must define this to read richer configuration data
*/
protected abstract void readInner(Element elm);
public void write(OutputStream os) throws IOException {
NewDOMDocument doc = new NewDOMDocument("config",".xml");
Document dom = null;
try {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
DOMImplementation domImpl = builder.getDOMImplementation();
dom = domImpl.createDocument("","config",null);
}
catch (Throwable t) {
t.printStackTrace();
}
Element rootElement = dom.getDocumentElement();
for (int i=0; i<getOptionCount(); i++) {
Element optionNode = dom.createElement("option");
optionNode.setAttribute("name",options[i].getName());
optionNode.setAttribute("value",options[i].getString());
rootElement.appendChild(optionNode);
}
writeInner(dom);
doc.setContentDOM(dom);
doc.write(os); // may throw an IOException
}
public void write(File file) throws IOException {
write(new FileOutputStream(file));
}
/** Write configuration information to an xml document.
* The subclass must define this to write richer configuration data
*/
protected abstract void writeInner(Document dom);
}

View file

@ -0,0 +1,117 @@
/************************************************************************
*
* ConverterBase.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.base;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.IOException;
import writer2latex.api.GraphicConverter;
import writer2latex.api.Converter;
import writer2latex.api.ConverterResult;
import writer2latex.api.OutputFile;
import writer2latex.office.ImageLoader;
import writer2latex.office.MetaData;
import writer2latex.office.OfficeReader;
import writer2latex.xmerge.EmbeddedObject;
import writer2latex.xmerge.ConvertData;
import writer2latex.xmerge.OfficeDocument;
/**<p>Abstract base implementation of <code>writer2latex.api.Converter</code></p>
*/
public abstract class ConverterBase implements Converter {
// Helper
protected GraphicConverter graphicConverter;
// The source document
protected OfficeDocument odDoc;
protected OfficeReader ofr;
protected MetaData metaData;
protected ImageLoader imageLoader;
// The output file(s)
protected String sTargetFileName;
protected ConvertData convertData;
// Constructor
public ConverterBase() {
graphicConverter = null;
convertData = new ConvertData();
}
// Implement the interface
public void setGraphicConverter(GraphicConverter graphicConverter) {
this.graphicConverter = graphicConverter;
}
// Provide a do noting fallback method
public void readTemplate(InputStream is) throws IOException { }
// Provide a do noting fallback method
public void readTemplate(File file) throws IOException { }
public ConverterResult convert(File source, String sTargetFileName) throws FileNotFoundException,IOException {
return convert(new FileInputStream(source), sTargetFileName);
}
public ConverterResult convert(InputStream is, String sTargetFileName) throws IOException {
// Read document
odDoc = new OfficeDocument("InFile");
odDoc.read(is);
ofr = new OfficeReader(odDoc,false);
metaData = new MetaData(odDoc);
imageLoader = new ImageLoader(odDoc,sTargetFileName,true);
imageLoader.setGraphicConverter(graphicConverter);
// Prepare output
this.sTargetFileName = sTargetFileName;
convertData.reset();
convertInner();
return convertData;
}
// The subclass must provide the implementation
public abstract void convertInner() throws IOException;
public MetaData getMetaData() { return metaData; }
public ImageLoader getImageLoader() { return imageLoader; }
public void addDocument(OutputFile doc) { convertData.addDocument(doc); }
public EmbeddedObject getEmbeddedObject(String sHref) {
return odDoc.getEmbeddedObject(sHref);
}
}

View file

@ -0,0 +1,40 @@
/************************************************************************
*
* IntegerOption.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-09-08)
*
*/
package writer2latex.base;
// An IntegerOption must always be subclassed (must override setString)
public abstract class IntegerOption extends Option {
protected int nValue;
public int getValue() { return nValue; }
public IntegerOption(String sName, String sDefaultValue) {
super(sName,sDefaultValue);
}
}

View file

@ -0,0 +1,45 @@
/************************************************************************
*
* Option.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-09-08)
*
*/
package writer2latex.base;
// The mother of all options; reads and writes string values
public class Option {
protected String sValue;
private String sName;
public void setString(String sValue) { this.sValue = sValue; }
public String getString() { return sValue; }
public String getName() { return sName; }
public Option(String sName, String sDefaultValue) {
this.sName = sName;
setString(sDefaultValue);
}
}

View file

@ -0,0 +1,17 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>The package writer2latex.base</title>
</head>
<body>
<p>This package contains (abstract) base implementations of some of the
interfaces in writer2latex.api</p>
<p>They are intended to be subclassed by converters into specific formats
e.g. LaTeX, xhtml</p>
</body>
</html>

View file

@ -0,0 +1,238 @@
/************************************************************************
*
* BibTeXDocument.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2009 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2009-02-17)
*
*/
package writer2latex.bibtex;
import writer2latex.xmerge.Document;
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import writer2latex.api.ConverterFactory;
import writer2latex.latex.LaTeXConfig;
import writer2latex.latex.i18n.ClassicI18n;
import writer2latex.latex.i18n.I18n;
import writer2latex.util.ExportNameCollection;
//import writer2latex.util.Misc;
import writer2latex.office.BibMark;
/**
* <p>Class representing a BibTeX document.</p>
*
*/
public class BibTeXDocument implements Document {
private static final String FILE_EXTENSION = ".bib";
private String sName;
private Hashtable entries = new Hashtable();
private ExportNameCollection exportNames = new ExportNameCollection(true);
private I18n i18n;
/**
* <p>Constructs a new BibTeX Document.</p>
*
* <p>This new document is empty. Bibliographic data must added
* using the <code>put</code> method.</p>
*
* @param sName The name of the <code>BibTeXDocument</code>.
*/
public BibTeXDocument(String sName) {
this.sName = trimDocumentName(sName);
// Use default config (only ascii, no extra font packages)
i18n = new ClassicI18n(new LaTeXConfig());
}
/**
* <p>This method is supposed to read <code>byte</code> data from the InputStream.
* Currently it does nothing, since we don't need it.</p>
*
* @param is InputStream containing a BibTeX data file.
*
* @throws IOException In case of any I/O errors.
*/
public void read(InputStream is) throws IOException {
// Do nothing.
}
/**
* <p>Returns the <code>Document</code> name with no file extension.</p>
*
* @return The <code>Document</code> name with no file extension.
*/
public String getName() {
return sName;
}
/**
* <p>Returns the <code>Document</code> name with file extension.</p>
*
* @return The <code>Document</code> name with file extension.
*/
public String getFileName() {
return new String(sName + FILE_EXTENSION);
}
/**
* <p>Writes out the <code>Document</code> content to the specified
* <code>OutputStream</code>.</p>
*
* <p>This method may not be thread-safe.
* Implementations may or may not synchronize this
* method. User code (i.e. caller) must make sure that
* calls to this method are thread-safe.</p>
*
* @param os <code>OutputStream</code> to write out the
* <code>Document</code> content.
*
* @throws IOException If any I/O error occurs.
*/
public void write(OutputStream os) throws IOException {
// BibTeX files are plain ascii
OutputStreamWriter osw = new OutputStreamWriter(os,"ASCII");
osw.write("%% This file was converted to BibTeX by Writer2BibTeX ver. "+ConverterFactory.getVersion()+".\n");
osw.write("%% See http://www.hj-gym.dk/~hj/writer2latex for more info.\n");
osw.write("\n");
Enumeration enumeration = entries.elements();
while (enumeration.hasMoreElements()) {
BibMark entry = (BibMark) enumeration.nextElement();
osw.write("@");
osw.write(entry.getEntryType().toUpperCase());
osw.write("{");
osw.write(exportNames.getExportName(entry.getIdentifier()));
osw.write(",\n");
for (int i=0; i<BibMark.FIELD_COUNT; i++) {
String sValue = entry.getField(i);
if (sValue!=null) {
if (i==BibMark.AUTHOR || i==BibMark.EDITOR) {
// OOo uses ; to separate authors and editors - BibTeX uses and
sValue = sValue.replaceAll(";" , " and ");
}
osw.write(" ");
osw.write(getFieldName(i).toUpperCase());
osw.write(" = {");
for (int j=0; j<sValue.length(); j++) {
String s = i18n.convert(Character.toString(sValue.charAt(j)),false,"en");
if (s.charAt(0)=='\\') { osw.write("{"); }
osw.write(s);
if (s.charAt(0)=='\\') { osw.write("}"); }
}
osw.write("},\n");
}
}
osw.write("}\n\n");
}
osw.flush();
osw.close();
}
/**
* <p> Return BibTeX name of field </p>
*/
public static final String getFieldName(int nField) {
switch (nField) {
case BibMark.ADDRESS: return "address";
case BibMark.ANNOTE: return "annote";
case BibMark.AUTHOR: return "author";
case BibMark.BOOKTITLE: return "booktitle";
case BibMark.CHAPTER: return "chapter";
// case BibMark.CROSSREF: return "croosref"; // not in OOo
case BibMark.EDITION: return "edition";
case BibMark.EDITOR: return "editor";
case BibMark.HOWPUBLISHED: return "howpublished";
case BibMark.INSTITUTION: return "institution";
case BibMark.JOURNAL: return "journal";
// case BibMark.KEY: return "key"; // not in OOo
case BibMark.MONTH: return "month";
case BibMark.NOTE: return "note";
case BibMark.NUMBER: return "number";
case BibMark.ORGANIZATIONS: return "organization";
case BibMark.PAGES: return "pages";
case BibMark.PUBLISHER: return "publisher";
case BibMark.SCHOOL: return "school";
case BibMark.SERIES: return "series";
case BibMark.TITLE: return "title";
case BibMark.REPORT_TYPE: return "type";
case BibMark.VOLUME: return "volume";
case BibMark.YEAR: return "year";
case BibMark.URL: return "url";
case BibMark.CUSTOM1: return "custom1";
case BibMark.CUSTOM2: return "custom2";
case BibMark.CUSTOM3: return "custom3";
case BibMark.CUSTOM4: return "custom4";
case BibMark.CUSTOM5: return "custom5";
case BibMark.ISBN: return "isbn";
default: return null;
}
}
/*
* <p>Check if this entry exists</p>
*/
public boolean containsKey(String sIdentifier) {
return entries.containsKey(sIdentifier);
}
/*
* <p>Add an entry</p>
*/
public void put(BibMark entry) {
entries.put(entry.getIdentifier(),entry);
exportNames.addName(entry.getIdentifier());
}
/*
* <p>Get export name for an identifier</p>
*/
public String getExportName(String sIdentifier) {
return exportNames.getExportName(sIdentifier);
}
/*
* Utility method to make sure the document name is stripped of any file
* extensions before use.
*/
private String trimDocumentName(String name) {
String temp = name.toLowerCase();
if (temp.endsWith(FILE_EXTENSION)) {
// strip the extension
int nlen = name.length();
int endIndex = nlen - FILE_EXTENSION.length();
name = name.substring(0,endIndex);
}
return name;
}
}

View file

@ -0,0 +1,91 @@
/************************************************************************
*
* Converter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2001-2008 by Henrik Just
*
* All Rights Reserved.
*
* version 1.0 (2008-11-22)
*
*/
package writer2latex.bibtex;
import writer2latex.api.Config;
//import writer2latex.api.ConverterResult;
import writer2latex.base.ConverterBase;
import writer2latex.latex.LaTeXConfig;
import writer2latex.office.BibMark;
import writer2latex.office.XMLString;
import writer2latex.util.Misc;
//import writer2latex.xmerge.ConvertData;
//import writer2latex.xmerge.OfficeDocument;
import java.io.IOException;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* <p>BibTeX export</p>
*
* <p>This class extracts bibliographic information from an OpenDocument text file to a BibTeX data file.</p>
*
*/
public final class Converter extends ConverterBase {
// Configuration - TODO: Doesn't really use it - should use some fake config
private LaTeXConfig config;
public Config getConfig() { return config; }
// Constructor
public Converter() {
super();
config = new LaTeXConfig();
}
/**
* <p>Convert the data passed into the <code>InputStream</code>
* into BibTeX format.</p>
*
* @throws IOException If any I/O error occurs.
*/
public void convertInner() throws IOException {
sTargetFileName = Misc.trimDocumentName(sTargetFileName,".bib");
BibTeXDocument bibDoc = new BibTeXDocument(sTargetFileName);
// Collect all text:bibliography-mark elements from the content
Element doc = ofr.getContent();
NodeList list;
list = doc.getElementsByTagName(XMLString.TEXT_BIBLIOGRAPHY_MARK);
int nLen = list.getLength();
for (int i=0; i<nLen; i++) {
String sIdentifier = Misc.getAttribute(list.item(i),XMLString.TEXT_IDENTIFIER);
if (sIdentifier!=null && !bibDoc.containsKey(sIdentifier)) {
bibDoc.put(new BibMark(list.item(i)));
}
}
// Add result
convertData.addDocument(bibDoc);
}
}

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>The package writer2latex.bibtex</title>
</head>
<body>
<p>This package contains BibTeX specific code.</p>
<p>It contains a <code>writerlatex.api.Converter</code> implementation for
conversion into BibTeX, as well as code to convert to BibTeX as part of a
conversion into LaTeX.</p>
</body>
</html>

View file

@ -0,0 +1,157 @@
/************************************************************************
*
* BibConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-09-08)
*
*/
package writer2latex.latex;
import org.w3c.dom.Element;
import writer2latex.bibtex.BibTeXDocument;
import writer2latex.latex.util.Context;
import writer2latex.office.BibMark;
import writer2latex.office.OfficeReader;
import writer2latex.office.XMLString;
import writer2latex.util.Misc;
/**
* This class handles the bibliography. The result depends on these
* configuration options. The citations will be treated like this:
* <ul>
* <li><code>use_bibtex</code>: If true, citations will be exported as \cite
* commands. If false, citations will be exported as static text</li>
* </ul>
* The bibliography will be treated like this:
* <ul>
* <li><code>use_index</code>: If false, the bibliography will be omitted</li>
* <li><code>use_bibtex</code> true and <code>external_bibtex_files</code>
* empty: The citations will be exported to a BibTeX file, which will be used
* for the bibliography</li>
* <li><code>use_bibtex</code> true and <code>external_bibtex_files</code>
* non-empty: The citations will be not be exported to a BibTeX file, the
* files referred to by the option will be used instead</li>
* <li><code>use_bibtex</code> false: The bibliography will be exported as
* static text.
* <li><code>bibtex_style</code> If BibTeX is used, this style will be applied
* </ul>
*/
public class BibConverter extends ConverterHelper {
private BibTeXDocument bibDoc;
/** Construct a new BibConverter.
* @param config the configuration to use
* @param palette the ConverterPalette to use
*/
public BibConverter(OfficeReader ofr,LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
}
/** Append declarations needed by the <code>BibConverter</code> to
* the preamble.
* @param pack the LaTeXDocumentPortion to which
* declarations of packages should be added (\\usepackage).
* @param decl the LaTeXDocumentPortion to which
* other declarations should be added.
*/
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
// Currently nothing; may add support for eg. natbib later
}
/** Process a bibliography (text:bibliography tag)
* @param node The element containing the Bibliography
* @param ldp the LaTeXDocumentPortion to which LaTeX code should be added
* @param oc the current context
*/
public void handleBibliography (Element node, LaTeXDocumentPortion ldp, Context oc) {
if (config.noIndex()) { return; }
if (config.useBibtex()) {
// Use the style given in the configuration
// TODO: Create a bst file from the settings of the text:bibliography
ldp.append("\\bibliographystyle{")
.append(config.bibtexStyle())
.append("}").nl();
// Use BibTeX file from configuration, or exported BibTeX file
if (config.externalBibtexFiles().length()>0) {
ldp.append("\\bibliography{")
.append(config.externalBibtexFiles())
.append("}").nl();
}
else {
if (bibDoc==null) { bibDoc = new BibTeXDocument(palette.getOutFileName()); }
ldp.append("\\bibliography{")
.append(bibDoc.getName())
.append("}").nl();
}
}
else { // typeset current content
Element body = Misc.getChildByTagName(node,XMLString.TEXT_INDEX_BODY);
if (body!=null) {
Element title = Misc.getChildByTagName(body,XMLString.TEXT_INDEX_TITLE);
if (title!=null) { palette.getBlockCv().traverseBlockText(title,ldp,oc); }
palette.getBlockCv().traverseBlockText(body,ldp,oc);
}
}
}
/** Process a Bibliography Mark (text:bibliography-mark tag)
* @param node The element containing the Mark
* @param ldp the LaTeXDocumentPortion to which LaTeX code should be added
* @param oc the current context
*/
public void handleBibliographyMark(Element node, LaTeXDocumentPortion ldp, Context oc) {
if (config.useBibtex()) {
String sIdentifier = node.getAttribute(XMLString.TEXT_IDENTIFIER);
if (sIdentifier!=null) {
if (config.externalBibtexFiles().length()==0) {
if (bibDoc==null) { bibDoc = new BibTeXDocument(palette.getOutFileName()); }
if (!bibDoc.containsKey(sIdentifier)) {
bibDoc.put(new BibMark(node));
}
}
ldp.append("\\cite{")
.append(bibDoc.getExportName(sIdentifier))
.append("}");
}
}
else { // use current value
palette.getInlineCv().traverseInlineText(node,ldp,oc);
}
}
/** Get the BibTeX document, if any (the document is only created if it's
* specified in the configuration *and* the document contains bibliographic
* data *and* the configuration does not specify external BibTeX files
* @return the BiBTeXDocument, or null if it does not exist).
*/
public BibTeXDocument getBibTeXDocument () {
return bibDoc;
}
}

View file

@ -0,0 +1,378 @@
/************************************************************************
*
* BlockConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.latex;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import writer2latex.latex.util.BeforeAfter;
import writer2latex.latex.util.Context;
//import writer2latex.latex.util.HeadingMap;
import writer2latex.latex.util.StyleMap;
import writer2latex.office.ListStyle;
import writer2latex.office.OfficeReader;
//import writer2latex.office.TableReader;
import writer2latex.office.XMLString;
import writer2latex.util.Misc;
/**
* <p>This class handles basic block content, including the main text body,
* sections, tables, lists, headings and paragraphs.</p>
*/
public class BlockConverter extends ConverterHelper {
public BlockConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
// currently do nothing..
}
/** <p> Traverse block text (eg. content of body, section, list item).
* This is traversed in logical order and dedicated handlers take care of
* each block element.</p>
* <p> (Note: As a rule, all handling of block level elements should add a
* newline to the LaTeX document at the end of the block)</p>
* @param node The element containing the block text
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void traverseBlockText(Element node, LaTeXDocumentPortion ldp, Context oc) {
Context ic = (Context) oc.clone();
// The current paragraph block:
StyleMap blockMap = config.getParBlockStyleMap();
String sBlockName = null;
if (node.hasChildNodes()) {
NodeList list = node.getChildNodes();
int nLen = list.getLength();
for (int i = 0; i < nLen; i++) {
Node childNode = list.item(i);
if (childNode.getNodeType() == Node.ELEMENT_NODE) {
Element child = (Element)childNode;
String sTagName = child.getTagName();
// Start/End a paragraph block (not in tables)
if (!ic.isInTable()) {
if (sTagName.equals(XMLString.TEXT_P)) {
String sStyleName = ofr.getParStyles().getDisplayName(child.getAttribute(XMLString.TEXT_STYLE_NAME));
if (sBlockName!=null && !blockMap.isNext(sBlockName,sStyleName)) {
// end current block
String sAfter = blockMap.getAfter(sBlockName);
if (sAfter.length()>0) ldp.append(sAfter).nl();
sBlockName = null;
ic.setVerbatim(false);
}
if (sBlockName==null && blockMap.contains(sStyleName)) {
// start a new block
sBlockName = sStyleName;
String sBefore = blockMap.getBefore(sBlockName);
if (sBefore.length()>0) ldp.append(sBefore).nl();
ic.setVerbatim(blockMap.getVerbatim(sStyleName));
}
}
else if (sBlockName!=null) {
// non-paragraph: end current block
String sAfter = blockMap.getAfter(sBlockName);
if (sAfter.length()>0) ldp.append(sAfter).nl();
sBlockName = null;
ic.setVerbatim(false);
}
}
palette.getFieldCv().flushReferenceMarks(ldp,ic);
palette.getIndexCv().flushIndexMarks(ldp,ic);
palette.getInfo().addDebugInfo(child,ldp);
// Basic block content; handle by this class
if (sTagName.equals(XMLString.TEXT_P)) {
// is this a caption?
String sSequence = ofr.getSequenceName(child);
if (ofr.isFigureSequenceName(sSequence)) {
palette.getDrawCv().handleCaption(child,ldp,ic);
}
else if (ofr.isTableSequenceName(sSequence)) {
// Next node *should* be a table
if (i+1<nLen && Misc.isElement(list.item(i+1),XMLString.TABLE_TABLE)) {
// Found table with caption above
palette.getTableCv().handleTable((Element)list.item(++i),child,true,ldp,ic);
}
else {
// Found lonely caption
palette.getTableCv().handleCaption(child,ldp,ic);
}
}
else {
palette.getParCv().handleParagraph(child,ldp,ic,i==nLen-1);
}
}
else if(sTagName.equals(XMLString.TEXT_H)) {
palette.getHeadingCv().handleHeading(child,ldp,ic);
}
else if (sTagName.equals(XMLString.TEXT_LIST)) { // oasis
handleList(child,ldp,ic);
}
else if (sTagName.equals(XMLString.TEXT_UNORDERED_LIST)) {
handleList(child,ldp,ic);
}
else if (sTagName.equals(XMLString.TEXT_ORDERED_LIST)) {
handleList(child,ldp,ic);
}
else if (sTagName.equals(XMLString.TABLE_TABLE)) {
// Next node *could* be a caption
if (i+1<nLen && Misc.isElement(list.item(i+1),XMLString.TEXT_P) &&
ofr.isTableSequenceName(ofr.getSequenceName((Element)list.item(i+1)))) {
// Found table with caption below
palette.getTableCv().handleTable(child,(Element)list.item(++i),false,ldp,oc);
}
else {
// Found table without caption
palette.getTableCv().handleTable(child,null,false,ldp,oc);
}
}
else if (sTagName.equals(XMLString.TABLE_SUB_TABLE)) {
palette.getTableCv().handleTable(child,null,true,ldp,ic);
}
else if (sTagName.equals(XMLString.TEXT_SECTION)) {
palette.getSectionCv().handleSection(child,ldp,ic);
}
// Draw elements may appear in block context if they are
// anchored to page
else if (sTagName.startsWith("draw:")) {
palette.getDrawCv().handleDrawElement(child,ldp,ic);
}
// Indexes
else if (sTagName.equals(XMLString.TEXT_TABLE_OF_CONTENT)) {
palette.getIndexCv().handleTOC(child,ldp,ic);
}
else if (sTagName.equals(XMLString.TEXT_ILLUSTRATION_INDEX)) {
palette.getIndexCv().handleLOF(child,ldp,ic);
}
else if (sTagName.equals(XMLString.TEXT_TABLE_INDEX)) {
palette.getIndexCv().handleLOT(child,ldp,ic);
}
else if (sTagName.equals(XMLString.TEXT_OBJECT_INDEX)) {
palette.getIndexCv().handleObjectIndex(child,ldp,ic);
}
else if (sTagName.equals(XMLString.TEXT_USER_INDEX)) {
palette.getIndexCv().handleUserIndex(child,ldp,ic);
}
else if (sTagName.equals(XMLString.TEXT_ALPHABETICAL_INDEX)) {
palette.getIndexCv().handleAlphabeticalIndex(child,ldp,ic);
}
else if (sTagName.equals(XMLString.TEXT_BIBLIOGRAPHY)) {
palette.getBibCv().handleBibliography(child,ldp,ic);
}
// Sequence declarations appear in the main text body (before the actual content)
else if (sTagName.equals(XMLString.TEXT_SEQUENCE_DECLS)) {
palette.getFieldCv().handleSequenceDecls(child);
}
// other tags are ignored
}
}
}
if (!oc.isInTable() && sBlockName!=null) {
// end current block
String sAfter = blockMap.getAfter(sBlockName);
if (sAfter.length()>0) ldp.append(sAfter).nl();
sBlockName = null;
}
palette.getFieldCv().flushReferenceMarks(ldp,ic);
palette.getIndexCv().flushIndexMarks(ldp,ic);
}
/** <p> Process a list (text:ordered-lst or text:unordered-list tag)</p>
* @param node The element containing the list
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleList(Element node, LaTeXDocumentPortion ldp, Context oc) {
// Set up new context
Context ic = (Context) oc.clone();
ic.incListLevel();
// Get the style name, if we don't know it already
if (ic.getListStyleName()==null) {
ic.setListStyleName(node.getAttribute(XMLString.TEXT_STYLE_NAME));
}
// Use the style to determine the type of list
ListStyle style = ofr.getListStyle(ic.getListStyleName());
boolean bOrdered = style!=null && style.isNumber(ic.getListLevel());
// If the list contains headings, ignore it!
if (ic.isIgnoreLists() || listContainsHeadings(node)) {
ic.setIgnoreLists(true);
traverseList(node,ldp,ic);
return;
}
// Apply the style
BeforeAfter ba = new BeforeAfter();
palette.getListSc().applyListStyle(ic.getListStyleName(),ic.getListLevel(),
bOrdered,"true".equals(node.getAttribute(XMLString.TEXT_CONTINUE_NUMBERING)),
ba);
// Export the list
if (ba.getBefore().length()>0) { ldp.append(ba.getBefore()).nl(); }
traverseList(node,ldp,ic);
if (ba.getAfter().length()>0) { ldp.append(ba.getAfter()).nl(); }
}
/*
* Process the contents of a list
*/
private void traverseList (Element node, LaTeXDocumentPortion ldp, Context oc) {
if (node.hasChildNodes()) {
NodeList list = node.getChildNodes();
int nLen = list.getLength();
for (int i = 0; i < nLen; i++) {
Node child = list.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
String nodeName = child.getNodeName();
palette.getInfo().addDebugInfo((Element)child,ldp);
if (nodeName.equals(XMLString.TEXT_LIST_ITEM)) {
handleListItem((Element)child,ldp,oc);
}
if (nodeName.equals(XMLString.TEXT_LIST_HEADER)) {
handleListItem((Element)child,ldp,oc);
}
}
}
}
}
private void handleListItem(Element node, LaTeXDocumentPortion ldp, Context oc) {
// Are we ignoring this list?
if (oc.isIgnoreLists()) {
traverseBlockText(node,ldp,oc);
return;
}
// Apply the style
BeforeAfter ba = new BeforeAfter();
palette.getListSc().applyListItemStyle(
oc.getListStyleName(), oc.getListLevel(),
node.getNodeName().equals(XMLString.TEXT_LIST_HEADER),
"true".equals(node.getAttribute(XMLString.TEXT_RESTART_NUMBERING)),
Misc.getPosInteger(node.getAttribute(XMLString.TEXT_START_VALUE),1)-1,
ba);
// export the list item
if (ba.getBefore().length()>0) {
ldp.append(ba.getBefore());
if (config.formatting()>=LaTeXConfig.CONVERT_MOST) { ldp.nl(); }
}
traverseBlockText(node,ldp,oc);
if (ba.getAfter().length()>0) { ldp.append(ba.getAfter()).nl(); }
}
/*
* Helper: Check to see, if this list contains headings
* (in that case we will ignore the list!)
*/
private boolean listContainsHeadings (Node node) {
if (node.hasChildNodes()) {
NodeList nList = node.getChildNodes();
int len = nList.getLength();
for (int i = 0; i < len; i++) {
Node child = nList.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
String nodeName = child.getNodeName();
if (nodeName.equals(XMLString.TEXT_LIST_ITEM)) {
if (listItemContainsHeadings(child)) return true;
}
if (nodeName.equals(XMLString.TEXT_LIST_HEADER)) {
if (listItemContainsHeadings(child)) return true;
}
}
}
}
return false;
}
private boolean listItemContainsHeadings(Node node) {
if (node.hasChildNodes()) {
NodeList nList = node.getChildNodes();
int len = nList.getLength();
for (int i = 0; i < len; i++) {
Node child = nList.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
String nodeName = child.getNodeName();
if(nodeName.equals(XMLString.TEXT_H)) {
return true;
}
if (nodeName.equals(XMLString.TEXT_LIST)) {
if (listContainsHeadings(child)) return true;
}
if (nodeName.equals(XMLString.TEXT_ORDERED_LIST)) {
if (listContainsHeadings(child)) return true;
}
if (nodeName.equals(XMLString.TEXT_UNORDERED_LIST)) {
if (listContainsHeadings(child)) return true;
}
}
}
}
return false;
}
}

View file

@ -0,0 +1,163 @@
/************************************************************************
*
* CaptionConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.latex;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import writer2latex.latex.util.Context;
import writer2latex.office.OfficeReader;
import writer2latex.office.XMLString;
/**
* <p>This class converts captions (for figures and tables) to LaTeX.</p>
* <p>Packages:
* <ul><li>caption.sty is used implement non-floating captions</li></ul>
* <p>Options:
* <ul><li>use_caption is a boolean option to determine whether or not
* to use caption.sty. If this option is set to false, a simple definition of
* \captionof (borrowed from capt-of.sty) is inserted in the preamble</li></ul>
* <p>TODO: Implement formatting of captions using the features of caption.sty
* (only if formatting>=CONVERT_BASIC)
*/
public class CaptionConverter extends ConverterHelper {
private boolean bNeedCaptionOf = false;
private Element seqField = null; // the sequence field within the current caption
public CaptionConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
if (bNeedCaptionOf) {
if (config.useCaption()) {
pack.append("\\usepackage{caption}").nl();
}
else { // use definition borrowed from capt-of.sty
decl.append("% Non-floating captions").nl()
.append("\\makeatletter").nl()
.append("\\newcommand\\captionof[1]{\\def\\@captype{#1}\\caption}").nl()
.append("\\makeatother").nl();
}
}
}
/**
* <p> Process content of a text:p tag as a caption body (inluding label)</p>
* @param node The text:p element node containing the caption
* @param ldp The <code>LaTeXDocumentPortion</code> to add LaTeX code to
* @param oc The current context
* @param bIsCaptionOf true if this is caption uses captionof
*/
public void handleCaptionBody(Element node,LaTeXDocumentPortion ldp, Context oc, boolean bIsCaptionOf) {
bNeedCaptionOf|=bIsCaptionOf;
// Get rid of the caption label before converting
removeCaptionLabel(node,0);
Element label = seqField;
seqField = null;
// Get the stylename of the paragraph and push the font used
String sStyleName = node.getAttribute(XMLString.TEXT_STYLE_NAME);
palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(ofr.getParStyle(sStyleName)));
if (palette.getHeadingCv().containsElements(node)) {
ldp.append("[");
palette.getInlineCv().traversePlainInlineText(node,ldp,oc);
ldp.append("]");
}
// Update context before traversing text
Context ic = (Context) oc.clone();
ldp.append("{");
palette.getInlineCv().traverseInlineText(node,ldp,ic);
ldp.append("}").nl();
// Insert label
palette.getFieldCv().handleSequence(label,ldp,oc);
// Flush any index marks
palette.getIndexCv().flushIndexMarks(ldp,oc);
// pop the font name
palette.getI18n().popSpecialTable();
}
// In OpenDocument a caption is an ordinary paragraph with a text:seqence
// element. For example
// Table <text:sequence>3</text:sequence>: Caption text
// The first part is the caption label which is autogenerated by LaTeX.
// Before converting, we remove this in 3 steps:
// nStep = 0: Remove all text before the text:sequence
// nStep = 1: Remove all text up to the first alphanumeric character
// after the text:sequence
// nStep = 2: Finished!
private int removeCaptionLabel(Element node, int nStep) {
if (nStep==2) { return 2; }
Node removeMe = null;
Node child = node.getFirstChild();
while (child!=null) {
if (child.getNodeType()==Node.ELEMENT_NODE) {
if (nStep==0 && child.getNodeName().equals(XMLString.TEXT_SEQUENCE)) {
removeMe = child;
seqField = (Element) child; // remember me...
nStep = 1;
}
else if (nStep<2 && !OfficeReader.isDrawElement(child)) {
// draw elements (frames) should not be touched..
nStep = removeCaptionLabel((Element)child,nStep);
}
}
else if (child.getNodeType()==Node.TEXT_NODE) {
if (nStep==0) {
child.setNodeValue("");
}
else if (nStep==1) {
String s = child.getNodeValue();
int n = s.length();
for (int j=0; j<n; j++) {
if (Character.isLetterOrDigit(s.charAt(j))) {
child.setNodeValue(s.substring(j));
nStep = 2;
break;
}
}
}
}
child = child.getNextSibling();
}
if (removeMe!=null) { node.removeChild(removeMe); }
return nStep;
}
}

View file

@ -0,0 +1,507 @@
/************************************************************************
*
* CharStyleConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-12-03)
*
*/
package writer2latex.latex;
import java.util.Hashtable;
import writer2latex.util.*;
import writer2latex.office.*;
import writer2latex.latex.util.BeforeAfter;
import writer2latex.latex.util.Context;
import writer2latex.latex.util.StyleMap;
/** This class creates LaTeX code from OOo character formatting
Character formatting in OOo includes font, font effects/decorations and color.
In addition it includes color and language/country information, this is however handled
by the classes <code>writer2latex.latex.ColorConverter</code> and
<code>writer2latex.latex.style.I18n</code>
*/
public class CharStyleConverter extends StyleConverter {
// Cache of converted font declarations
private Hashtable fontDecls = new Hashtable();
// Which formatting should we export?
private boolean bIgnoreHardFontsize;
private boolean bIgnoreFontsize;
private boolean bIgnoreFont;
private boolean bIgnoreAll;
private boolean bUseUlem;
// Do we need actually use ulem.sty or \textsubscript?
private boolean bNeedUlem = false;
private boolean bNeedSubscript = false;
/** <p>Constructs a new <code>CharStyleConverter</code>.</p>
*/
public CharStyleConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
bUseUlem = config.useUlem();
// No character formatting at all:
bIgnoreAll = config.formatting()==LaTeXConfig.IGNORE_ALL;
// No font family or size:
bIgnoreFont = config.formatting()<=LaTeXConfig.IGNORE_MOST;
// No fontsize:
bIgnoreFontsize = config.formatting()<=LaTeXConfig.CONVERT_BASIC;
// No hard fontsize
bIgnoreHardFontsize = config.formatting()<=LaTeXConfig.CONVERT_MOST;
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
if (bNeedUlem) {
pack.append("\\usepackage[normalem]{ulem}").nl();
}
if (bNeedSubscript && !config.getTextAttributeStyleMap().contains("subscript")) {
decl.append("\\newcommand\\textsubscript[1]{\\ensuremath{{}_{\\text{#1}}}}").nl();
}
if (!styleNames.isEmpty()) {
decl.append("% Text styles").nl().append(declarations);
}
}
/** <p>Use a text style in LaTeX.</p>
* @param sName the name of the text style
* @param ba a <code>BeforeAfter</code> to put code into
*/
public void applyTextStyle(String sName, BeforeAfter ba, Context context) {
if (sName==null) { return; }
String sDisplayName = ofr.getTextStyles().getDisplayName(sName);
if (bIgnoreAll) {
// Even if all is ignored, we still apply style maps from config..
StyleMap sm = config.getTextStyleMap();
if (sm.contains(sDisplayName)) {
ba.add(sm.getBefore(sDisplayName),sm.getAfter(sDisplayName));
}
return;
}
// Style already converted?
if (styleMap.contains(sName)) {
ba.add(styleMap.getBefore(sName),styleMap.getAfter(sName));
context.updateFormattingFromStyle(ofr.getTextStyle(sName));
// it's verbatim if specified as such in the configuration
StyleMap sm = config.getTextStyleMap();
boolean bIsVerbatim = sm.contains(sDisplayName) && sm.getVerbatim(sDisplayName);
context.setVerbatim(bIsVerbatim);
context.setNoLineBreaks(bIsVerbatim);
return;
}
// The style may already be declared in the configuration:
StyleMap sm = config.getTextStyleMap();
if (sm.contains(sDisplayName)) {
styleMap.put(sName,sm.getBefore(sDisplayName),sm.getAfter(sDisplayName));
applyTextStyle(sName,ba,context);
return;
}
// Get the style, if it exists:
StyleWithProperties style = ofr.getTextStyle(sName);
if (style==null) {
styleMap.put(sName,"","");
applyTextStyle(sName,ba,context);
return;
}
// Convert automatic style
if (style.isAutomatic()) {
palette.getI18n().applyLanguage(style,false,true,ba);
applyFont(style,false,true,ba,context);
applyFontEffects(style,true,ba);
context.updateFormattingFromStyle(ofr.getTextStyle(sName));
return;
}
// Convert soft style:
// This must be converted relative to a blank context!
BeforeAfter baText = new BeforeAfter();
palette.getI18n().applyLanguage(style,false,true,baText);
applyFont(style,false,true,baText,new Context());
applyFontEffects(style,true,baText);
// declare the text style (\newcommand)
String sTeXName = styleNames.getExportName(ofr.getTextStyles().getDisplayName(sName));
styleMap.put(sName,"\\textstyle"+sTeXName+"{","}");
declarations.append("\\newcommand\\textstyle")
.append(sTeXName).append("[1]{")
.append(baText.getBefore()).append("#1").append(baText.getAfter())
.append("}").nl();
applyTextStyle(sName,ba,context);
}
public String getFontName(StyleWithProperties style) {
if (style!=null) {
String sName = style.getProperty(XMLString.STYLE_FONT_NAME);
if (sName!=null) {
FontDeclaration fd = ofr.getFontDeclaration(sName);
if (fd!=null) {
return fd.getFontFamily();
}
}
}
return null;
}
// Get the font name from a char style
public String getFontName(String sStyleName) {
return getFontName(ofr.getTextStyle(sStyleName));
}
/** <p>Apply hard character formatting (no inheritance).</p>
* <p>This is used in sections and {foot|end}notes</p>
* @param style the style to use
* @param ba the <code>BeforeAfter</code> to add LaTeX code to
*/
public void applyHardCharFormatting(StyleWithProperties style, BeforeAfter ba) {
palette.getI18n().applyLanguage(style,true,false,ba);
applyFont(style,true,false,ba,new Context());
if (!ba.isEmpty()) { ba.add(" ",""); }
}
/** <p>Apply all font attributes (family, series, shape, size and color).</p>
* @param style the OOo style to read attributesfrom
* @param bDecl true if declaration form is required
* @param bInherit true if inherited properties should be used
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
*/
public void applyFont(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba, Context context) {
// Note: if bDecl is true, nothing will be put in the "after" part of ba.
if (style==null) { return; }
applyNfssSize(style,bDecl,bInherit,ba,context);
applyNfssFamily(style,bDecl,bInherit,ba,context);
applyNfssSeries(style,bDecl,bInherit,ba,context);
applyNfssShape(style,bDecl,bInherit,ba,context);
palette.getColorCv().applyColor(style,bDecl,bInherit,ba,context);
}
/** <p>Reset to normal font, size and color.</p>
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
*/
public void applyNormalFont(BeforeAfter ba) {
ba.add("\\normalfont\\normalsize","");
palette.getColorCv().applyNormalColor(ba);
}
/** <p>Apply default font attributes (family, series, shape, size and color).</p>
* @param style the OOo style to read attributesfrom
* @param ldp the <code>LaTeXDocumentPortion</code> to add LaTeX code to.
*/
public void applyDefaultFont(StyleWithProperties style, LaTeXDocumentPortion ldp) {
if (style==null) { return; }
String s = convertFontDeclaration(style.getProperty(XMLString.STYLE_FONT_NAME));
if (s!=null){
ldp.append("\\renewcommand\\familydefault{\\")
.append(s).append("default}").nl();
} // TODO: Else read props directly from the style
s = nfssSeries(style.getProperty(XMLString.FO_FONT_WEIGHT));
if (s!=null) {
ldp.append("\\renewcommand\\seriesdefault{\\")
.append(s).append("default}").nl();
}
s = nfssShape(style.getProperty(XMLString.FO_FONT_VARIANT),
style.getProperty(XMLString.FO_FONT_STYLE));
if (s!=null) {
ldp.append("\\renewcommand\\shapedefault{\\")
.append(s).append("default}").nl();
}
palette.getColorCv().setNormalColor(style.getProperty(XMLString.FO_COLOR),ldp);
}
/** <p>Apply font effects (position, underline, crossout, change case.</p>
* @param style the OOo style to read attributesfrom
* @param bInherit true if inherited properties should be used
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
*/
public void applyFontEffects(StyleWithProperties style, boolean bInherit, BeforeAfter ba) {
if (style==null) { return; }
applyTextPosition(style, bInherit, ba);
applyUnderline(style, bInherit, ba);
applyCrossout(style, bInherit, ba);
applyChangeCase(style, bInherit, ba);
}
// Remaining methods are private
/** <p>Apply font family.</p>
* @param style the OOo style to read attributesfrom
* @param bDecl true if declaration form is required
* @param bInherit true if inherited properties should be used
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
*/
private void applyNfssFamily(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba, Context context) {
// Note: if bDecl is true, nothing will be put in the "after" part of ba.
if (style==null || bIgnoreFont) { return; }
String sFontName=style.getProperty(XMLString.STYLE_FONT_NAME,bInherit);
if (sFontName!=null){
String sFamily = convertFontDeclaration(sFontName);
if (sFamily==null) { return; }
if (sFamily.equals(convertFontDeclaration(context.getFontName()))) { return; }
if (bDecl) { ba.add("\\"+sFamily+"family",""); }
else { ba.add("\\text"+sFamily+"{","}"); }
} // TODO: Else read props directly from the style
}
/** <p>Apply font series.</p>
* @param style the OOo style to read attributesfrom
* @param bDecl true if declaration form is required
* @param bInherit true if inherited properties should be used
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
*/
private void applyNfssSeries(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba, Context context) {
// Note: if bDecl is true, nothing will be put in the "after" part of ba.
if (style!=null && !bIgnoreAll) {
String sSeries = nfssSeries(style.getProperty(XMLString.FO_FONT_WEIGHT,bInherit));
if (sSeries!=null) {
// Temporary: Support text-attribute style maps for this particular case
// TODO: Reimplement the CharStyleConverter to properly support this...
if (!bDecl && "bf".equals(sSeries) && config.getTextAttributeStyleMap().contains("bold")) {
ba.add(config.getTextAttributeStyleMap().getBefore("bold"),
config.getTextAttributeStyleMap().getAfter("bold"));
}
else {
if (style.isAutomatic()) { // optimize hard formatting
if (sSeries.equals(nfssSeries(context.getFontWeight()))) { return; }
if (context.getFontWeight()==null && sSeries.equals("md")) { return; }
}
if (bDecl) { ba.add("\\"+sSeries+"series",""); }
else { ba.add("\\text"+sSeries+"{","}"); }
}
}
}
}
/** <p>Apply font shape.</p>
* @param style the OOo style to read attributesfrom
* @param bDecl true if declaration form is required
* @param bInherit true if inherited properties should be used
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
*/
private void applyNfssShape(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba, Context context) {
// Note: if bDecl is true, nothing will be put in the "after" part of ba.
if (style!=null && !bIgnoreAll) {
String sVariant = style.getProperty(XMLString.FO_FONT_VARIANT, bInherit);
String sStyle = style.getProperty(XMLString.FO_FONT_STYLE, bInherit);
String sShape = nfssShape(sVariant,sStyle);
if (sShape!=null) {
// Temporary: Support text-attribute style maps for this particular case
// TODO: Reimplement the CharStyleConverter to properly support this...
if (!bDecl && "sc".equals(sShape) && config.getTextAttributeStyleMap().contains("small-caps")) {
ba.add(config.getTextAttributeStyleMap().getBefore("small-caps"),
config.getTextAttributeStyleMap().getAfter("small-caps"));
}
else if (!bDecl && "it".equals(sShape) && config.getTextAttributeStyleMap().contains("italic")) {
ba.add(config.getTextAttributeStyleMap().getBefore("italic"),
config.getTextAttributeStyleMap().getAfter("italic"));
}
else {
if (style.isAutomatic()) { // optimize hard formatting
if (sShape.equals(nfssShape(context.getFontVariant(),context.getFontStyle()))) return;
if (context.getFontVariant()==null && context.getFontStyle()==null && sShape.equals("up")) return;
}
if (bDecl) ba.add("\\"+sShape+"shape","");
else ba.add("\\text"+sShape+"{","}");
}
}
}
}
/** <p>Apply font size.</p>
* @param style the OOo style to read attributesfrom
* @param bDecl true if declaration form is required
* @param bInherit true if inherited properties should be used
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
*/
private void applyNfssSize(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba, Context context) {
// Note: if bDecl is true, nothing will be put in the "after" part of ba.
if (style==null|| bIgnoreFontsize || (bIgnoreHardFontsize && style.isAutomatic())) { return; }
if (style.getProperty(XMLString.FO_FONT_SIZE, bInherit)==null) { return; }
String sSize = nfssSize(style.getAbsoluteProperty(XMLString.FO_FONT_SIZE));
if (sSize==null) { return; }
if (sSize.equals(nfssSize(context.getFontSize()))) { return; }
if (bDecl) { ba.add(sSize,""); }
else { ba.add("{"+sSize+" ","}"); }
}
// Remaining methods are not context-sensitive
/** <p>Apply text position.</p>
* @param style the OOo style to read attributesfrom
* @param bInherit true if inherited properties should be used
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
*/
private void applyTextPosition(StyleWithProperties style, boolean bInherit, BeforeAfter ba) {
if (style!=null && !bIgnoreAll) {
String s = textPosition(style.getProperty(XMLString.STYLE_TEXT_POSITION, bInherit));
// Temporary: Support text-attribute style maps for this particular case
// TODO: Reimplement the CharStyleConverter to properly support this...
if (config.getTextAttributeStyleMap().contains("superscript") && "\\textsuperscript".equals(s)) {
ba.add(config.getTextAttributeStyleMap().getBefore("superscript"),
config.getTextAttributeStyleMap().getAfter("superscript"));
}
else if (config.getTextAttributeStyleMap().contains("subscript") && "\\textsubscript".equals(s)) {
ba.add(config.getTextAttributeStyleMap().getBefore("subscript"),
config.getTextAttributeStyleMap().getAfter("subscript"));
}
else if (s!=null) {
ba.add(s+"{","}");
}
}
}
/** <p>Apply text underline.</p>
* @param style the OOo style to read attributesfrom
* @param bInherit true if inherited properties should be used
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
*/
private void applyUnderline(StyleWithProperties style, boolean bInherit, BeforeAfter ba) {
if (style==null || !bUseUlem) { return; }
if (bIgnoreAll) { return; }
String sTag = ofr.isOpenDocument() ?
XMLString.STYLE_TEXT_UNDERLINE_STYLE :
XMLString.STYLE_TEXT_UNDERLINE;
String s = underline(style.getProperty(sTag, bInherit));
if (s!=null) { bNeedUlem = true; ba.add(s+"{","}"); }
}
/** <p>Apply text crossout.</p>
* @param style the OOo style to read attributesfrom
* @param bInherit true if inherited properties should be used
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
*/
private void applyCrossout(StyleWithProperties style, boolean bInherit, BeforeAfter ba) {
if (style==null || !bUseUlem) { return; }
if (bIgnoreAll) { return; }
String sTag = ofr.isOpenDocument() ?
XMLString.STYLE_TEXT_LINE_THROUGH_STYLE :
XMLString.STYLE_TEXT_CROSSING_OUT;
String s = crossout(style.getProperty(sTag, bInherit));
if (s!=null) { bNeedUlem = true; ba.add(s+"{","}"); }
}
/** <p>Apply change case.</p>
* @param style the OOo style to read attributesfrom
* @param bInherit true if inherited properties should be used
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
*/
private void applyChangeCase(StyleWithProperties style, boolean bInherit, BeforeAfter ba) {
if (style==null) { return; }
if (bIgnoreAll) { return; }
String s = changeCase(style.getProperty(XMLString.FO_TEXT_TRANSFORM));
if (s!=null) { ba.add(s+"{","}"); }
}
/** <p>Convert font declarations to LaTeX.</p>
* <p>It returns a generic LaTeX font family (rm, tt, sf).</p>
* <p>It returns null if the font declaration doesn't exist.</p>
* @param sName the name of the font declaration
* @return <code>String</code> with a LaTeX generic fontfamily
*/
private String convertFontDeclaration(String sName) {
FontDeclaration fd = ofr.getFontDeclaration(sName);
if (fd==null) { return null; }
if (!fontDecls.containsKey(sName)) {
String sFontFamily = fd.getFontFamily();
String sFontPitch = fd.getFontPitch();
String sFontFamilyGeneric = fd.getFontFamilyGeneric();
fontDecls.put(sName,nfssFamily(sFontFamily,sFontFamilyGeneric,sFontPitch));
}
return (String) fontDecls.get(sName);
}
// The remaining methods are static helpers to convert single style properties
// Font change. These methods return the declaration form if the paramater
// bDecl is true, and otherwise the command form
private static final String nfssFamily(String sFontFamily, String sFontFamilyGeneric,
String sFontPitch){
// Note: Defaults to rm
// TODO: What about decorative, script, system?
if ("fixed".equals(sFontPitch)) return "tt";
else if ("modern".equals(sFontFamilyGeneric)) return "tt";
else if ("swiss".equals(sFontFamilyGeneric)) return "sf";
else return "rm";
}
private static final String nfssSeries(String sFontWeight){
if (sFontWeight==null) return null;
if ("bold".equals(sFontWeight)) return "bf";
else return "md";
}
private static final String nfssShape(String sFontVariant, String sFontStyle){
if (sFontVariant==null && sFontStyle==null) return null;
if ("small-caps".equals(sFontVariant)) return "sc";
else if ("italic".equals(sFontStyle)) return "it";
else if ("oblique".equals(sFontStyle)) return "sl";
else return "up";
}
private static final String nfssSize(String sFontSize){
if (sFontSize==null) return null;
return "\\fontsize{"+sFontSize+"}{"+Misc.multiply("120%",sFontSize)+"}\\selectfont";
}
// other character formatting
private final String textPosition(String sTextPosition){
if (sTextPosition==null) return null;
if (sTextPosition.startsWith("super")) return "\\textsuperscript";
if (sTextPosition.startsWith("sub") || sTextPosition.startsWith("-")) {
bNeedSubscript = true;
return "\\textsubscript";
}
if (sTextPosition.startsWith("0%")) return null;
return "\\textsuperscript";
}
private static final String underline(String sUnderline) {
if (sUnderline==null) { return null; }
if (sUnderline.equals("none")) { return null; }
if (sUnderline.indexOf("wave")>=0) { return "\\uwave"; }
return "\\uline";
}
private static final String crossout(String sCrossout) {
if (sCrossout==null) { return null; }
if (sCrossout.equals("X")) { return "\\xout"; }
if (sCrossout.equals("slash")) { return "\\xout"; }
return "\\sout";
}
private static final String changeCase(String sTextTransform){
if ("lowercase".equals(sTextTransform)) return "\\MakeLowercase";
if ("uppercase".equals(sTextTransform)) return "\\MakeUppercase";
return null;
}
}

View file

@ -0,0 +1,202 @@
/************************************************************************
*
* ColorConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.latex;
import writer2latex.latex.util.BeforeAfter;
import writer2latex.latex.util.Context;
import writer2latex.office.OfficeReader;
import writer2latex.office.StyleWithProperties;
import writer2latex.office.XMLString;
import writer2latex.util.Misc;
/** This class converts color
*/
public class ColorConverter extends ConverterHelper {
private static final int RED = 0;
private static final int GREEN = 1;
private static final int BLUE = 2;
private boolean bUseColor;
/** <p>Constructs a new <code>CharStyleConverter</code>.</p>
*/
public ColorConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
// We use color if requested in the configuration, however ignoring
// all formatting overrides this
bUseColor = config.useColor() && config.formatting()>LaTeXConfig.IGNORE_ALL;
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
if (bUseColor) {
pack.append("\\usepackage{color}").nl();
}
}
public void setNormalColor(String sColor, LaTeXDocumentPortion ldp) {
if (bUseColor && sColor!=null) {
ldp.append("\\renewcommand\\normalcolor{\\color")
.append(color(sColor)).append("}").nl();
}
}
public void applyNormalColor(BeforeAfter ba) {
if (bUseColor) { ba.add("\\normalcolor",""); }
}
/** <p>Apply foreground color.</p>
* @param style the OOo style to read attributesfrom
* @param bDecl true if declaration form is required
* @param bInherit true if inherited properties should be used
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
* @param context the current context
*/
public void applyColor(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba, Context context) {
// Note: if bDecl is true, nothing will be put in the "after" part of ba.
if (bUseColor && style!=null) {
String sColor = style.getProperty(XMLString.FO_COLOR,bInherit);
if (sColor!=null) {
if (!sColor.equals(context.getFontColor())) {
// Convert color if it differs from the current font color
context.setFontColor(sColor);
applyColor(sColor, bDecl, ba, context);
}
}
else {
// No color; maybe automatic color?
String sAutomatic = style.getProperty(XMLString.STYLE_USE_WINDOW_FONT_COLOR,bInherit);
if (sAutomatic==null && bInherit) {
// We may need to inherit this property from the default style
StyleWithProperties defaultStyle = ofr.getDefaultParStyle();
if (defaultStyle!=null) {
sAutomatic = defaultStyle.getProperty(XMLString.STYLE_USE_WINDOW_FONT_COLOR,bInherit);
}
}
if ("true".equals(sAutomatic)) {
// Automatic color based on background
if (context.getBgColor()!=null) { applyAutomaticColor(ba,bDecl,context); }
}
}
}
}
/** <p>Apply a specific foreground color.</p>
* @param sColor the rgb color to use
* @param bDecl true if declaration form is required
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
*/
public void applyColor(String sColor, boolean bDecl, BeforeAfter ba, Context context) {
// Note: if bDecl is true, nothing will be put in the "after" part of ba.
if (bUseColor && sColor!=null) {
// If there's a background color, allow all colors
String s = context.getBgColor()!=null ? fullcolor(sColor) : color(sColor);
if (s!=null) {
if (bDecl) { ba.add("\\color"+s,""); }
else { ba.add("\\textcolor"+s+"{","}"); }
}
}
}
public void applyBgColor(String sCommand, String sColor, BeforeAfter ba, Context context) {
// Note: Will only fill "before" part of ba
if (sColor!=null && !"transparent".equals(sColor)) {
String s = fullcolor(sColor);
if (bUseColor && s!=null) {
context.setBgColor(sColor);
ba.add(sCommand+s,"");
}
}
}
public void applyAutomaticColor(BeforeAfter ba, boolean bDecl, Context context) {
String s = automaticcolor(context.getBgColor());
if (s!=null) {
if (bDecl) { ba.add("\\color"+s,""); }
else { ba.add("\\textcolor"+s+"{","}"); }
}
}
private static final String automaticcolor(String sBgColor) {
if (sBgColor!=null && sBgColor.length()==7) {
float[] rgb = getRgb(sBgColor);
if (rgb[RED]+rgb[GREEN]+rgb[BLUE]<0.6) {
// Dark background
return "{white}";
}
}
return "{black}";
}
private static final String color(String sColor){
if ("#000000".equalsIgnoreCase(sColor)) { return "{black}"; }
else if ("#ff0000".equalsIgnoreCase(sColor)) { return "{red}"; }
else if ("#00ff00".equalsIgnoreCase(sColor)) { return "{green}"; }
else if ("#0000ff".equalsIgnoreCase(sColor)) { return "{blue}"; }
else if ("#ffff00".equalsIgnoreCase(sColor)) { return "{yellow}"; }
else if ("#ff00ff".equalsIgnoreCase(sColor)) { return "{magenta}"; }
else if ("#00ffff".equalsIgnoreCase(sColor)) { return "{cyan}"; }
//no white, since we don't have background colors:
//else if ("#ffffff".equalsIgnoreCase(sColor)) { return "{white}"; }
else {
if (sColor==null || sColor.length()!=7) return null;
float[] rgb = getRgb(sColor);
// avoid very bright colors (since we don't have background colors):
if (rgb[RED]+rgb[GREEN]+rgb[BLUE]>2.7) { return "{black}"; }
else { return "[rgb]{"+rgb[RED]+","+rgb[GREEN]+","+rgb[BLUE]+"}"; }
}
}
private static final String fullcolor(String sColor){
if ("#000000".equalsIgnoreCase(sColor)) { return "{black}"; }
else if ("#ff0000".equalsIgnoreCase(sColor)) { return "{red}"; }
else if ("#00ff00".equalsIgnoreCase(sColor)) { return "{green}"; }
else if ("#0000ff".equalsIgnoreCase(sColor)) { return "{blue}"; }
else if ("#ffff00".equalsIgnoreCase(sColor)) { return "{yellow}"; }
else if ("#ff00ff".equalsIgnoreCase(sColor)) { return "{magenta}"; }
else if ("#00ffff".equalsIgnoreCase(sColor)) { return "{cyan}"; }
else if ("#ffffff".equalsIgnoreCase(sColor)) { return "{white}"; }
else {
// This could mean transparent:
if (sColor==null || sColor.length()!=7) return null;
float[] rgb = getRgb(sColor);
return "[rgb]{"+rgb[RED]+","+rgb[GREEN]+","+rgb[BLUE]+"}";
}
}
private static final float[] getRgb(String sColor) {
float[] rgb = new float[3];
rgb[RED] = (float)Misc.getIntegerFromHex(sColor.substring(1,3),0)/255;
rgb[GREEN] = (float)Misc.getIntegerFromHex(sColor.substring(3,5),0)/255;
rgb[BLUE] = (float)Misc.getIntegerFromHex(sColor.substring(5,7),0)/255;
return rgb;
}
}

View file

@ -0,0 +1,43 @@
/************************************************************************
*
* ContentHandlingOption.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-09-08)
*
*/
package writer2latex.latex;
import writer2latex.base.IntegerOption;
public class ContentHandlingOption extends IntegerOption {
public ContentHandlingOption(String sName, String sDefaultValue) {
super(sName,sDefaultValue);
}
public void setString(String sValue) {
super.setString(sValue);
if ("accept".equals(sValue)) nValue = LaTeXConfig.ACCEPT;
else if ("ignore".equals(sValue)) nValue = LaTeXConfig.IGNORE;
else if ("warning".equals(sValue)) nValue = LaTeXConfig.WARNING;
else if ("error".equals(sValue)) nValue = LaTeXConfig.ERROR;
}
}

View file

@ -0,0 +1,49 @@
/************************************************************************
*
* ConverterHelper.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-09-08)
*
*/
package writer2latex.latex;
import writer2latex.office.OfficeReader;
/**
* <p>This is an abstract superclass for converter helpers.</p>
*/
public abstract class ConverterHelper {
protected OfficeReader ofr;
protected LaTeXConfig config;
protected ConverterPalette palette;
protected ConverterHelper(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
this.ofr = ofr;
this.config = config;
this.palette = palette;
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
}
}

View file

@ -0,0 +1,303 @@
/************************************************************************
*
* ConverterPalette.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2009 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2009-02-17)
*
*/
package writer2latex.latex;
import org.w3c.dom.Element;
import java.io.IOException;
import java.util.LinkedList;
import writer2latex.api.Config;
import writer2latex.api.ConverterFactory;
//import writer2latex.api.ConverterResult;
import writer2latex.base.ConverterBase;
import writer2latex.latex.i18n.ClassicI18n;
import writer2latex.latex.i18n.I18n;
import writer2latex.latex.i18n.XeTeXI18n;
import writer2latex.latex.util.Context;
import writer2latex.latex.util.Info;
import writer2latex.util.CSVList;
import writer2latex.util.ExportNameCollection;
import writer2latex.util.Misc;
import writer2latex.office.MIMETypes;
import writer2latex.office.StyleWithProperties;
import writer2latex.office.XMLString;
/**
* <p>This class converts a Writer XML file to a LaTeX file<.</p>
*/
public final class ConverterPalette extends ConverterBase {
// Configuration
private LaTeXConfig config;
public Config getConfig() { return config; }
// The main outfile
private LaTeXDocument texDoc;
// Various data used in conversion
private Context mainContext; // main context
private CSVList globalOptions; // global options
// The helpers (the "colors" of the palette)
private I18n i18n;
private ColorConverter colorCv;
private CharStyleConverter charSc;
private ListStyleConverter listSc;
private PageStyleConverter pageSc;
private BlockConverter blockCv;
private ParConverter parCv;
private HeadingConverter headingCv;
private IndexConverter indexCv;
private BibConverter bibCv;
private SectionConverter sectionCv;
private TableConverter tableCv;
private NoteConverter noteCv;
private CaptionConverter captionCv;
private InlineConverter inlineCv;
private FieldConverter fieldCv;
private DrawConverter drawCv;
private MathmlConverter mathmlCv;
private Info info;
// Constructor
public ConverterPalette() {
super();
config = new LaTeXConfig();
}
// Accessor methods for data
public String getOutFileName() { return sTargetFileName; }
public Context getMainContext() { return mainContext; }
public void addGlobalOption(String sOption) {
globalOptions.addValue(sOption);
}
// Accessor methods for helpers
public I18n getI18n() { return i18n; }
public ColorConverter getColorCv() { return colorCv; }
public CharStyleConverter getCharSc() { return charSc; }
public ListStyleConverter getListSc() { return listSc; }
public PageStyleConverter getPageSc() { return pageSc; }
public BlockConverter getBlockCv() { return blockCv; }
public ParConverter getParCv() { return parCv; }
public HeadingConverter getHeadingCv() { return headingCv; }
public IndexConverter getIndexCv() { return indexCv; }
public BibConverter getBibCv() { return bibCv; }
public SectionConverter getSectionCv() { return sectionCv; }
public TableConverter getTableCv() { return tableCv; }
public NoteConverter getNoteCv() { return noteCv; }
public CaptionConverter getCaptionCv() { return captionCv; }
public InlineConverter getInlineCv() { return inlineCv; }
public FieldConverter getFieldCv() { return fieldCv; }
public DrawConverter getDrawCv() { return drawCv; }
public MathmlConverter getMathmlCv() { return mathmlCv; }
public Info getInfo() { return info; }
// fill out inner converter method
public void convertInner() throws IOException {
sTargetFileName = Misc.trimDocumentName(sTargetFileName,".tex");
imageLoader.setOutFileName(new ExportNameCollection(true).getExportName(sTargetFileName));
imageLoader.setUseSubdir(config.saveImagesInSubdir());
// Set graphics formats depending on backend
if (config.getBackend()==LaTeXConfig.PDFTEX || config.getBackend()==LaTeXConfig.XETEX) {
imageLoader.setDefaultFormat(MIMETypes.PNG);
imageLoader.setDefaultVectorFormat(MIMETypes.PDF);
imageLoader.addAcceptedFormat(MIMETypes.JPEG);
}
else if (config.getBackend()==LaTeXConfig.DVIPS) {
imageLoader.setDefaultFormat(MIMETypes.EPS);
}
// Other values: keep original format
// Inject user sequence names for tables and figures into OfficeReader
if (config.getTableSequenceName().length()>0) {
ofr.addTableSequenceName(config.getTableSequenceName());
}
if (config.getFigureSequenceName().length()>0) {
ofr.addFigureSequenceName(config.getFigureSequenceName());
}
// Create helpers
if (config.getBackend()!=LaTeXConfig.XETEX) {
i18n = new ClassicI18n(ofr,config,this);
}
else {
i18n = new XeTeXI18n(ofr,config,this);
}
colorCv = new ColorConverter(ofr,config,this);
charSc = new CharStyleConverter(ofr,config,this);
listSc = new ListStyleConverter(ofr,config,this);
pageSc = new PageStyleConverter(ofr,config,this);
blockCv = new BlockConverter(ofr,config,this);
parCv = new ParConverter(ofr,config,this);
headingCv = new HeadingConverter(ofr,config,this);
indexCv = new IndexConverter(ofr,config,this);
bibCv = new BibConverter(ofr,config,this);
sectionCv = new SectionConverter(ofr,config,this);
tableCv = new TableConverter(ofr,config,this);
noteCv = new NoteConverter(ofr,config,this);
captionCv = new CaptionConverter(ofr,config,this);
inlineCv = new InlineConverter(ofr,config,this);
fieldCv = new FieldConverter(ofr,config,this);
drawCv = new DrawConverter(ofr,config,this);
mathmlCv = new MathmlConverter(ofr,config,this);
info = new Info(ofr,config,this);
// Create master document and add this
this.texDoc = new LaTeXDocument(sTargetFileName,config.getWrapLinesAfter());
if (config.getBackend()!=LaTeXConfig.XETEX) {
texDoc.setEncoding(ClassicI18n.writeJavaEncoding(config.getInputencoding()));
}
else {
texDoc.setEncoding("UTF-8");
}
convertData.addDocument(texDoc);
// Create other data
globalOptions = new CSVList(',');
// Setup context.
// The default language is specified in the default paragraph style:
mainContext = new Context();
mainContext.resetFormattingFromStyle(ofr.getDefaultParStyle());
mainContext.setInMulticols(pageSc.isTwocolumn());
// Create main LaTeXDocumentPortions
LaTeXDocumentPortion packages = new LaTeXDocumentPortion(false);
LaTeXDocumentPortion declarations = new LaTeXDocumentPortion(false);
LaTeXDocumentPortion body = new LaTeXDocumentPortion(true);
// Traverse the content
Element content = ofr.getContent();
blockCv.traverseBlockText(content,body,mainContext);
noteCv.insertEndnotes(body);
// Add declarations from our helpers
i18n.appendDeclarations(packages,declarations);
colorCv.appendDeclarations(packages,declarations);
charSc.appendDeclarations(packages,declarations);
headingCv.appendDeclarations(packages,declarations);
parCv.appendDeclarations(packages,declarations);
listSc.appendDeclarations(packages,declarations);
pageSc.appendDeclarations(packages,declarations);
blockCv.appendDeclarations(packages,declarations);
indexCv.appendDeclarations(packages,declarations);
bibCv.appendDeclarations(packages,declarations);
sectionCv.appendDeclarations(packages,declarations);
tableCv.appendDeclarations(packages,declarations);
noteCv.appendDeclarations(packages,declarations);
captionCv.appendDeclarations(packages,declarations);
inlineCv.appendDeclarations(packages,declarations);
fieldCv.appendDeclarations(packages,declarations);
drawCv.appendDeclarations(packages,declarations);
mathmlCv.appendDeclarations(packages,declarations);
// Add custom preamble
LinkedList customPreamble = config.getCustomPreamble();
int nCPLen = customPreamble.size();
for (int i=0; i<nCPLen; i++) {
declarations.append( (String) customPreamble.get(i) ).nl();
}
// Set \title, \author and \date (for \maketitle)
createMeta("title",metaData.getTitle(),declarations);
if (config.metadata()) {
createMeta("author",metaData.getCreator(),declarations);
// According to the spec, the date has the format YYYY-MM-DDThh:mm:ss
String sDate = metaData.getDate();
if (sDate.length()==19 && sDate.charAt(10)=='T') {
sDate = sDate.substring(0,10);
}
createMeta("date",sDate,declarations);
}
// Create options for documentclass
if (config.formatting()>=LaTeXConfig.CONVERT_MOST) {
StyleWithProperties dpStyle = ofr.getDefaultParStyle();
if (dpStyle!=null) {
String s = dpStyle.getProperty(XMLString.FO_FONT_SIZE);
if ("10pt".equals(s)) { globalOptions.addValue("10pt"); }
if ("11pt".equals(s)) { globalOptions.addValue("11pt"); }
if ("12pt".equals(s)) { globalOptions.addValue("12pt"); }
}
}
// Temp solution. TODO: Fix when new CSVList is implemented
if (config.getGlobalOptions().length()>0) {
globalOptions.addValue(config.getGlobalOptions());
}
// Assemble the document
LaTeXDocumentPortion result = texDoc.getContents();
if (!config.noPreamble()) {
// Create document class declaration
result.append("% This file was converted to LaTeX by Writer2LaTeX ver. "+ConverterFactory.getVersion()).nl()
.append("% see http://www.hj-gym.dk/~hj/writer2latex for more info").nl();
result.append("\\documentclass");
if (!globalOptions.isEmpty()) {
result.append("[").append(globalOptions.toString()).append("]");
}
result.append("{").append(config.getDocumentclass()).append("}").nl();
result.append(packages)
.append(declarations)
.append("\\begin{document}").nl();
}
result.append(body);
if (!config.noPreamble()) {
result.append("\\end{document}").nl();
}
else {
result.append("\\endinput").nl();
}
// Add BibTeX document if there's any bibliographic data
if (bibCv.getBibTeXDocument()!=null) {
convertData.addDocument(bibCv.getBibTeXDocument());
}
}
private void createMeta(String sName, String sValue,LaTeXDocumentPortion ldp) {
if (sValue==null) { return; }
// Meta data is assumed to be in the default language:
ldp.append("\\"+sName+"{"+i18n.convert(sValue,false,mainContext.getLang())+"}").nl();
}
}

View file

@ -0,0 +1,496 @@
/************************************************************************
*
* DrawConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2009 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2009-02-17)
*
*/
package writer2latex.latex;
import java.util.LinkedList;
import java.util.Stack;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
//import org.w3c.dom.Node;
import writer2latex.xmerge.EmbeddedObject;
import writer2latex.xmerge.EmbeddedXMLObject;
import writer2latex.latex.util.BeforeAfter;
import writer2latex.latex.util.Context;
//import writer2latex.office.ImageLoader;
import writer2latex.office.MIMETypes;
import writer2latex.office.OfficeReader;
import writer2latex.office.StyleWithProperties;
import writer2latex.office.XMLString;
import writer2latex.util.CSVList;
import writer2latex.util.Misc;
import writer2latex.xmerge.BinaryGraphicsDocument;
/**
* <p>This class handles draw elements.</p>
*/
public class DrawConverter extends ConverterHelper {
private boolean bNeedGraphicx = false;
private boolean bNeedOOoLaTeXPreamble = false;
// Keep track of floating frames (images, textboxes...)
private Stack floatingFramesStack = new Stack();
private Element getFrame(Element onode) {
if (ofr.isOpenDocument()) return (Element) onode.getParentNode();
else return onode;
}
public DrawConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
floatingFramesStack.push(new LinkedList());
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
if (bNeedGraphicx) {
pack.append("\\usepackage");
if (config.getBackend()==LaTeXConfig.PDFTEX) pack.append("[pdftex]");
//else if (config.getBackend()==LaTeXConfig.XETEX) pack.append("[xetex]");
else if (config.getBackend()==LaTeXConfig.DVIPS) pack.append("[dvips]");
pack.append("{graphicx}").nl();
}
if (bNeedOOoLaTeXPreamble) {
// The preamble may be stored in the description
String sDescription = palette.getMetaData().getDescription();
int nStart = sDescription.indexOf("%%% OOoLatex Preamble %%%%%%%%%%%%%%");
int nEnd = sDescription.indexOf("%%% End OOoLatex Preamble %%%%%%%%%%%%");
if (nStart>-1 && nEnd>nStart) {
decl.append("% OOoLaTeX preamble").nl()
.append(sDescription.substring(nStart+37,nEnd));
}
// TODO: Otherwise try the user settings...
}
}
public void handleCaption(Element node, LaTeXDocumentPortion ldp, Context oc) {
// Floating frames should be positioned *above* the label, hence
// we use a separate ldp for the paragraphs and add this later
LaTeXDocumentPortion capLdp = new LaTeXDocumentPortion(true);
// Convert the caption
if (oc.isInFigureFloat()) { // float
capLdp.append("\\caption");
palette.getCaptionCv().handleCaptionBody(node,capLdp,oc,false);
}
else { // nonfloat
capLdp.append("\\captionof{figure}");
palette.getCaptionCv().handleCaptionBody(node,capLdp,oc,true);
}
flushFloatingFrames(ldp,oc);
ldp.append(capLdp);
}
public void handleDrawElement(Element node, LaTeXDocumentPortion ldp, Context oc) {
// node must be an elment in the draw namespace
String sName = node.getTagName();
if (sName.equals(XMLString.DRAW_OBJECT)) {
handleDrawObject(node,ldp,oc);
}
else if (sName.equals(XMLString.DRAW_OBJECT_OLE)) {
handleDrawObject(node,ldp,oc);
}
else if ((!oc.isInHeaderFooter()) && sName.equals(XMLString.DRAW_IMAGE)) {
handleDrawImage(node,ldp,oc);
}
else if ((!oc.isInHeaderFooter()) && sName.equals(XMLString.DRAW_TEXT_BOX)) {
handleDrawTextBox(node,ldp,oc);
}
else if (sName.equals(XMLString.DRAW_A)) {
// we handle this like text:a
palette.getFieldCv().handleAnchor(node,ldp,oc);
}
else if (sName.equals(XMLString.DRAW_FRAME)) {
// OpenDocument: Get the actual draw element in the frame
handleDrawElement(Misc.getFirstChildElement(node),ldp,oc);
}
else {
// Other drawing objects (eg. shapes) are currently not supported
ldp.append("[Warning: Draw object ignored]");
}
}
//-----------------------------------------------------------------
// handle draw:object elements (OOo objects such as Chart, Math,...)
private void handleDrawObject(Element node, LaTeXDocumentPortion ldp, Context oc) {
String sHref = Misc.getAttribute(node,XMLString.XLINK_HREF);
if (sHref!=null) { // Embedded object in package or linked object
if (ofr.isInPackage(sHref)) { // Embedded object in package
if (sHref.startsWith("#")) { sHref=sHref.substring(1); }
if (sHref.startsWith("./")) { sHref=sHref.substring(2); }
EmbeddedObject object = palette.getEmbeddedObject(sHref);
if (object!=null) {
if (MIMETypes.MATH.equals(object.getType()) || MIMETypes.ODF.equals(object.getType())) { // Formula!
try {
Document settings = ((EmbeddedXMLObject) object).getSettingsDOM();
Document formuladoc = ((EmbeddedXMLObject) object).getContentDOM();
Element formula = Misc.getChildByTagName(formuladoc,XMLString.MATH_MATH);
ldp.append(" $")
.append(palette.getMathmlCv().convert(settings,formula))
.append("$");
if (Character.isLetterOrDigit(OfficeReader.getNextChar(node))) { ldp.append(" "); }
}
catch (org.xml.sax.SAXException e) {
e.printStackTrace();
}
catch (java.io.IOException e) {
e.printStackTrace();
}
}
else { // unsupported object
boolean bIgnore = true;
if (ofr.isOpenDocument()) { // look for replacement image
Element replacementImage = Misc.getChildByTagName(getFrame(node),XMLString.DRAW_IMAGE);
if (replacementImage!=null) {
handleDrawImage(replacementImage,ldp,oc);
bIgnore = false;
}
}
if (bIgnore) {
ldp.append("[Warning: object ignored]");
}
}
}
}
}
else { // flat xml, object is contained in node
Element formula = Misc.getChildByTagName(node,XMLString.MATH_MATH);
if (formula!=null) {
ldp.append(" $")
.append(palette.getMathmlCv().convert(null,formula))
.append("$");
if (Character.isLetterOrDigit(OfficeReader.getNextChar(node))) { ldp.append(" "); }
}
else { // unsupported object
boolean bIgnore = true;
if (ofr.isOpenDocument()) { // look for replacement image
Element replacementImage = Misc.getChildByTagName(getFrame(node),XMLString.DRAW_IMAGE);
if (replacementImage!=null) {
handleDrawImage(replacementImage,ldp,oc);
bIgnore = false;
}
}
if (bIgnore) {
ldp.append("[Warning: object ignored]");
}
}
}
}
//--------------------------------------------------------------------------
// Create float environment
private void applyFigureFloat(BeforeAfter ba, Context oc) {
// todo: check context...
if (config.floatFigures() && !oc.isInFrame() && !oc.isInTable()) {
if (oc.isInMulticols()) {
ba.add("\\begin{figure*}","\\end{figure*}\n");
}
else {
ba.add("\\begin{figure}","\\end{figure}\n");
}
if (config.getFloatOptions().length()>0) {
ba.add("["+config.getFloatOptions()+"]","");
}
ba.add("\n","");
oc.setInFigureFloat(true);
}
if (!oc.isInFrame() && config.alignFrames()) {
// Avoid nesting center environment
ba.add("\\begin{center}\n","\n\\end{center}\n");
}
}
//--------------------------------------------------------------------------
// Handle draw:image elements
private void handleDrawImage(Element node, LaTeXDocumentPortion ldp, Context oc) {
// Include graphics if allowed by the configuration
switch (config.imageContent()) {
case LaTeXConfig.IGNORE:
// Ignore graphics silently
return;
case LaTeXConfig.WARNING:
System.err.println("Warning: Images are not allowed");
return;
case LaTeXConfig.ERROR:
ldp.append("% Error in document: An image was ignored");
return;
}
Element frame = getFrame(node);
//String sName = frame.getAttribute(XMLString.DRAW_NAME);
palette.getFieldCv().addTarget(frame,"|graphics",ldp);
String sAnchor = frame.getAttribute(XMLString.TEXT_ANCHOR_TYPE);
// TODO: Recognize Jex equations (needs further testing of Jex)
/*Element desc = Misc.getChildByTagName(frame,XMLString.SVG_DESC);
if (desc!=null) {
String sDesc = Misc.getPCDATA(desc);
if (sDesc.startsWith("jex149$tex={") && sDesc.endsWith("}")) {
String sTeX = sDesc.substring(12,sDesc.length()-1);
if (sTeX.length()>0) {
// Succesfully extracted Jex equation!
ldp.append("$"+sTeX+"$");
return;
}
}
}*/
// Recognize OOoLaTeX equation
// The LaTeX code is embedded in a custom style attribute:
StyleWithProperties style = ofr.getFrameStyle(
Misc.getAttribute(frame, XMLString.DRAW_STYLE_NAME));
if (style!=null) {
String sOOoLaTeX = style.getProperty("OOoLatexArgs");
// The content of the attribute is <point size><paragraph sign><mode><paragraph sign><TeX code>
int n=0;
if (sOOoLaTeX!=null) {
if ((n=sOOoLaTeX.indexOf("\u00A7display\u00A7"))>-1) {
ldp.append("\\[").append(sOOoLaTeX.substring(n+9)).append("\\]");
bNeedOOoLaTeXPreamble = true;
return;
}
else if ((n=sOOoLaTeX.indexOf("\u00A7inline\u00A7"))>-1) {
ldp.append("$").append(sOOoLaTeX.substring(n+8)).append("$");
bNeedOOoLaTeXPreamble = true;
return;
}
else if ((n=sOOoLaTeX.indexOf("\u00A7text\u00A7"))>-1) {
ldp.append(sOOoLaTeX.substring(n+6));
bNeedOOoLaTeXPreamble = true;
return;
}
}
}
//if (oc.isInFrame() || "as-char".equals(sAnchor)) {
if ("as-char".equals(sAnchor)) {
handleDrawImageAsChar(node,ldp,oc);
}
else {
((LinkedList) floatingFramesStack.peek()).add(node);
}
}
private void handleDrawImageAsChar(Element node, LaTeXDocumentPortion ldp, Context oc) {
ldp.append(" ");
includeGraphics(node,ldp,oc);
ldp.append(" ");
}
private void handleDrawImageFloat(Element node, LaTeXDocumentPortion ldp, Context oc) {
Context ic = (Context) oc.clone();
BeforeAfter ba = new BeforeAfter();
applyFigureFloat(ba,ic);
ldp.append(ba.getBefore());
includeGraphics(node,ldp,ic);
ldp.append(ba.getAfter());
}
private void includeGraphics(Element node, LaTeXDocumentPortion ldp, Context oc) {
String sFileName = null;
boolean bCommentOut = true;
String sHref = node.getAttribute(XMLString.XLINK_HREF);
if (node.hasAttribute(XMLString.XLINK_HREF) && !ofr.isInPackage(sHref)) {
// Linked image is not yet handled by ImageLoader. This is a temp.
// solution (will go away when ImageLoader is finished)
sFileName = sHref;
// In OpenDocument package format ../ means "leave the package"
if (ofr.isOpenDocument() && ofr.isPackageFormat() && sFileName.startsWith("../")) {
sFileName=sFileName.substring(3);
}
int nExtStart = sHref.lastIndexOf(".");
String sExt = nExtStart>=0 ? sHref.substring(nExtStart).toLowerCase() : "";
// Accept only relative filenames and supported filetypes:
bCommentOut = sFileName.indexOf(":")>-1 || !(
config.getBackend()==LaTeXConfig.UNSPECIFIED ||
(config.getBackend()==LaTeXConfig.PDFTEX && MIMETypes.JPEG_EXT.equals(sExt)) ||
(config.getBackend()==LaTeXConfig.PDFTEX && MIMETypes.PNG_EXT.equals(sExt)) ||
(config.getBackend()==LaTeXConfig.PDFTEX && MIMETypes.PDF_EXT.equals(sExt)) ||
(config.getBackend()==LaTeXConfig.XETEX && MIMETypes.JPEG_EXT.equals(sExt)) ||
(config.getBackend()==LaTeXConfig.XETEX && MIMETypes.PNG_EXT.equals(sExt)) ||
(config.getBackend()==LaTeXConfig.XETEX && MIMETypes.PDF_EXT.equals(sExt)) ||
(config.getBackend()==LaTeXConfig.DVIPS && MIMETypes.EPS_EXT.equals(sExt)));
}
else { // embedded or base64 encoded image
BinaryGraphicsDocument bgd = palette.getImageLoader().getImage(node);
if (bgd!=null) {
palette.addDocument(bgd);
sFileName = bgd.getFileName();
String sMIME = bgd.getDocumentMIMEType();
bCommentOut = !(
config.getBackend()==LaTeXConfig.UNSPECIFIED ||
(config.getBackend()==LaTeXConfig.PDFTEX && MIMETypes.JPEG.equals(sMIME)) ||
(config.getBackend()==LaTeXConfig.PDFTEX && MIMETypes.PNG.equals(sMIME)) ||
(config.getBackend()==LaTeXConfig.PDFTEX && MIMETypes.PDF.equals(sMIME)) ||
(config.getBackend()==LaTeXConfig.XETEX && MIMETypes.JPEG.equals(sMIME)) ||
(config.getBackend()==LaTeXConfig.XETEX && MIMETypes.PNG.equals(sMIME)) ||
(config.getBackend()==LaTeXConfig.XETEX && MIMETypes.PDF.equals(sMIME)) ||
(config.getBackend()==LaTeXConfig.DVIPS && MIMETypes.EPS.equals(sMIME)));
}
}
if (sFileName==null) {
ldp.append("[Warning: Image not found]");
return;
}
// Now for the actual inclusion:
bNeedGraphicx = true;
/* TODO: handle cropping and mirror:
style:mirror can be none, vertical (lodret), horizontal (vandret),
horizontal-on-odd, or
horizontal-on-even (horizontal on odd or even pages).
mirror is handled with scalebox, eg:
%\\scalebox{-1}[1]{...}
can check for even/odd page first!!
fo:clip="rect(t,r,b,l) svarer til trim
value can be auto - no clip!
cropping is handled with clip and trim:
\\includegraphics[clip,trim=l b r t]{...}
note the different order from xsl-fo!
*/
if (bCommentOut) {
ldp.append(" [Warning: Image ignored] ");
ldp.append("% Unhandled or unsupported graphics:").nl().append("%");
}
ldp.append("\\includegraphics");
CSVList options = new CSVList(',');
if (!config.originalImageSize()) {
Element frame = getFrame(node);
String sWidth = Misc.truncateLength(frame.getAttribute(XMLString.SVG_WIDTH));
String sHeight = Misc.truncateLength(frame.getAttribute(XMLString.SVG_HEIGHT));
if (sWidth!=null) { options.addValue("width="+sWidth); }
if (sHeight!=null) { options.addValue("height="+sHeight); }
}
if (config.getImageOptions().length()>0) {
options.addValue(config.getImageOptions()); // TODO: New CSVList...
}
if (!options.isEmpty()) {
ldp.append("[").append(options.toString()).append("]");
}
if (config.removeGraphicsExtension()) {
sFileName = Misc.removeExtension(sFileName);
}
ldp.append("{").append(sFileName).append("}");
if (bCommentOut) { ldp.nl(); }
}
//--------------------------------------------------------------------------
// handle draw:text-box element
private void handleDrawTextBox(Element node, LaTeXDocumentPortion ldp, Context oc) {
Element frame = getFrame(node);
//String sName = frame.getAttribute(XMLString.DRAW_NAME);
palette.getFieldCv().addTarget(frame,"|frame",ldp);
String sAnchor = frame.getAttribute(XMLString.TEXT_ANCHOR_TYPE);
//if (oc.isInFrame() || "as-char".equals(sAnchor)) {
if ("as-char".equals(sAnchor)) {
makeDrawTextBox(node, ldp, oc);
}
else {
((LinkedList) floatingFramesStack.peek()).add(node);
}
}
private void handleDrawTextBoxFloat(Element node, LaTeXDocumentPortion ldp, Context oc) {
BeforeAfter ba = new BeforeAfter();
Context ic = (Context) oc.clone();
applyFigureFloat(ba,ic);
ldp.append(ba.getBefore());
makeDrawTextBox(node, ldp, ic);
ldp.append(ba.getAfter());
}
private void makeDrawTextBox(Element node, LaTeXDocumentPortion ldp, Context oc) {
Context ic = (Context) oc.clone();
ic.setInFrame(true);
ic.setNoFootnotes(true);
// Check to see, if this is really a container for a figure caption
boolean bIsCaption = false;
if (OfficeReader.isSingleParagraph(node)) {
Element par = Misc.getFirstChildElement(node);
String sSeqName = ofr.getSequenceName(par);
if (ofr.isFigureSequenceName(sSeqName)) { bIsCaption = true; }
}
String sWidth = Misc.truncateLength(getFrame(node).getAttribute(XMLString.SVG_WIDTH));
if (!bIsCaption) {
ldp.append("\\begin{minipage}{").append(sWidth).append("}").nl();
}
floatingFramesStack.push(new LinkedList());
palette.getBlockCv().traverseBlockText(node,ldp,ic);
flushFloatingFrames(ldp,ic);
floatingFramesStack.pop();
if (!bIsCaption) {
ldp.append("\\end{minipage}");
}
if (!oc.isNoFootnotes()) { palette.getNoteCv().flushFootnotes(ldp,oc); }
}
//-------------------------------------------------------------------------
//handle any pending floating frames
public void flushFloatingFrames(LaTeXDocumentPortion ldp, Context oc) {
// todo: fix language
LinkedList floatingFrames = (LinkedList) floatingFramesStack.peek();
int n = floatingFrames.size();
if (n==0) { return; }
for (int i=0; i<n; i++) {
Element node = (Element) floatingFrames.get(i);
String sName = node.getNodeName();
if (sName.equals(XMLString.DRAW_IMAGE)) {
handleDrawImageFloat(node,ldp,oc);
}
else if (sName.equals(XMLString.DRAW_TEXT_BOX)) {
handleDrawTextBoxFloat(node,ldp,oc);
}
}
floatingFrames.clear();
}
}

View file

@ -0,0 +1,672 @@
/************************************************************************
*
* FieldConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.latex;
//import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import writer2latex.latex.util.Context;
import writer2latex.latex.util.HeadingMap;
import writer2latex.office.OfficeReader;
import writer2latex.office.XMLString;
import writer2latex.util.CSVList;
import writer2latex.util.ExportNameCollection;
import writer2latex.util.Misc;
import writer2latex.util.SimpleInputBuffer;
/**
* This class handles text fields and links in the document.
* Packages: lastpage, hyperref, titleref, oooref (all optional)
* TODO: Need proper treatment of "caption" and "text" for sequence
* references not to figures and tables (should be fairly rare, though)
*/
public class FieldConverter extends ConverterHelper {
// Links & references
private ExportNameCollection targets = new ExportNameCollection(true);
private ExportNameCollection refnames = new ExportNameCollection(true);
private ExportNameCollection bookmarknames = new ExportNameCollection(true);
private ExportNameCollection seqnames = new ExportNameCollection(true);
private ExportNameCollection seqrefnames = new ExportNameCollection(true);
// sequence declarations (maps name->text:sequence-decl element)
private Hashtable seqDecl = new Hashtable();
// first usage of sequence (maps name->text:sequence element)
private Hashtable seqFirst = new Hashtable();
private Vector postponedReferenceMarks = new Vector();
private Vector postponedBookmarks = new Vector();
private boolean bUseHyperref = false;
private boolean bUsesPageCount = false;
private boolean bUsesTitleref = false;
private boolean bUsesOooref = false;
public FieldConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
// hyperref.sty is not compatible with titleref.sty and oooref.sty:
bUseHyperref = config.useHyperref() && !config.useTitleref() && !config.useOooref();
}
/** <p>Append declarations needed by the <code>FieldConverter</code> to
* the preamble.</p>
* @param pack the <code>LaTeXDocumentPortion</code> to which
* declarations of packages should be added (<code>\\usepackage</code>).
* @param decl the <code>LaTeXDocumentPortion</code> to which
* other declarations should be added.
*/
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
// use lastpage.sty
if (bUsesPageCount) {
pack.append("\\usepackage{lastpage}").nl();
}
// use titleref.sty
if (bUsesTitleref) {
pack.append("\\usepackage{titleref}").nl();
}
// use oooref.sty
if (bUsesOooref) {
pack.append("\\usepackage[");
HeadingMap hm = config.getHeadingMap();
CSVList opt = new CSVList(",");
for (int i=0; i<=hm.getMaxLevel(); i++) { opt.addValue(hm.getName(i)); }
pack.append(opt.toString()).append("]{oooref}").nl();
}
// use hyperref.sty
if (bUseHyperref){
pack.append("\\usepackage{hyperref}").nl();
pack.append("\\hypersetup{");
if (config.getBackend()==LaTeXConfig.PDFTEX) pack.append("pdftex, ");
else if (config.getBackend()==LaTeXConfig.DVIPS) pack.append("dvips, ");
//else pack.append("hypertex");
pack.append("colorlinks=true, linkcolor=blue, citecolor=blue, filecolor=blue, urlcolor=blue");
if (config.getBackend()==LaTeXConfig.PDFTEX) {
pack.append(createPdfMeta("pdftitle",palette.getMetaData().getTitle()));
if (config.metadata()) {
pack.append(createPdfMeta("pdfauthor",palette.getMetaData().getCreator()))
.append(createPdfMeta("pdfsubject",palette.getMetaData().getSubject()))
.append(createPdfMeta("pdfkeywords",palette.getMetaData().getKeywords()));
}
}
pack.append("}").nl();
}
// Export sequence declarations
// The number format is fetched from the first occurence of the
// sequence in the text, while the outline level and the separation
// character are fetched from the declaration
Enumeration names = seqFirst.keys();
while (names.hasMoreElements()) {
// Get first text:sequence element
String sName = (String) names.nextElement();
Element first = (Element) seqFirst.get(sName);
// Collect data
String sNumFormat = Misc.getAttribute(first,XMLString.STYLE_NUM_FORMAT);
if (sNumFormat==null) { sNumFormat="1"; }
int nLevel = 0;
String sSepChar = ".";
if (seqDecl.containsKey(sName)) {
Element sdecl = (Element) seqDecl.get(sName);
nLevel = Misc.getPosInteger(sdecl.getAttribute(XMLString.TEXT_DISPLAY_OUTLINE_LEVEL),0);
if (sdecl.hasAttribute(XMLString.TEXT_SEPARATION_CHARACTER)) {
sSepChar = palette.getI18n().convert(
sdecl.getAttribute(XMLString.TEXT_SEPARATION_CHARACTER),
false,palette.getMainContext().getLang());
}
}
// Create counter
decl.append("\\newcounter{")
.append(seqnames.getExportName(sName))
.append("}");
String sPrefix = "";
if (nLevel>0) {
HeadingMap hm = config.getHeadingMap();
int nUsedLevel = nLevel<=hm.getMaxLevel() ? nLevel : hm.getMaxLevel();
if (nUsedLevel>0) {
decl.append("[").append(hm.getName(nUsedLevel)).append("]");
sPrefix = "\\the"+hm.getName(nUsedLevel)+sSepChar;
}
}
decl.nl()
.append("\\renewcommand\\the")
.append(seqnames.getExportName(sName))
.append("{").append(sPrefix)
.append(ListStyleConverter.numFormat(sNumFormat))
.append("{").append(seqnames.getExportName(sName))
.append("}}").nl();
}
}
/** <p>Process sequence declarations</p>
* @param node the text:sequence-decls node
*/
public void handleSequenceDecls(Element node) {
Node child = node.getFirstChild();
while (child!=null) {
if (Misc.isElement(child,XMLString.TEXT_SEQUENCE_DECL)) {
// Don't process the declaration, but store a reference
seqDecl.put(((Element)child).getAttribute(XMLString.TEXT_NAME),child);
}
child = child.getNextSibling();
}
}
/** <p>Process a sequence field (text:sequence tag)</p>
* @param node The element containing the sequence field
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleSequence(Element node, LaTeXDocumentPortion ldp, Context oc) {
String sName = Misc.getAttribute(node,XMLString.TEXT_NAME);
String sRefName = Misc.getAttribute(node,XMLString.TEXT_REF_NAME);
String sFormula = Misc.getAttribute(node,XMLString.TEXT_FORMULA);
if (sFormula==null) {
// If there's no formula, we must use the content as formula
// The parser below requires a namespace, so we add that..
sFormula = "ooow:"+Misc.getPCDATA(node);
}
if (sName!=null) {
if (ofr.isFigureSequenceName(sName) || ofr.isTableSequenceName(sName)) {
// Export \label only, assuming the number is generated by \caption
if (sRefName!=null && ofr.hasSequenceRefTo(sRefName)) {
ldp.append("\\label{seq:")
.append(seqrefnames.getExportName(sRefName))
.append("}");
}
}
else {
// General purpose sequence -> export as counter
if (!seqFirst.containsKey(sName)) {
// Save first occurence -> used to determine number format
seqFirst.put(sName,node);
}
if (sRefName!=null && ofr.hasSequenceRefTo(sRefName)) {
// Export as {\refstepcounter{name}\thename\label{refname}}
ldp.append("{").append(changeCounter(sName,sFormula,true))
.append("\\the").append(seqnames.getExportName(sName))
.append("\\label{seq:")
.append(seqrefnames.getExportName(sRefName))
.append("}}");
}
else {
// Export as \stepcounter{name}{\thename}
ldp.append(changeCounter(sName,sFormula,false))
.append("{\\the")
.append(seqnames.getExportName(sName))
.append("}");
}
}
}
}
/** <p>Create label for a sequence field (text:sequence tag)</p>
* @param node The element containing the sequence field
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
*/
public void handleSequenceLabel(Element node, LaTeXDocumentPortion ldp) {
String sRefName = Misc.getAttribute(node,XMLString.TEXT_REF_NAME);
if (sRefName!=null && ofr.hasSequenceRefTo(sRefName)) {
ldp.append("\\label{seq:")
.append(seqrefnames.getExportName(sRefName))
.append("}");
}
}
// According to the spec for OpenDocument, the formula is application
// specific, prefixed with a namespace. OOo uses the namespace ooow, and
// we accept the formulas ooow:<number>, ooow:<name>, ooow:<name>+<number>
// and ooow:<name>-<number>
// Note: In OOo a counter is a 16 bit unsigned integer, whereas a (La)TeX
// counter can be negative - thus there will be a slight deviation in the
// (rare) case of a negative number
private String changeCounter(String sName, String sFormula, boolean bRef) {
if (sFormula!=null) {
sFormula = sFormula.trim();
if (sFormula.startsWith("ooow:")) {
SimpleInputBuffer input = new SimpleInputBuffer(sFormula.substring(5));
if (input.peekChar()>='0' && input.peekChar()<='9') {
// Value is <number>
String sNumber = input.getInteger();
if (input.atEnd()) {
return setCounter(sName, Misc.getPosInteger(sNumber,0), bRef);
}
}
else if (input.peekChar()=='-') {
// Value is a negative <number>
input.getChar();
if (input.peekChar()>='0' && input.peekChar()<='9') {
String sNumber = input.getInteger();
if (input.atEnd()) {
return setCounter(sName, -Misc.getPosInteger(sNumber,0), bRef);
}
}
}
else {
// Value starts with <name>
String sToken = input.getIdentifier();
if (sToken.equals(sName)) {
input.skipSpaces();
if (input.peekChar()=='+') {
// Value is <name>+<number>
input.getChar();
input.skipSpaces();
String sNumber = input.getInteger();
if (input.atEnd()) {
return addtoCounter(sName, Misc.getPosInteger(sNumber,0), bRef);
}
}
else if (input.peekChar()=='-') {
// Value is <name>-<number>
input.getChar();
input.skipSpaces();
String sNumber = input.getInteger();
if (input.atEnd()) {
return addtoCounter(sName, -Misc.getPosInteger(sNumber,0), bRef);
}
}
else if (input.atEnd()) {
// Value is <name>
return addtoCounter(sName, 0, bRef);
}
}
}
}
}
// No formula, or a formula we don't understand -> use default behavior
return stepCounter(sName, bRef);
}
private String stepCounter(String sName, boolean bRef) {
if (bRef) {
return "\\refstepcounter{" + seqnames.getExportName(sName) + "}";
}
else {
return "\\stepcounter{" + seqnames.getExportName(sName) + "}";
}
}
private String addtoCounter(String sName, int nValue, boolean bRef) {
if (nValue==1) {
return stepCounter(sName, bRef);
}
else if (bRef) {
return "\\addtocounter{" + seqnames.getExportName(sName) + "}"
+ "{" + Integer.toString(nValue-1) + "}"
+ "\\refstepcounter{" + seqnames.getExportName(sName) + "}";
}
else if (nValue!=0) {
return "\\addtocounter{" + seqnames.getExportName(sName) + "}"
+ "{" + Integer.toString(nValue) + "}";
}
else {
return "";
}
}
private String setCounter(String sName, int nValue, boolean bRef) {
if (bRef) {
return "\\setcounter{" + seqnames.getExportName(sName) + "}"
+ "{" + Integer.toString(nValue-1) + "}"
+ "\\refstepcounter{" + seqnames.getExportName(sName) + "}";
}
else {
return "\\setcounter{" + seqnames.getExportName(sName) + "}"
+ "{" + Integer.toString(nValue) + "}";
}
}
/** <p>Process a sequence reference (text:sequence-ref tag)</p>
* @param node The element containing the sequence reference
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleSequenceRef(Element node, LaTeXDocumentPortion ldp, Context oc) {
String sRefName = Misc.getAttribute(node,XMLString.TEXT_REF_NAME);
String sFormat = Misc.getAttribute(node,XMLString.TEXT_REFERENCE_FORMAT);
String sName = ofr.getSequenceFromRef(sRefName);
if (sRefName!=null) {
if (sFormat==null || "page".equals(sFormat)) {
ldp.append("\\pageref{seq:")
.append(seqrefnames.getExportName(sRefName))
.append("}");
}
else if ("value".equals(sFormat)) {
ldp.append("\\ref{seq:")
.append(seqrefnames.getExportName(sRefName))
.append("}");
}
else if ("category-and-value".equals(sFormat)) {
// Export as Name~\\ref{refname}
if (sName!=null) {
if (ofr.isFigureSequenceName(sName)) {
ldp.append("\\figurename~");
}
else if (ofr.isTableSequenceName(sName)) {
ldp.append("\\tablename~");
}
else {
ldp.append(sName).append("~");
}
}
ldp.append("\\ref{seq:")
.append(seqrefnames.getExportName(sRefName))
.append("}");
}
else if ("chapter".equals(sFormat) && config.useOooref()) {
ldp.append("\\chapterref{seq:")
.append(seqrefnames.getExportName(sRefName))
.append("}");
bUsesOooref = true;
}
else if ("caption".equals(sFormat) && config.useTitleref() &&
(ofr.isFigureSequenceName(sName) || ofr.isTableSequenceName(sName))) {
ldp.append("\\titleref{seq:")
.append(seqrefnames.getExportName(sRefName))
.append("}");
bUsesTitleref = true;
}
else if ("text".equals(sFormat) && config.useTitleref() &&
(ofr.isFigureSequenceName(sName) || ofr.isTableSequenceName(sName))) {
// This is a combination of "category-and-value" and "caption"
// Export as \\figurename~\ref{refname}:~\titleref{refname}
if (ofr.isFigureSequenceName(sName)) {
ldp.append("\\figurename");
}
else if (ofr.isTableSequenceName(sName)) {
ldp.append("\\tablename");
}
ldp.append("~\\ref{seq:")
.append(seqrefnames.getExportName(sRefName))
.append("}:~\\titleref{")
.append(seqrefnames.getExportName(sRefName))
.append("}");
bUsesTitleref = true;
}
else { // use current value
palette.getInlineCv().traversePCDATA(node,ldp,oc);
}
}
}
/** <p>Process a reference mark (text:reference-mark tag)</p>
* @param node The element containing the reference mark
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleReferenceMark(Element node, LaTeXDocumentPortion ldp, Context oc) {
if (!oc.isInSection() && !oc.isInCaption() && !oc.isVerbatim()) {
// Note: Always include \label here, even when it's not used
String sName = node.getAttribute(XMLString.TEXT_NAME);
if (sName!=null) {
ldp.append("\\label{ref:"+refnames.getExportName(sName)+"}");
}
}
else {
// Reference marks should not appear within \section or \caption
postponedReferenceMarks.add(node);
}
}
/** <p>Process a reference (text:reference-ref tag)</p>
* @param node The element containing the reference
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleReferenceRef(Element node, LaTeXDocumentPortion ldp, Context oc) {
String sFormat = node.getAttribute(XMLString.TEXT_REFERENCE_FORMAT);
String sName = node.getAttribute(XMLString.TEXT_REF_NAME);
if (("page".equals(sFormat) || "".equals(sFormat)) && sName!=null) {
ldp.append("\\pageref{ref:"+refnames.getExportName(sName)+"}");
}
else if ("chapter".equals(sFormat) && ofr.referenceMarkInHeading(sName)) {
// This is safe if the reference mark is contained in a heading
ldp.append("\\ref{ref:"+refnames.getExportName(sName)+"}");
}
else { // use current value
palette.getInlineCv().traversePCDATA(node,ldp,oc);
}
}
/** <p>Process a bookmark (text:bookmark tag)</p>
* <p>A bookmark may be the target for either a hyperlink or a reference,
* so this will generate a <code>\\hyperref</code> and/or a <code>\\label</code></p>
* @param node The element containing the bookmark
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleBookmark(Element node, LaTeXDocumentPortion ldp, Context oc) {
if (!oc.isInSection() && !oc.isInCaption() && !oc.isVerbatim()) {
String sName = node.getAttribute(XMLString.TEXT_NAME);
if (sName!=null) {
// A bookmark may be used as a target for a hyperlink as well as
// for a reference. We export whatever is actually used:
addTarget(node,"",ldp);
if (ofr.hasBookmarkRefTo(sName)) {
ldp.append("\\label{bkm:"+bookmarknames.getExportName(sName)+"}");
}
}
}
else {
// Bookmarks should not appear within \section or \caption
postponedBookmarks.add(node);
}
}
/** <p>Process a bookmark reference (text:bookmark-ref tag).</p>
* @param node The element containing the bookmark reference
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleBookmarkRef(Element node, LaTeXDocumentPortion ldp, Context oc) {
String sFormat = node.getAttribute(XMLString.TEXT_REFERENCE_FORMAT);
String sName = node.getAttribute(XMLString.TEXT_REF_NAME);
if (("page".equals(sFormat) || "".equals(sFormat)) && sName!=null) {
ldp.append("\\pageref{bkm:"+bookmarknames.getExportName(sName)+"}");
}
else if ("chapter".equals(sFormat) && ofr.bookmarkInHeading(sName)) {
// This is safe if the bookmark is contained in a heading
ldp.append("\\ref{bkm:"+bookmarknames.getExportName(sName)+"}");
}
else { // use current value
palette.getInlineCv().traversePCDATA(node,ldp,oc);
}
}
/** <p>Process pending reference marks and bookmarks (which may have been
* postponed within sections, captions or verbatim text.</p>
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void flushReferenceMarks(LaTeXDocumentPortion ldp, Context oc) {
// We may still be in a context with no reference marks
if (!oc.isInSection() && !oc.isInCaption() && !oc.isVerbatim()) {
// Type out all postponed reference marks
int n = postponedReferenceMarks.size();
for (int i=0; i<n; i++) {
handleReferenceMark((Element) postponedReferenceMarks.get(i),ldp,oc);
}
postponedReferenceMarks.clear();
// Type out all postponed bookmarks
n = postponedBookmarks.size();
for (int i=0; i<n; i++) {
handleBookmark((Element) postponedBookmarks.get(i),ldp,oc);
}
postponedBookmarks.clear();
}
}
/** <p>Process a hyperlink (text:a tag)</p>
* @param node The element containing the hyperlink
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleAnchor(Element node, LaTeXDocumentPortion ldp, Context oc) {
String sHref = node.getAttribute(XMLString.XLINK_HREF);
if (sHref!=null) {
if (sHref.startsWith("#")) {
// TODO: hyperlinks to headings (?) and objects
if (bUseHyperref) {
ldp.append("\\hyperlink{")
.append(targets.getExportName(Misc.urlDecode(sHref.substring(1))))
.append("}{");
// ignore text style (let hyperref.sty handle the decoration):
palette.getInlineCv().traverseInlineText(node,ldp,oc);
ldp.append("}");
}
else { // user don't want to include hyperlinks
palette.getInlineCv().handleTextSpan(node,ldp,oc);
}
}
else {
if (bUseHyperref) {
if (ofr.getTextContent(node).trim().equals(sHref)) {
// The link text equals the url
ldp.append("\\url{")
.append(oc.isInFootnote() ? escapeHref(Misc.urlDecode(sHref)) : Misc.urlDecode(sHref))
.append("}");
}
else {
ldp.append("\\href{")
.append(oc.isInFootnote() ? escapeHref(Misc.urlDecode(sHref)) : Misc.urlDecode(sHref))
.append("}{");
// ignore text style (let hyperref.sty handle the decoration):
palette.getInlineCv().traverseInlineText(node,ldp,oc);
ldp.append("}");
}
}
else { // user don't want to include hyperlinks
palette.getInlineCv().handleTextSpan(node,ldp,oc);
}
}
}
else {
palette.getInlineCv().handleTextSpan(node,ldp,oc);
}
}
/** <p>Add a <code>\\hypertarget</code></p>
* @param node The element containing the name of the target
* @param sSuffix A suffix to be added to the target,
* e.g. "|table" for a reference to a table.
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
*/
public void addTarget(Element node, String sSuffix, LaTeXDocumentPortion ldp) {
// TODO: Remove this and use addTarget by name only
String sName = node.getAttribute(XMLString.TEXT_NAME);
if (sName == null) { sName = node.getAttribute(XMLString.TABLE_NAME); }
if (sName == null || !bUseHyperref) { return; }
if (!ofr.hasLinkTo(sName+sSuffix)) { return; }
ldp.append("\\hypertarget{")
.append(targets.getExportName(sName+sSuffix))
.append("}{}");
}
/** <p>Add a <code>\\hypertarget</code></p>
* @param sName The name of the target
* @param sSuffix A suffix to be added to the target,
* e.g. "|table" for a reference to a table.
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
*/
public void addTarget(String sName, String sSuffix, LaTeXDocumentPortion ldp) {
if (sName!=null && bUseHyperref && ofr.hasLinkTo(sName+sSuffix)) {
ldp.append("\\hypertarget{")
.append(targets.getExportName(sName+sSuffix))
.append("}{}");
}
}
/** <p>Process a page number field (text:page-number tag)</p>
* @param node The element containing the page number field
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handlePageNumber(Element node, LaTeXDocumentPortion ldp, Context oc) {
// TODO: Obey attributes!
ldp.append("\\thepage{}");
}
/** <p>Process a page count field (text:page-count tag)</p>
* @param node The element containing the page count field
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handlePageCount(Element node, LaTeXDocumentPortion ldp, Context oc) {
// TODO: Obey attributes!
// Note: Actually LastPage refers to the page number of the last page, not the number of pages
if (config.useLastpage()) {
bUsesPageCount = true;
ldp.append("\\pageref{LastPage}");
}
else {
ldp.append("?");
}
}
// Helpers:
private String createPdfMeta(String sName, String sValue) {
if (sValue==null) { return ""; }
// Replace commas with semicolons (the keyval package doesn't like commas):
sValue = sValue.replace(',', ';');
// Meta data is assumed to be in the default language:
return ", "+sName+"="+palette.getI18n().convert(sValue,false,palette.getMainContext().getLang());
}
// For href within footnote, we have to escape the #
private String escapeHref(String s) {
StringBuffer buf = new StringBuffer();
for (int i=0; i<s.length(); i++) {
if (s.charAt(i)=='#') { buf.append("\\#"); }
else { buf.append(s.charAt(i)); }
}
return buf.toString();
}
}

View file

@ -0,0 +1,357 @@
/************************************************************************
*
* HeadingConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-22)
*
*/
package writer2latex.latex;
//import java.util.Hashtable;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import writer2latex.util.*;
import writer2latex.office.*;
import writer2latex.latex.util.BeforeAfter;
import writer2latex.latex.util.Context;
import writer2latex.latex.util.HeadingMap;
//import writer2latex.latex.util.StyleMap;
/* This class converts OpenDocument headings (<code>text:h</code>) and
* paragraph styles/formatting into LaTeX
* Export of formatting depends on the option "formatting":
* <ul>
* <li><code>ignore_all</code>
* <li><code>ignore_most</code>
* <li><code>convert_basic</code>
* <li><code>convert_most</code>
* <li><code>convert_all</code>
* </ul>
*/
public class HeadingConverter extends ConverterHelper {
private String[] sHeadingStyles = new String[11];
/** Constructs a new <code>HeadingConverter</code>.
*/
public HeadingConverter(OfficeReader ofr, LaTeXConfig config,
ConverterPalette palette) {
super(ofr,config,palette);
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
appendHeadingStyles(decl);
}
/** Process a heading
* @param node The text:h element node containing the heading
* @param ldp The LaTeXDocumentPortion to add LaTeX code to
* @param oc The current context
*/
public void handleHeading(Element node, LaTeXDocumentPortion ldp, Context oc) {
// Get the level, the heading map and the style name
int nLevel = ofr.isOpenDocument() ?
Misc.getPosInteger(Misc.getAttribute(node, XMLString.TEXT_OUTLINE_LEVEL),1) :
Misc.getPosInteger(Misc.getAttribute(node, XMLString.TEXT_LEVEL),1);
HeadingMap hm = config.getHeadingMap();
String sStyleName = node.getAttribute(XMLString.TEXT_STYLE_NAME);
if (nLevel<=hm.getMaxLevel()) {
// Always push the font used
palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(ofr.getParStyle(sStyleName)));
Context ic = (Context) oc.clone();
ic.setInSection(true);
// Footnotes with more than one paragraph are not allowed within
// sections. To be safe, we disallow all footnotes
ic.setNoFootnotes(true);
// Apply style
BeforeAfter baHardPage = new BeforeAfter();
BeforeAfter baHardChar = new BeforeAfter();
applyHardHeadingStyle(nLevel, sStyleName,
baHardPage, baHardChar, ic);
// Export the heading
ldp.append(baHardPage.getBefore());
ldp.append("\\"+hm.getName(nLevel));
// If this heading contains formatting, add optional argument:
if (baHardChar.getBefore().length()>0 || containsElements(node)) {
ldp.append("[");
palette.getInlineCv().traversePlainInlineText(node,ldp,ic);
ldp.append("]");
}
ldp.append("{").append(baHardChar.getBefore());
palette.getInlineCv().traverseInlineText(node,ldp,ic);
ldp.append(baHardChar.getAfter()).append("}").nl();
ldp.append(baHardPage.getAfter());
// Include pending index marks, labels, footnotes & floating frames
palette.getFieldCv().flushReferenceMarks(ldp,oc);
palette.getIndexCv().flushIndexMarks(ldp,oc);
palette.getNoteCv().flushFootnotes(ldp,oc);
palette.getDrawCv().flushFloatingFrames(ldp,ic);
// Pop the font name
palette.getI18n().popSpecialTable();
}
else { // beyond supported headings - export as ordinary paragraph
palette.getParCv().handleParagraph(node,ldp,oc,false);
}
}
/** Use a paragraph style on a heading. If hard paragraph formatting
* is applied to a heading, page break and font is converted - other
* hard formatting is ignored.
* This method also collects name of heading style
* @param <code>nLevel</code> The level of this heading
* @param <code>sStyleName</code> the name of the paragraph style to use
* @param <code>baPage</code> a <code>BeforeAfter</code> to put page break code into
* @param <code>baText</code> a <code>BeforeAfter</code> to put character formatting code into
* @param <code>context</code> the current context. This method will use and update the formatting context
*/
private void applyHardHeadingStyle(int nLevel, String sStyleName,
BeforeAfter baPage, BeforeAfter baText, Context context) {
// Get the style
StyleWithProperties style = ofr.getParStyle(sStyleName);
if (style==null) { return; }
// Register heading style
if (sHeadingStyles[nLevel]==null) {
sHeadingStyles[nLevel] = style.isAutomatic() ? style.getParentName() : sStyleName;
}
// Do conversion
if (style.isAutomatic()) {
palette.getPageSc().applyPageBreak(style,false,baPage);
palette.getCharSc().applyHardCharFormatting(style,baText);
}
// Update context
context.updateFormattingFromStyle(style);
}
/** Convert heading styles and outline numbering to LaTeX.
* An array of stylenames to use is required: The OOo writer file format
* allows different paragraph styles to be applied to individual headings,
* so this is not included in the styles.
* LaTeX (and OOo Writer!) usually uses the same format for all headings.
* @param ldp the LaTeXDocumentPortion to add definitions to.
*/
// TODO: use method from ListStyleConverter to create labels
private void appendHeadingStyles(LaTeXDocumentPortion ldp) {
// The user may not want to convert the formatting of headings
if (config.formatting()<=LaTeXConfig.IGNORE_MOST) { return; }
HeadingMap hm = config.getHeadingMap();
// OK, we are going to convert. First find the max level for headings
int nMaxLevel = 0;
for (int i=1; i<=5; i++) { if (sHeadingStyles[i]!=null) { nMaxLevel=i; } }
if (nMaxLevel==0) { return; } // no headings, nothing to do!
if (nMaxLevel>hm.getMaxLevel()) { nMaxLevel = hm.getMaxLevel(); }
boolean bOnlyNum = config.formatting()==LaTeXConfig.CONVERT_BASIC;
if (bOnlyNum) {
ldp.append("% Outline numbering").nl();
}
else {
ldp.append("% Headings and outline numbering").nl()
.append("\\makeatletter").nl();
}
// Paragraph style for headings:
if (!bOnlyNum) {
for (int i=1; i<=nMaxLevel; i++) {
if (sHeadingStyles[i]!=null) {
StyleWithProperties style = ofr.getParStyle(sHeadingStyles[i]);
if (style!=null) {
BeforeAfter decl = new BeforeAfter();
BeforeAfter comm = new BeforeAfter();
palette.getPageSc().applyPageBreak(style,true,decl);
palette.getCharSc().applyNormalFont(decl);
palette.getCharSc().applyFont(style,true,true,decl,new Context());
palette.getParCv().applyAlignment(style,false,true,decl);
palette.getI18n().applyLanguage(style,false,true,comm);
palette.getCharSc().applyFontEffects(style,true,comm);
String sMarginTop = style.getAbsoluteLength(XMLString.FO_MARGIN_TOP);
String sMarginBottom = style.getAbsoluteLength(XMLString.FO_MARGIN_BOTTOM);
String sMarginLeft = style.getAbsoluteLength(XMLString.FO_MARGIN_LEFT);
String sSecName = hm.getName(i);
if (!comm.isEmpty()) { // have to create a cs for this heading
ldp.append("\\newcommand\\cs").append(sSecName).append("[1]{")
.append(comm.getBefore()).append("#1").append(comm.getAfter())
.append("}").nl();
}
ldp.append("\\renewcommand\\").append(sSecName)
.append("{\\@startsection{").append(sSecName).append("}{"+hm.getLevel(i))
.append("}{"+sMarginLeft+"}{");
// Suppress indentation after heading? currently not..
// ldp.append("-");
ldp.append(sMarginTop)
.append("}{").append(sMarginBottom).append("}{");
// Note: decl.getAfter() may include a page break after, which we ignore
ldp.append(decl.getBefore());
if (!comm.isEmpty()) {
ldp.append("\\cs").append(sSecName);
}
ldp.append("}}").nl();
}
}
}
}
// redefine formatting of section counters
// simplified if the user wants to ignore formatting
if (!bOnlyNum) {
ldp.append("\\renewcommand\\@seccntformat[1]{")
.append("\\csname @textstyle#1\\endcsname{\\csname the#1\\endcsname}")
.append("\\csname @distance#1\\endcsname}").nl();
}
// Collect numbering styles and set secnumdepth
int nSecnumdepth = nMaxLevel;
ListStyle outline = ofr.getOutlineStyle();
String[] sNumFormat = new String[6];
for (int i=nMaxLevel; i>=1; i--) {
sNumFormat[i] = ListStyleConverter.numFormat(outline.getLevelProperty(i,
XMLString.STYLE_NUM_FORMAT));
if (sNumFormat[i]==null || "".equals(sNumFormat[i])) {
nSecnumdepth = i-1;
}
}
ldp.append("\\setcounter{secnumdepth}{"+nSecnumdepth+"}").nl();
for (int i=1; i<=nMaxLevel; i++) {
if (sNumFormat[i]==null || "".equals(sNumFormat[i])) {
// no numbering at this level
if (!bOnlyNum) {
ldp.append("\\newcommand\\@distance")
.append(hm.getName(i)).append("{}").nl()
.append("\\newcommand\\@textstyle")
.append(hm.getName(i)).append("[1]{#1}").nl();
}
}
else {
if (!bOnlyNum) {
// Distance between label and text:
String sDistance = outline.getLevelStyleProperty(i,XMLString.TEXT_MIN_LABEL_DISTANCE);
ldp.append("\\newcommand\\@distance")
.append(hm.getName(i)).append("{");
if (sDistance!=null) {
ldp.append("\\hspace{").append(sDistance).append("}");
}
ldp.append("}").nl();
// Label width and alignment
String sLabelWidth = outline.getLevelStyleProperty(i,XMLString.TEXT_MIN_LABEL_WIDTH);
String sTextAlign = outline.getLevelStyleProperty(i,XMLString.FO_TEXT_ALIGN);
String sAlignmentChar = "l"; // start (or left) is default
if (sTextAlign!=null) {
if ("end".equals(sTextAlign)) { sAlignmentChar="r"; }
else if ("right".equals(sTextAlign)) { sAlignmentChar="r"; }
else if ("center".equals(sTextAlign)) { sAlignmentChar="c"; }
}
// Textstyle to use for label:
String sStyleName = outline.getLevelProperty(i,XMLString.TEXT_STYLE_NAME);
// Prefix and suffix text to decorate the label
String sPrefix = outline.getLevelProperty(i,XMLString.STYLE_NUM_PREFIX);
String sSuffix = outline.getLevelProperty(i,XMLString.STYLE_NUM_SUFFIX);
// TODO is this correct?? todo: space-before??? start value???
BeforeAfter baText = new BeforeAfter();
if (!bOnlyNum) {palette.getCharSc().applyTextStyle(sStyleName,baText,new Context()); }
ldp.append("\\newcommand\\@textstyle")
.append(hm.getName(i)).append("[1]{");
if (!bOnlyNum && sLabelWidth!=null) {
ldp.append("\\makebox[").append(sLabelWidth).append("][").append(sAlignmentChar).append("]{");
}
ldp.append(baText.getBefore())
.append(sPrefix!=null ? sPrefix : "")
.append("#1")
.append(sSuffix!=null ? sSuffix : "")
.append(baText.getAfter());
if (!bOnlyNum && sLabelWidth!=null) {
ldp.append("}");
}
ldp.append("}").nl();
}
// The label:
int nLevels = Misc.getPosInteger(outline.getLevelProperty(i,
XMLString.TEXT_DISPLAY_LEVELS),1);
ldp.append("\\renewcommand\\the")
.append(hm.getName(i))
.append("{");
for (int j=i-nLevels+1; j<i; j++) {
ldp.append(sNumFormat[j])
.append("{").append(sectionName(j)).append("}")
.append(".");
}
ldp.append(sNumFormat[i])
.append("{").append(hm.getName(i)).append("}")
.append("}").nl();
}
}
if (!bOnlyNum) {
ldp.append("\\makeatother").nl();
}
}
/* Check to see if this node contains any element nodes, except reference marks */
public boolean containsElements(Node node) {
if (!node.hasChildNodes()) { return false; }
NodeList list = node.getChildNodes();
int nLen = list.getLength();
for (int i = 0; i < nLen; i++) {
Node child = list.item(i);
if (child.getNodeType()==Node.ELEMENT_NODE &&
!child.getNodeName().startsWith(XMLString.TEXT_REFERENCE_MARK)) {
return true;
}
}
return false;
}
static final String sectionName(int nLevel){
switch (nLevel) {
case 1: return "section";
case 2: return "subsection";
case 3: return "subsubsection";
case 4: return "paragraph";
case 5: return "subparagraph";
default: return null;
}
}
}

View file

@ -0,0 +1,242 @@
/************************************************************************
*
* IndexConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.latex;
import java.util.Vector;
import org.w3c.dom.Element;
//import org.w3c.dom.Node;
import writer2latex.util.Misc;
import writer2latex.office.IndexMark;
import writer2latex.office.OfficeReader;
import writer2latex.office.XMLString;
import writer2latex.latex.util.Context;
/**
* <p>This class handles indexes (table of contents, list of tables, list of
* illustrations, object index, user index, alphabetical index)
* as well as their associated index marks.</p>
*/
public class IndexConverter extends ConverterHelper {
private boolean bContainsAlphabeticalIndex = false;
private Vector postponedIndexMarks = new Vector();
/** <p>Construct a new <code>IndexConverter</code>.
* @param config the configuration to use
* @param palette the <code>ConverterPalette</code> to link to
* if such a document is created by the <code>IndexConverter</code>
*/
public IndexConverter(OfficeReader ofr,LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
}
/** <p>Append declarations needed by the <code>IndexConverter</code> to
* the preamble.
* @param pack the <code>LaTeXDocumentPortion</code> to which
* declarations of packages should be added (<code>\\usepackage</code>).
* @param decl the <code>LaTeXDocumentPortion</code> to which
* other declarations should be added.
*/
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
if (bContainsAlphabeticalIndex) {
pack.append("\\usepackage{makeidx}").nl();
decl.append("\\makeindex").nl();
}
}
/** Process Table of Contents (text:table-of-content tag)
* @param node The element containing the Table of Contents
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleTOC (Element node, LaTeXDocumentPortion ldp, Context oc) {
if (config.noIndex()) { return; }
/* TODO: Apply more formatting by modfification of \l@section etc.
Something like this:
\newcommand\l@section[2]{\@dottedtocline{1}{1.5em}{2.3em}{\textbf{#1}}{\textit{#2}}
Textformatting is trivial; see article.cls for examples of more complicated
formatting. Note: The section number can't be formatted indivdually.*/
Element source = Misc.getChildByTagName(node,XMLString.TEXT_TABLE_OF_CONTENT_SOURCE);
if (source!=null) {
if ("chapter".equals(source.getAttribute(XMLString.TEXT_INDEX_SOURCE))) {
ldp.append("[Warning: Table of content (for this chapter) ignored!]").nl().nl();
}
else {
int nLevel = Misc.getPosInteger(source.getAttribute(XMLString.TEXT_OUTLINE_LEVEL),1);
ldp.append("\\setcounter{tocdepth}{"+nLevel+"}").nl();
Element title = Misc.getChildByTagName(source,XMLString.TEXT_INDEX_TITLE_TEMPLATE);
if (title!=null) {
ldp.append("\\renewcommand\\contentsname{");
palette.getInlineCv().traversePCDATA(title,ldp,oc);
ldp.append("}").nl();
}
}
}
ldp.append("\\tableofcontents").nl();
}
/** Process List of Illustrations (text:list-of-illustrations tag)
* @param node The element containing the List of Illustrations
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleLOF (Element node, LaTeXDocumentPortion ldp, Context oc) {
if (config.noIndex()) { return; }
ldp.append("\\listoffigures").nl();
}
/** Process List of Tables (text:list-of-tables tag)
* @param node The element containing the List of Tables
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleLOT (Element node, LaTeXDocumentPortion ldp, Context oc) {
if (config.noIndex()) { return; }
ldp.append("\\listoftables").nl();
}
/** Process Object Index (text:object index tag)
* @param node The element containing the Object Index
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleObjectIndex (Element node, LaTeXDocumentPortion ldp, Context oc) {
if (config.noIndex()) { return; }
ldp.append("[Warning: Object index ignored]").nl().nl();
}
/** Process User Index (text:user-index tag)
* @param node The element containing the User Index
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleUserIndex (Element node, LaTeXDocumentPortion ldp, Context oc) {
if (config.noIndex()) { return; }
ldp.append("[Warning: User index ignored]").nl().nl();
}
/** Process Alphabetical Index (text:alphabetical-index tag)
* @param node The element containing the Alphabetical Index
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleAlphabeticalIndex (Element node, LaTeXDocumentPortion ldp, Context oc) {
if (config.noIndex()) { return; }
ldp.append("\\printindex").nl();
bContainsAlphabeticalIndex = true;
}
/** Process an Alphabetical Index Mark (text:alphabetical-index-mark{-start} tag)
* @param node The element containing the Mark
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleAlphabeticalIndexMark(Element node, LaTeXDocumentPortion ldp, Context oc) {
if (!oc.isInSection() && !oc.isInCaption() && !oc.isVerbatim()) {
String sValue = IndexMark.getIndexValue(node);
if (sValue!=null) {
ldp.append("\\index{");
String sKey1 = IndexMark.getKey1(node);
if (sKey1!=null) {
writeIndexText(sKey1.trim(),ldp,oc);
ldp.append("!");
}
String sKey2 = IndexMark.getKey2(node);
if (sKey2!=null) {
writeIndexText(sKey2.trim(),ldp,oc);
ldp.append("!");
}
writeIndexText(sValue.trim(),ldp,oc);
ldp.append("}");
}
}
else {
// Index marks should not appear within \section or \caption
postponedIndexMarks.add(node);
}
}
public void flushIndexMarks(LaTeXDocumentPortion ldp, Context oc) {
// We may still be in a context with no index marks
if (!oc.isInSection() && !oc.isInCaption() && !oc.isVerbatim()) {
// Type out all postponed index marks
int n = postponedIndexMarks.size();
for (int i=0; i<n; i++) {
handleAlphabeticalIndexMark((Element) postponedIndexMarks.get(i),ldp,oc);
}
postponedIndexMarks.clear();
}
}
// Helper: Write the text of an index mark, escaping special characters
private void writeIndexText(String sText, LaTeXDocumentPortion ldp, Context oc) {
String sTextOut = palette.getI18n().convert(sText,false,oc.getLang());
// need to escape !, @, | and ":
int nLen = sTextOut.length();
boolean bBackslash = false;
for (int i=0; i<nLen; i++) {
if (bBackslash) {
ldp.append(sTextOut.substring(i,i+1));
bBackslash = false;
}
else {
switch (sTextOut.charAt(i)) {
case '\\' : bBackslash = true;
ldp.append("\\");
break;
case '~' :
case '\u00A0' :
// Non-breaking space causes trouble in index:
ldp.append(" ");
break;
case '!' :
case '@' :
case '|' :
case '"' : ldp.append("\"");
default : ldp.append(sTextOut.substring(i,i+1));
}
}
}
}
}

View file

@ -0,0 +1,636 @@
/************************************************************************
*
* InlineConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-22)
*
*/
package writer2latex.latex;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import writer2latex.util.Misc;
import writer2latex.office.OfficeReader;
import writer2latex.office.StyleWithProperties;
import writer2latex.office.XMLString;
import writer2latex.latex.util.BeforeAfter;
import writer2latex.latex.util.Context;
import writer2latex.latex.util.HeadingMap;
/**
* <p>This class handles basic inline text.</p>
*/
public class InlineConverter extends ConverterHelper {
private String sTabstop = "\\ \\ ";
private boolean bHasPdfannotation = false;
public InlineConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
// Get custom code for tab stops
if (config.getTabstop().length()>0) {
sTabstop = config.getTabstop();
}
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
if (bHasPdfannotation) {
decl.append("\\newcommand\\pdfannotation[1]")
.append("{\\ifx\\pdfoutput\\undefined\\marginpar{#1}\\else")
.append("\\pdfstringdef\\tempstring{#1}\\marginpar{")
.append("\\pdfannot width 5cm height 12pt depth 4cm ")
.append("{ /Subtype /Text /Open false /Contents(\\tempstring) /Color [1 0 0]}")
.append("}\\fi}").nl();
}
}
/** Handle a text:span element
*/
public void handleTextSpan(Element node, LaTeXDocumentPortion ldp, Context oc) {
if (oc.isMathMode()) { handleTextSpanMath(node, ldp, oc); }
else { handleTextSpanText(node, ldp, oc); }
}
private void handleTextSpanMath(Element node, LaTeXDocumentPortion ldp, Context oc) {
// TODO: Handle a selection of formatting attributes: color, supscript...
String sStyleName = node.getAttribute(XMLString.TEXT_STYLE_NAME);
StyleWithProperties style = ofr.getTextStyle(sStyleName);
// Always push the font used
palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(style));
// Convert formatting
BeforeAfter ba = new BeforeAfter();
if (style!=null) {
String sPos = style.getProperty(XMLString.STYLE_TEXT_POSITION, true);
if (sPos!=null) {
if (sPos.startsWith("sub") || sPos.startsWith("-")) {
ba.add("_{", "}");
}
else if (sPos.startsWith("super") || !sPos.startsWith("0%")) {
ba.add("^{", "}");
}
}
}
ldp.append(ba.getBefore());
traverseInlineMath(node, ldp, oc);
ldp.append(ba.getAfter());
// finally pop the font table
palette.getI18n().popSpecialTable();
}
private void handleTextSpanText(Element node, LaTeXDocumentPortion ldp, Context oc) {
String styleName = node.getAttribute(XMLString.TEXT_STYLE_NAME);
// Check for strict handling of styles
String sDisplayName = ofr.getTextStyles().getDisplayName(styleName);
if (config.otherStyles()!=LaTeXConfig.ACCEPT && !config.getTextStyleMap().contains(sDisplayName)) {
if (config.otherStyles()==LaTeXConfig.WARNING) {
System.err.println("Warning: Text with style "+sDisplayName+" was ignored");
}
else if (config.otherStyles()==LaTeXConfig.ERROR) {
ldp.append("% Error in source document: Text with style ")
.append(palette.getI18n().convert(sDisplayName,false,oc.getLang()))
.append(" was ignored").nl();
}
// Ignore this text:
return;
}
boolean styled = true;
// don't style it if a {foot|end}note is the only content
if (onlyNote(node) || OfficeReader.getCharacterCount(node)==0) { styled = false; }
// Also don't style it if we're already within a verbatim environment
if (oc.isVerbatim()) { styled = false; }
boolean bNoFootnotes = false;
// Always push the font used
palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(ofr.getTextStyle(styleName)));
// Apply the style
BeforeAfter ba = new BeforeAfter();
Context ic = (Context) oc.clone();
if (styled) { palette.getCharSc().applyTextStyle(styleName,ba,ic); }
// Footnote problems:
// No footnotes in sub/superscript (will disappear)
// No multiparagraph footnotes embedded in text command (eg. \textbf{..})
// Simple solution: styled text element is forbidden footnote area
if (styled && !ic.isInFootnote()) { bNoFootnotes = true; }
// Temp solution: Ignore hard formatting in header/footer (name clash problem)
// only in package format.
StyleWithProperties style = ofr.getTextStyle(styleName);
if (ofr.isPackageFormat() && (style!=null && style.isAutomatic()) && ic.isInHeaderFooter()) {
styled = false;
}
if (styled) {
if (bNoFootnotes) { ic.setNoFootnotes(true); }
ldp.append(ba.getBefore());
}
traverseInlineText(node,ldp,ic);
if (styled) {
ldp.append(ba.getAfter());
ic.setNoFootnotes(false);
if (!ic.isInFootnote()) { palette.getNoteCv().flushFootnotes(ldp,oc); }
}
// Flush any pending index marks and reference marks
palette.getFieldCv().flushReferenceMarks(ldp,oc);
palette.getIndexCv().flushIndexMarks(ldp,oc);
// finally pop the special table
palette.getI18n().popSpecialTable();
}
public void traverseInlineText(Element node, LaTeXDocumentPortion ldp, Context oc) {
if (oc.isVerbatim()) {
traverseVerbatimInlineText(node,ldp,oc);
}
else if (oc.isMathMode()) {
traverseInlineMath(node,ldp,oc);
}
else {
traverseOrdinaryInlineText(node,ldp,oc);
}
}
// Traverse ordinary inline text in text mode (temporarily changing to math
// mode for a sequence of text:span with style "OOoLaTeX")
private void traverseOrdinaryInlineText(Element node,LaTeXDocumentPortion ldp, Context oc) {
Node childNode = node.getFirstChild();
while (childNode!=null) {
short nodeType = childNode.getNodeType();
switch (nodeType) {
case Node.TEXT_NODE:
String s = childNode.getNodeValue();
if (s.length() > 0) {
ldp.append(palette.getI18n().convert(s, false, oc.getLang()));
}
break;
case Node.ELEMENT_NODE:
Element child = (Element)childNode;
String sName = child.getTagName();
if (sName.equals(XMLString.TEXT_SPAN)) {
String sStyleName = child.getAttribute(XMLString.TEXT_STYLE_NAME);
boolean bIsMathSpan = "OOoLaTeX".equals(ofr.getTextStyles().getDisplayName(sStyleName));
if (bIsMathSpan) {
// Temporarily change to math mode
Context ic = (Context) oc.clone();
ic.setMathMode(true);
ldp.append("$");
Node remember;
boolean bContinue = false;
do {
handleTextSpanMath((Element)childNode, ldp, ic);
remember = childNode;
childNode = childNode.getNextSibling();
bContinue = false;
if (childNode!=null && childNode.getNodeType()==Node.ELEMENT_NODE &&
childNode.getNodeName().equals(XMLString.TEXT_SPAN)) {
sStyleName = Misc.getAttribute(childNode,XMLString.TEXT_STYLE_NAME);
if ("OOoLaTeX".equals(ofr.getTextStyles().getDisplayName(sStyleName)))
//child = (Element) childNode;
bContinue = true;
}
} while(bContinue);
childNode = remember;
ldp.append("$");
}
else {
handleTextSpan(child,ldp,oc);
}
}
else if (child.getNodeName().startsWith("draw:")) {
palette.getDrawCv().handleDrawElement(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_S)) {
if (config.ignoreDoubleSpaces()) {
ldp.append(" ");
}
else {
int count= Misc.getPosInteger(child.getAttribute(XMLString.TEXT_C),1);
//String sSpace = config.ignoreDoubleSpaces() ? " " : "\\ ";
for ( ; count > 0; count--) { ldp.append("\\ "); }
}
}
else if (sName.equals(XMLString.TEXT_TAB_STOP) || sName.equals(XMLString.TEXT_TAB)) { // text:tab in oasis
// tab stops are not supported by the converter, but the special usage
// of tab stops in header and footer can be emulated with \hfill
// TODO: Sometimes extra \hfill should be added at end of line
if (oc.isInHeaderFooter()) { ldp.append("\\hfill "); }
else { ldp.append(sTabstop); }
}
else if (sName.equals(XMLString.TEXT_LINE_BREAK)) {
if (!oc.isInHeaderFooter() && !config.ignoreHardLineBreaks()) {
ldp.append("\\newline").nl();
}
else { ldp.append(" "); }
}
else if (sName.equals(XMLString.TEXT_A)) {
palette.getFieldCv().handleAnchor(child,ldp,oc);
}
else if (sName.equals(XMLString.OFFICE_ANNOTATION)) {
handleOfficeAnnotation(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_PAGE_NUMBER)) {
palette.getFieldCv().handlePageNumber(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_PAGE_COUNT)) {
palette.getFieldCv().handlePageCount(child,ldp,oc);
}
else if (oc.isInHeaderFooter()) {
if (sName.equals(XMLString.TEXT_CHAPTER)) {
handleChapterField(child,ldp,oc);
}
else if (sName.startsWith("text:")) {
traverseInlineText(child,ldp,oc);
}
}
else {
// These tags are ignored in header and footer
if (sName.equals(XMLString.TEXT_FOOTNOTE)) {
palette.getNoteCv().handleFootnote(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_ENDNOTE)) {
palette.getNoteCv().handleEndnote(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_NOTE)) {
if ("endnote".equals(child.getAttribute(XMLString.TEXT_NOTE_CLASS))) {
palette.getNoteCv().handleEndnote(child,ldp,oc);
}
else {
palette.getNoteCv().handleFootnote(child,ldp,oc);
}
}
else if (sName.equals(XMLString.TEXT_SEQUENCE)) {
palette.getFieldCv().handleSequence(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_SEQUENCE_REF)) {
palette.getFieldCv().handleSequenceRef(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_FOOTNOTE_REF)) {
palette.getNoteCv().handleFootnoteRef(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_ENDNOTE_REF)) {
palette.getNoteCv().handleEndnoteRef(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_NOTE_REF)) { // oasis
palette.getNoteCv().handleNoteRef(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_REFERENCE_MARK)) {
palette.getFieldCv().handleReferenceMark(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_REFERENCE_MARK_START)) {
palette.getFieldCv().handleReferenceMark(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_REFERENCE_REF)) {
palette.getFieldCv().handleReferenceRef(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_BOOKMARK)) {
palette.getFieldCv().handleBookmark(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_BOOKMARK_START)) {
palette.getFieldCv().handleBookmark(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_BOOKMARK_REF)) {
palette.getFieldCv().handleBookmarkRef(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_BIBLIOGRAPHY_MARK)) {
palette.getBibCv().handleBibliographyMark(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_ALPHABETICAL_INDEX_MARK)) {
palette.getIndexCv().handleAlphabeticalIndexMark(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_ALPHABETICAL_INDEX_MARK_START)) {
palette.getIndexCv().handleAlphabeticalIndexMark(child,ldp,oc);
}
else if (sName.startsWith("text:")) {
traverseInlineText(child,ldp,oc);
}
}
break;
default:
// Do nothing
}
childNode = childNode.getNextSibling();
}
}
/* traverse inline text, ignoring any draw objects, footnotes, formatting and hyperlinks */
public void traversePlainInlineText(Element node,LaTeXDocumentPortion ldp, Context oc) {
String styleName = node.getAttribute(XMLString.TEXT_STYLE_NAME);
// Always push the font used
palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(ofr.getTextStyle(styleName)));
Node childNode = node.getFirstChild();
while (childNode!=null) {
short nodeType = childNode.getNodeType();
switch (nodeType) {
case Node.TEXT_NODE:
String s = childNode.getNodeValue();
if (s.length() > 0) {
// Need to protect ]
for (int j=0; j<s.length(); j++) {
if (s.charAt(j)!=']') {
ldp.append(palette.getI18n().convert(Character.toString(s.charAt(j)),false,oc.getLang()));
}
else {
ldp.append("{]}");
}
}
}
break;
case Node.ELEMENT_NODE:
Element child = (Element)childNode;
String sName = child.getTagName();
if (sName.equals(XMLString.TEXT_SPAN)) {
traversePlainInlineText(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_S)) {
int count= Misc.getPosInteger(child.getAttribute(XMLString.TEXT_C),1);
for ( ; count > 0; count--) {
ldp.append("\\ ");
}
}
else if (sName.equals(XMLString.TEXT_TAB_STOP) || sName.equals(XMLString.TEXT_TAB)) { // text:tab in oasis
// tab stops are not supported by the converter
ldp.append(sTabstop);
}
else if (OfficeReader.isNoteElement(child)) {
// ignore
}
else if (OfficeReader.isTextElement(child)) {
traversePlainInlineText(child,ldp,oc);
}
break;
default:
// Do nothing
}
childNode = childNode.getNextSibling();
}
// finally pop the special table
palette.getI18n().popSpecialTable();
}
/* traverse inline math, ignoring any draw objects, footnotes, formatting and hyperlinks */
public void traverseInlineMath(Element node,LaTeXDocumentPortion ldp, Context oc) {
String styleName = node.getAttribute(XMLString.TEXT_STYLE_NAME);
// Always push the font used
palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(ofr.getTextStyle(styleName)));
Node childNode = node.getFirstChild();
while (childNode!=null) {
short nodeType = childNode.getNodeType();
switch (nodeType) {
case Node.TEXT_NODE:
String s = childNode.getNodeValue();
ldp.append(palette.getI18n().convert(s,true,oc.getLang()));
break;
case Node.ELEMENT_NODE:
Element child = (Element)childNode;
String sName = child.getTagName();
if (sName.equals(XMLString.TEXT_S)) {
int count= Misc.getPosInteger(child.getAttribute(XMLString.TEXT_C),1);
for ( ; count > 0; count--) {
ldp.append("\\ ");
}
}
else if (sName.equals(XMLString.TEXT_TAB_STOP) || sName.equals(XMLString.TEXT_TAB)) { // text:tab in oasis
// tab stops are not supported by the converter
ldp.append(" ");
}
else if (OfficeReader.isNoteElement(child)) {
// ignore
}
else if (OfficeReader.isTextElement(child)) {
traversePlainInlineText(child,ldp,oc);
}
break;
default:
// Do nothing
}
childNode = childNode.getNextSibling();
}
// finally pop the special table
palette.getI18n().popSpecialTable();
}
/* traverse verbatim inline text, ignoring any draw objects, footnotes, formatting and hyperlinks */
private void traverseVerbatimInlineText(Element node,LaTeXDocumentPortion ldp, Context oc) {
if (node.hasChildNodes()) {
NodeList nList = node.getChildNodes();
int len = nList.getLength();
for (int i = 0; i < len; i++) {
Node childNode = nList.item(i);
short nodeType = childNode.getNodeType();
switch (nodeType) {
case Node.TEXT_NODE:
String s = childNode.getNodeValue();
if (s.length() > 0) {
// text is copied verbatim! (Will be replaced by
// question marks if outside inputenc)
ldp.append(s);
}
break;
case Node.ELEMENT_NODE:
Element child = (Element)childNode;
String sName = child.getTagName();
if (sName.equals(XMLString.TEXT_S)) {
int count= Misc.getPosInteger(child.getAttribute(XMLString.TEXT_C),1);
for ( ; count > 0; count--) {
ldp.append(" ");
}
}
else if (sName.equals(XMLString.TEXT_TAB_STOP) || sName.equals(XMLString.TEXT_TAB)) { // text:tab in oasis
// tab stops are not supported by the onverter
ldp.append(sTabstop);
}
else if (sName.equals(XMLString.TEXT_LINE_BREAK)) {
if (!oc.isNoLineBreaks()) { ldp.nl(); }
}
else if (sName.equals(XMLString.TEXT_NOTE)) {
// oasis; ignore
}
else if (sName.equals(XMLString.TEXT_FOOTNOTE)) {
// ignore
}
else if (sName.equals(XMLString.TEXT_ENDNOTE)) {
// ignore
}
// The respective handlers know how to postpone these marks in verbatim context:
else if (sName.equals(XMLString.TEXT_ALPHABETICAL_INDEX_MARK)) {
palette.getIndexCv().handleAlphabeticalIndexMark(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_ALPHABETICAL_INDEX_MARK_START)) {
palette.getIndexCv().handleAlphabeticalIndexMark(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_REFERENCE_MARK)) {
palette.getFieldCv().handleReferenceMark(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_REFERENCE_MARK_START)) {
palette.getFieldCv().handleReferenceMark(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_BOOKMARK)) {
palette.getFieldCv().handleBookmark(child,ldp,oc);
}
else if (sName.equals(XMLString.TEXT_BOOKMARK_START)) {
palette.getFieldCv().handleBookmark(child,ldp,oc);
}
else if (sName.startsWith("text:")) {
traverseVerbatimInlineText(child,ldp,oc);
}
break;
default:
// Do nothing
}
}
}
}
public void traversePCDATA(Element node, LaTeXDocumentPortion ldp, Context oc) {
if (node.hasChildNodes()) {
NodeList nl = node.getChildNodes();
int nLen = nl.getLength();
for (int i=0; i<nLen; i++) {
if (nl.item(i).getNodeType()==Node.TEXT_NODE) {
ldp.append(palette.getI18n().convert(nl.item(i).getNodeValue(),false,oc.getLang()));
}
}
}
}
private void handleChapterField(Element node, LaTeXDocumentPortion ldp, Context oc) {
HeadingMap hm = config.getHeadingMap();
int nLevel = Misc.getPosInteger(node.getAttribute(XMLString.TEXT_OUTLINE_LEVEL),1);
if (nLevel<=hm.getMaxLevel()) {
int nLaTeXLevel = hm.getLevel(nLevel);
if (nLaTeXLevel==1) {
palette.getPageSc().setChapterField1(node.getAttribute(XMLString.TEXT_DISPLAY));
ldp.append("{\\leftmark}");
}
else if (nLaTeXLevel==2) {
palette.getPageSc().setChapterField2(node.getAttribute(XMLString.TEXT_DISPLAY));
ldp.append("{\\rightmark}");
}
}
}
////////////////////////////////////////////////////////////////////
// Annotations
private void handleOfficeAnnotation(Element node, LaTeXDocumentPortion ldp, Context oc) {
String sCommand = null;
switch (config.notes()) {
case LaTeXConfig.IGNORE: return;
case LaTeXConfig.COMMENT:
// Get the unformatted text of all paragraphs and insert each paragraph as a single comment
ldp.append("%").nl();
Node child = node.getFirstChild();
while (child!=null) {
if (Misc.isElement(child, XMLString.TEXT_P)) {
ldp.append("%");
traversePlainInlineText((Element)child, ldp, oc);
ldp.nl();
}
child = child.getNextSibling();
}
return;
case LaTeXConfig.PDFANNOTATION:
bHasPdfannotation = true;
sCommand = "\\pdfannotation";
break;
case LaTeXConfig.MARGINPAR:
sCommand = "\\marginpar";
break;
case LaTeXConfig.CUSTOM:
sCommand = config.getNotesCommand();
break;
}
// Get the unformatted text of all paragraphs, separated by spaces
ldp.append(sCommand).append("{");
boolean bFirst = true;
Node child = node.getFirstChild();
while (child!=null) {
if (Misc.isElement(child, XMLString.TEXT_P)) {
if (!bFirst) ldp.append(" ");
traversePlainInlineText((Element)child, ldp, oc);
bFirst = false;
}
child = child.getNextSibling();
}
ldp.append("}");
}
/* Check to see if this node has a footnote or endnote as the only subnode */
private boolean onlyNote(Node node) {
if (!node.hasChildNodes()) { return false; }
NodeList nList = node.getChildNodes();
int nLen = nList.getLength();
for (int i = 0; i < nLen; i++) {
Node child = nList.item(i);
short nType = child.getNodeType();
switch (nType) {
case Node.TEXT_NODE: return false;
case Node.ELEMENT_NODE:
if (!OfficeReader.isNoteElement(child)) { return false; }
}
}
return true;
}
}

View file

@ -0,0 +1,543 @@
/************************************************************************
*
* LaTeXConfig.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2009 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2009-02-17)
*
*/
package writer2latex.latex;
import java.util.LinkedList;
import java.util.Hashtable;
import java.util.Enumeration;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import writer2latex.base.BooleanOption;
import writer2latex.base.IntegerOption;
import writer2latex.base.Option;
import writer2latex.latex.util.HeadingMap;
import writer2latex.latex.i18n.ClassicI18n;
import writer2latex.latex.i18n.ReplacementTrie;
import writer2latex.latex.util.StyleMap;
import writer2latex.util.Misc;
public class LaTeXConfig extends writer2latex.base.ConfigBase {
protected int getOptionCount() { return 59; }
protected String getDefaultConfigPath() { return "/writer2latex/latex/config/"; }
// Override setOption to be backwards compatible
public void setOption(String sName,String sValue) {
// this option has been renamed:
if (sName.equals("keep_image_size")) { sName = "original_image_size"; }
super.setOption(sName, sValue);
}
// Backend
public static final int GENERIC = 0;
public static final int DVIPS = 1;
public static final int PDFTEX = 2;
public static final int UNSPECIFIED = 3;
public static final int XETEX = 4;
// Formatting (must be ordered)
public static final int IGNORE_ALL = 0;
public static final int IGNORE_MOST = 1;
public static final int CONVERT_BASIC = 2;
public static final int CONVERT_MOST = 3;
public static final int CONVERT_ALL = 4;
// Page formatting
public static final int CONVERT_HEADER_FOOTER = 5;
// Handling of other formatting
public static final int IGNORE = 0;
public static final int ACCEPT = 1;
public static final int WARNING = 2;
public static final int ERROR = 3;
// Notes
//public static final int IGNORE = 0;
public static final int COMMENT = 1;
public static final int PDFANNOTATION = 2;
public static final int MARGINPAR = 3;
public static final int CUSTOM = 4;
// Options
protected int OPTION_COUNT = 59;
private static final int BACKEND = 0;
private static final int NO_PREAMBLE = 1;
private static final int NO_INDEX = 2;
private static final int DOCUMENTCLASS = 3;
private static final int GLOBAL_OPTIONS = 4;
private static final int INPUTENCODING = 5;
private static final int MULTILINGUAL = 6;
private static final int GREEK_MATH = 7;
private static final int USE_OOOMATH = 8;
private static final int USE_PIFONT = 9;
private static final int USE_IFSYM = 10;
private static final int USE_WASYSYM = 11;
private static final int USE_BBDING = 12;
private static final int USE_EUROSYM = 13;
private static final int USE_TIPA = 14;
private static final int USE_COLOR = 15;
private static final int USE_COLORTBL = 16;
private static final int USE_GEOMETRY = 17;
private static final int USE_FANCYHDR = 18;
private static final int USE_HYPERREF = 19;
private static final int USE_CAPTION = 20;
private static final int USE_LONGTABLE = 21;
private static final int USE_SUPERTABULAR = 22;
private static final int USE_TABULARY = 23;
private static final int USE_ENDNOTES = 24;
private static final int USE_ULEM = 25;
private static final int USE_LASTPAGE = 26;
private static final int USE_TITLEREF = 27;
private static final int USE_OOOREF = 28;
private static final int USE_BIBTEX = 29;
private static final int BIBTEX_STYLE = 30;
private static final int EXTERNAL_BIBTEX_FILES = 31;
private static final int FORMATTING = 32;
private static final int PAGE_FORMATTING = 33;
private static final int OTHER_STYLES = 34;
private static final int IMAGE_CONTENT = 35;
private static final int TABLE_CONTENT = 36;
private static final int IGNORE_HARD_PAGE_BREAKS = 37;
private static final int IGNORE_HARD_LINE_BREAKS = 38;
private static final int IGNORE_EMPTY_PARAGRAPHS = 39;
private static final int IGNORE_DOUBLE_SPACES = 40;
private static final int ALIGN_FRAMES = 41;
private static final int FLOAT_FIGURES = 42;
private static final int FLOAT_TABLES = 43;
private static final int FLOAT_OPTIONS = 44;
private static final int FIGURE_SEQUENCE_NAME = 45;
private static final int TABLE_SEQUENCE_NAME = 46;
private static final int IMAGE_OPTIONS = 47;
private static final int REMOVE_GRAPHICS_EXTENSION = 48;
private static final int ORIGINAL_IMAGE_SIZE = 49;
private static final int SIMPLE_TABLE_LIMIT = 50;
private static final int NOTES = 51;
private static final int METADATA = 52;
private static final int TABSTOP = 53;
private static final int WRAP_LINES_AFTER = 54;
private static final int SPLIT_LINKED_SECTIONS = 55;
private static final int SPLIT_TOPLEVEL_SECTIONS = 56;
private static final int SAVE_IMAGES_IN_SUBDIR = 57;
private static final int DEBUG = 58;
protected LinkedList customPreamble = new LinkedList();
protected StyleMap par = new StyleMap();
protected StyleMap parBlock = new StyleMap();
protected StyleMap text = new StyleMap();
protected StyleMap list = new StyleMap();
protected StyleMap listItem = new StyleMap();
protected StyleMap textAttr = new StyleMap();
protected HeadingMap headingMap = new HeadingMap(5);
protected Hashtable mathSymbols = new Hashtable();
protected ReplacementTrie stringReplace = new ReplacementTrie();
public LaTeXConfig() {
super();
// create options with default values
options[NO_PREAMBLE] = new BooleanOption("no_preamble","false");
options[NO_INDEX] = new BooleanOption("no_index","false");
options[DOCUMENTCLASS] = new Option("documentclass","article");
options[GLOBAL_OPTIONS] = new Option("global_options","");
options[BACKEND] = new IntegerOption("backend","pdftex") {
public void setString(String sValue) {
super.setString(sValue);
if ("generic".equals(sValue)) nValue = GENERIC;
else if ("dvips".equals(sValue)) nValue = DVIPS;
else if ("pdftex".equals(sValue)) nValue = PDFTEX;
else if ("unspecified".equals(sValue)) nValue = UNSPECIFIED;
else if ("xetex".equals(sValue)) nValue = XETEX;
}
};
options[INPUTENCODING] = new IntegerOption("inputencoding",ClassicI18n.writeInputenc(ClassicI18n.ASCII)) {
public void setString(String sValue) {
super.setString(sValue);
nValue = ClassicI18n.readInputenc(sValue);
}
};
options[MULTILINGUAL] = new BooleanOption("multilingual","true");
options[GREEK_MATH] = new BooleanOption("greek_math","true");
options[USE_OOOMATH] = new BooleanOption("use_ooomath","false");
options[USE_PIFONT] = new BooleanOption("use_pifont","false");
options[USE_IFSYM] = new BooleanOption("use_ifsym","false");
options[USE_WASYSYM] = new BooleanOption("use_wasysym","false");
options[USE_BBDING] = new BooleanOption("use_bbding","false");
options[USE_EUROSYM] = new BooleanOption("use_eurosym","false");
options[USE_TIPA] = new BooleanOption("use_tipa","false");
options[USE_COLOR] = new BooleanOption("use_color","true");
options[USE_COLORTBL] = new BooleanOption("use_colortbl","false");
options[USE_GEOMETRY] = new BooleanOption("use_geometry","false");
options[USE_FANCYHDR] = new BooleanOption("use_fancyhdr","false");
options[USE_HYPERREF] = new BooleanOption("use_hyperref","true");
options[USE_CAPTION] = new BooleanOption("use_caption","false");
options[USE_LONGTABLE] = new BooleanOption("use_longtable","false");
options[USE_SUPERTABULAR] = new BooleanOption("use_supertabular","true");
options[USE_TABULARY] = new BooleanOption("use_tabulary","false");
options[USE_ENDNOTES] = new BooleanOption("use_endnotes","false");
options[USE_ULEM] = new BooleanOption("use_ulem","false");
options[USE_LASTPAGE] = new BooleanOption("use_lastpage","false");
options[USE_TITLEREF] = new BooleanOption("use_titleref","false");
options[USE_OOOREF] = new BooleanOption("use_oooref","false");
options[USE_BIBTEX] = new BooleanOption("use_bibtex","false");
options[BIBTEX_STYLE] = new Option("bibtex_style","plain");
options[EXTERNAL_BIBTEX_FILES] = new Option("external_bibtex_files","");
options[FORMATTING] = new IntegerOption("formatting","convert_basic") {
public void setString(String sValue) {
super.setString(sValue);
if ("convert_all".equals(sValue)) nValue = CONVERT_ALL;
else if ("convert_most".equals(sValue)) nValue = CONVERT_MOST;
else if ("convert_basic".equals(sValue)) nValue = CONVERT_BASIC;
else if ("ignore_most".equals(sValue)) nValue = IGNORE_MOST;
else if ("ignore_all".equals(sValue)) nValue = IGNORE_ALL;
}
};
options[PAGE_FORMATTING] = new IntegerOption("page_formatting","convert_all") {
public void setString(String sValue) {
super.setString(sValue);
if ("convert_all".equals(sValue)) nValue = CONVERT_ALL;
else if ("convert_header_footer".equals(sValue)) nValue = CONVERT_HEADER_FOOTER;
else if ("ignore_all".equals(sValue)) nValue = IGNORE_ALL;
}
};
options[OTHER_STYLES] = new ContentHandlingOption("other_styles","accept");
options[IMAGE_CONTENT] = new ContentHandlingOption("image_content","accept");
options[TABLE_CONTENT] = new ContentHandlingOption("table_content","accept");
options[IGNORE_HARD_PAGE_BREAKS] = new BooleanOption("ignore_hard_page_breaks","false");
options[IGNORE_HARD_LINE_BREAKS] = new BooleanOption("ignore_hard_line_breaks","false");
options[IGNORE_EMPTY_PARAGRAPHS] = new BooleanOption("ignore_empty_paragraphs","false");
options[IGNORE_DOUBLE_SPACES] = new BooleanOption("ignore_double_spaces","false");
options[ALIGN_FRAMES] = new BooleanOption("align_frames","true");
options[FLOAT_FIGURES] = new BooleanOption("float_figures","false");
options[FLOAT_TABLES] = new BooleanOption("float_tables","false");
options[FLOAT_OPTIONS] = new Option("float_options","h");
options[FIGURE_SEQUENCE_NAME] = new BooleanOption("figure_sequence_name","");
options[TABLE_SEQUENCE_NAME] = new BooleanOption("table_sequence_name","");
options[IMAGE_OPTIONS] = new Option("image_options","");
options[REMOVE_GRAPHICS_EXTENSION] = new BooleanOption("remove_graphics_extension","false");
options[ORIGINAL_IMAGE_SIZE] = new BooleanOption("original_image_size","false");
options[SIMPLE_TABLE_LIMIT] = new IntegerOption("simple_table_limit","0") {
public void setString(String sValue) {
super.setString(sValue);
nValue = Misc.getPosInteger(sValue,0);
}
};
options[NOTES] = new IntegerOption("notes","comment") {
public void setString(String sValue) {
super.setString(sValue);
if ("ignore".equals(sValue)) nValue = IGNORE;
else if ("comment".equals(sValue)) nValue = COMMENT;
else if ("pdfannotation".equals(sValue)) nValue = PDFANNOTATION;
else if ("marginpar".equals(sValue)) nValue = MARGINPAR;
else nValue = CUSTOM;
}
};
options[METADATA] = new BooleanOption("metadata","true");
options[TABSTOP] = new Option("tabstop","");
options[WRAP_LINES_AFTER] = new IntegerOption("wrap_lines_after","72") {
public void setString(String sValue) {
super.setString(sValue);
nValue = Misc.getPosInteger(sValue,0);
}
};
options[SPLIT_LINKED_SECTIONS] = new BooleanOption("split_linked_sections","false");
options[SPLIT_TOPLEVEL_SECTIONS] = new BooleanOption("split_toplevel_sections","false");
options[SAVE_IMAGES_IN_SUBDIR] = new BooleanOption("save_images_in_subdir","false");
options[DEBUG] = new BooleanOption("debug","false");
// Headings for article class:
headingMap.setLevelData(1,"section",1);
headingMap.setLevelData(2,"subsection",2);
headingMap.setLevelData(3,"subsubsection",3);
headingMap.setLevelData(4,"paragraph",4);
headingMap.setLevelData(5,"subparagraph",5);
// Standard string replace:
// Fix french spacing; replace nonbreaking space
// right before em-dash, !, ?, : and ; (babel handles this)
stringReplace.put("\u00A0\u2014"," \u2014",ClassicI18n.readFontencs("any"));
stringReplace.put("\u00A0!"," !",ClassicI18n.readFontencs("any"));
stringReplace.put("\u00A0?"," ?",ClassicI18n.readFontencs("any"));
stringReplace.put("\u00A0:"," :",ClassicI18n.readFontencs("any"));
stringReplace.put("\u00A0;"," ;",ClassicI18n.readFontencs("any"));
// Right after opening guillemet and right before closing guillemet:
// Here we must *keep* the non-breaking space
// TODO: Use \og and \fg if the document contains french...
//stringReplace.put("\u00AB\u00A0","\u00AB ",I18n.readFontencs("any"));
//stringReplace.put("\u00A0\u00BB"," \u00BB",I18n.readFontencs("any"));
}
protected void readInner(Element elm) {
if (elm.getTagName().equals("style-map")) {
String sName = elm.getAttribute("name");
String sFamily = elm.getAttribute("family");
if (sFamily.length()==0) { // try old name
sFamily = elm.getAttribute("class");
}
String sBefore = elm.getAttribute("before");
String sAfter = elm.getAttribute("after");
boolean bLineBreak = !"false".equals(elm.getAttribute("line-break"));
boolean bVerbatim = "true".equals(elm.getAttribute("verbatim"));
if ("paragraph".equals(sFamily)) {
par.put(sName,sBefore,sAfter,bLineBreak,bVerbatim);
}
if ("paragraph-block".equals(sFamily)) {
String sNext = elm.getAttribute("next");
parBlock.put(sName,sBefore,sAfter,sNext,bVerbatim);
}
else if ("text".equals(sFamily)) {
text.put(sName,sBefore,sAfter,false,bVerbatim);
}
else if ("list".equals(sFamily)) {
list.put(sName,sBefore,sAfter);
}
else if ("listitem".equals(sFamily)) {
listItem.put(sName,sBefore,sAfter);
}
else if ("text-attribute".equals(sFamily)) {
textAttr.put(sName, sBefore, sAfter);
}
}
else if (elm.getTagName().equals("heading-map")) {
readHeadingMap(elm);
}
else if (elm.getTagName().equals("string-replace")) {
String sInput = elm.getAttribute("input");
String sLaTeXCode = elm.getAttribute("latex-code");
String sFontencs = elm.getAttribute("fontencs");
int nFontencs = ClassicI18n.readFontencs(sFontencs.length()>0 ? sFontencs : "any");
stringReplace.put(sInput,sLaTeXCode,nFontencs);
}
else if (elm.getTagName().equals("custom-preamble")) {
Node child = elm.getFirstChild();
while (child!=null) {
if (child.getNodeType()==Node.TEXT_NODE) {
customPreamble.add(child.getNodeValue());
}
child = child.getNextSibling();
}
}
else if (elm.getTagName().equals("math-symbol-map")) {
String sName = elm.getAttribute("name");
String sLatex = elm.getAttribute("latex");
mathSymbols.put(sName,sLatex);
}
}
public void readHeadingMap(Element node) {
int nMaxLevel = Misc.getPosInteger(node.getAttribute("max-level"),0);
headingMap.reset(nMaxLevel);
Node child = node.getFirstChild();
while (child!=null) {
if (child.getNodeType()==Node.ELEMENT_NODE) {
Element elm = (Element) child;
if (elm.getTagName().equals("heading-level-map")) {
int nWriterLevel = Misc.getPosInteger(elm.getAttribute("writer-level"),1);
String sName = elm.getAttribute("name");
int nLevel = Misc.getPosInteger(elm.getAttribute("level"),0);
headingMap.setLevelData(nWriterLevel,sName,nLevel);
}
}
child = child.getNextSibling();
}
}
protected void writeInner(Document dom) {
// Write math symbol map
Enumeration msEnum = mathSymbols.keys();
while (msEnum.hasMoreElements()) {
String sName = (String) msEnum.nextElement();
String sLatex = (String) mathSymbols.get(sName);
Element msNode = dom.createElement("math-symbol-map");
msNode.setAttribute("name",sName);
msNode.setAttribute("latex",sLatex);
dom.getDocumentElement().appendChild(msNode);
}
writeStyleMap(dom,par,"paragraph");
writeStyleMap(dom,parBlock,"paragraph-block");
writeStyleMap(dom,text,"text");
writeStyleMap(dom,list,"list");
writeStyleMap(dom,listItem,"listitem");
writeStyleMap(dom,textAttr,"text-attribute");
Element hmNode = dom.createElement("heading-map");
hmNode.setAttribute("max-level",Integer.toString(headingMap.getMaxLevel()));
dom.getDocumentElement().appendChild(hmNode);
for (int i=1; i<=headingMap.getMaxLevel(); i++) {
Element hlmNode = dom.createElement("heading-level-map");
hlmNode.setAttribute("writer-level",Integer.toString(i));
hlmNode.setAttribute("name",headingMap.getName(i));
hlmNode.setAttribute("level",Integer.toString(headingMap.getLevel(i)));
hmNode.appendChild(hlmNode);
}
// TODO: Export string replacements
//String[] sInputStrings = stringReplace.getInputStrings();
/*
int nSize = sInputStrings.size();
for (int i=0; i<nSize; i++) {
String sInput = sInputStrings[i];
ReplacementTrieNode node = stringReplace.get(sInput);
Element srNode = dom.createElement("string-replace");
srNode.setAttribute("input",sInput);
srNode.setAttribute("latex-code",node.getLaTeXCode());
srNode.setAttribute("fontenc",I18n.writeFontencs(node.getFontencs()));
hmNode.appendChild(srNode);
}
*/
writeContent(dom,customPreamble,"custom-preamble");
}
private void writeStyleMap(Document dom, StyleMap sm, String sFamily) {
Enumeration smEnum = sm.getNames();
while (smEnum.hasMoreElements()) {
String sName = (String) smEnum.nextElement();
Element smNode = dom.createElement("style-map");
smNode.setAttribute("name",sName);
smNode.setAttribute("family",sFamily);
smNode.setAttribute("before",sm.getBefore(sName));
smNode.setAttribute("after",sm.getAfter(sName));
if (sm.getNext(sName)!=null) {
smNode.setAttribute("next",sm.getNext(sName));
}
if (!sm.getLineBreak(sName)) {
smNode.setAttribute("line-break","false");
}
if (sm.getVerbatim(sName)) {
smNode.setAttribute("verbatim","true");
}
dom.getDocumentElement().appendChild(smNode);
}
}
private void writeContent(Document dom, LinkedList list, String sElement) {
Element node = dom.createElement(sElement);
int nLen = list.size();
for (int i=0; i<nLen; i++) {
node.appendChild( dom.createTextNode( (String) list.get(i) ) );
}
dom.getDocumentElement().appendChild(node);
}
// Convenience accessor methods
// String replace
public ReplacementTrie getStringReplace() { return stringReplace; }
// Common options
public boolean debug() { return ((BooleanOption) options[DEBUG]).getValue(); }
// General options
public String getDocumentclass() { return options[DOCUMENTCLASS].getString(); }
public String getGlobalOptions() { return options[GLOBAL_OPTIONS].getString(); }
public int getBackend() { return ((IntegerOption) options[BACKEND]).getValue(); }
public int getInputencoding() { return ((IntegerOption) options[INPUTENCODING]).getValue(); }
public boolean multilingual() { return ((BooleanOption) options[MULTILINGUAL]).getValue(); }
public boolean greekMath() { return ((BooleanOption) options[GREEK_MATH]).getValue(); }
public boolean noPreamble() { return ((BooleanOption) options[NO_PREAMBLE]).getValue(); }
public boolean noIndex() { return ((BooleanOption) options[NO_INDEX]).getValue(); }
// Package options
public boolean useOoomath() { return ((BooleanOption) options[USE_OOOMATH]).getValue(); }
public boolean usePifont() { return ((BooleanOption) options[USE_PIFONT]).getValue(); }
public boolean useIfsym() { return ((BooleanOption) options[USE_IFSYM]).getValue(); }
public boolean useWasysym() { return ((BooleanOption) options[USE_WASYSYM]).getValue(); }
public boolean useBbding() { return ((BooleanOption) options[USE_BBDING]).getValue(); }
public boolean useEurosym() { return ((BooleanOption) options[USE_EUROSYM]).getValue(); }
public boolean useTipa() { return ((BooleanOption) options[USE_TIPA]).getValue(); }
public boolean useColor() { return ((BooleanOption) options[USE_COLOR]).getValue(); }
public boolean useColortbl() { return ((BooleanOption) options[USE_COLORTBL]).getValue(); }
public boolean useGeometry() { return ((BooleanOption) options[USE_GEOMETRY]).getValue(); }
public boolean useFancyhdr() { return ((BooleanOption) options[USE_FANCYHDR]).getValue(); }
public boolean useHyperref() { return ((BooleanOption) options[USE_HYPERREF]).getValue(); }
public boolean useCaption() { return ((BooleanOption) options[USE_CAPTION]).getValue(); }
public boolean useLongtable() { return ((BooleanOption) options[USE_LONGTABLE]).getValue(); }
public boolean useSupertabular() { return ((BooleanOption) options[USE_SUPERTABULAR]).getValue(); }
public boolean useTabulary() { return ((BooleanOption) options[USE_TABULARY]).getValue(); }
public boolean useEndnotes() { return ((BooleanOption) options[USE_ENDNOTES]).getValue(); }
public boolean useUlem() { return ((BooleanOption) options[USE_ULEM]).getValue(); }
public boolean useLastpage() { return ((BooleanOption) options[USE_LASTPAGE]).getValue(); }
public boolean useTitleref() { return ((BooleanOption) options[USE_TITLEREF]).getValue(); }
public boolean useOooref() { return ((BooleanOption) options[USE_OOOREF]).getValue(); }
public boolean useBibtex() { return ((BooleanOption) options[USE_BIBTEX]).getValue(); }
public String bibtexStyle() { return options[BIBTEX_STYLE].getString(); }
public String externalBibtexFiles() { return options[EXTERNAL_BIBTEX_FILES].getString(); }
// Formatting options
public int formatting() { return ((IntegerOption) options[FORMATTING]).getValue(); }
public int pageFormatting() { return ((IntegerOption) options[PAGE_FORMATTING]).getValue(); }
public int otherStyles() { return ((IntegerOption) options[OTHER_STYLES]).getValue(); }
public int imageContent() { return ((IntegerOption) options[IMAGE_CONTENT]).getValue(); }
public int tableContent() { return ((IntegerOption) options[TABLE_CONTENT]).getValue(); }
public boolean ignoreHardPageBreaks() { return ((BooleanOption) options[IGNORE_HARD_PAGE_BREAKS]).getValue(); }
public boolean ignoreHardLineBreaks() { return ((BooleanOption) options[IGNORE_HARD_LINE_BREAKS]).getValue(); }
public boolean ignoreEmptyParagraphs() { return ((BooleanOption) options[IGNORE_EMPTY_PARAGRAPHS]).getValue(); }
public boolean ignoreDoubleSpaces() { return ((BooleanOption) options[IGNORE_DOUBLE_SPACES]).getValue(); }
// Graphics options
public boolean alignFrames() { return ((BooleanOption) options[ALIGN_FRAMES]).getValue(); }
public boolean floatFigures() { return ((BooleanOption) options[FLOAT_FIGURES]).getValue(); }
public boolean floatTables() { return ((BooleanOption) options[FLOAT_TABLES]).getValue(); }
public String getFloatOptions() { return options[FLOAT_OPTIONS].getString(); }
public String getFigureSequenceName() { return options[FIGURE_SEQUENCE_NAME].getString(); }
public String getTableSequenceName() { return options[TABLE_SEQUENCE_NAME].getString(); }
public String getImageOptions() { return options[IMAGE_OPTIONS].getString(); }
public boolean removeGraphicsExtension() { return ((BooleanOption) options[REMOVE_GRAPHICS_EXTENSION]).getValue(); }
public boolean originalImageSize() { return ((BooleanOption) options[ORIGINAL_IMAGE_SIZE]).getValue(); }
// Tables
public int getSimpleTableLimit() { return ((IntegerOption) options[SIMPLE_TABLE_LIMIT]).getValue(); }
// Notes
public int notes() { return ((IntegerOption) options[NOTES]).getValue(); }
public String getNotesCommand() { return options[NOTES].getString(); }
// Metadata
public boolean metadata() { return ((BooleanOption) options[METADATA]).getValue(); }
// Tab stops
public String getTabstop() { return options[TABSTOP].getString(); }
// Files
public int getWrapLinesAfter() { return ((IntegerOption) options[WRAP_LINES_AFTER]).getValue(); }
public boolean splitLinkedSections() { return ((BooleanOption) options[SPLIT_LINKED_SECTIONS]).getValue(); }
public boolean splitToplevelSections() { return ((BooleanOption) options[SPLIT_TOPLEVEL_SECTIONS]).getValue(); }
public boolean saveImagesInSubdir() { return ((BooleanOption) options[SAVE_IMAGES_IN_SUBDIR]).getValue(); }
public Hashtable getMathSymbols() { return mathSymbols; }
public StyleMap getParStyleMap() { return par; }
public StyleMap getParBlockStyleMap() { return parBlock; }
public StyleMap getTextStyleMap() { return text; }
public StyleMap getListStyleMap() { return list; }
public StyleMap getListItemStyleMap() { return listItem; }
public StyleMap getTextAttributeStyleMap() { return textAttr; }
public HeadingMap getHeadingMap() { return headingMap; }
public LinkedList getCustomPreamble() { return customPreamble; }
}

View file

@ -0,0 +1,152 @@
/************************************************************************
*
* LaTeXDocument.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.latex;
import writer2latex.xmerge.Document;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
/**
* <p>Class representing a LaTeX document.</p>
*
*/
public class LaTeXDocument implements Document {
private static final String FILE_EXTENSION = ".tex";
private String sName;
private String sEncoding = "ASCII";
private int nWrap;
private LaTeXDocumentPortion contents;
/**
* <p>Constructs a new LaTeX Document.</p>
*
* <p>This new document is empty. Document data must added to the preamble and
* the body using appropriate methods.</p>
*
* @param sName The name of the <code>LaTeXDocument</code>.
* @param nWrap Lines should be wrapped after this position
*/
public LaTeXDocument(String sName,int nWrap) {
this.nWrap = nWrap;
this.sName = trimDocumentName(sName);
contents = new LaTeXDocumentPortion(true);
}
/**
* <p>This method is supposed to read <code>byte</code> data from the InputStream.
* Currently it does nothing, since we don't need it.</p>
*
* @param is InputStream containing a LaTeX data file.
*
* @throws IOException In case of any I/O errors.
*/
public void read(InputStream is) throws IOException {
// Do nothing.
}
/**
* <p>Returns the <code>Document</code> name with no file extension.</p>
*
* @return The <code>Document</code> name with no file extension.
*/
public String getName() {
return sName;
}
/**
* <p>Returns the <code>Document</code> name with file extension.</p>
*
* @return The <code>Document</code> name with file extension.
*/
public String getFileName() {
return new String(sName + FILE_EXTENSION);
}
/**
* <p>Writes out the <code>Document</code> content to the specified
* <code>OutputStream</code>.</p>
*
* <p>This method may not be thread-safe.
* Implementations may or may not synchronize this
* method. User code (i.e. caller) must make sure that
* calls to this method are thread-safe.</p>
*
* @param os <code>OutputStream</code> to write out the
* <code>Document</code> content.
*
* @throws IOException If any I/O error occurs.
*/
public void write(OutputStream os) throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(os,sEncoding);
contents.write(osw,nWrap,"\n");
osw.flush();
osw.close();
}
/**
* <p> Set the output encoding to use when writing the document.</p>
*/
public void setEncoding(String sEncoding) { this.sEncoding = sEncoding; }
/**
* <p>Returns the <code>LaTeXDocumentPortion</code>, that contains the
* contents of the document.</p>
*
* @return The content <code>LaTeXDocumentPortion</code>.
*/
public LaTeXDocumentPortion getContents(){
return contents;
}
/*
* Utility method to make sure the document name is stripped of any file
* extensions before use.
*/
private String trimDocumentName(String name) {
String temp = name.toLowerCase();
if (temp.endsWith(FILE_EXTENSION)) {
// strip the extension
int nlen = name.length();
int endIndex = nlen - FILE_EXTENSION.length();
name = name.substring(0,endIndex);
}
return name;
}
}

View file

@ -0,0 +1,214 @@
/************************************************************************
*
* LaTeXDocumentPortion.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2006 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2007-10-02)
*
*/
package writer2latex.latex;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.util.Vector;
import writer2latex.util.Misc;
/** This class represents a portion of a LaTeX document. A portion is any
number of lines, and may include subportions. */
public class LaTeXDocumentPortion {
private Vector nodes; // The collection of all nodes in this portion
private StringBuffer curText; // The currently active node (always the last node)
private boolean bEmpty; // Is the active node empty?
private boolean bWrap; // Do we allow line wrap in this portion?
public LaTeXDocumentPortion(boolean bWrap){
this.bWrap = bWrap;
nodes = new Vector();
curText = new StringBuffer();
bEmpty = true;
}
/** Add another portion to the end of this portion */
public LaTeXDocumentPortion append(LaTeXDocumentPortion ldp) {
if (!bEmpty) {
// add the current node to the node list and create new current node
nodes.add(curText);
curText = new StringBuffer();
bEmpty = true;
}
nodes.add(ldp);
return this;
}
/** Add a string to the end of this portion */
public LaTeXDocumentPortion append(String s){
curText.append(s);
bEmpty = false; // even if this is the empty string!
return this;
}
/** Add a newline to the end of this portion */
public LaTeXDocumentPortion nl(){
curText.append("\n");
bEmpty = false;
return this;
}
/** write a segment of text (eg. a word) to the output */
private void writeSegment(String s, int nStart, int nEnd, OutputStreamWriter osw) throws IOException {
for (int i=nStart; i<nEnd; i++) { osw.write(s.charAt(i)); }
}
/** write the contents of a StringBuffer to the output */
private void writeBuffer(StringBuffer text, OutputStreamWriter osw, int nLineLen, String sNewline) throws IOException {
String s = text.toString();
int nLen = s.length();
int[] nBreakPoints = new int[100];
int nLastBPIndex = 99;
int nStart = 0;
while (nStart<nLen) {
// identify line and breakpoints
int nBPIndex = 0;
boolean bEscape = false;
boolean bComment = false;
int nNewline = nStart;
char c;
while (nNewline<nLen) {
if (nBPIndex==nLastBPIndex) {
nBreakPoints = Misc.doubleIntArray(nBreakPoints);
nLastBPIndex = nBreakPoints.length-1;
}
c = s.charAt(nNewline);
if (c=='\n') {
nBreakPoints[nBPIndex++] = nNewline;
break;
}
if (bEscape) { bEscape = false; }
else if (c=='\\') { bEscape = true; }
else if (c=='%') { bComment = true; }
else if (!bComment && c==' ') { nBreakPoints[nBPIndex++] = nNewline; }
nNewline++;
}
if (nBPIndex==nLastBPIndex) {
nBreakPoints = Misc.doubleIntArray(nBreakPoints);
nLastBPIndex = nBreakPoints.length-1;
}
if (nNewline==nLen) { nBreakPoints[nBPIndex++] = nNewline; }
// write out line
int nCurLineLen = nBreakPoints[0]-nStart;
writeSegment(s,nStart,nBreakPoints[0],osw);
for (int i=0; i<nBPIndex-1; i++) {
int nSegmentLen = nBreakPoints[i+1]-nBreakPoints[i];
if (nSegmentLen+nCurLineLen>nLineLen) {
// break line before this segment
osw.write(sNewline);
nCurLineLen = nSegmentLen;
}
else {
// segment fits in current line
osw.write(" ");
nCurLineLen += nSegmentLen;
}
writeSegment(s,nBreakPoints[i]+1,nBreakPoints[i+1],osw);
}
osw.write(sNewline);
nStart = nNewline+1;
}
}
/** write the contents of a StringBuffer to the output without wrap */
private void writeBuffer(StringBuffer text, OutputStreamWriter osw, String sNewline) throws IOException {
String s = text.toString();
int nLen = s.length();
int nStart = 0;
while (nStart<nLen) {
// identify line
int nNewline = nStart;
while (nNewline<nLen) {
if (s.charAt(nNewline)=='\n') { break; }
nNewline++;
}
// write out line
writeSegment(s,nStart,nNewline,osw);
osw.write(sNewline);
nStart = nNewline+1;
}
}
/** Write this portion to the output (note: nLineLen=0 means no wrap) */
public void write(OutputStreamWriter osw, int nLineLen, String sNewline) throws IOException {
int n = nodes.size();
for (int i=0; i<n; i++) {
if (nodes.get(i) instanceof LaTeXDocumentPortion) {
((LaTeXDocumentPortion) nodes.get(i)).write(osw,nLineLen,sNewline);
}
else if (bWrap && nLineLen>0) {
writeBuffer((StringBuffer) nodes.get(i),osw,nLineLen,sNewline);
}
else {
writeBuffer((StringBuffer) nodes.get(i),osw,sNewline);
}
}
if (!bEmpty) { // write current node as well
if (bWrap && nLineLen>0) {
writeBuffer(curText,osw,nLineLen,sNewline);
}
else {
writeBuffer(curText,osw,sNewline);
}
}
}
/** Return the content of this LaTeXDocumentStream as a string */
public String toString() {
StringBuffer buf = new StringBuffer();
int n = nodes.size();
for (int i=0; i<n; i++) {
if (nodes.get(i) instanceof LaTeXDocumentPortion) {
buf.append(((LaTeXDocumentPortion) nodes.get(i)).toString());
}
else {
buf.append(((StringBuffer) nodes.get(i)).toString());
}
}
if (!bEmpty) { // write current node as well
buf.append(curText.toString());
}
return buf.toString();
}
} // end class LaTeXDocumentPortion
// TO DO: consider StringBuffer->ByteArrayOutputStream (performance??)

View file

@ -0,0 +1,384 @@
/************************************************************************
*
* ListStyleConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-09-08)
*
*/
package writer2latex.latex;
import java.util.Hashtable;
import writer2latex.util.*;
import writer2latex.office.*;
import writer2latex.latex.util.BeforeAfter;
import writer2latex.latex.util.Context;
/* This class creates LaTeX code from OOo list styles
*/
public class ListStyleConverter extends StyleConverter {
boolean bNeedSaveEnumCounter = false;
private Hashtable listStyleLevelNames = new Hashtable();
/** <p>Constructs a new <code>ListStyleConverter</code>.</p>
*/
public ListStyleConverter(OfficeReader ofr, LaTeXConfig config,
ConverterPalette palette) {
super(ofr,config,palette);
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
if (config.formatting()>=LaTeXConfig.CONVERT_MOST || !styleNames.isEmpty()) {
decl.append("% List styles").nl();
// May need an extra counter to handle continued numbering in lists
if (bNeedSaveEnumCounter) {
decl.append("\\newcounter{saveenum}").nl();
}
// If we export formatting, we need some hooks from lists to paragraphs:
if (config.formatting()>=LaTeXConfig.CONVERT_MOST) {
decl.append("\\newcommand\\writerlistleftskip{}").nl()
.append("\\newcommand\\writerlistparindent{}").nl()
.append("\\newcommand\\writerlistlabel{}").nl()
.append("\\newcommand\\writerlistremovelabel{")
.append("\\aftergroup\\let\\aftergroup\\writerlistparindent\\aftergroup\\relax")
.append("\\aftergroup\\let\\aftergroup\\writerlistlabel\\aftergroup\\relax}").nl();
}
super.appendDeclarations(pack,decl);
}
}
/** <p>Apply a list style to an ordered or unordered list.</p> */
public void applyListStyle(String sStyleName, int nLevel, boolean bOrdered,
boolean bContinue, BeforeAfter ba) {
// Step 1. We may have a style map, this always takes precedence
String sDisplayName = ofr.getListStyles().getDisplayName(sStyleName);
if (config.getListStyleMap().contains(sDisplayName)) {
ba.add(config.getListStyleMap().getBefore(sDisplayName),
config.getListStyleMap().getAfter(sDisplayName));
return;
}
// Step 2: The list style may not exist, or the user wants to ignore it.
// In this case we create default lists
ListStyle style = ofr.getListStyle(sStyleName);
if (style==null || config.formatting()<=LaTeXConfig.IGNORE_MOST) {
if (nLevel<=4) {
if (bOrdered) {
ba.add("\\begin{enumerate}","\\end{enumerate}");
}
else {
ba.add("\\begin{itemize}","\\end{itemize}");
}
}
return;
}
// Step 3: Export as default lists, but redefine labels
if (config.formatting()==LaTeXConfig.CONVERT_BASIC) {
if (nLevel==1) {
if (!styleNames.containsName(getDisplayName(sStyleName))) {
createListStyleLabels(sStyleName);
}
ba.add("\\liststyle"+styleNames.getExportName(getDisplayName(sStyleName))+"\n","");
}
if (nLevel<=4) {
String sCounterName = ((String[]) listStyleLevelNames.get(sStyleName))[nLevel];
if (bContinue && style.isNumber(nLevel)) {
bNeedSaveEnumCounter = true;
ba.add("\\setcounter{saveenum}{\\value{"+sCounterName+"}}\n","");
}
if (bOrdered) {
ba.add("\\begin{enumerate}","\\end{enumerate}");
}
else {
ba.add("\\begin{itemize}","\\end{itemize}");
}
if (bContinue && style.isNumber(nLevel)) {
ba.add("\n\\setcounter{"+sCounterName+"}{\\value{saveenum}}","");
}
}
return;
}
// Step 4: Export with formatting, as "Writer style" custom lists
if (nLevel<=4) { // TODO: Max level should not be fixed
if (!styleNames.containsName(getDisplayName(sStyleName))) {
createListStyle(sStyleName);
}
String sTeXName="list"+styleNames.getExportName(getDisplayName(sStyleName))
+"level"+Misc.int2roman(nLevel);
if (!bContinue && style.isNumber(nLevel)) {
int nStartValue = Misc.getPosInteger(style.getLevelProperty(nLevel,XMLString.TEXT_START_VALUE),1)-1;
ba.add("\\setcounter{"+sTeXName+"}{"+Integer.toString(nStartValue)+"}\n","");
}
ba.add("\\begin{"+sTeXName+"}","\\end{"+sTeXName+"}");
}
}
/** <p>Apply a list style to a list item.</p> */
public void applyListItemStyle(String sStyleName, int nLevel, boolean bHeader,
boolean bRestart, int nStartValue, BeforeAfter ba) {
// Step 1. We may have a style map, this always takes precedence
String sDisplayName = ofr.getListStyles().getDisplayName(sStyleName);
if (config.getListItemStyleMap().contains(sDisplayName)) {
ba.add(config.getListItemStyleMap().getBefore(sDisplayName),
config.getListItemStyleMap().getAfter(sDisplayName));
return;
}
// Step 2: The list style may not exist, or the user wants to ignore it.
// In this case we create default lists
ListStyle style = ofr.getListStyle(sStyleName);
if (style==null || config.formatting()<=LaTeXConfig.IGNORE_MOST) {
if (nLevel<=4) {
if (bHeader) { ba.add("\\item[] ",""); }
else { ba.add("\\item ",""); }
}
return;
}
// Step 3: Export as default lists (with redefined labels)
if (config.formatting()==LaTeXConfig.CONVERT_BASIC) {
if (nLevel<=4) {
if (bHeader) {
ba.add("\\item[] ","");
}
else if (bRestart && style.isNumber(nLevel)) {
ba.add("\n\\setcounter{enum"+Misc.int2roman(nLevel)
+"}{"+(nStartValue-1)+"}\n\\item ","");
}
else {
ba.add("\\item ","");
}
}
return;
}
// Step 4: Export with formatting, as "Writer style" custom lists
if (nLevel<=4 && !bHeader) { // TODO: Max level should not be fixed
String sTeXName="list"+styleNames.getExportName(getDisplayName(sStyleName))
+"level"+Misc.int2roman(nLevel);
if (bRestart && style.isNumber(nLevel)) {
ba.add("\\setcounter{"+sTeXName+"}{"+(nStartValue-1)+"}\n","");
}
ba.add("\\item ","");
}
}
/** <p>Create labels for default lists (enumerate/itemize) based on
* a List Style
*/
private void createListStyleLabels(String sStyleName) {
String sTeXName = styleNames.getExportName(getDisplayName(sStyleName));
declarations.append("\\newcommand\\liststyle")
.append(sTeXName).append("{%").nl();
ListStyle style = ofr.getListStyle(sStyleName);
int nEnum = 0;
int nItem = 0;
String sName[] = new String[5];
for (int i=1; i<=4; i++) {
if (style.isNumber(i)) { sName[i]="enum"+Misc.int2roman(++nEnum); }
else { sName[i]="item"+Misc.int2roman(++nItem); }
}
listStyleLevelNames.put(sStyleName, sName);
createLabels(style, sName, 4, false, true, false, declarations);
declarations.append("}").nl();
}
/** <p>Create "Writer style" lists based on a List Style.
<p>A list in writer is really a sequence of numbered paragraphs, so
this is also how we implement it in LaTeX.
The enivronment + redefined \item defines three hooks:
\writerlistleftskip, \writerlistparindent, \writerlistlabel
which are used by exported paragraph styles to apply numbering.
*/
private void createListStyle(String sStyleName) {
ListStyle style = ofr.getListStyle(sStyleName);
// Create labels
String sTeXName = styleNames.getExportName(getDisplayName(sStyleName));
String[] sLevelName = new String[5];
for (int i=1; i<=4; i++) {
sLevelName[i]="list"+sTeXName+"level"+Misc.int2roman(i);
}
createLabels(style,sLevelName,4,true,false,true,declarations);
// Create environments
for (int i=1; i<=4; i++) {
String sSpaceBefore = getLength(style,i,XMLString.TEXT_SPACE_BEFORE);
String sLabelWidth = getLength(style,i,XMLString.TEXT_MIN_LABEL_WIDTH);
String sLabelDistance = getLength(style,i,XMLString.TEXT_MIN_LABEL_DISTANCE);
String sTextAlign = style.getLevelStyleProperty(i,XMLString.FO_TEXT_ALIGN);
String sAlignmentChar = "l"; // start (or left) is default
if (sTextAlign!=null) {
if ("end".equals(sTextAlign)) { sAlignmentChar="r"; }
else if ("right".equals(sTextAlign)) { sAlignmentChar="r"; }
else if ("center".equals(sTextAlign)) { sAlignmentChar="c"; }
}
declarations
.append("\\newenvironment{")
.append(sLevelName[i]).append("}{")
.append("\\def\\writerlistleftskip{\\addtolength\\leftskip{")
.append(Misc.add(sSpaceBefore,sLabelWidth)).append("}}")
.append("\\def\\writerlistparindent{}")
.append("\\def\\writerlistlabel{}");
// Redefine \item
declarations
.append("\\def\\item{")
.append("\\def\\writerlistparindent{\\setlength\\parindent{")
.append("-").append(sLabelWidth).append("}}")
.append("\\def\\writerlistlabel{");
if (style.isNumber(i)) {
declarations.append("\\stepcounter{")
.append(sLevelName[i]).append("}");
}
declarations
.append("\\makebox[").append(sLabelWidth).append("][")
.append(sAlignmentChar).append("]{")
.append("\\label").append(sLevelName[i]).append("}")
.append("\\hspace{").append(sLabelDistance).append("}")
.append("\\writerlistremovelabel}}}{}").nl();
}
}
/** <p>Create LaTeX list labels from an OOo list style. Examples:</p>
* <p>Bullets:</p>
* <pre>\newcommand\labelliststylei{\textbullet}
* \newcommand\labelliststyleii{*}
* \newcommand\labelliststyleiii{\textstylebullet{>}}</pre>
* <p>Numbering:</p>
* <pre>\newcounter{liststylei}
* \newcounter{liststyleii}[liststylei]
* \newcounter{liststyleiii}[liststyleii]
* \renewcommand\theliststylei{\Roman{liststylei}}
* \renewcommand\theliststyleii{\Roman{liststylei}.\arabic{liststyleii}}
* \renewcommand\theliststyleiii{\alph{liststyleiii}}
* \newcommand\labelliststylei{\textstylelabel{\theliststylei .}}
* \newcommand\labelliststyleii{\textstylelabel{\theliststyleii .}}
* \newcommand\labelliststyleiii{\textstylelabel{\theliststyleiii )}}</pre>
*
* @param <code>style</code> the OOo list style to use
* @param <code>sName</code> an array of label basenames to use
* @param <code>nMaxLevel</code> the highest level in this numbering
* @param <code>bDeclareCounters</code> true if counters should be declared (they may
* exist already, eg. "section", "subsection"... or "enumi", "enumii"...
* @param <code>bRenewLabels</code> true if labels should be defined with \renewcommand
* @param <code>bUseTextStyle</code> true if labels should be formatted with the associated text style
* (rather than \newcommand).
* @param <code>ldp</code> the <code>LaTeXDocumentPortion</code> to add LaTeX code to.
*/
private void createLabels(ListStyle style, String[] sName, int nMaxLevel,
boolean bDeclareCounters, boolean bRenewLabels,
boolean bUseTextStyle, LaTeXDocumentPortion ldp) {
// Declare counters if required (eg. "\newcounter{countername1}[countername2]")
if (bDeclareCounters) {
int j = 0;
for (int i=1; i<=nMaxLevel; i++) {
if (style.isNumber(i)) {
ldp.append("\\newcounter{").append(sName[i]).append("}");
if (j>0) { ldp.append("[").append(sName[j]).append("]"); }
ldp.nl();
j = i;
}
}
}
// Create numbering for each level (eg. "\arabic{countername}")
String[] sNumFormat = new String[nMaxLevel+1];
for (int i=1; i<=nMaxLevel; i++) {
String s = numFormat(style.getLevelProperty(i,XMLString.STYLE_NUM_FORMAT));
if (s==null) { sNumFormat[i]=""; }
else { sNumFormat[i] = s + "{" + sName[i] + "}"; }
}
// Create numberings (ie. define "\thecountername"):
for (int i=1; i<=nMaxLevel; i++) {
if (style.isNumber(i)) {
ldp.append("\\renewcommand\\the").append(sName[i]).append("{");
int nLevels = Misc.getPosInteger(style.getLevelProperty(i,XMLString.TEXT_DISPLAY_LEVELS),1);
for (int j=i-nLevels+1; j<i; j++) {
if (style.isNumber(j)) {
ldp.append(sNumFormat[j]).append(".");
}
}
ldp.append(sNumFormat[i]);
ldp.append("}").nl();
}
}
// Create labels (ie. define "\labelcountername"):
for (int i=1; i<=nMaxLevel; i++) {
ldp.append(bRenewLabels ? "\\renewcommand" : "\\newcommand")
.append("\\label").append(sName[i]).append("{");
// Apply text style if required
BeforeAfter baText = new BeforeAfter();
if (bUseTextStyle) {
String sStyleName = style.getLevelProperty(i,XMLString.TEXT_STYLE_NAME);
palette.getCharSc().applyTextStyle(sStyleName,baText,new Context());
}
// Create label content
if (style.isNumber(i)) {
String sPrefix = style.getLevelProperty(i,XMLString.STYLE_NUM_PREFIX);
String sSuffix = style.getLevelProperty(i,XMLString.STYLE_NUM_SUFFIX);
// Apply style
ldp.append(baText.getBefore());
if (sPrefix!=null) { ldp.append(sPrefix); }
ldp.append("\\the").append(sName[i]);
if (sSuffix!=null) { ldp.append(sSuffix); }
ldp.append(baText.getAfter());
}
else if (style.isBullet(i)) {
String sBullet = style.getLevelProperty(i,XMLString.TEXT_BULLET_CHAR);
// Apply style
ldp.append(baText.getBefore());
if (sBullet!=null) {
// Bullets are usually symbols, so this should be OK:
ldp.append(palette.getI18n().convert(sBullet,false,"en"));
}
ldp.append(baText.getAfter());
}
else {
// TODO: Support images!
ldp.append("\\textbullet");
}
ldp.append("}").nl();
}
}
/* Helper: Get a length property that defaults to 0cm. */
private String getLength(ListStyle style,int nLevel,String sProperty) {
String s = style.getLevelStyleProperty(nLevel,sProperty);
if (s==null) { return "0cm"; }
else { return s; }
}
/* Helper: Get display name, or original name if it doesn't exist */
private String getDisplayName(String sName) {
String sDisplayName = ofr.getListStyles().getDisplayName(sName);
return sDisplayName!=null ? sDisplayName : sName;
}
/* Helper: Convert OOo number format to LaTeX number format */
public static final String numFormat(String sFormat){
if ("1".equals(sFormat)) { return "\\arabic"; }
else if ("i".equals(sFormat)) { return "\\roman"; }
else if ("I".equals(sFormat)) { return "\\Roman"; }
else if ("a".equals(sFormat)) { return "\\alph"; }
else if ("A".equals(sFormat)) { return "\\Alph"; }
else { return null; }
}
}

View file

@ -0,0 +1,232 @@
/************************************************************************
*
* MathmlConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-22)
*
*/
package writer2latex.latex;
//import java.util.Hashtable;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
//import writer2latex.latex.i18n.I18n;
import writer2latex.office.MIMETypes;
import writer2latex.office.OfficeReader;
import writer2latex.office.XMLString;
import writer2latex.util.Misc;
import writer2latex.xmerge.EmbeddedObject;
import writer2latex.xmerge.EmbeddedXMLObject;
/**
* This class converts mathml nodes to LaTeX.
* (Actually it only converts the starmath annotation currently, if available).
*/
public final class MathmlConverter extends ConverterHelper {
private StarMathConverter smc;
private boolean bContainsFormulas = false;
public MathmlConverter(OfficeReader ofr,LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
smc = new StarMathConverter(palette.getI18n(),config);
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
if (bContainsFormulas) {
if (config.useOoomath()) {
pack.append("\\usepackage{ooomath}").nl();
}
else {
smc.appendDeclarations(pack,decl);
}
}
}
public String convert(Node settings, Node formula) {
// TODO: Use settings to determine display mode/text mode
// formula must be a math:math node
// First try to find a StarMath annotation
Node semantics = Misc.getChildByTagName(formula,XMLString.MATH_SEMANTICS);
if (semantics!=null) {
Node annotation = Misc.getChildByTagName(semantics,XMLString.MATH_ANNOTATION);
if (annotation!=null) {
String sStarMath = "";
if (annotation.hasChildNodes()) {
NodeList anl = annotation.getChildNodes();
int nLen = anl.getLength();
for (int i=0; i<nLen; i++) {
if (anl.item(i).getNodeType() == Node.TEXT_NODE) {
sStarMath+=anl.item(i).getNodeValue();
}
}
bContainsFormulas = true;
return smc.convert(sStarMath);
}
}
}
// No annotation was found. In this case we should convert the mathml,
// but currently we ignore the problem.
// TODO: Investigate if Vasil I. Yaroshevich's MathML->LaTeX
// XSL transformation could be used here. (Potential problem:
// OOo uses MathML 1.01, not MathML 2)
return "\\text{Warning: No StarMath annotation}";
}
// Data for display equations
private Element theEquation = null;
private Element theSequence = null;
/**Try to convert a paragraph as a display equation:
* A paragraph which contains exactly one formula + at most one sequence
* number is treated as a display equation. Other content must be brackets
* or whitespace (possible with formatting).
* @param node the paragraph
* @param ldp the LaTeXDocumentPortion to contain the converted equation
* @return true if the conversion was succesful, false if the paragraph
* did not contain a display equation
*/
public boolean handleDisplayEquation(Element node, LaTeXDocumentPortion ldp) {
theEquation = null;
theSequence = null;
if (parseDisplayEquation(node) && theEquation!=null) {
if (theSequence!=null) {
// Numbered equation
ldp.append("\\begin{equation}");
palette.getFieldCv().handleSequenceLabel(theSequence,ldp);
ldp.nl()
.append(convert(null,theEquation)).nl()
.append("\\end{equation}").nl();
}
else {
// Unnumbered equation
ldp.append("\\begin{equation*}").nl()
.append(convert(null,theEquation)).nl()
.append("\\end{equation*}").nl();
}
return true;
}
else {
return false;
}
}
private boolean parseDisplayEquation(Node node) {
Node child = node.getFirstChild();
while (child!=null) {
Node equation = getFormula(child);
if (equation!=null) {
if (theEquation==null) {
theEquation = (Element) equation;
}
else { // two or more equations -> not a display
return false;
}
}
else if (Misc.isElement(child)) {
String sName = child.getNodeName();
if (XMLString.TEXT_SEQUENCE.equals(sName)) {
if (theSequence==null) {
theSequence = (Element) child;
}
else { // two sequence numbers -> not a display
return false;
}
}
else if (XMLString.TEXT_SPAN.equals(sName)) {
if (!parseDisplayEquation(child)) {
return false;
}
}
else if (XMLString.TEXT_S.equals(sName)) {
// Spaces are allowed
}
else if (XMLString.TEXT_TAB.equals(sName)) {
// Tab stops are allowed
}
else if (XMLString.TEXT_TAB_STOP.equals(sName)) { // old
// Tab stops are allowed
}
else {
// Other elements -> not a display
return false;
}
}
else if (Misc.isText(child)) {
String s = child.getNodeValue();
int nLen = s.length();
for (int i=0; i<nLen; i++) {
char c = s.charAt(i);
if (c!='(' && c!=')' && c!='[' && c!=']' && c!='{' && c!='}' && c!=' ' && c!='\u00A0') {
// Characters except brackets and whitespace -> not a display
return false;
}
}
}
child = child.getNextSibling();
}
return true;
}
// TODO: Extend OfficeReader to handle frames
private Node getFormula(Node node) {
if (Misc.isElement(node,XMLString.DRAW_FRAME)) {
node=Misc.getFirstChildElement(node);
}
String sHref = Misc.getAttribute(node,XMLString.XLINK_HREF);
if (sHref!=null) { // Embedded object in package or linked object
if (ofr.isInPackage(sHref)) { // Embedded object in package
if (sHref.startsWith("#")) { sHref=sHref.substring(1); }
if (sHref.startsWith("./")) { sHref=sHref.substring(2); }
EmbeddedObject object = palette.getEmbeddedObject(sHref);
if (object!=null) {
if (MIMETypes.MATH.equals(object.getType()) || MIMETypes.ODF.equals(object.getType())) { // Formula!
try {
Document formuladoc = ((EmbeddedXMLObject) object).getContentDOM();
return Misc.getChildByTagName(formuladoc,XMLString.MATH_MATH);
}
catch (org.xml.sax.SAXException e) {
e.printStackTrace();
}
catch (java.io.IOException e) {
e.printStackTrace();
}
}
}
}
}
else { // flat xml, object is contained in node
return Misc.getChildByTagName(node,XMLString.MATH_MATH);
}
return null;
}
}

View file

@ -0,0 +1,391 @@
/************************************************************************
*
* NoteConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.latex;
import java.util.LinkedList;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import writer2latex.util.Misc;
import writer2latex.util.ExportNameCollection;
import writer2latex.office.OfficeReader;
import writer2latex.office.PropertySet;
import writer2latex.office.StyleWithProperties;
import writer2latex.office.XMLString;
import writer2latex.latex.util.BeforeAfter;
import writer2latex.latex.util.Context;
/**
* <p>This class handles conversion of footnotes and endnotes, including
* references. It takes advantage of the packages <code>endnotes.sty</code>
* and <code>perpage.sty</code> if allowed in the configuration.</p>
*/
public class NoteConverter extends ConverterHelper {
private ExportNameCollection footnotenames = new ExportNameCollection(true);
private ExportNameCollection endnotenames = new ExportNameCollection(true);
private boolean bContainsEndnotes = false;
private boolean bContainsFootnotes = false;
// Keep track of footnotes (inside minipage etc.), that should be typeset later
private LinkedList postponedFootnotes = new LinkedList();
public NoteConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
}
/** <p>Append declarations needed by the <code>NoteConverter</code> to
* the preamble.
* @param pack the <code>LaTeXDocumentPortion</code> to which
* declarations of packages should be added (<code>\\usepackage</code>).
* @param decl the <code>LaTeXDocumentPortion</code> to which
* other declarations should be added.
*/
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
if (bContainsEndnotes) { pack.append("\\usepackage{endnotes}").nl(); }
if (bContainsFootnotes) convertFootnotesConfiguration(decl);
if (bContainsEndnotes) convertEndnotesConfiguration(decl);
}
/** <p>Process a footnote (text:footnote tag)
* @param node The element containing the footnote
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleFootnote(Element node, LaTeXDocumentPortion ldp, Context oc) {
Context ic = (Context) oc.clone();
ic.setInFootnote(true);
String sId = node.getAttribute(XMLString.TEXT_ID);
Element fntbody = Misc.getChildByTagName(node,XMLString.TEXT_FOOTNOTE_BODY);
if (fntbody==null) { // try oasis
fntbody = Misc.getChildByTagName(node,XMLString.TEXT_NOTE_BODY);
}
if (fntbody != null) {
bContainsFootnotes = true;
if (ic.isNoFootnotes()) {
ldp.append("\\footnotemark{}");
postponedFootnotes.add(fntbody);
}
else {
ldp.append("\\footnote");
ldp.append("{");
if (sId != null && ofr.hasFootnoteRefTo(sId)) {
ldp.append("\\label{fnt:"+footnotenames.getExportName(sId)+"}");
}
traverseNoteBody(fntbody,ldp,ic);
ldp.append("}");
}
}
}
/** Flush the queue of postponed footnotes */
public void flushFootnotes(LaTeXDocumentPortion ldp, Context oc) {
// We may still be in a context with no footnotes
if (oc.isNoFootnotes()) { return; }
// Type out all postponed footnotes:
Context ic = (Context) oc.clone();
ic.setInFootnote(true);
int n = postponedFootnotes.size();
if (n==1) {
ldp.append("\\footnotetext{");
traverseNoteBody((Element) postponedFootnotes.get(0),ldp,ic);
ldp.append("}").nl();
postponedFootnotes.clear();
}
else if (n>1) {
// Several footnotes; have to adjust the footnote counter
ldp.append("\\addtocounter{footnote}{-"+n+"}").nl();
for (int i=0; i<n; i++) {
ldp.append("\\stepcounter{footnote}\\footnotetext{");
traverseNoteBody((Element) postponedFootnotes.get(i),ldp,ic);
ldp.append("}").nl();
}
postponedFootnotes.clear();
}
}
/** <p>Process an endnote (text:endnote tag)
* @param node The element containing the endnote
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleEndnote(Element node, LaTeXDocumentPortion ldp, Context oc) {
Context ic = (Context) oc.clone();
ic.setInFootnote(true);
String sId = node.getAttribute(XMLString.TEXT_ID);
Element entbody = Misc.getChildByTagName(node,XMLString.TEXT_ENDNOTE_BODY);
if (entbody==null) { // try oasis
entbody = Misc.getChildByTagName(node,XMLString.TEXT_NOTE_BODY);
}
if (entbody != null) {
if (ic.isNoFootnotes() && !config.useEndnotes()) {
ldp.append("\\footnotemark()");
postponedFootnotes.add(entbody);
}
else {
if (config.useEndnotes()) {
ldp.append("\\endnote");
bContainsEndnotes = true;
}
else {
ldp.append("\\footnote");
bContainsFootnotes = true;
}
ldp.append("{");
if (sId != null && ofr.hasEndnoteRefTo(sId)) {
ldp.append("\\label{ent:"+endnotenames.getExportName(sId)+"}");
}
traverseNoteBody(entbody,ldp,ic);
ldp.append("}");
}
}
}
/** <p>Insert the endnotes into the documents.
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* the endnotes should be added.
*/
public void insertEndnotes(LaTeXDocumentPortion ldp) {
if (bContainsEndnotes) {
ldp.append("\\clearpage").nl()
.append("\\theendnotes").nl();
}
}
/** <p>Process a note reference (text:note-ref tag, oasis)
* @param node The element containing the note reference
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleNoteRef(Element node, LaTeXDocumentPortion ldp, Context oc) {
String sClass=node.getAttribute(XMLString.TEXT_NOTE_CLASS);
if (sClass.equals("footnote")) { handleFootnoteRef(node,ldp,oc); }
else if (sClass.equals("endnote")) { handleEndnoteRef(node,ldp,oc); }
}
/** <p>Process a footnote reference (text:footnote-ref tag)
* @param node The element containing the footnote reference
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleFootnoteRef(Element node, LaTeXDocumentPortion ldp, Context oc) {
String sFormat = node.getAttribute(XMLString.TEXT_REFERENCE_FORMAT);
String sName = node.getAttribute(XMLString.TEXT_REF_NAME);
if (("page".equals(sFormat) || "".equals(sFormat)) && sName!=null) {
ldp.append("\\pageref{fnt:"+footnotenames.getExportName(sName)+"}");
}
else if ("text".equals(sFormat) && sName!=null) {
ldp.append("\\ref{fnt:"+footnotenames.getExportName(sName)+"}");
}
else { // use current value
palette.getInlineCv().traversePCDATA(node,ldp,oc);
}
}
/** <p>Process an endnote reference (text:endnote-ref tag)
* @param node The element containing the endnote reference
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleEndnoteRef(Element node, LaTeXDocumentPortion ldp, Context oc) {
String sFormat = node.getAttribute(XMLString.TEXT_REFERENCE_FORMAT);
String sName = node.getAttribute(XMLString.TEXT_REF_NAME);
if (("page".equals(sFormat) || "".equals(sFormat)) && sName!=null) {
ldp.append("\\pageref{ent:"+endnotenames.getExportName(sName)+"}");
}
else if ("text".equals(sFormat) && sName!=null) {
ldp.append("\\ref{ent:"+endnotenames.getExportName(sName)+"}");
}
else { // use current value
palette.getInlineCv().traversePCDATA(node,ldp,oc);
}
}
/** <p>Add a footnote name. The method <code>handleFootnote</code> includes
* a <code>\label</code> only if the footnote name is already known to the
* <code>NoteConverter</code>. Hence this method is invoked by the prepass
* for each footnote reference. The end result is, that only necessary
* labels will be included.
* @param sName the name (id) of the footnote
*/
public void addFootnoteName(String sName) { footnotenames.addName(sName); }
/** <p>Add an endnote name. The method <code>handleEndnote</code> includes
* a <code>\label</code> only if the endnote name is already known to the
* <code>NoteConverter</code>. Hence this method is invoked by the prepass
* for each endnote reference. The end result is, that only necessary
* labels will be included.
* @param sName the name (id) of the endnote
*/
public void addEndnoteName(String sName) { endnotenames.addName(sName); }
/*
* Process the contents of a footnote or endnote
* TODO: Merge with BlockConverter.traverseBlockText?
*/
private void traverseNoteBody (Element node, LaTeXDocumentPortion ldp, Context oc) {
if (node.hasChildNodes()) {
NodeList nList = node.getChildNodes();
int len = nList.getLength();
for (int i = 0; i < len; i++) {
Node childNode = nList.item(i);
if (childNode.getNodeType() == Node.ELEMENT_NODE) {
Element child = (Element)childNode;
String nodeName = child.getTagName();
palette.getInfo().addDebugInfo(child,ldp);
if (nodeName.equals(XMLString.TEXT_H)) {
palette.getHeadingCv().handleHeading(child,ldp,oc);
}
if (nodeName.equals(XMLString.TEXT_P)) {
palette.getInlineCv().traverseInlineText(child,ldp,oc);
if (i<len-1) {
if (nList.item(i+1).getNodeName().startsWith(XMLString.TEXT_)) {
ldp.append("\\par ");
}
else {
ldp.nl();
}
}
}
else if (nodeName.equals(XMLString.TEXT_LIST)) { // oasis
palette.getBlockCv().handleList(child,ldp,oc);
}
if (nodeName.equals(XMLString.TEXT_ORDERED_LIST)) {
palette.getBlockCv().handleList(child,ldp,oc);
}
if (nodeName.equals(XMLString.TEXT_UNORDERED_LIST)) {
palette.getBlockCv().handleList(child,ldp,oc);
}
}
}
}
}
// Convert footnotes configuration.
private void convertFootnotesConfiguration(LaTeXDocumentPortion ldp) {
// Note: Continuation notices are not supported in LaTeX
// TODO: Support text:footnotes-postion="document" (footnotes as endnotes)
// TODO: Support text:start-numbering-at="page" (footnpag.sty/footmisc.sty)
convertFootEndnotesConfiguration(ofr.getFootnotesConfiguration(),"foot",ldp);
}
// Convert endnotes configuration.
private void convertEndnotesConfiguration(LaTeXDocumentPortion ldp) {
// Note: Continuation notices are not supported in LaTeX
convertFootEndnotesConfiguration(ofr.getEndnotesConfiguration(),"end",ldp);
}
/* Convert {foot|end}notes configuration.
* Note: All {foot|end}notes are formatted with the default style for {foot|end}footnotes.
* (This doesn't conform with the file format specification, but in LaTeX
* all {foot|end}notes are usually formatted in a fixed style.)
*/
private void convertFootEndnotesConfiguration(PropertySet notes, String sType, LaTeXDocumentPortion ldp) {
if (config.formatting()<LaTeXConfig.CONVERT_BASIC) { return; }
String sTypeShort = sType.equals("foot") ? "fn" : "en";
if (notes==null) { return; }
ldp.append("% ").append(sType).append("notes configuration").nl()
.append("\\makeatletter").nl();
// The numbering style is controlled by \the{foot|end}note
String sFormat = notes.getProperty(XMLString.STYLE_NUM_FORMAT);
if (sFormat!=null) {
ldp.append("\\renewcommand\\the").append(sType).append("note{")
.append(ListStyleConverter.numFormat(sFormat))
.append("{").append(sType).append("note}}").nl();
}
// Number {foot|end}notes by sections
if ("chapter".equals(notes.getProperty(XMLString.TEXT_START_NUMBERING_AT))) {
ldp.append("\\@addtoreset{").append(sType).append("note}{section}").nl();
}
// Set start value offset (default 0)
int nStartValue = Misc.getPosInteger(notes.getProperty(XMLString.TEXT_START_VALUE),0);
if (nStartValue!=0) {
ldp.append("\\setcounter{").append(sType).append("note}{"+nStartValue+"}").nl();
}
if (config.formatting()>=LaTeXConfig.CONVERT_MOST) {
// The formatting of the {foot|end}note citation is controlled by \@make{fn|en}mark
String sCitBodyStyle = notes.getProperty(XMLString.TEXT_CITATION_BODY_STYLE_NAME);
if (sCitBodyStyle!=null && ofr.getTextStyle(sCitBodyStyle)!=null) {
BeforeAfter baText = new BeforeAfter();
palette.getCharSc().applyTextStyle(sCitBodyStyle,baText,new Context());
ldp.append("\\renewcommand\\@make").append(sTypeShort).append("mark{\\mbox{")
.append(baText.getBefore())
.append("\\@the").append(sTypeShort).append("mark")
.append(baText.getAfter())
.append("}}").nl();
}
// The layout and formatting of the {foot|end}note is controlled by \@make{fn|en}text
String sCitStyle = notes.getProperty(XMLString.TEXT_CITATION_STYLE_NAME);
String sStyleName = notes.getProperty(XMLString.TEXT_DEFAULT_STYLE_NAME);
if (sStyleName!=null) {
BeforeAfter baText = new BeforeAfter();
palette.getCharSc().applyTextStyle(sCitStyle,baText,new Context());
StyleWithProperties style = ofr.getParStyle(sStyleName);
if (style!=null) {
BeforeAfter baPar = new BeforeAfter();
palette.getCharSc().applyHardCharFormatting(style,baPar);
ldp.append("\\renewcommand\\@make").append(sTypeShort)
.append("text[1]{\\noindent")
.append(baText.getBefore())
.append("\\@the").append(sTypeShort).append("mark\\ ")
.append(baText.getAfter())
.append(baPar.getBefore())
.append("#1")
.append(baPar.getAfter());
ldp.append("}").nl();
}
}
}
ldp.append("\\makeatother").nl();
}
}

View file

@ -0,0 +1,16 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>The package writer2latex.latex</title>
</head>
<body>
<p>This package contains LaTeX specific code.</p>
<p>It contains a <code>writerlatex.api.Converter</code> implementation for
conversion into LaTeX.</p>
<p>Since version 1.0 you can build Writer2LaTeX without this package if you
don't need LaTeX support (in this case you can exclude writer2latex.bibtex as
well).</p>
</body>
</html>

View file

@ -0,0 +1,596 @@
/************************************************************************
*
* PageStyleConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.latex;
import java.util.Enumeration;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import writer2latex.util.CSVList;
import writer2latex.util.Misc;
import writer2latex.office.*;
import writer2latex.latex.util.BeforeAfter;
import writer2latex.latex.util.Context;
// TODO: chngpage.sty??
/* This class creates LaTeX code from OOo page layouts/master pages
*/
public class PageStyleConverter extends StyleConverter {
// Value of attribute text:display of most recent text:chapter field
// This is used to handle chaptermarks in headings
private String sChapterField1 = null;
private String sChapterField2 = null;
// The page layout used for the page geometry
// (LaTeX only supports one page geometry per page)
private PageLayout mainPageLayout;
/** <p>Constructs a new <code>PageStyleConverter</code>.</p>
*/
public PageStyleConverter(OfficeReader ofr, LaTeXConfig config,
ConverterPalette palette) {
super(ofr,config,palette);
// Determine the main page master
MasterPage firstMasterPage = ofr.getFirstMasterPage();
String sPageLayoutName = null;
if (firstMasterPage!=null) {
MasterPage nextMasterPage = ofr.getMasterPage(
firstMasterPage.getProperty(XMLString.STYLE_NEXT_STYLE_NAME));
if (nextMasterPage!=null) {
sPageLayoutName = nextMasterPage.getPageLayoutName();
}
else {
sPageLayoutName = firstMasterPage.getPageLayoutName();
}
}
mainPageLayout = ofr.getPageLayout(sPageLayoutName);
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
if (config.useFancyhdr()) { pack.append("\\usepackage{fancyhdr}").nl(); }
// The first master page must be known
MasterPage firstMasterPage = ofr.getFirstMasterPage();
if (firstMasterPage!=null) {
styleNames.addName(getDisplayName(firstMasterPage.getName()));
}
// Convert page geometry
convertPageMasterGeometry(pack,decl);
// Convert master pages
convertMasterPages(decl);
if (firstMasterPage!=null) {
BeforeAfter ba = new BeforeAfter();
applyMasterPage(firstMasterPage.getName(),ba);
decl.append(ba.getBefore());
}
}
public void setChapterField1(String s) { sChapterField1 = s; }
public void setChapterField2(String s) { sChapterField2 = s; }
public boolean isTwocolumn() {
return mainPageLayout!=null && mainPageLayout.getColCount()>1;
}
/** <p>Apply page break properties from a style.</p>
* @param style the style to use
* @param bInherit true if inheritance from parent style should be used
* @param ba a <code>BeforeAfter</code> to put code into
*/
public void applyPageBreak(StyleWithProperties style, boolean bInherit, BeforeAfter ba) {
if (style==null) { return; }
if (style.isAutomatic() && config.ignoreHardPageBreaks()) { return; }
// A page break can be a simple page break before or after...
String s = style.getProperty(XMLString.FO_BREAK_BEFORE,bInherit);
if ("page".equals(s)) { ba.add("\\clearpage",""); }
s = style.getProperty(XMLString.FO_BREAK_AFTER,bInherit);
if ("page".equals(s)) { ba.add("","\\clearpage"); }
// ...or it can be a new master page
String sMasterPage = style.getMasterPageName();
if (sMasterPage==null || sMasterPage.length()==0) { return; }
ba.add("\\clearpage","");
String sPageNumber=style.getProperty(XMLString.STYLE_PAGE_NUMBER);
if (sPageNumber!=null) {
int nPageNumber = Misc.getPosInteger(sPageNumber,1);
ba.add("\\setcounter{page}{"+nPageNumber+"}","");
}
applyMasterPage(sMasterPage,ba);
}
/** <p>Use a Master Page (pagestyle in LaTeX)</p>
* @param sName name of the master page to use
* @param ba the <code>BeforeAfter</code> to add code to.
*/
private void applyMasterPage(String sName, BeforeAfter ba) {
if (config.pageFormatting()==LaTeXConfig.IGNORE_ALL) return;
MasterPage style = ofr.getMasterPage(sName);
if (style==null) { return; }
String sNextName = style.getProperty(XMLString.STYLE_NEXT_STYLE_NAME);
MasterPage nextStyle = ofr.getMasterPage(sNextName);
if (style==nextStyle || nextStyle==null) {
ba.add("\\pagestyle{"+styleNames.getExportName(getDisplayName(sName))+"}\n", "");
}
else {
ba.add("\\pagestyle{"+styleNames.getExportName(getDisplayName(sNextName))+"}\n"+
"\\thispagestyle{"+styleNames.getExportName(getDisplayName(sName))+"}\n","");
}
// todo: should warn the user if next master also contains a next-style-name;
// LaTeX's page style mechanism cannot handle that
}
/*
* Process header or footer contents
*/
private void convertMasterPages(LaTeXDocumentPortion ldp) {
if (config.pageFormatting()==LaTeXConfig.IGNORE_ALL) { return; }
Context context = new Context();
context.resetFormattingFromStyle(ofr.getDefaultParStyle());
context.setInHeaderFooter(true);
Enumeration styles = ofr.getMasterPages().getStylesEnumeration();
ldp.append("% Pages styles").nl();
if (!config.useFancyhdr()) {
ldp.append("\\makeatletter").nl();
}
while (styles.hasMoreElements()) {
MasterPage style = (MasterPage) styles.nextElement();
String sName = style.getName();
if (styleNames.containsName(getDisplayName(sName))) {
sChapterField1 = null;
sChapterField2 = null;
String sPageLayout = style.getPageLayoutName();
PageLayout pageLayout = ofr.getPageLayout(sPageLayout);
if (config.useFancyhdr()) {
ldp.append("\\fancypagestyle{")
.append(styleNames.getExportName(getDisplayName(sName)))
.append("}{\\fancyhf{}").nl();
// Header - odd or both
ldp.append(" \\fancyhead[")
.append(getParAlignment(style.getHeader()))
.append(style.getHeaderLeft()!=null ? "O" : "")
.append("]{");
traverseHeaderFooter((Element)style.getHeader(),ldp,context);
ldp.append("}").nl();
// Header - even
if (style.getHeaderLeft()!=null) {
ldp.append(" \\fancyhead[")
.append(getParAlignment(style.getHeaderLeft()))
.append("E]{");
traverseHeaderFooter((Element)style.getHeaderLeft(),ldp,context);
ldp.append("}").nl();
}
// Footer - odd or both
ldp.append(" \\fancyfoot[")
.append(getParAlignment(style.getFooter()))
.append(style.getFooterLeft()!=null ? "O" : "")
.append("]{");
traverseHeaderFooter((Element)style.getFooter(),ldp,context);
ldp.append("}").nl();
// Footer - even
if (style.getFooterLeft()!=null) {
ldp.append(" \\fancyfoot[")
.append(getParAlignment(style.getFooterLeft()))
.append("E]{");
traverseHeaderFooter((Element)style.getFooterLeft(),ldp,context);
ldp.append("}").nl();
}
// Rules
ldp.append(" \\renewcommand\\headrulewidth{")
.append(getBorderWidth(pageLayout,true))
.append("}").nl()
.append(" \\renewcommand\\footrulewidth{")
.append(getBorderWidth(pageLayout,false))
.append("}").nl();
}
else { // use low-level page styles
ldp.append("\\newcommand\\ps@")
.append(styleNames.getExportName(getDisplayName(sName)))
.append("{").nl();
// Header
ldp.append(" \\renewcommand\\@oddhead{");
traverseHeaderFooter((Element)style.getHeader(),ldp,context);
ldp.append("}").nl();
ldp.append(" \\renewcommand\\@evenhead{");
if (style.getHeaderLeft()!=null) {
traverseHeaderFooter((Element)style.getHeaderLeft(),ldp,context);
}
else if (style.getHeader()!=null) {
ldp.append("\\@oddhead");
}
ldp.append("}").nl();
// Footer
ldp.append(" \\renewcommand\\@oddfoot{");
traverseHeaderFooter((Element)style.getFooter(),ldp,context);
ldp.append("}").nl();
ldp.append(" \\renewcommand\\@evenfoot{");
if (style.getFooterLeft()!=null) {
traverseHeaderFooter((Element)style.getFooterLeft(),ldp,context);
}
else if (style.getFooter()!=null) {
ldp.append("\\@oddfoot");
}
ldp.append("}").nl();
}
// Sectionmark and subsectionmark
if (sChapterField1!=null) {
ldp.append(" \\def\\sectionmark##1{\\markboth{");
if ("name".equals(sChapterField1)) { ldp.append("##1"); }
else if ("number".equals(sChapterField1) || "plain-number".equals(sChapterField1)) {
ldp.append("\\thesection");
}
else { ldp.append("\\thesection\\ ##1"); }
ldp.append("}{}}").nl();
}
if (sChapterField2!=null) {
if (sChapterField1==null) {
ldp.append(" \\def\\sectionmark##1{\\markboth{}{}}").nl();
}
ldp.append(" \\def\\subsectionmark##1{\\markright{");
if ("name".equals(sChapterField2)) { ldp.append("##1"); }
else if ("number".equals(sChapterField2) || "plain-number".equals(sChapterField1)) {
ldp.append("\\thesubsection");
}
else { ldp.append("\\thesubsection\\ ##1"); }
ldp.append("}{}}").nl();
}
// Page number (this is the only part of the page master used in each page style)
if (pageLayout!=null) {
String sNumFormat = pageLayout.getProperty(XMLString.STYLE_NUM_FORMAT);
if (sNumFormat!=null) {
ldp.append(" \\renewcommand\\thepage{")
.append(ListStyleConverter.numFormat(sNumFormat))
.append("{page}}").nl();
}
String sPageNumber = pageLayout.getProperty(XMLString.STYLE_FIRST_PAGE_NUMBER);
if (sPageNumber!=null && !sPageNumber.equals("continue")) {
ldp.append(" \\setcounter{page}{")
.append(Integer.toString(Misc.getPosInteger(sPageNumber,0)))
.append("}").nl();
}
}
ldp.append("}").nl();
}
}
if (!config.useFancyhdr()) {
ldp.append("\\makeatother").nl();
}
}
// Get alignment of first paragraph in node
private String getParAlignment(Node node) {
String sAlign = "L";
if (node!=null) {
Element par = Misc.getChildByTagName(node,XMLString.TEXT_P);
if (par!=null) {
String sStyleName = Misc.getAttribute(par,XMLString.TEXT_STYLE_NAME);
StyleWithProperties style = ofr.getParStyle(sStyleName);
if (style!=null) {
String s = style.getProperty(XMLString.FO_TEXT_ALIGN);
if ("center".equals(s)) { sAlign = "C"; }
else if ("end".equals(s)) { sAlign = "R"; }
}
}
}
return sAlign;
}
// Get border width from header/footer style
private String getBorderWidth(PageLayout style, boolean bHeader) {
if (style==null) { return "0pt"; }
String sBorder;
if (bHeader) {
sBorder = style.getHeaderProperty(XMLString.FO_BORDER_BOTTOM);
if (sBorder==null) {
sBorder = style.getHeaderProperty(XMLString.FO_BORDER);
}
}
else {
sBorder = style.getFooterProperty(XMLString.FO_BORDER_TOP);
if (sBorder==null) {
sBorder = style.getFooterProperty(XMLString.FO_BORDER);
}
}
if (sBorder!=null && !sBorder.equals("none")) {
return sBorder.substring(0,sBorder.indexOf(' '));
}
else {
return "0pt";
}
}
private void traverseHeaderFooter(Element node, LaTeXDocumentPortion ldp, Context context) {
if (node==null) { return; }
// get first paragraph; all other content is ignored
Element par = Misc.getChildByTagName(node,XMLString.TEXT_P);
if (par==null) { return; }
String sStyleName = par.getAttribute(XMLString.TEXT_STYLE_NAME);
BeforeAfter ba = new BeforeAfter();
// Temp solution: Ignore hard formatting in header/footer (name clash problem)
// only in package format. TODO: Find a better solution!
StyleWithProperties style = ofr.getParStyle(sStyleName);
if (style!=null && (!ofr.isPackageFormat() || !style.isAutomatic())) {
palette.getCharSc().applyHardCharFormatting(style,ba);
}
if (par.hasChildNodes()) {
ldp.append(ba.getBefore());
palette.getInlineCv().traverseInlineText(par,ldp,context);
ldp.append(ba.getAfter());
}
}
// TODO: Reenable several geometries per document??
private void convertPageMasterGeometry(LaTeXDocumentPortion pack, LaTeXDocumentPortion ldp) {
if (config.pageFormatting()!=LaTeXConfig.CONVERT_ALL) { return; }
if (mainPageLayout==null) { return; }
// Set global document options
if ("mirrored".equals(mainPageLayout.getPageUsage())) {
palette.addGlobalOption("twoside");
}
if (isTwocolumn()) {
palette.addGlobalOption("twocolumn");
}
// Collect all page geometry
// 1. Page size
String sPaperHeight = mainPageLayout.getAbsoluteProperty(XMLString.FO_PAGE_HEIGHT);
String sPaperWidth = mainPageLayout.getAbsoluteProperty(XMLString.FO_PAGE_WIDTH);
// 2. Margins
String sMarginTop = mainPageLayout.getAbsoluteProperty(XMLString.FO_MARGIN_TOP);
String sMarginBottom = mainPageLayout.getAbsoluteProperty(XMLString.FO_MARGIN_BOTTOM);
String sMarginLeft = mainPageLayout.getAbsoluteProperty(XMLString.FO_MARGIN_LEFT);
String sMarginRight = mainPageLayout.getAbsoluteProperty(XMLString.FO_MARGIN_RIGHT);
// 3. Header+footer dimensions
String sHeadHeight = "0cm";
String sHeadSep = "0cm";
String sFootHeight = "0cm";
String sFootSep = "0cm";
boolean bIncludeHead = false;
boolean bIncludeFoot = false;
// Look through all applied page layouts and use largest heights
Enumeration masters = ofr.getMasterPages().getStylesEnumeration();
while (masters.hasMoreElements()) {
MasterPage master = (MasterPage) masters.nextElement();
if (styleNames.containsName(getDisplayName(master.getName()))) {
PageLayout layout = ofr.getPageLayout(master.getPageLayoutName());
if (layout!=null) {
if (layout.hasHeaderStyle()) {
String sThisHeadHeight = layout.getHeaderProperty(XMLString.FO_MIN_HEIGHT);
if (sThisHeadHeight!=null && Misc.isLessThan(sHeadHeight,sThisHeadHeight)) {
sHeadHeight = sThisHeadHeight;
}
String sThisHeadSep = layout.getHeaderProperty(XMLString.FO_MARGIN_BOTTOM);
if (sThisHeadSep!=null && Misc.isLessThan(sHeadSep,sThisHeadSep)) {
sHeadSep = sThisHeadSep;
}
bIncludeHead = true;
}
if (layout.hasFooterStyle()) {
String sThisFootHeight = layout.getFooterProperty(XMLString.FO_MIN_HEIGHT);
if (sThisFootHeight!=null && Misc.isLessThan(sFootHeight,sThisFootHeight)) {
sFootHeight = sThisFootHeight;
}
String sThisFootSep = layout.getFooterProperty(XMLString.FO_MARGIN_TOP);
if (sThisFootSep!=null && Misc.isLessThan(sFootSep,sThisFootSep)) {
sFootSep = sThisFootSep;
}
bIncludeFoot = true;
}
}
}
}
// Define 12pt as minimum height (the source may specify 0pt..)
if (bIncludeHead && Misc.isLessThan(sHeadHeight,"12pt")) {
sHeadHeight = "12pt";
}
if (bIncludeFoot && Misc.isLessThan(sFootHeight,"12pt")) {
sFootHeight = "12pt";
}
String sFootSkip = Misc.add(sFootHeight,sFootSep);
if (config.useGeometry()) {
// Set up options for geometry.sty
CSVList props = new CSVList(",");
if (!standardPaperSize(sPaperWidth,sPaperHeight)) {
props.addValue("paperwidth="+sPaperWidth);
props.addValue("paperheight="+sPaperHeight);
}
props.addValue("top="+sMarginTop);
props.addValue("bottom="+sMarginBottom);
props.addValue("left="+sMarginLeft);
props.addValue("right="+sMarginRight);
if (bIncludeHead) {
props.addValue("includehead");
props.addValue("head="+sHeadHeight);
props.addValue("headsep="+sHeadSep);
}
else {
props.addValue("nohead");
}
if (bIncludeFoot) {
props.addValue("includefoot");
props.addValue("foot="+sFootHeight);
props.addValue("footskip="+sFootSkip);
}
else {
props.addValue("nofoot");
}
// Use the package
pack.append("\\usepackage[").append(props.toString()).append("]{geometry}").nl();
}
else {
// Calculate text height and text width
String sTextHeight = Misc.sub(sPaperHeight,sMarginTop);
sTextHeight = Misc.sub(sTextHeight,sHeadHeight);
sTextHeight = Misc.sub(sTextHeight,sHeadSep);
sTextHeight = Misc.sub(sTextHeight,sFootSkip);
sTextHeight = Misc.sub(sTextHeight,sMarginBottom);
String sTextWidth = Misc.sub(sPaperWidth,sMarginLeft);
sTextWidth = Misc.sub(sTextWidth,sMarginRight);
ldp.append("% Page layout (geometry)").nl();
// Page dimensions
if (!standardPaperSize(sPaperWidth,sPaperHeight)) {
ldp.append("\\setlength\\paperwidth{").append(sPaperWidth).append("}").nl()
.append("\\setlength\\paperheight{").append(sPaperHeight).append("}").nl();
}
// PDF page dimensions, only if hyperref.sty is not loaded
if (config.getBackend()==LaTeXConfig.PDFTEX && !config.useHyperref()) {
ldp.append("\\setlength\\pdfpagewidth{").append(sPaperWidth).append("}").nl()
.append("\\setlength\\pdfpageheight{").append(sPaperHeight).append("}").nl();
}
// Page starts in upper left corner of paper!!
ldp.append("\\setlength\\voffset{-1in}").nl()
.append("\\setlength\\hoffset{-1in}").nl();
// Margins
ldp.append("\\setlength\\topmargin{").append(sMarginTop).append("}").nl()
.append("\\setlength\\oddsidemargin{").append(sMarginLeft).append("}").nl();
// Left margin for even (left) pages; only for mirrored page master
if ("mirrored".equals(mainPageLayout.getPageUsage())) {
ldp.append("\\setlength\\evensidemargin{").append(sMarginRight).append("}").nl();
}
// Text size (sets bottom and right margins indirectly)
ldp.append("\\setlength\\textheight{").append(sTextHeight).append("}").nl();
ldp.append("\\setlength\\textwidth{").append(sTextWidth).append("}").nl();
// Header and footer
ldp.append("\\setlength\\footskip{").append(sFootSkip).append("}").nl();
ldp.append("\\setlength\\headheight{").append(sHeadHeight).append("}").nl();
ldp.append("\\setlength\\headsep{").append(sHeadSep).append("}").nl();
}
// Footnote rule
// TODO: Support alignment.
String sAdjustment = mainPageLayout.getFootnoteProperty(XMLString.STYLE_ADJUSTMENT);
String sBefore = mainPageLayout.getFootnoteProperty(XMLString.STYLE_DISTANCE_BEFORE_SEP);
if (sBefore==null) { sBefore = "1mm"; }
String sAfter = mainPageLayout.getFootnoteProperty(XMLString.STYLE_DISTANCE_AFTER_SEP);
if (sAfter==null) { sAfter = "1mm"; }
String sHeight = mainPageLayout.getFootnoteProperty(XMLString.STYLE_WIDTH);
if (sHeight==null) { sHeight = "0.2mm"; }
String sWidth = mainPageLayout.getFootnoteProperty(XMLString.STYLE_REL_WIDTH);
if (sWidth==null) { sWidth = "25%"; }
sWidth=Float.toString(Misc.getFloat(sWidth.substring(0,sWidth.length()-1),1)/100);
BeforeAfter baColor = new BeforeAfter();
String sColor = mainPageLayout.getFootnoteProperty(XMLString.STYLE_COLOR);
palette.getColorCv().applyColor(sColor,false,baColor,new Context());
String sSkipFootins = Misc.add(sBefore,sHeight);
ldp.append("% Footnote rule").nl()
.append("\\setlength{\\skip\\footins}{").append(sSkipFootins).append("}").nl()
.append("\\renewcommand\\footnoterule{\\vspace*{-").append(sHeight)
.append("}");
if ("right".equals(sAdjustment)) {
ldp.append("\\setlength\\leftskip{0pt plus 1fil}\\setlength\\rightskip{0pt}");
}
else if ("center".equals(sAdjustment)) {
ldp.append("\\setlength\\leftskip{0pt plus 1fil}\\setlength\\rightskip{0pt plus 1fil}");
}
else { // default left
ldp.append("\\setlength\\leftskip{0pt}\\setlength\\rightskip{0pt plus 1fil}");
}
ldp.append("\\noindent")
.append(baColor.getBefore()).append("\\rule{").append(sWidth)
.append("\\columnwidth}{").append(sHeight).append("}")
.append(baColor.getAfter())
.append("\\vspace*{").append(sAfter).append("}}").nl();
}
private boolean standardPaperSize(String sWidth, String sHeight) {
if (standardPaperSize1(sWidth,sHeight)) {
return true;
}
else if (standardPaperSize1(sHeight,sWidth)) {
palette.addGlobalOption("landscape");
return true;
}
return false;
}
private boolean standardPaperSize1(String sWidth, String sHeight) {
// The list of known paper sizes in LaTeX's standard classes is rather short
if (compare(sWidth, "210mm", "0.5mm") && compare(sHeight, "297mm", "0.5mm")) {
palette.addGlobalOption("a4paper");
return true;
}
else if (compare(sWidth, "148mm", "0.5mm") && compare(sHeight, "210mm", "0.5mm")) {
palette.addGlobalOption("a5paper");
return true;
}
else if (compare(sWidth, "176mm", "0.5mm") && compare(sHeight, "250mm", "0.5mm")) {
palette.addGlobalOption("b5paper");
return true;
}
else if (compare(sWidth, "8.5in", "0.02in") && compare(sHeight, "11in", "0.02in")) {
palette.addGlobalOption("letterpaper");
return true;
}
else if (compare(sWidth, "8.5in", "0.02in") && compare(sHeight, "14in", "0.02in")) {
palette.addGlobalOption("legalpaper");
return true;
}
else if (compare(sWidth, "7.25in", "0.02in") && compare(sHeight, "10.5in", "0.02in")) {
palette.addGlobalOption("executivepaper");
return true;
}
return false;
}
private boolean compare(String sLength1, String sLength2, String sTolerance) {
return Misc.isLessThan(Misc.abs(Misc.sub(sLength1,sLength2)),sTolerance);
}
/* Helper: Get display name, or original name if it doesn't exist */
private String getDisplayName(String sName) {
String sDisplayName = ofr.getMasterPages().getDisplayName(sName);
return sDisplayName!=null ? sDisplayName : sName;
}
}

View file

@ -0,0 +1,546 @@
/************************************************************************
*
* ParConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.latex;
//import java.util.Hashtable;
import org.w3c.dom.Element;
//import org.w3c.dom.Node;
//import org.w3c.dom.NodeList;
import writer2latex.util.*;
import writer2latex.office.*;
import writer2latex.latex.util.BeforeAfter;
import writer2latex.latex.util.Context;
//import writer2latex.latex.util.HeadingMap;
import writer2latex.latex.util.StyleMap;
/* <p>This class converts OpenDocument paragraphs (<code>text:p</code>) and
* paragraph styles/formatting into LaTeX</p>
* <p>Export of formatting depends on the option "formatting":</p>
* <ul>
* <li><code>ignore_all</code>
* <li><code>ignore_most</code>
* <li><code>convert_basic</code>
* <li><code>convert_most</code>
* <li><code>convert_all</code>
* </ul>
* <p>TODO: Captions and {foot|end}notes should also use this class
*/
public class ParConverter extends StyleConverter {
private boolean bNeedArrayBslash = false;
/** <p>Constructs a new <code>ParConverter</code>.</p>
*/
public ParConverter(OfficeReader ofr, LaTeXConfig config,
ConverterPalette palette) {
super(ofr,config,palette);
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
if (bNeedArrayBslash) {
// centering and raggedright redefines \\, fix this
// Note: aviods nameclash with tabularx (arraybackslash)
// TODO: Should perhaps choose to load tabularx instead?
decl.append("\\makeatletter").nl()
.append("\\newcommand\\arraybslash{\\let\\\\\\@arraycr}").nl()
.append("\\makeatother").nl();
}
if (config.formatting()>=LaTeXConfig.CONVERT_MOST) {
// We typeset with \raggedbottom since OOo doesn't use rubber lengths
// TODO: Maybe turn vertical spacing from OOo into rubber lengths?
decl.append("\\raggedbottom").nl();
}
if (config.formatting()>=LaTeXConfig.CONVERT_MOST) {
decl.append("% Paragraph styles").nl();
// First default paragraph style
palette.getCharSc().applyDefaultFont(ofr.getDefaultParStyle(),decl);
super.appendDeclarations(pack,decl);
}
}
/**
* <p> Process a text:p tag</p>
* @param node The text:h element node containing the heading
* @param ldp The <code>LaTeXDocumentPortion</code> to add LaTeX code to
* @param oc The current context
* @param bLastInBlock If this is true, the paragraph is the
* last one in a block, and we need no trailing blank line (eg. right before
* \end{enumerate}).
*/
public void handleParagraph(Element node, LaTeXDocumentPortion ldp, Context oc, boolean bLastInBlock) {
if (palette.getMathmlCv().handleDisplayEquation(node,ldp)) { return; }
// Get the style name for this paragraph
String sStyleName = node.getAttribute(XMLString.TEXT_STYLE_NAME);
String sDisplayName = ofr.getParStyles().getDisplayName(sStyleName);
// Check for strict handling of styles
if (config.otherStyles()!=LaTeXConfig.ACCEPT && !config.getParStyleMap().contains(sDisplayName)) {
if (config.otherStyles()==LaTeXConfig.WARNING) {
System.err.println("Warning: A paragraph with style "+sDisplayName+" was ignored");
}
else if (config.otherStyles()==LaTeXConfig.ERROR) {
ldp.append("% Error in source document: A paragraph with style ")
.append(palette.getI18n().convert(sDisplayName,false,oc.getLang()))
.append(" was ignored").nl();
}
// Ignore this paragraph:
return;
}
// Empty paragraphs are often (mis)used to achieve vertical spacing in WYSIWYG
// word processors. Hence we translate an empty paragraph to \bigskip.
// This also solves the problem that LaTeX ignores empty paragraphs, Writer doesn't.
// In a well-structured document, an empty paragraph is probably a mistake,
// hence the configuration can specify that it should be ignored.
// Note: Don't use \bigskip in tables (this can lead to strange results)
if (OfficeReader.isWhitespaceContent(node)) {
// Always add page break; other formatting is ignored
BeforeAfter baPage = new BeforeAfter();
StyleWithProperties style = ofr.getParStyle(sStyleName);
palette.getPageSc().applyPageBreak(style,true,baPage);
if (!oc.isInTable()) { ldp.append(baPage.getBefore()); }
if (!config.ignoreEmptyParagraphs()) {
if (!oc.isInTable()) {
ldp.nl().append("\\bigskip").nl();
}
else {
ldp.append("~").nl();
}
if (!bLastInBlock) { ldp.nl(); }
}
if (!oc.isInTable()) { ldp.append(baPage.getAfter()); }
return;
}
Context ic = (Context) oc.clone();
// Always push the font used
palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(ofr.getParStyle(sStyleName)));
// Apply the style
BeforeAfter ba = new BeforeAfter();
if (oc.isInTable()) {
applyCellParStyle(sStyleName,ba,ic,OfficeReader.getCharacterCount(node)==0,bLastInBlock);
}
else {
applyParStyle(sStyleName,ba,ic,OfficeReader.getCharacterCount(node)==0);
}
// Do conversion
ldp.append(ba.getBefore());
palette.getInlineCv().traverseInlineText(node,ldp,ic);
ldp.append(ba.getAfter());
// Add a blank line except within verbatim and last in a block:
if (!bLastInBlock && !ic.isVerbatim() && !ic.isInSimpleTable()) { ldp.nl(); }
// Flush any pending index marks, reference marks and floating frames
palette.getFieldCv().flushReferenceMarks(ldp,oc);
palette.getIndexCv().flushIndexMarks(ldp,oc);
palette.getDrawCv().flushFloatingFrames(ldp,oc);
// pop the font name
palette.getI18n().popSpecialTable();
}
private void applyCellParStyle(String sName, BeforeAfter ba, Context context, boolean bNoTextPar, boolean bLastInBlock) {
// Paragraph formatting for paragraphs within table cells
// We always use simple par styles here
// Add newline if *between* paragraphs
if (!bLastInBlock) { ba.add("","\n"); }
if (context.isInSimpleTable()) {
if (config.formatting()!=LaTeXConfig.IGNORE_ALL) {
// only character formatting!
StyleWithProperties style = ofr.getParStyle(sName);
if (style!=null) {
palette.getI18n().applyLanguage(style,true,true,ba);
palette.getCharSc().applyFont(style,true,true,ba,context);
if (ba.getBefore().length()>0) { ba.add(" ",""); }
}
}
}
else if (bNoTextPar && (config.formatting()==LaTeXConfig.CONVERT_BASIC || config.formatting()==LaTeXConfig.IGNORE_MOST) ) {
// only alignment!
StyleWithProperties style = ofr.getParStyle(sName);
if (style!=null) {
// Apply hard formatting attributes
// Note: Left justified text is exported as full justified text!
palette.getPageSc().applyPageBreak(style,false,ba);
String sTextAlign = style.getProperty(XMLString.FO_TEXT_ALIGN,true);
if (bLastInBlock && context.isInLastTableColumn()) { // no grouping needed, but need to fix problem with \\
if ("center".equals(sTextAlign)) { ba.add("\\centering\\arraybslash ",""); }
else if ("end".equals(sTextAlign)) { ba.add("\\raggedleft\\arraybslash ",""); }
bNeedArrayBslash = true;
}
else if (bLastInBlock) { // no grouping needed
if ("center".equals(sTextAlign)) { ba.add("\\centering ",""); }
else if ("end".equals(sTextAlign)) { ba.add("\\raggedleft ",""); }
}
else {
if ("center".equals(sTextAlign)) { ba.add("{\\centering ","\\par}"); }
else if ("end".equals(sTextAlign)) { ba.add("{\\raggedleft ","\\par}"); }
}
}
}
else {
// Export character formatting + alignment only
BeforeAfter baText = new BeforeAfter();
// Apply hard formatting attributes
// Note: Left justified text is exported as full justified text!
StyleWithProperties style = ofr.getParStyle(sName);
if (style!=null) {
String sTextAlign = style.getProperty(XMLString.FO_TEXT_ALIGN,true);
if (bLastInBlock && context.isInLastTableColumn()) { // no grouping needed, but need to fix problem with \\
if ("center".equals(sTextAlign)) { ba.add("\\centering\\arraybslash ",""); }
else if ("end".equals(sTextAlign)) { ba.add("\\raggedleft\\arraybslash ",""); }
bNeedArrayBslash = true;
}
else if (bLastInBlock) { // no \par needed
if ("center".equals(sTextAlign)) { ba.add("\\centering ",""); }
else if ("end".equals(sTextAlign)) { ba.add("\\raggedleft ",""); }
}
else {
if ("center".equals(sTextAlign)) { ba.add("\\centering ","\\par"); }
else if ("end".equals(sTextAlign)) { ba.add("\\raggedleft ","\\par"); }
}
palette.getI18n().applyLanguage(style,true,true,baText);
palette.getCharSc().applyFont(style,true,true,baText,context);
}
// Assemble the bits. If there is any hard character formatting
// or alignment we must group the contents.
if (!baText.isEmpty() && !bLastInBlock) { ba.add("{","}"); }
ba.add(baText.getBefore(),baText.getAfter());
if (baText.getBefore().length()>0) { ba.add(" ",""); }
}
// Update context
StyleWithProperties style = ofr.getParStyle(sName);
if (style==null) { return; }
context.updateFormattingFromStyle(style);
context.setVerbatim(styleMap.getVerbatim(sName));
}
/** <p>Use a paragraph style in LaTeX.</p>
* @param <code>sName</code> the name of the text style
* @param <code>ba</code> a <code>BeforeAfter</code> to put code into
* @param <code>context</code> the current context. This method will use and update the formatting context
* @param <code>bNoTextPar</code> true if this paragraph has no text content (hence character formatting is not needed)
*/
private void applyParStyle(String sName, BeforeAfter ba, Context context, boolean bNoTextPar) {
applyParStyle(sName,ba,context,bNoTextPar,true);
}
private void applyParStyle(String sName, BeforeAfter ba, Context context, boolean bNoTextPar, boolean bBreakInside) {
// No style specified?
if (sName==null) { return; }
if (context.isInSimpleTable()) {
if (config.formatting()!=LaTeXConfig.IGNORE_ALL) {
// only character formatting!
StyleWithProperties style = ofr.getParStyle(sName);
if (style!=null) {
palette.getI18n().applyLanguage(style,true,true,ba);
palette.getCharSc().applyFont(style,true,true,ba,context);
if (ba.getBefore().length()>0) { ba.add(" ",""); }
}
}
}
else if (bNoTextPar && (config.formatting()==LaTeXConfig.CONVERT_BASIC || config.formatting()==LaTeXConfig.IGNORE_MOST) ) {
// Always end with a line break
ba.add("","\n");
// only alignment!
StyleWithProperties style = ofr.getParStyle(sName);
if (style!=null) {
// Apply hard formatting attributes
// Note: Left justified text is exported as full justified text!
palette.getPageSc().applyPageBreak(style,false,ba);
String sTextAlign = style.getProperty(XMLString.FO_TEXT_ALIGN,true);
if ("center".equals(sTextAlign)) { ba.add("{\\centering ","\\par}"); }
else if ("end".equals(sTextAlign)) { ba.add("{\\raggedleft ","\\par}"); }
}
}
else {
// Always end with a line break
ba.add("","\n");
// Apply the style
if (!styleMap.contains(sName)) { createParStyle(sName); }
String sBefore = styleMap.getBefore(sName);
String sAfter = styleMap.getAfter(sName);
ba.add(sBefore,sAfter);
// Add line breaks inside?
if (bBreakInside && styleMap.getLineBreak(sName)) {
if (sBefore.length()>0) { ba.add("\n",""); }
if (sAfter.length()>0 && !"}".equals(sAfter)) { ba.add("","\n"); }
}
}
// Update context
StyleWithProperties style = ofr.getParStyle(sName);
if (style==null) { return; }
context.updateFormattingFromStyle(style);
context.setVerbatim(styleMap.getVerbatim(sName));
}
/** <p>Convert a paragraph style to LaTeX. </p>
* <p>A soft style is declared in <code>styleDeclarations</code> as
* <code>\newenvironment...</code></p>
* <p>A hard style is used by applying LaTeX code directly</p>
* @param <code>sName</code> the OOo name of the style
*/
private void createParStyle(String sName) {
// A paragraph style should always be created relative to main context
Context context = (Context) palette.getMainContext().clone();
// The style may already be declared in the configuration:
String sDisplayName = ofr.getParStyles().getDisplayName(sName);
StyleMap sm = config.getParStyleMap();
if (sm.contains(sDisplayName)) {
styleMap.put(sName,sm.getBefore(sDisplayName),sm.getAfter(sDisplayName),
sm.getLineBreak(sDisplayName),sm.getVerbatim(sDisplayName));
return;
}
// Does the style exist?
StyleWithProperties style = ofr.getParStyle(sName);
if (style==null) {
styleMap.put(sName,"","");
return;
}
// Convert the style!
switch (config.formatting()) {
case LaTeXConfig.CONVERT_MOST:
if (style.isAutomatic()) {
createAutomaticParStyle(style,context);
return;
}
case LaTeXConfig.CONVERT_ALL:
createSoftParStyle(style,context);
return;
case LaTeXConfig.CONVERT_BASIC:
case LaTeXConfig.IGNORE_MOST:
createSimpleParStyle(style,context);
return;
case LaTeXConfig.IGNORE_ALL:
default:
styleMap.put(sName,"","");
}
}
private void createAutomaticParStyle(StyleWithProperties style, Context context) {
// Hard paragraph formatting from this style should be ignored
// (because the user wants to ignore hard paragraph formatting
// or there is a style map for the parent.)
BeforeAfter ba = new BeforeAfter();
BeforeAfter baPar = new BeforeAfter();
BeforeAfter baText = new BeforeAfter();
// Apply paragraph formatting from parent
// If parent is verbatim, this is all
String sParentName = style.getParentName();
if (styleMap.getVerbatim(sParentName)) {
styleMap.put(style.getName(),styleMap.getBefore(sParentName),styleMap.getAfter(sParentName),
styleMap.getLineBreak(sParentName),styleMap.getVerbatim(sParentName));
return;
}
applyParStyle(sParentName,baPar,context,false,false);
// Apply hard formatting properties:
palette.getPageSc().applyPageBreak(style,false,ba);
palette.getI18n().applyLanguage(style,true,false,baText);
palette.getCharSc().applyFont(style,true,false,baText,context);
// Assemble the bits. If there is any hard character formatting
// we must group the contents.
if (baPar.isEmpty() && !baText.isEmpty()) { ba.add("{","}"); }
else { ba.add(baPar.getBefore(),baPar.getAfter()); }
ba.add(baText.getBefore(),baText.getAfter());
boolean bLineBreak = styleMap.getLineBreak(sParentName);
if (!bLineBreak && !baText.isEmpty()) { ba.add(" ",""); }
styleMap.put(style.getName(),ba.getBefore(),ba.getAfter(),bLineBreak,false);
}
private void createSimpleParStyle(StyleWithProperties style, Context context) {
// Export character formatting + alignment only
if (style.isAutomatic() && config.getParStyleMap().contains(ofr.getParStyles().getDisplayName(style.getParentName()))) {
createAutomaticParStyle(style,context);
return;
}
BeforeAfter ba = new BeforeAfter();
BeforeAfter baText = new BeforeAfter();
// Apply hard formatting attributes
// Note: Left justified text is exported as full justified text!
palette.getPageSc().applyPageBreak(style,false,ba);
String sTextAlign = style.getProperty(XMLString.FO_TEXT_ALIGN,true);
if ("center".equals(sTextAlign)) { baText.add("\\centering","\\par"); }
else if ("end".equals(sTextAlign)) { baText.add("\\raggedleft","\\par"); }
palette.getI18n().applyLanguage(style,true,true,baText);
palette.getCharSc().applyFont(style,true,true,baText,context);
// Assemble the bits. If there is any hard character formatting
// or alignment we must group the contents.
if (!baText.isEmpty()) { ba.add("{","}"); }
ba.add(baText.getBefore(),baText.getAfter());
styleMap.put(style.getName(),ba.getBefore(),ba.getAfter());
}
private void createSoftParStyle(StyleWithProperties style, Context context) {
// This style should be converted to an enviroment, except if
// it's automatic and there is a config style map for the parent
if (style.isAutomatic() && config.getParStyleMap().contains(ofr.getParStyles().getDisplayName(style.getParentName()))) {
createAutomaticParStyle(style,context);
}
BeforeAfter ba = new BeforeAfter();
applyParProperties(style,ba);
ba.add("\\writerlistparindent\\writerlistleftskip","");
palette.getI18n().applyLanguage(style,true,true,ba);
ba.add("\\leavevmode","");
palette.getCharSc().applyNormalFont(ba);
palette.getCharSc().applyFont(style,true,true,ba,context);
ba.add("\\writerlistlabel","");
ba.add("\\ignorespaces","");
// Declare the paragraph style (\newenvironment)
String sTeXName = "style" + styleNames.getExportName(style.getDisplayName());
styleMap.put(style.getName(),"\\begin{"+sTeXName+"}","\\end{"+sTeXName+"}");
declarations.append("\\newenvironment{").append(sTeXName)
.append("}{").append(ba.getBefore()).append("}{")
.append(ba.getAfter()).append("}").nl();
}
// Remaining methods are private helpers
/** <p>Apply line spacing from a style.</p>
* @param <code>style</code> the paragraph style to use
* @param <code>ba</code> a <code>BeforeAfter</code> to put code into
*/
private void applyLineSpacing(StyleWithProperties style, BeforeAfter ba) {
if (style==null) { return; }
String sLineHeight = style.getProperty(XMLString.FO_LINE_HEIGHT);
if (sLineHeight==null || !sLineHeight.endsWith("%")) { return; }
float fPercent=Misc.getFloat(sLineHeight.substring(0,sLineHeight.length()-1),100);
// Do not allow less that 120% (LaTeX default)
if (fPercent<120) { fPercent = 120; }
ba.add("\\renewcommand\\baselinestretch{"+fPercent/120+"}","");
}
/** <p>Helper: Create a horizontal border. Currently unused</p>
*/
/*private String createBorder(String sLeft, String sRight, String sTop,
String sHeight, String sColor) {
BeforeAfter baColor = new BeforeAfter();
palette.getColorCv().applyColor(sColor,false,baColor, new Context());
return "{\\setlength\\parindent{0pt}\\setlength\\leftskip{" + sLeft + "}"
+ "\\setlength\\baselineskip{0pt}\\setlength\\parskip{" + sHeight + "}"
+ baColor.getBefore()
+ "\\rule{\\textwidth-" + sLeft + "-" + sRight + "}{" + sHeight + "}"
+ baColor.getAfter()
+ "\\par}";
}*/
/** <p>Apply margin+alignment properties from a style.</p>
* @param <code>style</code> the paragraph style to use
* @param <code>ba</code> a <code>BeforeAfter</code> to put code into
*/
private void applyMargins(StyleWithProperties style, BeforeAfter ba) {
// Read padding/margin/indentation properties:
//String sPaddingTop = style.getAbsoluteLength(XMLString.FO_PADDING_TOP);
//String sPaddingBottom = style.getAbsoluteLength(XMLString.FO_PADDING_BOTTOM);
//String sPaddingLeft = style.getAbsoluteLength(XMLString.FO_PADDING_LEFT);
//String sPaddingRight = style.getAbsoluteLength(XMLString.FO_PADDING_RIGHT);
String sMarginTop = style.getAbsoluteLength(XMLString.FO_MARGIN_TOP);
String sMarginBottom = style.getAbsoluteLength(XMLString.FO_MARGIN_BOTTOM);
String sMarginLeft = style.getAbsoluteLength(XMLString.FO_MARGIN_LEFT);
String sMarginRight = style.getAbsoluteLength(XMLString.FO_MARGIN_RIGHT);
String sTextIndent;
if ("true".equals(style.getProperty(XMLString.STYLE_AUTO_TEXT_INDENT))) {
sTextIndent = "2em";
}
else {
sTextIndent = style.getAbsoluteLength(XMLString.FO_TEXT_INDENT);
}
// Read alignment properties:
boolean bRaggedLeft = false; // add 1fil to \leftskip
boolean bRaggedRight = false; // add 1fil to \rightskip
boolean bParFill = false; // add 1fil to \parfillskip
String sTextAlign = style.getProperty(XMLString.FO_TEXT_ALIGN);
if ("center".equals(sTextAlign)) {
bRaggedLeft = true; bRaggedRight = true; // centered paragraph
}
else if ("start".equals(sTextAlign)) {
bRaggedRight = true; bParFill = true; // left aligned paragraph
}
else if ("end".equals(sTextAlign)) {
bRaggedLeft = true; // right aligned paragraph
}
else if (!"justify".equals(style.getProperty(XMLString.FO_TEXT_ALIGN_LAST))) {
bParFill = true; // justified paragraph with ragged last line
}
// Create formatting:
String sRubberMarginTop = Misc.multiply("10%",sMarginTop);
if (Misc.length2px(sRubberMarginTop).equals("0")) { sRubberMarginTop="1pt"; }
String sRubberMarginBottom = Misc.multiply("10%",sMarginBottom);
if (Misc.length2px(sRubberMarginBottom).equals("0")) { sRubberMarginBottom="1pt"; }
ba.add("\\setlength\\leftskip{"+sMarginLeft+(bRaggedLeft?" plus 1fil":"")+"}","");
ba.add("\\setlength\\rightskip{"+sMarginRight+(bRaggedRight?" plus 1fil":"")+"}","");
ba.add("\\setlength\\parindent{"+sTextIndent+"}","");
ba.add("\\setlength\\parfillskip{"+(bParFill?"0pt plus 1fil":"0pt")+"}","");
ba.add("\\setlength\\parskip{"+sMarginTop+" plus "+sRubberMarginTop+"}",
"\\unskip\\vspace{"+sMarginBottom+" plus "+sRubberMarginBottom+"}");
}
public void applyAlignment(StyleWithProperties style, boolean bIsSimple, boolean bInherit, BeforeAfter ba) {
if (bIsSimple || style==null) { return; }
String sTextAlign = style.getProperty(XMLString.FO_TEXT_ALIGN,bInherit);
if ("center".equals(sTextAlign)) { ba.add("\\centering",""); }
else if ("start".equals(sTextAlign)) { ba.add("\\raggedright",""); }
else if ("end".equals(sTextAlign)) { ba.add("\\raggedleft",""); }
}
/** <p>Apply all paragraph properties.</p>
* @param <code>style</code> the paragraph style to use
* @param <code>ba</code> a <code>BeforeAfter</code> to put code into
*/
private void applyParProperties(StyleWithProperties style, BeforeAfter ba) {
palette.getPageSc().applyPageBreak(style,true,ba);
ba.add("","\\par");
applyLineSpacing(style,ba);
applyMargins(style,ba);
}
}

View file

@ -0,0 +1,135 @@
/************************************************************************
*
* SectionConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2009 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2009-02-17)
*
*/
package writer2latex.latex;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import writer2latex.util.*;
import writer2latex.office.*;
import writer2latex.latex.i18n.ClassicI18n;
import writer2latex.latex.util.BeforeAfter;
import writer2latex.latex.util.Context;
/** <p>This class creates LaTeX code from OOo sections.
* <p>Sections are converted to multicols environments using <code>multicol.sty</code>
*/
public class SectionConverter extends ConverterHelper {
// Do we need multicols.sty?
private boolean bNeedMulticol = false;
// Filenames for external sections
private ExportNameCollection fileNames = new ExportNameCollection(true);
/** <p>Constructs a new <code>SectionStyleConverter</code>.</p>
*/
public SectionConverter(OfficeReader ofr, LaTeXConfig config,
ConverterPalette palette) {
super(ofr,config,palette);
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
if (bNeedMulticol) { pack.append("\\usepackage{multicol}").nl(); }
}
/** <p> Process a section (text:section tag)</p>
* @param node The element containing the section
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleSection(Element node, LaTeXDocumentPortion ldp, Context oc) {
// We may need a hyperlink target, add this first
palette.getFieldCv().addTarget(node,"|region",ldp);
// Create new document, if desired
String sFileName = null;
Element source = Misc.getChildByTagName(node,XMLString.TEXT_SECTION_SOURCE);
if (config.splitLinkedSections() && source!=null) {
sFileName = fileNames.getExportName(Misc.removeExtension(Misc.urlDecode(source.getAttribute(XMLString.XLINK_HREF))));
}
else if (config.splitToplevelSections() && isToplevel(node)) {
//sFileName = fileNames.getExportName(palette.getOutFileName()+node.getAttribute(XMLString.TEXT_NAME));
sFileName = fileNames.getExportName(node.getAttribute(XMLString.TEXT_NAME));
}
LaTeXDocumentPortion sectionLdp = ldp;
if (sFileName!=null) {
LaTeXDocument newDoc = new LaTeXDocument(sFileName,config.getWrapLinesAfter());
if (config.getBackend()!=LaTeXConfig.XETEX) {
newDoc.setEncoding(ClassicI18n.writeJavaEncoding(config.getInputencoding()));
}
else {
newDoc.setEncoding("UTF-8");
}
palette.addDocument(newDoc);
sectionLdp = newDoc.getContents();
}
// Apply the style
String sStyleName = node.getAttribute(XMLString.TEXT_STYLE_NAME);
BeforeAfter ba = new BeforeAfter();
Context ic = (Context) oc.clone();
applySectionStyle(sStyleName,ba,ic);
// Do conversion
ldp.append(ba.getBefore());
if (sFileName!=null) {
ldp.append("\\input{").append(sFileName).append("}").nl();
}
palette.getBlockCv().traverseBlockText(node,sectionLdp,ic);
if (sectionLdp!=ldp) { sectionLdp.append("\\endinput").nl(); }
ldp.append(ba.getAfter());
}
// Create multicols environment as needed
private void applySectionStyle(String sStyleName, BeforeAfter ba, Context context) {
StyleWithProperties style = ofr.getSectionStyle(sStyleName);
// Don't nest multicols and require at least 2 columns
if (context.isInMulticols() || style==null || style.getColCount()<2) { return; }
int nCols = style.getColCount();
bNeedMulticol = true;
context.setInMulticols(true);
ba.add("\\begin{multicols}{"+(nCols>10 ? 10 : nCols)+"}\n", "\\end{multicols}\n");
}
// return true if this node is *not* contained in a text:section element
private boolean isToplevel(Node node) {
Node parent = node.getParentNode();
if (XMLString.TEXT_SECTION.equals(parent.getNodeName())) {
return false;
}
else if (XMLString.OFFICE_BODY.equals(parent.getNodeName())) {
return true;
}
return isToplevel(parent);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,51 @@
/************************************************************************
*
* StyleConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-09-08)
*
*/
package writer2latex.latex;
import writer2latex.latex.util.StyleMap;
import writer2latex.util.ExportNameCollection;
import writer2latex.office.OfficeReader;
/**
* <p>This is an abstract superclass for style converters.</p>
*/
public abstract class StyleConverter extends ConverterHelper {
// Names and maps + necessary declarations for these styles
protected ExportNameCollection styleNames = new ExportNameCollection(false);
protected StyleMap styleMap = new StyleMap();
protected LaTeXDocumentPortion declarations = new LaTeXDocumentPortion(false);
protected StyleConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
decl.append(declarations);
}
}

View file

@ -0,0 +1,366 @@
/************************************************************************
*
* TableConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.latex;
import org.w3c.dom.Element;
import writer2latex.util.*;
import writer2latex.office.*;
import writer2latex.latex.util.BeforeAfter;
import writer2latex.latex.util.Context;
/** <p>This class converts OpenDocument tables to LaTeX.</p>
* <p>The following LaTeX packages are used; some of them are optional</p>
* <p>array.sty, longtable.sty, supertabular.sty, tabulary.sty, hhline.sty,
* colortbl.sty.</p>
* <p>Options:</p>
* <ul>
* <li>use_longtable = true|false</li>
* <li>use_supertabular = true|false</li>
* <li>use_tabulary = true|false</li>
* <li>use_colortbl = true|false</li>
* <li>float_tables = true|false</li>
* <li>float_options = &lt;string&gt;</li>
* <li>table_content = accept|ignore|warning|error</li>
* </ul>
*
*/
public class TableConverter extends ConverterHelper {
private boolean bNeedLongtable = false;
private boolean bNeedSupertabular = false;
private boolean bNeedTabulary = false;
private boolean bNeedColortbl = false;
private boolean bContainsTables = false;
/** <p>Constructs a new <code>TableConverter</code>.</p>
*/
public TableConverter(OfficeReader ofr, LaTeXConfig config,
ConverterPalette palette) {
super(ofr,config,palette);
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
pack.append("\\usepackage{array}").nl(); // TODO: Make this optional
if (bNeedLongtable) { pack.append("\\usepackage{longtable}").nl(); }
if (bNeedSupertabular) { pack.append("\\usepackage{supertabular}").nl(); }
if (bNeedTabulary) { pack.append("\\usepackage{tabulary}").nl(); }
pack.append("\\usepackage{hhline}").nl(); // TODO: Make this optional
if (bNeedColortbl) { pack.append("\\usepackage{colortbl}").nl(); }
// Set padding for table cells (1mm is default in OOo!)
// For vertical padding we can only specify a relative size
if (bContainsTables) {
decl.append("\\setlength\\tabcolsep{1mm}").nl();
decl.append("\\renewcommand\\arraystretch{1.3}").nl();
}
}
// Export a lonely table caption
public void handleCaption(Element node, LaTeXDocumentPortion ldp, Context oc) {
ldp.append("\\captionof{table}");
palette.getCaptionCv().handleCaptionBody(node,ldp,oc,true);
}
/** <p> Process a table (table:table or table:sub-table tag)</p>
* @param node The element containing the table
* @param ldp the <code>LaTeXDocumentPortion</code> to which
* LaTeX code should be added
* @param oc the current context
*/
public void handleTable(Element node, Element caption, boolean bCaptionAbove,
LaTeXDocumentPortion ldp, Context oc) {
// Export table, if allowed by the configuration
switch (config.tableContent()) {
case LaTeXConfig.ACCEPT:
new SingleTableConverter().handleTable(node,caption,bCaptionAbove,ldp,oc);
bContainsTables = true;
break;
case LaTeXConfig.IGNORE:
// Ignore table silently
break;
case LaTeXConfig.WARNING:
System.err.println("Warning: Tables are not allowed");
break;
case LaTeXConfig.ERROR:
ldp.append("% Error in document: A table was ignored");
}
}
// Inner class to convert a single table
private class SingleTableConverter {
private TableReader table;
private TableFormatter formatter;
private Element caption;
private boolean bCaptionAbove;
private BeforeAfter baTable;
private BeforeAfter baTableAlign;
private void handleTable(Element node, Element caption, boolean bCaptionAbove,
LaTeXDocumentPortion ldp, Context oc) {
// Store the caption
this.caption = caption;
this.bCaptionAbove = bCaptionAbove;
// Read the table
table = ofr.getTableReader(node);
// Get formatter and update flags according to formatter
formatter = new TableFormatter(ofr,config,palette,table,!oc.isInMulticols(),oc.isInTable());
bContainsTables = true;
bNeedLongtable |= formatter.isLongtable();
bNeedSupertabular |= formatter.isSupertabular();
bNeedTabulary |= formatter.isTabulary();
bNeedColortbl |= formatter.isColortbl();
// Update the context
Context ic = (Context) oc.clone();
ic.setInTable(true);
ic.setInSimpleTable(formatter.isSimple());
// Never allow footnotes in tables
// (longtable.sty *does* allow footnotes in body, but not in head -
// only consistent solution is to disallow all footnotes)
ic.setNoFootnotes(true);
// Get table declarations
baTable = new BeforeAfter();
baTableAlign = new BeforeAfter();
formatter.applyTableStyle(baTable,baTableAlign);
// Convert table
if (formatter.isSupertabular()) {
handleSupertabular(ldp,ic);
}
else if (formatter.isLongtable()) {
handleLongtable(ldp,ic);
}
else if (config.floatTables() && !ic.isInFrame() && !table.isSubTable()) {
handleTableFloat(ldp,ic);
}
else {
handleTabular(ldp,ic);
}
// Insert any pending footnotes
palette.getNoteCv().flushFootnotes(ldp,oc);
}
private void handleSupertabular(LaTeXDocumentPortion ldp, Context oc) {
ldp.append(baTableAlign.getBefore());
// Caption
if (caption!=null) {
handleCaption(bCaptionAbove ? "\\topcaption" : "\\bottomcaption",
ldp,oc);
}
// Table head
ldp.append("\\tablehead{");
handleHeaderRows(ldp,oc);
ldp.append("}");
// The table
handleHyperTarget(ldp);
ldp.append(baTable.getBefore()).nl();
handleBodyRows(ldp,oc);
ldp.append(baTable.getAfter()).nl();
ldp.append(baTableAlign.getAfter());
}
private void handleLongtable(LaTeXDocumentPortion ldp, Context oc) {
handleHyperTarget(ldp);
ldp.append(baTable.getBefore()).nl();
// Caption above
if (caption!=null && bCaptionAbove) {
handleCaption("\\caption",ldp,oc);
ldp.append("\\\\").nl();
handleHeaderRows(ldp,oc);
ldp.nl().append("\\endfirsthead").nl();
}
// Table head
if (table.getFirstBodyRow()>0) {
handleHeaderRows(ldp,oc);
ldp.nl().append("\\endhead").nl();
}
// Caption below
if (caption!=null && !bCaptionAbove) {
handleCaption("\\caption",ldp,oc);
ldp.append("\\endlastfoot").nl();
}
// Table body
handleBodyRows(ldp,oc);
ldp.append(baTable.getAfter()).nl();
}
private void handleTableFloat(LaTeXDocumentPortion ldp, Context oc) {
ldp.append("\\begin{table}");
if (config.getFloatOptions().length()>0) {
ldp.append("[").append(config.getFloatOptions()).append("]");
}
ldp.nl();
ldp.append(baTableAlign.getBefore());
// Caption above
if (caption!=null && bCaptionAbove) {
handleCaption("\\caption",ldp,oc);
}
// The table
handleHyperTarget(ldp);
ldp.append(baTable.getBefore()).nl();
handleHeaderRows(ldp,oc);
ldp.nl();
handleBodyRows(ldp,oc);
ldp.append(baTable.getAfter()).nl();
// Caption below
if (caption!=null && !bCaptionAbove) {
handleCaption("\\caption",ldp,oc);
}
ldp.append(baTableAlign.getAfter());
ldp.append("\\end{table}").nl();
}
private void handleTabular(LaTeXDocumentPortion ldp, Context oc) {
ldp.append(baTableAlign.getBefore());
// Caption above
if (caption!=null && bCaptionAbove) {
TableConverter.this.handleCaption(caption,ldp,oc);
}
// The table
handleHyperTarget(ldp);
ldp.append(baTable.getBefore()).nl();
if (table.getFirstBodyRow()>0) {
handleHeaderRows(ldp,oc);
ldp.nl();
}
handleBodyRows(ldp,oc);
ldp.append(baTable.getAfter()).nl();
// Caption below
if (caption!=null && !bCaptionAbove) {
TableConverter.this.handleCaption(caption,ldp,oc);
}
ldp.append(baTableAlign.getAfter());
}
private void handleCaption(String sCommand, LaTeXDocumentPortion ldp, Context oc) {
ldp.append(sCommand);
palette.getCaptionCv().handleCaptionBody(caption,ldp,oc,false);
}
private void handleHyperTarget(LaTeXDocumentPortion ldp) {
// We may need a hyperlink target
if (!table.isSubTable()) {
palette.getFieldCv().addTarget(table.getTableName(),"|table",ldp);
}
}
private void handleHeaderRows(LaTeXDocumentPortion ldp, Context oc) {
// Note: does *not* add newline after last row
if (table.getFirstBodyRow()>0) {
// Add interrow material before first row:
String sInter = formatter.getInterrowMaterial(0);
if (sInter.length()>0) {
ldp.append(sInter).nl();
}
// Add header rows
handleRows(0,table.getFirstBodyRow(),ldp,oc);
}
}
private void handleBodyRows(LaTeXDocumentPortion ldp, Context oc) {
if (table.getFirstBodyRow()==0) {
// No head, add interrow material before first row:
String sInter = formatter.getInterrowMaterial(0);
if (sInter.length()>0) {
ldp.append(sInter).nl();
}
}
// Add body rows
handleRows(table.getFirstBodyRow(),table.getRowCount(),ldp,oc);
ldp.nl();
}
private void handleRows(int nStart, int nEnd, LaTeXDocumentPortion ldp, Context oc) {
int nColCount = table.getColCount();
for (int nRow=nStart; nRow<nEnd; nRow++) {
// Export columns in this row
Context icRow = (Context) oc.clone();
BeforeAfter baRow = new BeforeAfter();
formatter.applyRowStyle(nRow,baRow,icRow);
if (!baRow.isEmpty()) {
ldp.append(baRow.getBefore());
if (!formatter.isSimple()) { ldp.nl(); }
}
int nCol = 0;
while (nCol<nColCount) {
Element cell = (Element) table.getCell(nRow,nCol);
if (XMLString.TABLE_TABLE_CELL.equals(cell.getNodeName())) {
Context icCell = (Context) icRow.clone();
BeforeAfter baCell = new BeforeAfter();
formatter.applyCellStyle(nRow,nCol,baCell,icCell);
ldp.append(baCell.getBefore());
if (nCol==nColCount-1) { icCell.setInLastTableColumn(true); }
palette.getBlockCv().traverseBlockText(cell,ldp,icCell);
ldp.append(baCell.getAfter());
}
// Otherwise ignore; the cell is covered by a \multicolumn entry.
// (table:covered-table-cell)
int nColSpan = Misc.getPosInteger(cell.getAttribute(
XMLString.TABLE_NUMBER_COLUMNS_SPANNED),1);
if (nCol+nColSpan<nColCount) {
if (formatter.isSimple()) { ldp.append(" & "); }
else { ldp.append(" &").nl(); }
}
nCol+=nColSpan;
}
ldp.append("\\\\").append(formatter.getInterrowMaterial(nRow+1));
// Add newline, except after last row
if (nRow<nEnd-1) { ldp.nl(); }
}
}
}
}

View file

@ -0,0 +1,451 @@
/************************************************************************
*
* TableFormatter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2009 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2009-02-19)
*
*/
package writer2latex.latex;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import writer2latex.util.*;
import writer2latex.office.*;
import writer2latex.latex.util.BeforeAfter;
import writer2latex.latex.util.Context;
/**
* <p>This class converts OOo table styles to LaTeX.</p>
* <p> In OOo the table style is distributed on table, column and cell styles.
* <p> In LaTeX we have to rearrange this information slightly, so this class
* takes care of that.</p>
*/
public class TableFormatter extends ConverterHelper {
//private boolean bApplyCellFormat;
private TableReader table;
private char[][] cAlign;
private char[] cGlobalAlign;
private boolean[][] bHBorder;
private boolean[][] bVBorder;
private boolean[] bGlobalVBorder;
private String[] sRowColor;
private String[][] sCellColor;
private String[] sColumnWidth;
private boolean bIsLongtable;
private boolean bIsSupertabular;
private boolean bIsTabulary;
private boolean bIsColortbl;
private boolean bIsSimple;
/** <p>Constructor: Create from a TableReader.</p>
*/
public TableFormatter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette,
TableReader table, boolean bAllowPageBreak, boolean bIsInTable) {
super(ofr,config,palette);
this.table = table;
//bApplyCellFormat = config.formatting()>=LaTeXConfig.CONVERT_MOST;
int nRowCount = table.getRowCount();
int nColCount = table.getColCount();
int nSimpleTableLimit = config.getSimpleTableLimit();
// Step 1: Collect alignment and identify simple tables
bIsSimple = true;
cAlign = new char[nRowCount][nColCount];
cGlobalAlign = new char[nColCount];
// Keep track of characters to be counted
int[] nPendingChars = new int[nRowCount];
int[] nPendingColSpan = new int[nRowCount];
int nTableWidth = 0;
for (int nCol=0; nCol<nColCount; nCol++) {
// Collect chars to be counted in this column
for (int nRow=0; nRow<nRowCount; nRow++) {
Element cell = table.getCell(nRow, nCol);
if (Misc.isElement(cell, XMLString.TABLE_TABLE_CELL)) {
// Now we're here: Collect alignment
if (OfficeReader.isSingleParagraph(cell)) {
Node par = Misc.getChildByTagName(cell,XMLString.TEXT_P);
StyleWithProperties style = ofr.getParStyle(Misc.getAttribute(par,XMLString.TEXT_STYLE_NAME));
cAlign[nRow][nCol] = 'l';
if (style!=null) {
String sAlign = style.getProperty(XMLString.FO_TEXT_ALIGN,true);
if ("center".equals(sAlign)) { cAlign[nRow][nCol] = 'c'; }
else if ("end".equals(sAlign)) { cAlign[nRow][nCol] = 'r'; }
}
}
else {
// Found cell with more than one paragraph
bIsSimple = false;
}
// Collect characters (the cell contains this many characters that should be distributed over that many columns)
nPendingChars[nRow] = OfficeReader.getCharacterCount(cell);
nPendingColSpan[nRow] = Misc.getPosInteger(cell.getAttribute(XMLString.TABLE_NUMBER_COLUMNS_SPANNED), 1);
}
}
// Determine the number of characters to count *now* (because they cannot be postponed to next column)
int nColChars = 0;
for (int nRow=0; nRow<nRowCount; nRow++) {
if (nPendingColSpan[nRow]==1) {
nColChars = Math.max(nColChars, nPendingChars[nRow]);
}
}
// Reduce pending chars and increase table width
nTableWidth += nColChars;
for (int nRow=0; nRow<nRowCount; nRow++) {
if (nPendingColSpan[nRow]>=1) {
nPendingChars[nRow] = Math.max(0, nPendingChars[nRow]-nColChars);
nPendingColSpan[nRow]--;
}
}
}
if (nTableWidth>nSimpleTableLimit) bIsSimple = false;
// Step 2: Create global alignment
for (int nCol=0; nCol<nColCount; nCol++) {
int nCenter = 0;
int nRight = 0;
for (int nRow=0; nRow<nRowCount; nRow++) {
if (cAlign[nRow][nCol]=='c') { nCenter++; }
else if (cAlign[nRow][nCol]=='r') { nRight++; }
}
cGlobalAlign[nCol] = 'l';
int nLeft = nColCount-nCenter-nRight;
if (nCenter>nLeft) {
if (nRight>nLeft) { cGlobalAlign[nCol] = 'r'; }
else { cGlobalAlign[nCol] = 'c'; }
}
else if (nRight>nLeft) {
cGlobalAlign[nCol] = 'r';
}
}
// Step 3: Initialize borders:
bHBorder = new boolean[nRowCount+1][nColCount];
for (int nRow=0; nRow<=nRowCount; nRow++) {
for (int nCol=0; nCol<nColCount; nCol++) {
bHBorder[nRow][nCol] = false;
}
}
bVBorder = new boolean[nRowCount][nColCount+1];
for (int nRow=0; nRow<nRowCount; nRow++) {
for (int nCol=0; nCol<=nColCount; nCol++) {
bVBorder[nRow][nCol] = false;
}
}
// Step 4: Collect borders from cell styles:
for (int nRow=0; nRow<nRowCount; nRow++) {
int nCol = 0;
while (nCol<nColCount) {
Node cell = table.getCell(nRow,nCol);
String sStyleName = Misc.getAttribute(cell,XMLString.TABLE_STYLE_NAME);
StyleWithProperties style = ofr.getCellStyle(sStyleName);
int nColSpan = Misc.getPosInteger(Misc.getAttribute(cell,
XMLString.TABLE_NUMBER_COLUMNS_SPANNED),1);
boolean bLeft = false;
boolean bRight = false;
boolean bTop = false;
boolean bBottom = false;
if (style!=null) {
String sBorder = style.getProperty(XMLString.FO_BORDER);
if (sBorder!=null && !"none".equals(sBorder)) {
bLeft = true; bRight = true; bTop = true; bBottom = true;
}
sBorder = style.getProperty(XMLString.FO_BORDER_LEFT);
if (sBorder!=null && !"none".equals(sBorder)) {
bLeft = true;
}
sBorder = style.getProperty(XMLString.FO_BORDER_RIGHT);
if (sBorder!=null && !"none".equals(sBorder)) {
bRight = true;
}
sBorder = style.getProperty(XMLString.FO_BORDER_TOP);
if (sBorder!=null && !"none".equals(sBorder)) {
bTop = true;
}
sBorder = style.getProperty(XMLString.FO_BORDER_BOTTOM);
if (sBorder!=null && !"none".equals(sBorder)) {
bBottom = true;
}
}
bVBorder[nRow][nCol] |= bLeft;
bVBorder[nRow][nCol+nColSpan] |= bRight;
do {
bHBorder[nRow][nCol] |= bTop;
bHBorder[nRow+1][nCol] |= bBottom;
nCol++;
} while (--nColSpan>0);
}
}
// Step 5: Create global vertical borders based on simple majority
// (in order to minimize the number of \multicolum{1} entries)
bGlobalVBorder = new boolean[nColCount+1];
for (int nCol=0; nCol<=nColCount; nCol++) {
int nBalance = 0;
for (int nRow=0; nRow<nRowCount; nRow++) {
nBalance += bVBorder[nRow][nCol] ? 1 : -1;
}
bGlobalVBorder[nCol] = nBalance>0;
}
// Step 6: Get background colors
sRowColor = new String[nRowCount];
sCellColor = new String[nRowCount][nColCount];
if (config.useColortbl()) {
// Table background
String sTableColor = null;
StyleWithProperties tableStyle = ofr.getTableStyle(table.getTableStyleName());
if (tableStyle!=null) {
sTableColor = tableStyle.getProperty(XMLString.FO_BACKGROUND_COLOR);
}
// Row background
for (int nRow=0; nRow<nRowCount; nRow++) {
StyleWithProperties rowStyle = ofr.getRowStyle(table.getRow(nRow).getStyleName());
if (rowStyle!=null) {
sRowColor[nRow] = rowStyle.getProperty(XMLString.FO_BACKGROUND_COLOR);
}
if (sRowColor[nRow]==null) {
sRowColor[nRow] = sTableColor;
}
if (sRowColor[nRow]!=null) {
bIsColortbl = true;
}
}
// Cell background
for (int nRow=0; nRow<nRowCount; nRow++) {
for (int nCol=0; nCol<nColCount; nCol++) {
StyleWithProperties cellStyle = ofr.getCellStyle(Misc.getAttribute(table.getCell(nRow,nCol),XMLString.TABLE_STYLE_NAME));
if (cellStyle!=null) {
sCellColor[nRow][nCol] = cellStyle.getProperty(XMLString.FO_BACKGROUND_COLOR);
if (sCellColor[nRow][nCol]!=null) {
bIsColortbl = true;
if (sCellColor[nRow][nCol].equals(sRowColor[nRow])) {
// Avoid redundant cell background
sCellColor[nRow][nCol] = null;
}
}
}
}
}
}
// Step 7: Read column style information
sColumnWidth = new String[nColCount];
for (int nCol=0; nCol<nColCount; nCol++) {
StyleWithProperties colStyle
= ofr.getColumnStyle(table.getCol(nCol).getStyleName());
if (colStyle!=null) {
sColumnWidth[nCol]
= colStyle.getProperty(XMLString.STYLE_COLUMN_WIDTH);
}
if (sColumnWidth[nCol]==null) { // Emergency! should never happen!
sColumnWidth[nCol]="2cm";
}
}
// Step 8: Identify longtable, supertabular or tabulary
bIsLongtable = false; bIsSupertabular = false; bIsTabulary = false;
if (!table.isSubTable() && !bIsInTable) {
String sStyleName = table.getTableStyleName();
StyleWithProperties style = ofr.getTableStyle(sStyleName);
boolean bMayBreak = style==null ||
!"false".equals(style.getProperty(XMLString.STYLE_MAY_BREAK_BETWEEN_ROWS));
if (config.useLongtable() && bMayBreak && bAllowPageBreak) {
bIsLongtable = true;
}
else if (config.useSupertabular() && bMayBreak && bAllowPageBreak) {
bIsSupertabular = true;
}
else if (!bIsSimple && config.useTabulary()) {
bIsTabulary = true;
}
}
}
/** is this a longtable? */
public boolean isLongtable() { return bIsLongtable; }
/** is this a supertabular? */
public boolean isSupertabular() { return bIsSupertabular; }
/** is this a tabulary? */
public boolean isTabulary() { return bIsTabulary; }
/** is this a colortbl? */
public boolean isColortbl() { return bIsColortbl; }
/** is this a simple table (lcr columns rather than p{})? */
public boolean isSimple() { return bIsSimple; }
/**
* <p>Create table environment based on table style.</p>
* <p>Returns eg. "\begin{longtable}{m{2cm}|m{4cm}}", "\end{longtable}".</p>
*/
public void applyTableStyle(BeforeAfter ba, BeforeAfter baAlign) {
// Read formatting info from table style
// Only supported properties are alignment and may-break-between-rows.
String sStyleName = table.getTableStyleName();
StyleWithProperties style = ofr.getTableStyle(sStyleName);
char cAlign = 'c';
if (style!=null && !table.isSubTable()) {
String s = style.getProperty(XMLString.TABLE_ALIGN);
if ("left".equals(s)) { cAlign='l'; }
else if ("right".equals(s)) { cAlign='r'; }
}
String sAlign="center";
switch (cAlign) {
case 'c': sAlign="center"; break;
case 'r': sAlign="flushright"; break;
case 'l': sAlign="flushleft";
}
// Create table alignment (for supertabular, tabular and tabulary)
if (!bIsLongtable && !table.isSubTable()) {
baAlign.add("\\begin{"+sAlign+"}\n","\\end{"+sAlign+"}\n");
}
// Create table declaration
if (bIsLongtable) {
ba.add("\\begin{longtable}["+cAlign+"]", "\\end{longtable}");
}
else if (bIsSupertabular) {
ba.add("\\begin{supertabular}","\\end{supertabular}");
}
else if (bIsTabulary) {
ba.add("\\begin{tabulary}{"+table.getTableWidth()+"}","\\end{tabulary}");
}
else if (!table.isSubTable()) {
ba.add("\\begin{tabular}","\\end{tabular}");
}
else { // subtables should occupy the entire width, including padding!
ba.add("\\hspace*{-\\tabcolsep}\\begin{tabular}",
"\\end{tabular}\\hspace*{-\\tabcolsep}");
}
// columns
ba.add("{","");
if (bGlobalVBorder[0]) { ba.add("|",""); }
int nColCount = table.getColCount();
for (int nCol=0; nCol<nColCount; nCol++){
if (bIsSimple) {
ba.add(Character.toString(cGlobalAlign[nCol]),"");
}
else if (!bIsTabulary) {
// note: The column width in OOo includes padding, which we subtract
ba.add("m{"+Misc.add(sColumnWidth[nCol],"-0.2cm")+"}","");
}
else {
ba.add("J","");
}
if (bGlobalVBorder[nCol+1]) { ba.add("|",""); }
}
ba.add("}","");
}
/** <p>Create interrow material</p> */
public String getInterrowMaterial(int nRow) {
int nColCount = table.getColCount();
int nCount = 0;
for (int nCol=0; nCol<nColCount; nCol++) {
if (bHBorder[nRow][nCol]) { nCount++; }
}
if (nCount==0) { // no borders at this row
return "";
}
else if (nCount==nColCount) { // complete set of borders
return "\\hline";
}
else { // individual borders for each column
StringBuffer buf = new StringBuffer();
buf.append("\\hhline{");
for (int nCol=0; nCol<nColCount; nCol++) {
if (bHBorder[nRow][nCol]) { buf.append("-"); }
else { buf.append("~"); }
}
buf.append("}");
/* TODO: hhline.sty should be optional, and i not used, do as before:
boolean bInCline = false;
for (int nCol=0; nCol<nColCount; nCol++) {
if (bInCline && !bHBorder[nRow][nCol]) { // close \cline
buf.append(nCol).append("}");
bInCline = false;
}
else if (!bInCline && bHBorder[nRow][nCol]) { // open \cline
buf.append("\\cline{").append(nCol+1).append("-");
bInCline = true;
}
}
if (bInCline) { buf.append(nColCount).append("}"); }
*/
return buf.toString();
}
}
/** <p>Get material to put before a table row (background color)
*/
public void applyRowStyle(int nRow, BeforeAfter ba, Context context) {
palette.getColorCv().applyBgColor("\\rowcolor",sRowColor[nRow],ba,context);
}
/** Get material to put before and after a table cell.
* In case of columnspan or different borders this will contain a \multicolumn command.
*/
public void applyCellStyle(int nRow, int nCol, BeforeAfter ba, Context context) {
Node cell = table.getCell(nRow,nCol);
int nColSpan = Misc.getPosInteger(Misc.getAttribute(cell,
XMLString.TABLE_NUMBER_COLUMNS_SPANNED),1);
// Construct column declaration as needed
boolean bNeedLeft = (nCol==0) && (bVBorder[nRow][0]!=bGlobalVBorder[0]);
boolean bNeedRight = bVBorder[nRow][nCol+1]!=bGlobalVBorder[nCol+1];
boolean bNeedAlign = bIsSimple && cGlobalAlign[nCol]!=cAlign[nRow][nCol];
// calculate column width
String sTotalColumnWidth = sColumnWidth[nCol];
for (int i=nCol+1; i<nCol+nColSpan; i++) {
sTotalColumnWidth = Misc.add(sTotalColumnWidth,sColumnWidth[i]);
}
sTotalColumnWidth = Misc.add(sTotalColumnWidth,"-0.2cm");
if (bNeedAlign || bNeedLeft || bNeedRight || nColSpan>1) {
ba.add("\\multicolumn{"+nColSpan+"}{","");
if (nCol==0 && bVBorder[nRow][0]) { ba.add("|",""); }
if (bIsSimple) {
ba.add(Character.toString(cAlign[nRow][nCol]),"");
}
else {
ba.add("m{"+sTotalColumnWidth+"}","");
}
if (bVBorder[nRow][nCol+nColSpan]) { ba.add("|",""); }
ba.add("}{","}");
}
palette.getColorCv().applyBgColor("\\cellcolor",sCellColor[nRow][nCol],ba,context);
}
}

View file

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- clean.xml
This is a sample configuration file for Writer2LaTeX.
The options are set to produce a clean
LaTeX file from an arbitrary Writer document
- at the expense of loss of formatting.
An even cleaner LaTeX file is produced with ultraclean.xml.
-->
<config>
<option name="documentclass" value="article" />
<option name="backend" value="generic" />
<option name="inputencoding" value="ascii" />
<option name="multilingual" value="false" />
<option name="use_ooomath" value="false" />
<option name="use_color" value="true" />
<option name="use_colortbl" value="false" />
<option name="use_geometry" value="true" />
<option name="use_fancyhdr" value="false" />
<option name="use_hyperref" value="true" />
<option name="use_caption" value="true" />
<option name="use_endnotes" value="false" />
<option name="use_bibtex" value="true" />
<option name="bibtex_style" value="plain" />
<option name="formatting" value="ignore_most" />
<option name="page_formatting" value="convert_geometry" />
<option name="ignore_empty_paragraphs" value="true" />
<option name="ignore_hard_page_breaks" value="false" />
<option name="ignore_hard_line_breaks" value="false" />
<option name="ignore_double_spaces" value="true" />
<option name="debug" value="false" />
<heading-map max-level="5">
<heading-level-map writer-level="1" name="section" level="1" />
<heading-level-map writer-level="2" name="subsection" level="2" />
<heading-level-map writer-level="3" name="subsubsection" level="3" />
<heading-level-map writer-level="4" name="paragraph" level="4" />
<heading-level-map writer-level="5" name="subparagraph" level="5" />
</heading-map>
<custom-preamble />
<!-- Style maps: These rules defines how styles in OOo are mapped to LaTeX code.
A number of predefined Writer styles are converted -->
<!-- "Title" is mapped to \maketitle. If the user chooses to export meta data,
the author and date will be inserted automatically -->
<style-map name="Title" class="paragraph" before="\title{" after="}&#10;\maketitle" line-break="false" />
<!-- "Quotations" is mapped to a quotation environment -->
<style-map name="Quotations" family="paragraph-block" next="Quotations" before="\begin{quotation}" after="\end{quotation}" />
<style-map name="Quotations" family="paragraph" before="" after="" />
<!-- Preformatted Text is mapped to a verbatim environment
Note the attribute verbatim, which instructs OOo to output the content
verbatim (characters not available in the inputencoding will be replaced
by question marks; other content will be lost). -->
<style-map name="Preformatted Text" family="paragraph-block" next="Preformatted Text" before="\begin{verbatim}" after="\end{verbatim}" />
<style-map name="Preformatted Text" family="paragraph" before="" after="" verbatim="true" />
<!-- "Horizontal line" is mapped to a \hrule -->
<style-map name="Horizontal Line" family="paragraph" before="" after="&#10;\begin{center}\hrule\end{center}" />
<!-- "Emphasis" text style is mapped to \emph -->
<style-map name="Emphasis" family="text" before="\emph{" after="}" />
<!-- "Strong Emphasis" text style is mapped to \textbf -->
<style-map name="Strong Emphasis" family="text" before="\textbf{" after="}" />
<!-- "Teletype" text style is mapped to \texttt -->
<style-map name="Teletype" family="text" before="\texttt{" after="}" />
<!-- "List Heading" and "List Contents" are mapped to a description environment -->
<style-map name="List Heading" family="paragraph-block" next="List Heading;List Contents" before="\begin{description}" after="\end{description}"/>
<style-map name="List Heading" family="paragraph" before="\item[" after="]" line-break="false" />
<style-map name="List Contents" family="paragraph" before="" after="" />
</config>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- default.xml
This is a sample configuration file for Writer2LaTeX.
It sets no options, thus using defaults everywhere
-->
<config/>

View file

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- pdfprint.xml
This is a sample configuration file for Writer2LaTeX.
The output will be for pdfTeX, using all the packages pifont, ifsym,
wasysym, eurosym, hyperref, endnotes and ulem. -->
<config>
<option name="documentclass" value="article" />
<option name="backend" value="pdftex" />
<option name="inputencoding" value="ascii" />
<option name="use_geometry" value="true" />
<option name="use_fancyhdr" value="true" />
<option name="use_ooomath" value="true" />
<option name="use_pifont" value="true" />
<option name="use_ifsym" value="true" />
<option name="use_wasysym" value="true" />
<option name="use_eurosym" value="true" />
<option name="use_color" value="true" />
<option name="use_colortbl" value="true" />
<option name="use_hyperref" value="true" />
<option name="use_endnotes" value="true" />
<option name="use_ulem" value="true" />
<option name="use_lastpage" value="true" />
<option name="formatting" value="convert_all" />
<option name="page_formatting" value="convert_all" />
<option name="ignore_empty_paragraphs" value="false" />
<option name="ignore_hard_page_breaks" value="false" />
<option name="ignore_hard_line_breaks" value="false" />
<option name="ignore_double_spaces" value="false" />
<option name="debug" value="false" />
<heading-map max-level="5">
<heading-level-map writer-level="1" name="section" level="1" />
<heading-level-map writer-level="2" name="subsection" level="2" />
<heading-level-map writer-level="3" name="subsubsection" level="3" />
<heading-level-map writer-level="4" name="paragraph" level="4" />
<heading-level-map writer-level="5" name="subparagraph" level="5" />
</heading-map>
<!-- We add \sloppy to avoid overful hboxes. To get better results,
this should be removed and overful hboxes fixed by hand. -->
<custom-preamble>\sloppy</custom-preamble>
</config>

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- pdfscreen.xml
This is a sample configuration file for Writer2LaTeX.
The output will be for pdfTeX, using all the packages pifont, ifsym,
wasysym, eurosym, hyperref, endnotes and ulem.
The package pdfscreen.sty will be loaded to create a pdf file
suitable for viewing on screen. -->
<config>
<option name="documentclass" value="article" />
<option name="backend" value="pdftex" />
<option name="inputencoding" value="ascii" />
<option name="use_ooomath" value="true" />
<option name="use_pifont" value="true" />
<option name="use_ifsym" value="true" />
<option name="use_wasysym" value="true" />
<option name="use_bbding" value="false" />
<option name="use_eurosym" value="true" />
<option name="use_color" value="true" />
<option name="use_colortbl" value="true" />
<option name="use_hyperref" value="true" />
<option name="use_endnotes" value="true" />
<option name="use_ulem" value="true" />
<option name="use_lastpage" value="true" />
<option name="formatting" value="convert_all" />
<option name="page_formatting" value="ignore_all" />
<option name="ignore_empty_paragraphs" value="false" />
<option name="ignore_hard_page_breaks" value="false" />
<option name="ignore_hard_line_breaks" value="false" />
<option name="ignore_double_spaces" value="true" />
<option name="debug" value="false" />
<heading-map max-level="5">
<heading-level-map writer-level="1" name="section" level="1" />
<heading-level-map writer-level="2" name="subsection" level="2" />
<heading-level-map writer-level="3" name="subsubsection" level="3" />
<heading-level-map writer-level="4" name="paragraph" level="4" />
<heading-level-map writer-level="5" name="subparagraph" level="5" />
</heading-map>
<!-- load pdfscreen.sty with suitable options. (Note that export of page
formatting is disabled above; pdfscreen.sty takes care of page setup.)
The lines are relatively short, so we add \sloppy to avoid overful hboxes.
-->
<custom-preamble>\usepackage{palatino}
\usepackage[bluelace,screen,nopanel,sectionbreak]{pdfscreen}
%\hypersetup{pdfpagemode={FullScreen}}
\margins{0.5in}{0.5in}{0.5in}{0.5in}
\screensize{6in}{8in}
\sloppy</custom-preamble>
</config>

View file

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- ultraclean.xml
This is a sample configuration file for Writer2LaTeX.
The options are set to produce the cleanest possible
LaTeX file from an arbitrary Writer document
- by removing practically all formatting.
-->
<config>
<option name="documentclass" value="article" />
<option name="backend" value="generic" />
<option name="inputencoding" value="ascii" />
<option name="multilingual" value="false" />
<option name="use_ooomath" value="false" />
<option name="use_color" value="false" />
<option name="use_colortbl" value="false" />
<option name="use_geometry" value="false" />
<option name="use_fancyhdr" value="false" />
<option name="use_hyperref" value="false" />
<option name="use_caption" value="true" />
<option name="use_endnotes" value="false" />
<option name="use_bibtex" value="true" />
<option name="bibtex_style" value="plain" />
<option name="formatting" value="ignore_all" />
<option name="page_formatting" value="ignore_all" />
<option name="ignore_empty_paragraphs" value="true" />
<option name="ignore_hard_page_breaks" value="true" />
<option name="ignore_hard_line_breaks" value="true" />
<option name="ignore_double_spaces" value="true" />
<option name="debug" value="false" />
<heading-map max-level="5">
<heading-level-map writer-level="1" name="section" level="1" />
<heading-level-map writer-level="2" name="subsection" level="2" />
<heading-level-map writer-level="3" name="subsubsection" level="3" />
<heading-level-map writer-level="4" name="paragraph" level="4" />
<heading-level-map writer-level="5" name="subparagraph" level="5" />
</heading-map>
<custom-preamble />
<!-- Style maps: These rules defines how styles in OOo are mapped to LaTeX code.
A number of predefined Writer styles are converted -->
<!-- "Title" is mapped to \maketitle. If the user chooses to export meta data,
the author and date will be inserted automatically -->
<style-map name="Title" class="paragraph" before="\title{" after="}&#10;\maketitle" line-break="false" />
<!-- "Quotations" is mapped to a quotation environment -->
<style-map name="Quotations" family="paragraph-block" next="Quotations" before="\begin{quotation}" after="\end{quotation}" />
<style-map name="Quotations" family="paragraph" before="" after="" />
<!-- Preformatted Text is mapped to a verbatim environment
Note the attribute verbatim, which instructs OOo to output the content
verbatim (characters not available in the inputencoding will be replaced
by question marks; other content will be lost). -->
<style-map name="Preformatted Text" family="paragraph-block" next="Preformatted Text" before="\begin{verbatim}" after="\end{verbatim}" />
<style-map name="Preformatted Text" family="paragraph" before="" after="" verbatim="true" />
<!-- "Horizontal line" is mapped to a \hrule -->
<style-map name="Horizontal Line" family="paragraph" before="" after="&#10;\begin{center}\hrule\end{center}" />
<!-- "Emphasis" text style is mapped to \emph -->
<style-map name="Emphasis" family="text" before="\emph{" after="}" />
<!-- "Strong Emphasis" text style is mapped to \textbf -->
<style-map name="Strong Emphasis" family="text" before="\textbf{" after="}" />
<!-- "Teletype" text style is mapped to \texttt -->
<style-map name="Teletype" family="text" before="\texttt{" after="}" />
<!-- "List Heading" and "List Contents" are mapped to a description environment -->
<style-map name="List Heading" family="paragraph-block" next="List Heading;List Contents" before="\begin{description}" after="\end{description}"/>
<style-map name="List Heading" family="paragraph" before="\item[" after="]" line-break="false" />
<style-map name="List Contents" family="paragraph" before="" after="" />
</config>

View file

@ -0,0 +1,697 @@
/************************************************************************
*
* ClassicI18n.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2009 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2009-02-16)
*
*/
package writer2latex.latex.i18n;
import java.io.*;
import java.util.Hashtable;
import java.util.Stack;
//import java.util.Vector;
//import java.util.Enumeration;
import java.util.Iterator;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;
import writer2latex.util.CSVList;
import writer2latex.office.*;
import writer2latex.latex.LaTeXConfig;
import writer2latex.latex.LaTeXDocumentPortion;
import writer2latex.latex.ConverterPalette;
import writer2latex.latex.util.BeforeAfter;
/** This class (and the helpers in the same package) takes care of i18n in
* Writer2LaTeX. In classic LaTeX, i18n is a mixture of inputencodings, fontencodings
* and babel languages. The class ClassicI18n thus manages these, and in particular
* implements a Unicode->LaTeX translation that can handle different
* inputencodings and fontencodings.
* The translation is table driven, using symbols.xml (embedded in the jar)
* Various sections of symbols.xml handles different cases:
* <ul>
* <li>common symbols in various font encodings such as T1, T2A, LGR etc.</li>
* <li>input encodings such as ISO-8859-1 (latin-1), ISO-8859-7 (latin/greek) etc.</li>
* <li>additional symbol fonts such as wasysym, dingbats etc.</li>
* <li>font-specific symbols, eg. for 8-bit fonts/private use area</li>
* </ul>
* The class uses the packages inputenc, fontenc, babel, tipa, bbding,
* ifsym, pifont, eurosym, amsmath, wasysym, amssymb, amsfonts and textcomp
* in various combinations depending on the configuration.
*/
public class ClassicI18n extends I18n {
// **** Static data and methods: Inputencodings ****
public static final int ASCII = 0;
public static final int LATIN1 = 1; // ISO Latin 1 (ISO-8859-1)
public static final int LATIN2 = 2; // ISO Latin 1 (ISO-8859-1)
public static final int ISO_8859_7 = 3; // ISO latin/greek
public static final int CP1250 = 4; // Microsoft Windows Eastern European
public static final int CP1251 = 5; // Microsoft Windows Cyrillic
public static final int KOI8_R = 6; // Latin/russian
public static final int UTF8 = 7; // UTF-8
// Read an inputencoding from a string
public static final int readInputenc(String sInputenc) {
if ("ascii".equals(sInputenc)) return ASCII;
else if ("latin1".equals(sInputenc)) return LATIN1;
else if ("latin2".equals(sInputenc)) return LATIN2;
else if ("iso-8859-7".equals(sInputenc)) return ISO_8859_7;
else if ("cp1250".equals(sInputenc)) return CP1250;
else if ("cp1251".equals(sInputenc)) return CP1251;
else if ("koi8-r".equals(sInputenc)) return KOI8_R;
else if ("utf8".equals(sInputenc)) return UTF8;
else return ASCII; // unknown = ascii
}
// Return the LaTeX name of an inputencoding
public static final String writeInputenc(int nInputenc) {
switch (nInputenc) {
case ASCII : return "ascii";
case LATIN1 : return "latin1";
case LATIN2 : return "latin2";
case ISO_8859_7 : return "iso-8859-7";
case CP1250 : return "cp1250";
case CP1251 : return "cp1251";
case KOI8_R : return "koi8-r";
case UTF8 : return "utf8";
default : return "???";
}
}
// Return the java i18n name of an inputencoding
public static final String writeJavaEncoding(int nInputenc) {
switch (nInputenc) {
case ASCII : return "ASCII";
case LATIN1 : return "ISO8859_1";
case LATIN2 : return "ISO8859_2";
case ISO_8859_7 : return "ISO8859_7";
case CP1250 : return "Cp1250";
case CP1251 : return "Cp1251";
case KOI8_R : return "KOI8_R";
case UTF8 : return "UTF-8";
default : return "???";
}
}
// **** Static data and methods: Fontencodings ****
private static final int T1_ENC = 1;
private static final int T2A_ENC = 2;
private static final int T3_ENC = 4;
private static final int LGR_ENC = 8;
private static final int ANY_ENC = 15;
// read set of font encodings from a string
public static final int readFontencs(String sFontencs) {
sFontencs = sFontencs.toUpperCase();
if ("ANY".equals(sFontencs)) return ANY_ENC;
int nFontencs = 0;
if (sFontencs.indexOf("T1")>=0) nFontencs+=T1_ENC;
if (sFontencs.indexOf("T2A")>=0) nFontencs+=T2A_ENC;
if (sFontencs.indexOf("T3")>=0) nFontencs+=T3_ENC;
if (sFontencs.indexOf("LGR")>=0) nFontencs+=LGR_ENC;
return nFontencs;
}
// return string representation of a single font encoding
/*private static final String writeFontenc(int nFontenc) {
switch (nFontenc) {
case T1_ENC: return "T1";
case T2A_ENC: return "T2A";
case T3_ENC: return "T3";
case LGR_ENC: return "LGR";
}
return null;
}*/
// check that a given set of font encodings contains a specific font encoding
private static final boolean supportsFontenc(int nFontencs, int nFontenc) {
return (nFontencs & nFontenc) != 0;
}
// get one fontencoding from a set of fontencodings
private static final int getFontenc(int nFontencs) {
if (supportsFontenc(nFontencs,T1_ENC)) return T1_ENC;
if (supportsFontenc(nFontencs,T2A_ENC)) return T2A_ENC;
if (supportsFontenc(nFontencs,T3_ENC)) return T3_ENC;
if (supportsFontenc(nFontencs,LGR_ENC)) return LGR_ENC;
return 0;
}
// get the font encoding for a specific iso language
private static final int getFontenc(String sLang) {
// Greek uses "local greek" encoding
if ("el".equals(sLang)) return LGR_ENC;
// Russian, ukrainian, bulgarian and serbian uses T2A encoding
else if ("ru".equals(sLang)) return T2A_ENC;
else if ("uk".equals(sLang)) return T2A_ENC;
else if ("bg".equals(sLang)) return T2A_ENC;
else if ("sr".equals(sLang)) return T2A_ENC;
// Other languages uses T1 encoding
else return T1_ENC;
}
// return cs for a fontencoding
private static final String getFontencCs(int nFontenc) {
switch (nFontenc) {
case T1_ENC: return "\\textlatin"; // requires babel
case T2A_ENC: return "\\textcyrillic"; // requires babel with russian, bulgarian or ukrainian option
case T3_ENC: return "\\textipa"; // requires tipa.sty
case LGR_ENC: return "\\textgreek"; // requires babel with greek option
default: return null;
}
}
// End of static part of I18n!
// **** Global variables ****
private Hashtable babelLanguages; // mappings iso->babel language
// Unicode translation
private Hashtable tableSet; // all tables
private UnicodeTable table; // currently active table (top of stack)
private Stack tableStack; // stack of active tables
private UnicodeStringParser ucparser; // Unicode string parser
// Collected data
private int nDefaultFontenc; // Fontenc for the default language
private boolean bT2A = false; // Do we use cyrillic letters?
private boolean bGreek = false; // Do we use greek letters?
private boolean bPolytonicGreek = false; // Do we use polytonic greek letters?
// **** Constructors ****
/** Construct a new ClassicI18n as ConverterHelper
* @param ofr the OfficeReader to get language information from
* @param config the configuration which determines the symbols to use
* @param palette the ConverterPalette (unused)
*/
public ClassicI18n(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
// We don't need the palette and the office reader is only used to
// identify the default language
// Set up table for iso->babel translation
prepareBabelLanguages();
nDefaultFontenc = getFontenc(sDefaultLanguage);
// Unicode stuff
ucparser = new UnicodeStringParser();
String sSymbols="ascii"; // always load common symbols
if (config.getInputencoding()!=ASCII) {
sSymbols+="|"+writeInputenc(config.getInputencoding());
}
if (config.useWasysym()) sSymbols+="|wasysym";
if (config.useBbding()) sSymbols+="|bbding";
if (config.useIfsym()) sSymbols+="|ifsym";
if (config.usePifont()) sSymbols+="|dingbats";
if (config.useEurosym()) sSymbols+="|eurosym";
if (config.useTipa()) sSymbols+="|tipa";
tableSet = new Hashtable();
UnicodeTableHandler handler=new UnicodeTableHandler(tableSet, sSymbols);
SAXParserFactory factory=SAXParserFactory.newInstance();
InputStream is = this.getClass().getResourceAsStream("symbols.xml");
try {
SAXParser saxParser=factory.newSAXParser();
saxParser.parse(is,handler);
}
catch (Throwable t){
System.err.println("Oops - Unable to read symbols.xml");
t.printStackTrace();
}
// put root table at top of stack
tableStack = new Stack();
tableStack.push((UnicodeTable) tableSet.get("root"));
table = (UnicodeTable) tableSet.get("root");
}
/** Construct a new I18n for general use
* @param config the configuration which determines the symbols to use
*/
public ClassicI18n(LaTeXConfig config) {
this (null, config, null);
}
/** Add declarations to the preamble to load the required packages
* @param pack usepackage declarations
* @param decl other declarations
*/
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
pack.append("\\usepackage[")
.append(writeInputenc(config.getInputencoding()))
.append("]{inputenc}").nl();
// usepackage fontenc
CSVList fontencs = new CSVList(',');
if (bT2A) { fontencs.addValue("T2A"); }
if (bGreek) { fontencs.addValue("LGR"); }
if (config.useTipa()) { fontencs.addValue("T3"); }
fontencs.addValue("T1");
pack.append("\\usepackage[").append(fontencs.toString())
.append("]{fontenc}").nl();
// usepackage babel
// If the document contains "anonymous" greek letters we need greek in any case
// If the document contains "anonymous cyrillic letters we need one of the
// languages russian, ukrainian or bulgarian
if (greek() && !languages.contains("el")) languages.add("el");
if (cyrillic() && !(languages.contains("ru") || languages.contains("uk") || languages.contains("bg"))) {
languages.add("ru");
}
// Load babel with the used languages
CSVList babelopt = new CSVList(",");
Iterator langiter = languages.iterator();
while (langiter.hasNext()) {
String sLang = (String) langiter.next();
if (!sLang.equals(sDefaultLanguage)) {
if ("el".equals(sLang) && this.polytonicGreek()) {
babelopt.addValue("polutonikogreek");
}
else {
babelopt.addValue(getBabelLanguage(sLang));
}
}
}
// The default language must be the last one
if (sDefaultLanguage!=null) {
if ("el".equals(sDefaultLanguage) && this.polytonicGreek()) {
babelopt.addValue("polutonikogreek");
}
else {
babelopt.addValue(getBabelLanguage(sDefaultLanguage));
}
}
if (!babelopt.isEmpty()) {
pack.append("\\usepackage[")
.append(babelopt.toString())
.append("]{babel}").nl();
}
// usepackage tipa
if (config.useTipa()) {
pack.append("\\usepackage[noenc]{tipa}").nl()
.append("\\usepackage{tipx}").nl();
}
// usepackage bbding (Has to avoid some nameclashes.)
if (config.useBbding()) {
pack.append("\\usepackage{bbding}").nl()
.append("\\let\\bbCross\\Cross\\let\\Cross\\undefined").nl()
.append("\\let\\bbSquare\\Square\\let\\Square\\undefined").nl()
.append("\\let\\bbTrianbleUp\\TriangleUp\\let\\TriangleUp\\undefined").nl()
.append("\\let\\bbTrianlgeDown\\TriangleDown\\let\\TriangleDown\\undefined").nl();
}
// usepackage ifsym
if (config.useIfsym()) {
pack.append("\\usepackage[geometry,weather,misc,clock]{ifsym}").nl();
}
// usepackage pifont
if (config.usePifont()) { pack.append("\\usepackage{pifont}").nl(); }
// usepackage eurosym
if (config.useEurosym()) { pack.append("\\usepackage{eurosym}").nl(); }
// usepackage amsmath (always!)
pack.append("\\usepackage{amsmath}").nl();
// usepackage wasysym (*must* be loaded between amsmath and amsfonts!)
if (config.useWasysym()) {
pack.append("\\usepackage{wasysym}").nl();
}
// usepackage amssymb, amsfonts, textcomp (always!)
pack.append("\\usepackage{amssymb,amsfonts,textcomp}").nl();
}
/** Apply a language language
* @param style the OOo style to read attributesfrom
* @param bDecl true if declaration form is required
* @param bInherit true if inherited properties should be used
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
*/
public void applyLanguage(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba) {
if (!bAlwaysUseDefaultLang && style!=null) {
String sISOLang = style.getProperty(XMLString.FO_LANGUAGE,bInherit);
if (sISOLang!=null) {
languages.add(sISOLang);
String sLang = getBabelLanguage(sISOLang);
if (sLang!=null) {
if (bDecl) {
ba.add("\\selectlanguage{"+sLang+"}","");
//ba.add("\\begin{otherlanguage}{"+sLang+"}","\\end{otherlanguage}");
}
else {
ba.add("\\foreignlanguage{"+sLang+"}{","}");
}
}
}
}
}
/** Push a font to the font stack
* @param sName the name of the font
*/
public void pushSpecialTable(String sName) {
// If no name is specified we should keep the current table
// Otherwise try to find the table, and use root if it's not available
if (sName!=null) {
table = (UnicodeTable) tableSet.get(sName);
if (table==null) { table = (UnicodeTable) tableSet.get("root"); }
}
tableStack.push(table);
}
/** Pop a font from the font stack
*/
public void popSpecialTable() {
tableStack.pop();
table = (UnicodeTable) tableStack.peek();
}
/** Get the number of characters defined in the current table
* (for informational purposes only)
* @return the number of characters
*/
public int getCharCount() { return table.getCharCount(); }
/** Convert a string of characters into LaTeX
* @param s the source string
* @param bMathMode true if the string should be rendered in math mode
* @param sLang the iso language of the string
* @return the LaTeX string
*/
public String convert(String s, boolean bMathMode, String sLang){
if (!bAlwaysUseDefaultLang && sLang!=null) { languages.add(sLang); }
StringBuffer buf=new StringBuffer();
int nFontenc = bAlwaysUseDefaultLang ? nDefaultFontenc : getFontenc(sLang);
int nLen = s.length();
int i = 0;
int nStart = i;
while (i<nLen) {
ReplacementTrieNode node = stringReplace.get(s,i,nLen);
if (node!=null) {
if (i>nStart) {
convert(s,nStart,i,bMathMode,sLang,buf,nFontenc);
}
boolean bOtherFontenc = !supportsFontenc(node.getFontencs(),nFontenc);
if (bOtherFontenc) {
buf.append(getFontencCs(getFontenc(node.getFontencs()))).append("{");
}
buf.append(node.getLaTeXCode());
if (bOtherFontenc) {
buf.append("}");
}
i += node.getInputLength();
nStart = i;
}
else {
i++;
}
}
if (nStart<nLen) {
convert(s,nStart,nLen,bMathMode,sLang,buf,nFontenc);
}
return buf.toString();
}
private void convert(String s, int nStart, int nEnd, boolean bMathMode, String sLang, StringBuffer buf, int nFontenc) {
int nCurFontenc = nFontenc;
ucparser.reset(table,s,nStart,nEnd);
boolean bProtectDashes = true;
boolean bTempMathMode = false;
while (ucparser.next()) {
char c = ucparser.getChar();
if (bMathMode) {
buf.append(convertMathChar(c,nFontenc));
}
else if (greekMath(c,nFontenc) || (table.hasMathChar(c) && !table.hasTextChar(c))) {
if (!bTempMathMode) { // switch to math mode
buf.append("$");
bTempMathMode = true;
}
buf.append(convertMathChar(c,nFontenc));
bProtectDashes = false;
}
else if (table.hasTextChar(c)) {
if (bTempMathMode) { // switch to text mode
buf.append("$");
bTempMathMode = false;
}
int nFontencs = table.getFontencs(c);
if (supportsFontenc(nFontencs,nCurFontenc)) {
// The text character is valid in the current font encoding
// Note: Change of font encoding is greedy - change?
// Prevent unwanted --- ligatures
if (table.isDashes(c)) {
if (bProtectDashes) { buf.append("{}"); }
bProtectDashes = true;
}
else {
bProtectDashes = false;
}
setFlags(c,nCurFontenc);
if (ucparser.hasCombiningChar()) {
char cc = ucparser.getCombiningChar();
if (supportsFontenc(table.getFontencs(cc),nCurFontenc)) {
buf.append(table.getTextChar(cc)).append("{")
.append(table.getTextChar(c)).append("}");
}
else { // ignore combining char if not valid in this font encoding
buf.append(table.getTextChar(c));
}
}
else {
buf.append(table.getTextChar(c));
}
}
else {
// The text character is valid in another font encoding
bProtectDashes = table.isDashes(c);
int nFontenc1 = getFontenc(nFontencs);
setFlags(c,nFontenc1);
if (nCurFontenc!=nFontenc) { // end "other font encoding"
buf.append("}");
}
if (nFontenc1!=nFontenc) { // start "other font encoding"
buf.append(getFontencCs(nFontenc1)).append("{");
}
if (ucparser.hasCombiningChar()) {
char cc = ucparser.getCombiningChar();
if (supportsFontenc(table.getFontencs(cc),nCurFontenc)) {
buf.append(table.getTextChar(cc)).append("{")
.append(table.getTextChar(c)).append("}");
}
else { // ignore combining char if not valid in this font encoding
buf.append(table.getTextChar(c));
}
}
else {
buf.append(table.getTextChar(c));
}
nCurFontenc = nFontenc1;
}
}
else {
buf.append(notFound(c,nCurFontenc));
}
}
if (bTempMathMode) { // turn of math mode
buf.append("$");
}
if (nCurFontenc!=nFontenc) { // end unfinished "other font encoding"
buf.append("}");
}
}
// convert a single math character
private String convertMathChar(char c, int nFontenc) {
if (table.hasMathChar(c)) {
return table.getMathChar(c);
}
else if (table.hasTextChar(c)) { // use text mode as a fallback
int nFontencs = table.getFontencs(c);
if (supportsFontenc(nFontencs,nFontenc)) {
// The text character is valid in the current font encoding
setFlags(c,nFontenc);
if (table.getCharType(c)==UnicodeCharacter.COMBINING) {
return "\\text{" + table.getTextChar(c) +"{}}";
}
else {
return "\\text{" + table.getTextChar(c) +"}";
}
}
else {
// The text character is valid in another font encoding
int nFontenc1 = getFontenc(nFontencs);
setFlags(c,nFontenc1);
if (table.getCharType(c)==UnicodeCharacter.COMBINING) {
return "\\text{" + getFontencCs(nFontenc1) + "{" + table.getTextChar(c) +"{}}}";
}
else {
return "\\text{" + getFontencCs(nFontenc1) + "{" + table.getTextChar(c) +"}}";
}
}
}
else {
return "\\text{" + notFound(c,nFontenc) + "}";
}
}
// Missing symbol
private String notFound(char c,int nFontenc) {
//String sErrorMsg = "[Warning: Missing symbol " + Integer.toHexString(c).toUpperCase() +"]";
String sErrorMsg = "["+Integer.toHexString(c).toUpperCase() +"?]";
if (nFontenc==T1_ENC) return sErrorMsg;
else return "\\textlatin{"+sErrorMsg+"}";
}
// Convert a single character
/*private String convert(char c, boolean bMathMode, String sLang){
int nFontenc = bAlwaysUseDefaultLang ? nDefaultFontenc : getFontenc(sLang);
if (bMathMode) {
return convertMathChar(c,nFontenc);
}
else if (greekMath(c,nFontenc) || (table.hasMathChar(c) && !table.hasTextChar(c))) {
return "$" + convertMathChar(c,nFontenc) + "$";
}
else if (table.hasTextChar(c)) {
int nFontencs = table.getFontencs(c);
if (supportsFontenc(nFontencs,nFontenc)) {
// The text character is valid in the current font encoding
setFlags(c,nFontenc);
if (table.getCharType(c)==UnicodeCharacter.COMBINING) {
return table.getTextChar(c)+"{}";
}
else {
return table.getTextChar(c);
}
}
else {
// The text character is valid in another font encoding
int nFontenc1 = getFontenc(nFontencs);
setFlags(c,nFontenc1);
if (table.getCharType(c)==UnicodeCharacter.COMBINING) {
return getFontencCs(nFontenc1) + "{" + table.getTextChar(c) +"{}}";
}
else {
return getFontencCs(nFontenc1) + "{" + table.getTextChar(c) +"}";
}
}
}
else {
return notFound(c,nFontenc);
}
}*/
// **** Languages ****
// Convert iso language to babel language
// todo: include iso country
// todo: support automatic choice of inputenc (see comments)?
private String getBabelLanguage(String sLang) {
if (babelLanguages.containsKey(sLang)) {
return (String) babelLanguages.get(sLang);
}
else {
return "english"; // interpret unknown languages as English
}
}
private void prepareBabelLanguages() {
babelLanguages = new Hashtable();
babelLanguages.put("en", "english"); // latin1
babelLanguages.put("bg", "bulgarian"); // cp1251?
babelLanguages.put("cs", "czech"); // latin2
babelLanguages.put("da", "danish"); // latin1
babelLanguages.put("de", "ngerman"); // latin1
babelLanguages.put("el", "greek"); // iso-8859-7
babelLanguages.put("es", "spanish"); // latin1
babelLanguages.put("fi", "finnish"); // latin1 (latin9?)
babelLanguages.put("fr", "french"); // latin1 (latin9?)
babelLanguages.put("ga", "irish"); // latin1
babelLanguages.put("hr", "croatian"); // latin2
babelLanguages.put("hu", "magyar"); // latin2
babelLanguages.put("la", "latin"); // ascii
babelLanguages.put("is", "icelandic"); // latin1
babelLanguages.put("it", "italian"); // latin1
babelLanguages.put("nl", "dutch"); // latin1
babelLanguages.put("no", "norsk"); // latin1
babelLanguages.put("pl", "polish"); // latin2
babelLanguages.put("pt", "portuges"); // latin1
babelLanguages.put("ro", "romanian"); // latin2
babelLanguages.put("ru", "russian"); // cp1251?
babelLanguages.put("sk", "slovak"); // latin2
babelLanguages.put("sl", "slovene"); // latin2
babelLanguages.put("sr", "serbian"); // cp1251?
babelLanguages.put("sv", "swedish"); // latin1
babelLanguages.put("tr", "turkish");
babelLanguages.put("uk", "ukrainian"); // cp1251?
}
// **** Helpers to collect various information ****
// Did we use cyrillic?
private boolean cyrillic() { return bT2A; }
// Did we use greek?
private boolean greek() { return bGreek; }
// Did we use polytonic greek?
private boolean polytonicGreek() { return bPolytonicGreek; }
// Outside greek text, greek letters may be rendered in math mode,
// if the user requires that in the configuration.
private boolean greekMath(char c, int nFontenc) {
return bGreekMath && nFontenc!=LGR_ENC && table.getFontencs(c)==LGR_ENC;
}
// Set cyrillic and greek flags
private void setFlags(char c, int nFontenc) {
if ((c>='\u1F00') && (c<='\u1FFF')) bPolytonicGreek = true;
if (nFontenc==LGR_ENC) bGreek = true;
if (nFontenc==T2A_ENC) bT2A = true;
}
}

View file

@ -0,0 +1,118 @@
/************************************************************************
*
* I18n.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2009 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2009-02-16)
*
*/
package writer2latex.latex.i18n;
import java.util.HashSet;
import writer2latex.office.*;
import writer2latex.latex.LaTeXConfig;
import writer2latex.latex.LaTeXDocumentPortion;
import writer2latex.latex.ConverterPalette;
import writer2latex.latex.util.BeforeAfter;
/** This abstract class takes care of i18n in the LaTeX export.
* Since i18n is handled quite differently in LaTeX "Classic"
* and XeTeX, we use two different classes
*/
public abstract class I18n {
// **** Global variables ****
// Configuration items
protected LaTeXConfig config;
protected ReplacementTrie stringReplace;
protected boolean bGreekMath; // Use math mode for greek letters
protected boolean bAlwaysUseDefaultLang; // Ignore sLang parameter to convert()
// Collected data
protected String sDefaultLanguage; // The default iso language to use
protected HashSet languages = new HashSet(); // All languages used
// **** Constructors ****
/** Construct a new I18n as ConverterHelper
* @param ofr the OfficeReader to get language information from
* @param config the configuration which determines the symbols to use
* @param palette the ConverterPalette (unused)
*/
public I18n(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
// We don't need the palette and the office reader is only used to
// identify the default language
// Set up config items
this.config = config;
stringReplace = config.getStringReplace();
bGreekMath = config.greekMath();
bAlwaysUseDefaultLang = !config.multilingual();
// Default language
if (ofr!=null) {
if (config.multilingual()) {
// Read the default language from the default paragraph style
StyleWithProperties style = ofr.getDefaultParStyle();
if (style!=null) {
sDefaultLanguage = style.getProperty(XMLString.FO_LANGUAGE);
}
}
else {
// the most common language is the only language
sDefaultLanguage = ofr.getMajorityLanguage();
}
}
if (sDefaultLanguage==null) { sDefaultLanguage="en"; }
}
/** Add declarations to the preamble to load the required packages
* @param pack usepackage declarations
* @param decl other declarations
*/
public abstract void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl);
/** Apply a language language
* @param style the OOo style to read attributesfrom
* @param bDecl true if declaration form is required
* @param bInherit true if inherited properties should be used
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
*/
public abstract void applyLanguage(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba);
/** Push a font to the font stack
* @param sName the name of the font
*/
public abstract void pushSpecialTable(String sName);
/** Pop a font from the font stack
*/
public abstract void popSpecialTable();
/** Convert a string of characters into LaTeX
* @param s the source string
* @param bMathMode true if the string should be rendered in math mode
* @param sLang the iso language of the string
* @return the LaTeX string
*/
public abstract String convert(String s, boolean bMathMode, String sLang);
}

View file

@ -0,0 +1,16 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>The package writer2latex.xhtml.i18n</title>
</head>
<body>
<p>This package takes care of i18n for LaTeX.</p>
<p>In LaTeX, i18n is a mixture of inputencodings, fontencodings
and babel languages. In particualar, the package provides a Unicode->LaTeX
translation that can handle different inputencodings and fontencodings.</p>
<p>The pacakge could (with modification) in theory be used in other programs
that convert unicode to LaTeX.</p>
</body>
</html>

View file

@ -0,0 +1,56 @@
/************************************************************************
*
* ReplacementTrie.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2006 by Henrik Just
*
* All Rights Reserved.
*
* Version 0.5 (2006-11-02)
*
*/
package writer2latex.latex.i18n;
/** This class contains a trie of string -> LaTeX code replacements
*/
public class ReplacementTrie extends ReplacementTrieNode {
public ReplacementTrie() {
super('*',0);
}
public ReplacementTrieNode get(String sInput) {
return get(sInput,0,sInput.length());
}
public ReplacementTrieNode get(String sInput, int nStart, int nEnd) {
if (sInput.length()==0) { return null; }
else { return super.get(sInput,nStart,nEnd); }
}
public void put(String sInput, String sLaTeXCode, int nFontencs) {
if (sInput.length()==0) { return; }
else { super.put(sInput,sLaTeXCode,nFontencs); }
}
public String[] getInputStrings() {
return null; //TODO
}
}

View file

@ -0,0 +1,127 @@
/************************************************************************
*
* ReplacementTrieNode.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2006 by Henrik Just
*
* All Rights Reserved.
*
* Version 0.5 (2006-11-02)
*
*/
package writer2latex.latex.i18n;
/** This class contains a node in a trie of string -> LaTeX code replacements
*/
public class ReplacementTrieNode {
private char cLetter;
private int nInputLength;
private String sLaTeXCode = null;
private int nFontencs = 0;
private ReplacementTrieNode son = null;
private ReplacementTrieNode brother = null;
public ReplacementTrieNode(char cLetter, int nInputLength) {
this.cLetter = cLetter;
this.nInputLength = nInputLength;
}
public char getLetter() { return this.cLetter; }
public int getInputLength() { return this.nInputLength; }
public String getLaTeXCode() { return this.sLaTeXCode; }
public int getFontencs() { return this.nFontencs; }
protected void setLaTeXCode(String sLaTeXCode) {
this.sLaTeXCode = sLaTeXCode;
}
protected void setFontencs(int nFontencs) {
this.nFontencs = nFontencs;
}
protected ReplacementTrieNode getFirstChild() {
return this.son;
}
protected ReplacementTrieNode getNextSibling() {
return this.brother;
}
protected ReplacementTrieNode getChildByLetter(char cLetter) {
ReplacementTrieNode child = this.getFirstChild();
while (child!=null) {
if (cLetter==child.getLetter()) { return child; }
child = child.getNextSibling();
}
return null;
}
protected void appendChild(ReplacementTrieNode node) {
if (son==null) { son = node; }
else { son.appendSibling(node); }
}
protected void appendSibling(ReplacementTrieNode node) {
if (brother==null) { brother = node; }
else { brother.appendSibling(node); }
}
protected ReplacementTrieNode get(String sInput, int nStart, int nEnd) {
if (nStart>=nEnd) { return null; }
char c = sInput.charAt(nStart);
ReplacementTrieNode child = this.getFirstChild();
while (child!=null) {
if (child.getLetter()==c) {
if (child.getLaTeXCode()!=null) { return child; }
else { return child.get(sInput,nStart+1,nEnd); }
}
child = child.getNextSibling();
}
return null;
}
protected void put(String sInput, String sLaTeXCode, int nFontencs) {
char c = sInput.charAt(0);
ReplacementTrieNode child = this.getChildByLetter(c);
if (child==null) {
child = new ReplacementTrieNode(c,this.getInputLength()+1);
this.appendChild(child);
}
if (sInput.length()>1) {
child.put(sInput.substring(1),sLaTeXCode,nFontencs);
}
else {
child.setLaTeXCode(sLaTeXCode);
child.setFontencs(nFontencs);
}
}
public String toString() {
String s = Character.toString(cLetter);
if (brother!=null) { s+=brother.toString(); }
if (son!=null) { s+="\nInputLength "+(nInputLength+1)+", "+son.toString(); }
else { s+="\n"; }
return s;
}
}

View file

@ -0,0 +1,52 @@
/************************************************************************
*
* UnicodeCharacter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2007 by Henrik Just
*
* All Rights Reserved.
*
* Version 0.5 (2007-07-24)
*
*/
package writer2latex.latex.i18n;
// Helper class: A struct to hold the LaTeX representations of a unicode character
class UnicodeCharacter implements Cloneable {
final static int NORMAL = 0; // this is a normal character
final static int COMBINING = 1; // this character should be ignored
final static int IGNORE = 2; // this is a combining character
final static int UNKNOWN = 3; // this character is unknown
int nType; // The type of character
String sMath; // LaTeX representation in math mode
String sText; // LaTeX representation in text mode
int nFontencs; // Valid font encoding(s) for the text mode representation
boolean bDashes; // This character is represented by dashes (-,--,---)
protected Object clone() {
UnicodeCharacter uc = new UnicodeCharacter();
uc.nType = this.nType;
uc.sMath = this.sMath;
uc.sText = this.sText;
uc.nFontencs = this.nFontencs;
uc.bDashes = this.bDashes;
return uc;
}
}

View file

@ -0,0 +1,43 @@
/************************************************************************
*
* UnicodeRow.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2007 by Henrik Just
*
* All Rights Reserved.
*
* Version 0.5 (2007-07-24)
*
*/
package writer2latex.latex.i18n;
// Helper class: A row of 256 unicode characters
class UnicodeRow implements Cloneable {
UnicodeCharacter[] entries;
UnicodeRow(){ entries=new UnicodeCharacter[256]; }
protected Object clone() {
UnicodeRow ur = new UnicodeRow();
for (int i=0; i<256; i++) {
if (this.entries[i]!=null) {
ur.entries[i] = (UnicodeCharacter) this.entries[i].clone();
}
}
return ur;
}
}

View file

@ -0,0 +1,79 @@
/************************************************************************
*
* UnicodeStringParser.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2007 by Henrik Just
*
* All Rights Reserved.
*
* Version 0.5 (2007-07-24)
*
*/
package writer2latex.latex.i18n;
// Helper class: Parse a unicode string.
// Note: Some 8-bit fonts have additional "spacer" characters that are used
// for manual placement of accents. These are ignored between the base character
// and the combining character, thus we are parsing according to the rule
// <base char> <spacer char>* <combining char>?
class UnicodeStringParser {
private UnicodeTable table; // the table to use
private String s; // the string
private int i; // the current index
private int nEnd; // the maximal index
private char c; // the current character
private char cc; // the current combining character
protected void reset(UnicodeTable table, String s, int i, int nEnd) {
this.table=table;
this.s=s;
this.i=i;
this.nEnd=nEnd;
}
protected boolean next() {
if (i>=nEnd) { return false; }
// Pick up base character
c = s.charAt(i++);
if (table.getCharType(c)==UnicodeCharacter.COMBINING) {
// Lonely combining character - combine with space
cc = c;
c = ' ';
return true;
}
// Skip characters that should be ignored
while (i<s.length() && table.getCharType(s.charAt(i))==UnicodeCharacter.IGNORE) { i++; }
// Pick up combining character, if any
if (i<s.length() && table.getCharType(s.charAt(i))==UnicodeCharacter.COMBINING) {
cc = s.charAt(i++);
}
else {
cc = '\u0000';
}
return true;
}
protected char getChar() { return c; }
protected boolean hasCombiningChar() { return cc!='\u0000'; }
protected char getCombiningChar() { return cc; }
}

View file

@ -0,0 +1,169 @@
/************************************************************************
*
* UnicodeTable.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2007 by Henrik Just
*
* All Rights Reserved.
*
* Version 0.5 (2007-07-24)
*
*/
package writer2latex.latex.i18n;
// Helper class: Table of up to 65536 unicode characters
class UnicodeTable {
protected UnicodeRow[] table=new UnicodeRow[256];
private UnicodeTable parent;
// Constructor; creates a new table, possibly based on a parent
// Note: The parent must be fully loaded before the child is created.
public UnicodeTable(UnicodeTable parent){
this.parent = parent;
if (parent!=null) {
// *Copy* the rows from the parent
for (int i=0; i<256; i++) {
table[i] = parent.table[i];
}
}
}
// Make sure the required entry exists
private void createEntry(int nRow, int nCol) {
if (table[nRow]==null) {
table[nRow]=new UnicodeRow();
}
else if (parent!=null && table[nRow]==parent.table[nRow]) {
// Before changing a row it must be *cloned*
table[nRow] = (UnicodeRow) parent.table[nRow].clone();
}
if (table[nRow].entries[nCol]==null) {
table[nRow].entries[nCol]=new UnicodeCharacter();
}
}
// Addd a single character (type only), by number
protected void addCharType(char c, int nType) {
int nRow=c/256; int nCol=c%256;
createEntry(nRow,nCol);
table[nRow].entries[nCol].nType = nType;
}
// Addd a single character (type only), by name
protected void addCharType(char c, String sType) {
int nRow=c/256; int nCol=c%256;
createEntry(nRow,nCol);
if ("combining".equals(sType)) {
table[nRow].entries[nCol].nType = UnicodeCharacter.COMBINING;
}
else if ("ignore".equals(sType)) {
table[nRow].entries[nCol].nType = UnicodeCharacter.IGNORE;
}
else {
table[nRow].entries[nCol].nType = UnicodeCharacter.NORMAL;
}
}
// Add a single math character to the table
protected void addMathChar(char c, String sLaTeX){
int nRow=c/256; int nCol=c%256;
createEntry(nRow,nCol);
table[nRow].entries[nCol].sMath=sLaTeX;
}
// Add a single text character to the table
protected void addTextChar(char c, String sLaTeX, int nFontencs, boolean bDashes){
int nRow=c/256; int nCol=c%256;
createEntry(nRow,nCol);
table[nRow].entries[nCol].sText=sLaTeX;
table[nRow].entries[nCol].nFontencs=nFontencs;
table[nRow].entries[nCol].bDashes=bDashes;
}
// Retrieve entry for a character (or null)
private UnicodeCharacter getEntry(char c) {
int nRow=c/256; int nCol=c%256;
if (table[nRow]==null) return null;
return table[nRow].entries[nCol];
}
// Get character type
public int getCharType(char c) {
UnicodeCharacter entry = getEntry(c);
if (entry==null) return UnicodeCharacter.UNKNOWN;
return entry.nType;
}
// Check to see if this math character exists?
public boolean hasMathChar(char c) {
UnicodeCharacter entry = getEntry(c);
if (entry==null) return false;
return entry.sMath!=null;
}
// Get math character (or null)
public String getMathChar(char c) {
UnicodeCharacter entry = getEntry(c);
if (entry==null) return null;
return entry.sMath;
}
// Check to see if this text character exists?
public boolean hasTextChar(char c) {
UnicodeCharacter entry = getEntry(c);
if (entry==null) return false;
return entry.sText!=null;
}
// Get text character (or null)
public String getTextChar(char c) {
UnicodeCharacter entry = getEntry(c);
if (entry==null) return null;
return entry.sText;
}
// Get font encoding(s) for text character (or 0)
public int getFontencs(char c) {
UnicodeCharacter entry = getEntry(c);
if (entry==null) return 0;
return entry.nFontencs;
}
// Get dashes for text character
public boolean isDashes(char c) {
UnicodeCharacter entry = getEntry(c);
if (entry==null) return false;
return entry.bDashes;
}
// Get number of defined characters
public int getCharCount() {
int nCount = 0;
for (int nRow=0; nRow<256; nRow++) {
if (table[nRow]!=null) {
for (int nCol=0; nCol<256; nCol++) {
UnicodeCharacter entry = table[nRow].entries[nCol];
if (entry!=null) nCount++;
}
}
}
return nCount;
}
}

View file

@ -0,0 +1,166 @@
/************************************************************************
*
* UnicodeTableHandler.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2009 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2009-02-17)
*
*/
package writer2latex.latex.i18n;
import java.util.Hashtable;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
// Helper classs: SAX handler to parse symbols.xml from jar
class UnicodeTableHandler extends DefaultHandler{
private Hashtable tableSet; // collection of all tables
private UnicodeTable table; // the current table
private String sSymbolSets;
private boolean bGlobalReadThisSet;
private boolean bReadThisSet;
private int nGlobalFontencs = 0; // The global fontencodings for current symbol set
private int nFontencs = 0; // The currently active fontencodings
private boolean b8bit = false;
UnicodeTableHandler(Hashtable tableSet, String sSymbolSets){
this.sSymbolSets = sSymbolSets;
this.tableSet = tableSet;
}
public void startElement(String nameSpace, String localName, String qName, Attributes attributes){
if (qName.equals("symbols")) {
//root element - create root table!
table = new UnicodeTable(null);
tableSet.put("root",table);
}
else if (qName.equals("symbol-set")) {
// start a new symbol set; maybe we want to include it?
bGlobalReadThisSet = sSymbolSets.indexOf(attributes.getValue("name")) >= 0;
bReadThisSet = bGlobalReadThisSet;
// Change global and current fontencodings
nGlobalFontencs = ClassicI18n.readFontencs(attributes.getValue("fontenc"));
nFontencs = nGlobalFontencs;
}
else if (qName.equals("special-symbol-set")) {
// start a new special symbol set; this requires a new table
table = new UnicodeTable((UnicodeTable) tableSet.get("root"));
tableSet.put(attributes.getValue("name"),table);
// Read it if it requires nothing, or something we read
bGlobalReadThisSet = attributes.getValue("requires")==null ||
sSymbolSets.indexOf(attributes.getValue("requires")) >= 0;
bReadThisSet = bGlobalReadThisSet;
b8bit = "true".equals(attributes.getValue("eight-bit"));
// Change global and current fontencodings
nGlobalFontencs = ClassicI18n.readFontencs(attributes.getValue("fontenc"));
nFontencs = nGlobalFontencs;
}
else if (qName.equals("symbol-subset")) {
// Do we requires something here?
if (attributes.getValue("requires")!=null) {
bReadThisSet = sSymbolSets.indexOf(attributes.getValue("requires")) >= 0;
}
// Change current fontencodings
nFontencs = ClassicI18n.readFontencs(attributes.getValue("fontenc"));
}
else if (qName.equals("symbol")) {
if (bReadThisSet) {
char c=(char)Integer.parseInt(attributes.getValue("char"),16);
String sEqChar=attributes.getValue("eq-char");
if (sEqChar!=null) { // copy existing definitions, if any
char eqc = (char)Integer.parseInt(sEqChar,16);
if (table.getCharType(eqc)!=UnicodeCharacter.UNKNOWN) {
table.addCharType(c,table.getCharType(eqc));
}
if (table.hasMathChar(eqc)) {
table.addMathChar(c,table.getMathChar(eqc));
}
if (table.hasTextChar(eqc)) {
table.addTextChar(c,table.getTextChar(eqc),table.getFontencs(eqc),table.isDashes(eqc));
}
}
else {
String sType=attributes.getValue("char-type");
String sMath=attributes.getValue("math");
String sText=attributes.getValue("text");
boolean bDashes="true".equals(attributes.getValue("dashes"));
if (sType!=null) table.addCharType(c,sType);
if (sMath!=null) table.addMathChar(c,sMath);
if (sText!=null) table.addTextChar(c,sText,nFontencs,bDashes);
}
}
}
else if (qName.equals("preserve-symbol")) {
if (bReadThisSet) {
String sMode=attributes.getValue("mode");
char c=(char)Integer.parseInt(attributes.getValue("char"),16);
table.addCharType(c,attributes.getValue("char-type"));
if ("math".equals(sMode) || "both".equals(sMode)) {
table.addMathChar(c,Character.toString(c));
}
if ("text".equals(sMode) || "both".equals(sMode)) {
table.addTextChar(c,Character.toString(c),nFontencs,false);
}
}
}
else if (qName.equals("preserve-symbols")) {
if (bReadThisSet) {
String sMode=attributes.getValue("mode");
String sType=attributes.getValue("char-type");
char c1=(char)Integer.parseInt(attributes.getValue("first-char"),16);
char c2=(char)Integer.parseInt(attributes.getValue("last-char"),16);
boolean bMath = "math".equals(sMode) || "both".equals(sMode);
boolean bText = "text".equals(sMode) || "both".equals(sMode);
for (char c=c1; c<=c2; c++) {
table.addCharType(c,sType);
if (bMath) {
table.addMathChar(c,Character.toString(c));
}
if (bText) {
table.addTextChar(c,Character.toString(c),nFontencs,false);
}
}
}
}
}
public void endElement(String nameSpace, String localName, String qName){
if (qName.equals("symbol-subset")) {
// Revert to global setting of reading status
bReadThisSet = bGlobalReadThisSet;
// Revert to global fontencoding
nFontencs = nGlobalFontencs;
}
else if (qName.equals("special-symbol-set")) {
if (b8bit) {
// Row 0 = Row 240 (F0)
// Note: 8-bit fonts are supposed to be relocated to F000..F0FF
// This may fail on import from msword, hence this hack
table.table[0] = table.table[240];
}
b8bit = false;
}
}
}

View file

@ -0,0 +1,120 @@
/************************************************************************
*
* XeTeXI18n.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2009 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2009-02-17)
*
*/
package writer2latex.latex.i18n;
import writer2latex.office.*;
import writer2latex.latex.LaTeXConfig;
import writer2latex.latex.LaTeXDocumentPortion;
import writer2latex.latex.ConverterPalette;
import writer2latex.latex.util.BeforeAfter;
/** This class takes care of i18n in XeLaTeX
*/
public class XeTeXI18n extends I18n {
// **** Constructors ****
/** Construct a new XeTeXI18n as ConverterHelper
* @param ofr the OfficeReader to get language information from
* @param config the configuration which determines the symbols to use
* @param palette the ConverterPalette (unused)
*/
public XeTeXI18n(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
}
/** Add declarations to the preamble to load the required packages
* @param pack usepackage declarations
* @param decl other declarations
*/
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
pack.append("\\usepackage{fontspec}").nl()
.append("\\usepackage{xunicode}").nl()
.append("\\usepackage{xltxtra}").nl()
.append("\\usepackage{amsmath,amssymb,amsfonts}").nl();
}
/** Apply a language language
* @param style the OOo style to read attributesfrom
* @param bDecl true if declaration form is required
* @param bInherit true if inherited properties should be used
* @param ba the <code>BeforeAfter</code> to add LaTeX code to.
*/
public void applyLanguage(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba) {
// TODO (polyglossia)
}
/** Push a font to the font stack
* @param sName the name of the font
*/
public void pushSpecialTable(String sName) {
// TODO
}
/** Pop a font from the font stack
*/
public void popSpecialTable() {
// TODO
}
/** Convert a string of characters into LaTeX
* @param s the source string
* @param bMathMode true if the string should be rendered in math mode
* @param sLang the iso language of the string
* @return the LaTeX string
*/
public String convert(String s, boolean bMathMode, String sLang){
StringBuffer buf = new StringBuffer();
int nLen = s.length();
char c;
for (int i=0; i<nLen; i++) {
c = s.charAt(i);
switch (c) {
case '"' : buf.append("\\textquotedbl{}"); break;
case '#' : buf.append("\\#"); break;
case '$' : buf.append("\\$"); break;
case '%' : buf.append("\\%"); break;
case '&' : buf.append("\\&"); break;
case '\'' : buf.append("\\textbackslash{}"); break;
case '<' : buf.append("\\textless{}"); break;
case '>' : buf.append("\\textgreater{}"); break;
case '\\' : buf.append("\\textbackslash{}"); break;
case '\u005e' : buf.append("\\^{}"); break;
case '_' : buf.append("\\_"); break;
case '\u0060' : buf.append("\\textasciigrave{}"); break;
case '{' : buf.append("\\{"); break;
case '|' : buf.append("\\textbar{}"); break;
case '}' : buf.append("\\}"); break;
case '~' : buf.append("\\~{}"); break;
default: buf.append(c);
}
}
return buf.toString();
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,75 @@
/************************************************************************
*
* BeforeAfter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-12-02)
*
*/
package writer2latex.latex.util;
/** Utility class to hold LaTeX code to put before/after other LaTeX code
*/
public class BeforeAfter {
private String sBefore="";
private String sAfter="";
/** Constructor to initialize the object with a pair of strings
* @param sBefore1 LaTeX code to put before
* @param sAfter1 LaTeX code to put after
*/
public BeforeAfter(String sBefore1, String sAfter1) {
sBefore=sBefore1; sAfter=sAfter1;
}
/** Default constructor: Create with empty strings
*/
public BeforeAfter() { }
/** <p>Add data to the <code>BeforeAfter</code></p>
* <p>The new data will be be added "inside", thus for example</p>
* <ul><li><code>add("\textsf{","}");</code>
* <li><code>add("\textit{","}");</code></ul>
* <p>will create the pair <code>\textsf{\textit{</code>, <code>}}</code></p>
*
* @param sBefore1 LaTeX code to put before
* @param sAfter1 LaTeX code to put after
*/
public void add(String sBefore1, String sAfter1) {
sBefore+=sBefore1; sAfter=sAfter1+sAfter;
}
/** Get LaTeX code to put before
* @return then LaTeX code
*/
public String getBefore() { return sBefore; }
/** Get LaTeX code to put after
* @return then LaTeX code
*/
public String getAfter() { return sAfter; }
/** Check if this <code>BeforeAfter</code> contains any data
* @return true if there is data in at least one part
*/
public boolean isEmpty() { return sBefore.length()==0 && sAfter.length()==0; }
}

View file

@ -0,0 +1,310 @@
/************************************************************************
*
* Context.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2007 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2007-11-23)
*
*/
package writer2latex.latex.util;
import writer2latex.office.XMLString;
import writer2latex.office.StyleWithProperties;
/** <p>LaTeX code is in general very context dependent. This class tracks the
* current context, which is the used by the converter to create valid and
* optimal LaTeX code.</p>
*/
public class Context {
// *** Formatting Info (current values in the source OOo document) ***
// Current list style
private String sListStyleName = null;
// Current background color
private String sBgColor = null;
// Current character formatting attributes
private String sFontName = null;
private String sFontStyle = null;
private String sFontVariant = null;
private String sFontWeight = null;
private String sFontSize = null;
private String sFontColor = null;
private String sLang = null;
private String sCountry = null;
// *** Structural Info (identifies contructions in the LaTeX document) ***
// within the header or footer of a pagestyle
private boolean bInHeaderFooter = false;
// within a table cell
private boolean bInTable = false; // any column
private boolean bInLastTableColumn = false; // last column
private boolean bInSimpleTable = false; // l, c or r-column
// within a multicols environment
private boolean bInMulticols = false;
// within a list of this level
private int nListLevel = 0;
// within a section command
private boolean bInSection = false;
// within a caption
private boolean bInCaption = false;
// within a floating figure (figure environment)
private boolean bInFigureFloat = false;
// within a floating table (table envrionment)
private boolean bInTableFloat = false;
// within a minipage environment
private boolean bInFrame = false;
// within a \footnote or \endnote
private boolean bInFootnote = false;
// in verbatim mode
private boolean bVerbatim = false;
// in math mode
private boolean bMathMode = false;
// *** Special Info ***
// Inside (inline) verbatim text, where line breaks are disallowed
private boolean bNoLineBreaks = false;
// Inside a construction, where footnotes are disallowed
private boolean bNoFootnotes = false;
// Inside an area, where lists are ignored
private boolean bIgnoreLists = false;
// *** Accessor Methods ***
public void setBgColor(String sBgColor) { this.sBgColor = sBgColor; }
public String getBgColor() { return sBgColor; }
public void setListStyleName(String sListStyleName) { this.sListStyleName = sListStyleName; }
public String getListStyleName() { return sListStyleName; }
public void setFontName(String sFontName) { this.sFontName = sFontName; }
public String getFontName() { return sFontName; }
public void setFontStyle(String sFontStyle) { this.sFontStyle = sFontStyle; }
public String getFontStyle() { return sFontStyle; }
public void setFontVariant(String sFontVariant) { this.sFontVariant = sFontVariant; }
public String getFontVariant() { return sFontVariant; }
public void setFontWeight(String sFontWeight) { this.sFontWeight = sFontWeight; }
public String getFontWeight() { return sFontWeight; }
public void setFontSize(String sFontSize) { this.sFontSize = sFontSize; }
public String getFontSize() { return sFontSize; }
public void setFontColor(String sFontColor) { this.sFontColor = sFontColor; }
public String getFontColor() { return sFontColor; }
public void setLang(String sLang) { this.sLang = sLang; }
public String getLang() { return sLang; }
public void setCountry(String sCountry) { this.sCountry = sCountry; }
public String getCountry() { return sCountry; }
public void setInHeaderFooter(boolean bInHeaderFooter) {
this.bInHeaderFooter = bInHeaderFooter;
}
public boolean isInHeaderFooter() { return bInHeaderFooter; }
public void setInTable(boolean bInTable) { this.bInTable = bInTable; }
public boolean isInTable() { return bInTable; }
public void setInLastTableColumn(boolean bInLastTableColumn) { this.bInLastTableColumn = bInLastTableColumn; }
public boolean isInLastTableColumn() { return bInLastTableColumn; }
public void setInSimpleTable(boolean bInSimpleTable) { this.bInSimpleTable = bInSimpleTable; }
public boolean isInSimpleTable() { return bInSimpleTable; }
public void setInMulticols(boolean bInMulticols) {
this.bInMulticols = bInMulticols;
}
public boolean isInMulticols() { return bInMulticols; }
public void setListLevel(int nListLevel) { this.nListLevel = nListLevel; }
public void incListLevel() { nListLevel++; }
public int getListLevel() { return nListLevel; }
public void setInSection(boolean bInSection) { this.bInSection = bInSection; }
public boolean isInSection() { return bInSection; }
public void setInCaption(boolean bInCaption) { this.bInCaption = bInCaption; }
public boolean isInCaption() { return bInCaption; }
public void setInFigureFloat(boolean bInFigureFloat) { this.bInFigureFloat = bInFigureFloat; }
public boolean isInFigureFloat() { return bInFigureFloat; }
public void setInTableFloat(boolean bInTableFloat) { this.bInTableFloat = bInTableFloat; }
public boolean isInTableFloat() { return bInTableFloat; }
public void setInFrame(boolean bInFrame) { this.bInFrame = bInFrame; }
public boolean isInFrame() { return bInFrame; }
public void setInFootnote(boolean bInFootnote) {
this.bInFootnote = bInFootnote;
}
public boolean isInFootnote() { return bInFootnote; }
public void setNoFootnotes(boolean bNoFootnotes) {
this.bNoFootnotes = bNoFootnotes;
}
public boolean isNoFootnotes() { return bNoFootnotes; }
public void setIgnoreLists(boolean bIgnoreLists) {
this.bIgnoreLists = bIgnoreLists;
}
public boolean isIgnoreLists() { return bIgnoreLists; }
public void setNoLineBreaks(boolean bNoLineBreaks) {
this.bNoLineBreaks = bNoLineBreaks;
}
public boolean isNoLineBreaks() { return bNoLineBreaks; }
public boolean isVerbatim() { return bVerbatim; }
public void setVerbatim(boolean bVerbatim) { this.bVerbatim = bVerbatim; }
public boolean isMathMode() { return bMathMode; }
public void setMathMode(boolean bMathMode) { this.bMathMode = bMathMode; }
// update context
public void updateFormattingFromStyle(StyleWithProperties style) {
String s;
if (style==null) { return; }
s = style.getProperty(XMLString.STYLE_FONT_NAME);
if (s!=null) { setFontName(s); }
s = style.getProperty(XMLString.FO_FONT_STYLE);
if (s!=null) { setFontStyle(s); }
s = style.getProperty(XMLString.FO_FONT_VARIANT);
if (s!=null) { setFontVariant(s); }
s = style.getProperty(XMLString.FO_FONT_WEIGHT);
if (s!=null) { setFontWeight(s); }
s = style.getProperty(XMLString.FO_FONT_SIZE);
if (s!=null) { setFontSize(s); }
s = style.getProperty(XMLString.FO_COLOR);
if (s!=null) { setFontColor(s); }
s = style.getProperty(XMLString.FO_LANGUAGE);
if (s!=null) { setLang(s); }
s = style.getProperty(XMLString.FO_COUNTRY);
if (s!=null) { setCountry(s); }
}
public void resetFormattingFromStyle(StyleWithProperties style) {
setFontName(null);
setFontStyle(null);
setFontVariant(null);
setFontWeight(null);
setFontSize(null);
setFontColor(null);
setLang(null);
setCountry(null);
updateFormattingFromStyle(style);
}
// clone this Context
public Object clone() {
Context newContext = new Context();
newContext.setListStyleName(sListStyleName);
newContext.setBgColor(sBgColor);
newContext.setFontName(sFontName);
newContext.setFontStyle(sFontStyle);
newContext.setFontVariant(sFontVariant);
newContext.setFontWeight(sFontWeight);
newContext.setFontSize(sFontSize);
newContext.setFontColor(sFontColor);
newContext.setLang(sLang);
newContext.setCountry(sCountry);
newContext.setInHeaderFooter(bInHeaderFooter);
newContext.setInTable(bInTable);
newContext.setInLastTableColumn(bInLastTableColumn);
newContext.setInSimpleTable(bInSimpleTable);
newContext.setInMulticols(bInMulticols);
newContext.setListLevel(nListLevel);
newContext.setInSection(bInSection);
newContext.setInCaption(bInCaption);
newContext.setInFigureFloat(bInFigureFloat);
newContext.setInTableFloat(bInTableFloat);
newContext.setInFrame(bInFrame);
newContext.setInFootnote(bInFootnote);
newContext.setVerbatim(bVerbatim);
newContext.setMathMode(bMathMode);
newContext.setNoFootnotes(bNoFootnotes);
newContext.setIgnoreLists(bIgnoreLists);
newContext.setNoLineBreaks(bNoLineBreaks);
return newContext;
}
}

View file

@ -0,0 +1,69 @@
/************************************************************************
*
* HeadingMap.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2006 by Henrik Just
*
* All Rights Reserved.
*
* Version 0.5 (2006-11-02)
*
*/
package writer2latex.latex.util;
/** This class contains data for the mapping of OOo headings to LaTeX headings.
A LaTeX heading is characterized by a name and a level.
The heading is inserted with \name{...} or \name[...]{...}
The headings are supposed to be "normal" LaTeX headings,
ie. the names are also counter names, and the headings
can be reformatted using \@startsection etc.
Otherwise max-level should be zero.
*/
public class HeadingMap {
private int nMaxLevel;
private String[] sName;
private int[] nLevel;
/** Constructor: Create a new HeadingMap
@param nMaxLevel the maximal level of headings that are mapped */
public HeadingMap(int nMaxLevel) {
reset(nMaxLevel);
}
/** Clear all data associated with this HeadingMap (in order to reuse it) */
public void reset(int nMaxLevel) {
this.nMaxLevel = nMaxLevel;
sName = new String[nMaxLevel+1];
nLevel = new int[nMaxLevel+1];
}
/** Set data associated with a specific heading level */
public void setLevelData(int nWriterLevel, String sName, int nLevel) {
this.sName[nWriterLevel] = sName;
this.nLevel[nWriterLevel] = nLevel;
}
/** Returns the maximal Writer level associated with this HeadingMap */
public int getMaxLevel() { return nMaxLevel; }
/** Return the name (for counter and \@startsection) for this level */
public String getName(int nWriterLevel) { return sName[nWriterLevel]; }
/** Return the LaTeX level for this Writer level (for \@startsection) */
public int getLevel(int nWriterLevel) { return nLevel[nWriterLevel]; }
}

View file

@ -0,0 +1,76 @@
/************************************************************************
*
* Info.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-09-08)
*
*/
package writer2latex.latex.util;
import org.w3c.dom.Element;
import writer2latex.latex.LaTeXConfig;
import writer2latex.util.Misc;
import writer2latex.office.OfficeReader;
import writer2latex.office.StyleWithProperties;
import writer2latex.office.XMLString;
import writer2latex.latex.LaTeXDocumentPortion;
import writer2latex.latex.ConverterHelper;
import writer2latex.latex.ConverterPalette;
/**
* <p>This class creates various information to the user about the conversion.</p>
*/
public class Info extends ConverterHelper {
public Info(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
}
public void addDebugInfo(Element node, LaTeXDocumentPortion ldp) {
if (config.debug()) {
ldp.append("% ").append(node.getNodeName());
addDebugInfo(node,ldp,XMLString.TEXT_ID);
addDebugInfo(node,ldp,XMLString.TEXT_NAME);
addDebugInfo(node,ldp,XMLString.TABLE_NAME);
addDebugInfo(node,ldp,XMLString.TEXT_STYLE_NAME);
if (node.getNodeName().equals(XMLString.TEXT_P) || node.getNodeName().equals(XMLString.TEXT_H)) {
StyleWithProperties style = ofr.getParStyle(node.getAttribute(XMLString.TEXT_STYLE_NAME));
if (style!=null && style.isAutomatic()) {
ldp.append(" ("+style.getParentName()+")");
}
ldp.append(" ("+ofr.getParStyles().getDisplayName(node.getAttribute(XMLString.TEXT_STYLE_NAME))+")");
}
ldp.nl();
}
}
private void addDebugInfo(Element node, LaTeXDocumentPortion ldp, String sAttribute) {
String sValue = Misc.getAttribute(node,sAttribute);
if (sValue!=null) {
ldp.append(" ").append(sAttribute).append("=\"").append(sValue).append("\"");
}
}
}

View file

@ -0,0 +1,11 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>The package writer2latex.xhtml.util</title>
</head>
<body>
<p>Some general utility classes for LaTeX export.</p>
</body>
</html>

View file

@ -0,0 +1,99 @@
/************************************************************************
*
* StyleMap.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2007 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2007-07-30)
*
*/
package writer2latex.latex.util;
import java.util.Hashtable;
import java.util.Enumeration;
public class StyleMap {
private Hashtable items = new Hashtable();
public void put(String sName, String sBefore, String sAfter, boolean bLineBreak, boolean bVerbatim) {
StyleMapItem item = new StyleMapItem();
item.sBefore = sBefore;
item.sAfter = sAfter;
item.sNext = ";;";
item.bLineBreak = bLineBreak;
item.bVerbatim = bVerbatim;
items.put(sName,item);
}
public void put(String sName, String sBefore, String sAfter, String sNext, boolean bVerbatim) {
StyleMapItem item = new StyleMapItem();
item.sBefore = sBefore;
item.sAfter = sAfter;
item.sNext = ";"+sNext+";";
item.bLineBreak = true;
item.bVerbatim = bVerbatim;
items.put(sName,item);
}
public void put(String sName, String sBefore, String sAfter) {
StyleMapItem item = new StyleMapItem();
item.sBefore = sBefore;
item.sAfter = sAfter;
item.sNext = ";;";
item.bLineBreak = true;
item.bVerbatim = false;
items.put(sName,item);
}
public boolean contains(String sName) {
return sName!=null && items.containsKey(sName);
}
public String getBefore(String sName) {
return ((StyleMapItem) items.get(sName)).sBefore;
}
public String getAfter(String sName) {
return ((StyleMapItem) items.get(sName)).sAfter;
}
public String getNext(String sName) {
String sNext = ((StyleMapItem) items.get(sName)).sNext;
return sNext.substring(1,sNext.length()-1);
}
public boolean isNext(String sName, String sNext) {
String sNext1 = ((StyleMapItem) items.get(sName)).sNext;
return sNext1.indexOf(";"+sNext+";")>-1;
}
public boolean getLineBreak(String sName) {
return contains(sName) && ((StyleMapItem) items.get(sName)).bLineBreak;
}
public boolean getVerbatim(String sName) {
return contains(sName) && ((StyleMapItem) items.get(sName)).bVerbatim;
}
public Enumeration getNames() {
return items.keys();
}
}

View file

@ -0,0 +1,36 @@
/************************************************************************
*
* StyleMapItem.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2007 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2007-07-30)
*
*/
package writer2latex.latex.util;
// A struct to hold data about a style map
class StyleMapItem {
String sBefore;
String sAfter;
String sNext;
boolean bLineBreak;
boolean bVerbatim;
}

View file

@ -0,0 +1,146 @@
/************************************************************************
*
* BibMark.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-22)
*
*/
package writer2latex.office;
import org.w3c.dom.Node;
import writer2latex.util.*;
//import writer2latex.office.*;
/**
* <p>This class represents a single bibliography-mark.</p>
*/
public final class BibMark {
// Available fields
public static final int ADDRESS = 0;
public static final int ANNOTE = 1;
public static final int AUTHOR = 2;
public static final int BOOKTITLE = 3;
public static final int CHAPTER = 4;
// public static final int CROSSREF = 5; // BibTeX, missing in OOo
public static final int EDITION = 6;
public static final int EDITOR = 7;
public static final int HOWPUBLISHED = 8;
public static final int INSTITUTION = 9;
public static final int JOURNAL = 10;
// public static final int KEY = 11; // BibTeX, missing in OOo
public static final int MONTH = 12;
public static final int NOTE = 13;
public static final int NUMBER = 14;
public static final int ORGANIZATIONS = 15; // BibTeX: organization
public static final int PAGES = 16;
public static final int PUBLISHER = 17;
public static final int SCHOOL = 18;
public static final int SERIES = 19;
public static final int TITLE = 20 ;
public static final int REPORT_TYPE = 21; // BibTeX: report
public static final int VOLUME = 22;
public static final int YEAR = 23;
// remaining fields are not standard in BibTeX
public static final int URL = 24;
public static final int CUSTOM1 = 25;
public static final int CUSTOM2 = 26;
public static final int CUSTOM3 = 27;
public static final int CUSTOM4 = 28;
public static final int CUSTOM5 = 29;
public static final int ISBN = 30;
public static final int FIELD_COUNT = 31;
// Private data
private String sIdentifier;
private String sEntryType;
private String[] fields = new String[FIELD_COUNT];
/**
* <p>Create a new BibMark from scratch.</p>
*/
public BibMark(String sIdentifier, String sEntryType) {
this.sIdentifier = sIdentifier;
this.sEntryType = sEntryType;
}
/**
* <p>Create a new BibMark from a text:bibliography-mark node.</p>
*/
public BibMark(Node node) {
sIdentifier = Misc.getAttribute(node,XMLString.TEXT_IDENTIFIER);
sEntryType = Misc.getAttribute(node,XMLString.TEXT_BIBLIOGRAPHY_TYPE);
if (sEntryType==null) { // bug in OOo 1.0!
sEntryType = Misc.getAttribute(node,XMLString.TEXT_BIBILIOGRAPHIC_TYPE);
}
fields[ADDRESS] = Misc.getAttribute(node,XMLString.TEXT_ADDRESS);
fields[ANNOTE] = Misc.getAttribute(node,XMLString.TEXT_ANNOTE);
fields[AUTHOR] = Misc.getAttribute(node,XMLString.TEXT_AUTHOR);
fields[BOOKTITLE] = Misc.getAttribute(node,XMLString.TEXT_BOOKTITLE);
fields[CHAPTER] = Misc.getAttribute(node,XMLString.TEXT_CHAPTER);
fields[EDITION] = Misc.getAttribute(node,XMLString.TEXT_EDITION);
fields[EDITOR] = Misc.getAttribute(node,XMLString.TEXT_EDITOR);
fields[HOWPUBLISHED] = Misc.getAttribute(node,XMLString.TEXT_HOWPUBLISHED);
fields[INSTITUTION] = Misc.getAttribute(node,XMLString.TEXT_INSTITUTION);
fields[JOURNAL] = Misc.getAttribute(node,XMLString.TEXT_JOURNAL);
fields[MONTH] = Misc.getAttribute(node,XMLString.TEXT_MONTH);
fields[NOTE] = Misc.getAttribute(node,XMLString.TEXT_NOTE);
fields[NUMBER] = Misc.getAttribute(node,XMLString.TEXT_NUMBER);
fields[ORGANIZATIONS] = Misc.getAttribute(node,XMLString.TEXT_ORGANIZATIONS);
fields[PAGES] = Misc.getAttribute(node,XMLString.TEXT_PAGES);
fields[PUBLISHER] = Misc.getAttribute(node,XMLString.TEXT_PUBLISHER);
fields[SCHOOL] = Misc.getAttribute(node,XMLString.TEXT_SCHOOL);
fields[SERIES] = Misc.getAttribute(node,XMLString.TEXT_SERIES);
fields[TITLE] = Misc.getAttribute(node,XMLString.TEXT_TITLE);
fields[REPORT_TYPE] = Misc.getAttribute(node,XMLString.TEXT_REPORT_TYPE);
fields[VOLUME] = Misc.getAttribute(node,XMLString.TEXT_VOLUME);
fields[YEAR] = Misc.getAttribute(node,XMLString.TEXT_YEAR);
fields[URL] = Misc.getAttribute(node,XMLString.TEXT_URL);
fields[CUSTOM1] = Misc.getAttribute(node,XMLString.TEXT_CUSTOM1);
fields[CUSTOM2] = Misc.getAttribute(node,XMLString.TEXT_CUSTOM2);
fields[CUSTOM3] = Misc.getAttribute(node,XMLString.TEXT_CUSTOM3);
fields[CUSTOM4] = Misc.getAttribute(node,XMLString.TEXT_CUSTOM4);
fields[CUSTOM5] = Misc.getAttribute(node,XMLString.TEXT_CUSTOM5);
fields[ISBN] = Misc.getAttribute(node,XMLString.TEXT_ISBN);
}
/**
* <p>Get the identifier.</p>
*/
public String getIdentifier() { return sIdentifier; }
/**
* <p>Get the entry type.</p>
*/
public String getEntryType() { return sEntryType; }
/**
* <p>Set a specific field.</p>
*/
public void setField(int nField,String sValue) { fields[nField] = sValue; }
/**
* <p>Return a specific field.</p>
*/
public String getField(int nField) { return fields[nField]; }
}

View file

@ -0,0 +1,40 @@
/************************************************************************
*
* CellView.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-09-07)
*
*/
package writer2latex.office;
import org.w3c.dom.Element;
/**
* This class represent a cell in a table view</p>
*/
public class CellView {
public Element cell = null;
public int nRowSpan = 1;
public int nColSpan = 1;
public int nOriginalRow = -1;
public int nOriginalCol = -1;
}

View file

@ -0,0 +1,151 @@
/************************************************************************
*
* ControlReader.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.office;
//import java.util.Hashtable;
import java.util.Vector;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import writer2latex.util.Misc;
/** <p> This class reads a form control in an OOo document (a form:control
* node). A control always has an owner form.
* Properties and events are ignored.</p>
*/
public class ControlReader {
private FormReader ownerForm; // a control always belongs to a form
private String sId; // a control is identified by id
private Element control; // the control element
private Element controlType; // the type specific child element
private Vector items = new Vector(); // the options/items of a list/combobox
/** <p>The constructor reads the content of a control element</p>
* The representation in OpenDocument differs slightly from OOo 1.x.
* @param control a DOM element, which must be control node
*/
public ControlReader(Element control, FormReader ownerForm) {
this.ownerForm = ownerForm;
this.control = control;
sId = control.getAttribute(XMLString.FORM_ID);
// Read the control type specific info
if (control.getTagName().equals(XMLString.FORM_CONTROL)) { // old format
controlType = Misc.getFirstChildElement(control);
}
else { // oasos
controlType = control;
}
if (controlType!=null) { // must always be the case!
// Collect options/items
Node child = controlType.getFirstChild();
while (child!=null) {
if (child.getNodeType()==Node.ELEMENT_NODE && (
child.getNodeName().equals(XMLString.FORM_OPTION) ||
child.getNodeName().equals(XMLString.FORM_ITEM))) {
items.add(child);
}
child = child.getNextSibling();
}
}
}
/** <p>A control in OOo is identified by id (<code>form:control-id</code>
* attribute. The id is accessed by this method.</p>
* @return the id of the control
*/
public String getId() { return sId; }
/** <p>A control in OOo belongs to a form.</p>
* @return the form containing this control
*/
public FormReader getOwnerForm() { return ownerForm; }
/** <p>Get an attribute of the control. If the attribute does not exist,
* this method returns <code>null</code>.
* @param sName the name of the attribute
* @return the value of the attribute, or <code>null</code>
*/
public String getAttribute(String sName) {
return control.hasAttribute(sName) ? control.getAttribute(sName) : null;
}
/** <p>The type of the control is identified by a name, eg. form:submit</p>
* @return the type of this control
*/
public String getControlType() { return controlType.getTagName(); }
/** <p>Get an attribute specific to this type of control.
* If the attribute does not exist, this method returns <code>null</code>.
* @param sName the name of the attribute
* @return the value of the attribute, or <code>null</code>
*/
public String getTypeAttribute(String sName) {
return controlType!=null && controlType.hasAttribute(sName) ?
controlType.getAttribute(sName) : null;
}
/** <p>Return the number of options/items in this control.
* Only listbox (options) and combobox (items) controls can have these,
* for other controls this will return 0.
* @return the number of options/items
*/
public int getItemCount() { return items.size(); }
/** <p>Get an attribute of an option/item.
* If the index and/or the attribute does not exist, this method returns
* <code>null</code>.
* @param nIndex the index of the option/item
* @param sName the name of the attribute
* @return the value of the attribute, or <code>null</code>
*/
public String getItemAttribute(int nIndex, String sName) {
if (0<=nIndex && nIndex<=items.size()) {
return ((Element)items.get(nIndex)).hasAttribute(sName) ?
((Element)items.get(nIndex)).getAttribute(sName) : null;
}
else {
return null;
}
}
/** <p>Get the value of an option/item.
* If the index does not exist, this method returns
* <code>null</code>.
* @param nIndex the index of the option/item
* @return the value of the option/item, or <code>null</code>
*/
public String getItemValue(int nIndex) {
if (0<=nIndex && nIndex<=items.size()) {
return Misc.getPCDATA((Element)items.get(nIndex));
}
else {
return null;
}
}
}

View file

@ -0,0 +1,61 @@
/************************************************************************
*
* FontDeclaration.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2005 by Henrik Just
*
* All Rights Reserved.
*
* Version 0.5 (2005-10-10)
*
*/
package writer2latex.office;
import org.w3c.dom.Node;
/** <p> Class representing a font declaration in OOo</p>
*/
public class FontDeclaration extends OfficeStyle {
private PropertySet properties = new PropertySet();
private String sFontFamily = null;
private String sFontFamilyGeneric = null;
private String sFontPitch = null;
public void loadStyleFromDOM(Node node) {
super.loadStyleFromDOM(node);
properties.loadFromDOM(node);
sFontFamily = properties.getProperty(XMLString.FO_FONT_FAMILY);
if (sFontFamily==null) { // oasis
sFontFamily = properties.getProperty(XMLString.SVG_FONT_FAMILY);
}
sFontFamilyGeneric = properties.getProperty(XMLString.STYLE_FONT_FAMILY_GENERIC);
sFontPitch = properties.getProperty(XMLString.STYLE_FONT_PITCH);
}
public String getProperty(String sProperty){
return properties.getProperty(sProperty);
}
public String getFontFamily() { return sFontFamily; }
public String getFontFamilyGeneric() { return sFontFamilyGeneric; }
public String getFontPitch() { return sFontPitch; }
}

View file

@ -0,0 +1,79 @@
/************************************************************************
*
* FormReader.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*/
// Version 1.0 (2008-11-22)
package writer2latex.office;
//import java.util.Hashtable;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import writer2latex.util.Misc;
/** <p> This class reads a form in an OOo document (a form:form node)</p>
* Note: Subforms, properties and events are ignored.
*/
public class FormReader {
//private FormsReader forms; // the global collection of all forms
private String sName; // a form is identified by name
private Element form; // the form element
/** <p>The constructor reads the content of a <code>form:form</code> element</p>
* @param form a DOM element, which must be <code>form:form</code> node
*/
public FormReader(Element form, FormsReader forms) {
//this.forms = forms;
this.form = form;
sName = form.getAttribute(XMLString.FORM_NAME);
// Collect all controls contained in this form
Node child = form.getFirstChild();
while (child!=null) {
if (child.getNodeType()==Node.ELEMENT_NODE) {
String sId = Misc.getAttribute((Element)child,XMLString.FORM_ID);
if (sId!=null) {
ControlReader control = new ControlReader((Element) child, this);
forms.addControl(control);
}
}
child = child.getNextSibling();
}
}
/** <p>A form in OOo is identified by name (<code>form:name</code>
* attribute. The name is accessed by this method.</p>
* @return the name of the form
*/
public String getName() { return sName; }
/** <p>Get an attribute of the form. If the attribute does not exist,
* this method returns <code>null</code>.
* @param sName the name of the attribute
* @return the value of the attribute, or <code>null</code>
*/
public String getAttribute(String sName) {
return form.hasAttribute(sName) ? form.getAttribute(sName) : null;
}
}

View file

@ -0,0 +1,114 @@
/************************************************************************
*
* FormsReader.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.office;
import java.util.Hashtable;
import java.util.Iterator;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/** <p> This class reads the collection of all forms in an OOo document
* (the <code>office:forms</code> element).</p>
* <p>An OOo document may contain any number of forms; these are declared
* within this element. In OOo, unlike eg. html, the form declaration is
* separated from the presentation. This element contains the
* <em>declaration</em>. The <em>presentation</em> is given by inclusion of
* <code>draw:control</code> elements in the document flow. These refer to form
* controls by id.</p>
* <p>Note: A form is identified by a unique name, a control is
* identified by a (globally) unique id.</p>
*/
public class FormsReader {
private Element formsElement; // The office:forms element
private Hashtable forms = new Hashtable(); // all forms, indexed by name
private Hashtable controls = new Hashtable(); // all controls, indexed by id
/** <p>Read the content of an <code>office:forms</code> element</p>
* @param formsElement a DOM element, which must be <code>office:forms</code> node
*/
public void read(Element formsElement) {
this.formsElement = formsElement;
// Collect all forms
Node child = formsElement.getFirstChild();
while (child!=null) {
if (child.getNodeType()==Node.ELEMENT_NODE &&
child.getNodeName().equals(XMLString.FORM_FORM)) {
FormReader form = new FormReader((Element) child, this);
forms.put(form.getName(),form);
}
child = child.getNextSibling();
}
}
/** <p>Get an attribute of the forms. If the attribute does not exist,
* this method returns <code>null</code>.
* @param sName the name of the attribute
* @return the value of the attribute, or <code>null</code>
*/
public String getAttribute(String sName) {
return formsElement.hasAttribute(sName) ? formsElement.getAttribute(sName) : null;
}
/** <p>Get a <code>Iterator</code> over all forms.</p>
* @return a <code>Iterator</code> over all forms
*/
public Iterator getFormsIterator() {
return forms.values().iterator();
}
/** <p>Get a form by name</p>
* @param sName the <code>form:name</code> of the form
* @return the form as a <code>FormReader</code> object
*/
public FormReader getForm(String sName) {
return (FormReader) forms.get(sName);
}
/** <p>Get a <code>Iterator</code> over all controls.</p>
* @return a <code>Iterator</code> over all controls
*/
public Iterator getControlsIterator() {
return controls.values().iterator();
}
/** <p>Get a control by id</p>
* @param sId the <code>form:control-id</code> of the control
* @return the control as a <code>ControlReader</code> object
*/
public ControlReader getControl(String sId) {
return (ControlReader) controls.get(sId);
}
/** <p>Add a control</p>
* @param control a <code>ControlReader</code> representing the control
*/
protected void addControl(ControlReader control) {
controls.put(control.getId(),control);
}
}

View file

@ -0,0 +1,194 @@
/************************************************************************
*
* ImageLoader.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-22)
*
*/
package writer2latex.office;
//import java.io.ByteArrayInputStream;
//import java.io.ByteArrayOutputStream;
//import java.io.IOException;
import java.util.HashSet;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import writer2latex.api.GraphicConverter;
import writer2latex.util.Base64;
import writer2latex.util.Misc;
import writer2latex.xmerge.BinaryGraphicsDocument;
import writer2latex.xmerge.EmbeddedObject;
import writer2latex.xmerge.EmbeddedBinaryObject;
import writer2latex.xmerge.OfficeDocument;
//import writer2latex.util.*;
/**
* <p>This class extracts images from an OOo file.
* The images are returned as BinaryGraphicsDocument.</p>
*/
public final class ImageLoader {
// The Office document to load images from
private OfficeDocument oooDoc;
// Data for file name generation
private String sOutFileName;
private boolean bUseSubdir = false;
private int nImageCount = 0;
// should EPS be extracted from SVM?
private boolean bExtractEPS;
// Data for image conversion
private GraphicConverter gcv = null;
private boolean bAcceptOtherFormats = true;
private String sDefaultFormat = null;
private String sDefaultVectorFormat = null;
private HashSet acceptedFormats = new HashSet();
public ImageLoader(OfficeDocument oooDoc, String sOutFileName, boolean bExtractEPS) {
this.oooDoc = oooDoc;
this.sOutFileName = sOutFileName;
this.bExtractEPS = bExtractEPS;
}
public void setOutFileName(String sOutFileName) { this.sOutFileName = sOutFileName; }
public void setUseSubdir(boolean bUseSubdir) { this.bUseSubdir = bUseSubdir; }
public void setAcceptOtherFormats(boolean b) { bAcceptOtherFormats = b; }
public void setDefaultFormat(String sMime) {
addAcceptedFormat(sMime);
sDefaultFormat = sMime;
}
public void setDefaultVectorFormat(String sMime) {
addAcceptedFormat(sMime);
sDefaultVectorFormat = sMime;
}
public void addAcceptedFormat(String sMime) { acceptedFormats.add(sMime); }
private boolean isAcceptedFormat(String sMime) { return acceptedFormats.contains(sMime); }
public void setGraphicConverter(GraphicConverter gcv) { this.gcv = gcv; }
public BinaryGraphicsDocument getImage(Node node) {
// node must be a draw:image element.
// variables to hold data about the image:
String sMIME = null;
String sExt = null;
byte[] blob = null;
String sHref = Misc.getAttribute(node,XMLString.XLINK_HREF);
if (sHref==null || sHref.length()==0) {
// Image must be contained in an office:binary-element as base64:
Node obd = Misc.getChildByTagName(node,XMLString.OFFICE_BINARY_DATA);
if (obd!=null) {
StringBuffer buf = new StringBuffer();
NodeList nl = obd.getChildNodes();
int nLen = nl.getLength();
for (int i=0; i<nLen; i++) {
if (nl.item(i).getNodeType()==Node.TEXT_NODE) {
buf.append(nl.item(i).getNodeValue());
}
}
blob = Base64.decode(buf.toString());
sMIME = MIMETypes.getMagicMIMEType(blob);
sExt = MIMETypes.getFileExtension(sMIME);
}
}
else {
// Image may be embedded in package:
if (sHref.startsWith("#")) { sHref = sHref.substring(1); }
if (sHref.startsWith("./")) { sHref = sHref.substring(2); }
EmbeddedObject obj = oooDoc.getEmbeddedObject(sHref);
if (obj!=null && obj instanceof EmbeddedBinaryObject) {
EmbeddedBinaryObject object = (EmbeddedBinaryObject) obj;
blob = object.getBinaryData();
sMIME = object.getType();
sExt = MIMETypes.getFileExtension(sMIME);
}
else {
// This is a linked image
// TODO: Perhaps we should download the image from the url in sHref?
// Alternatively BinaryGraphicsDocument should be extended to
// handle external graphics.
}
}
if (blob==null) { return null; }
// Assign a name (without extension)
String sName = sOutFileName+"-img"+(++nImageCount);
if (bUseSubdir) { sName = sOutFileName + "-img/" + sName; }
BinaryGraphicsDocument bgd = null;
if (bExtractEPS && MIMETypes.SVM.equals(MIMETypes.getMagicMIMEType(blob))) {
// Look for postscript:
int[] offlen = new int[2];
if (SVMReader.readSVM(blob,offlen)) {
bgd = new BinaryGraphicsDocument(sName,
MIMETypes.EPS_EXT,MIMETypes.EPS);
bgd.read(blob,offlen[0],offlen[1]);
}
}
if (bgd==null) {
// If we have a converter AND a default format AND this image
// is not in an accepted format AND the converter knows how to
// convert it - try to convert...
if (gcv!=null && !isAcceptedFormat(sMIME) && sDefaultFormat!=null) {
String sTargetMIME = null;
if (MIMETypes.isVectorFormat(sMIME) && sDefaultVectorFormat!=null &&
gcv.supportsConversion(sMIME,sDefaultVectorFormat,false,false)) {
sTargetMIME = sDefaultVectorFormat;
}
else if (gcv.supportsConversion(sMIME,sDefaultFormat,false,false)) {
sTargetMIME = sDefaultFormat;
}
if (sTargetMIME!=null) {
byte[] newBlob = gcv.convert(blob,sMIME,sTargetMIME);
if (newBlob!=null) {
// Conversion succesful - create new data
blob = newBlob;
sMIME = sTargetMIME;
sExt = MIMETypes.getFileExtension(sMIME);
}
}
}
if (isAcceptedFormat(sMIME) || bAcceptOtherFormats) {
bgd = new BinaryGraphicsDocument(sName,sExt,sMIME);
bgd.read(blob);
}
}
return bgd;
}
}

View file

@ -0,0 +1,97 @@
/************************************************************************
*
* IndexMark.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-22)
*
*/
package writer2latex.office;
import org.w3c.dom.Node;
import writer2latex.util.*;
/**
* <p>This class contains static methods to read an index-mark.</p>
*/
public final class IndexMark {
// Node must be text:*-mark or text:*-mark-start
public final static String getIndexValue(Node node) {
if (!node.getNodeName().endsWith("start")) {
return Misc.getAttribute(node,XMLString.TEXT_STRING_VALUE);
}
else {
return collectMark(node);
}
}
// Node must be text:*-mark or text:*-mark-start
public final static String getKey1(Node node) {
return Misc.getAttribute(node,XMLString.TEXT_KEY1);
}
// Node must be text:*-mark or text:*-mark-start
public final static String getKey2(Node node) {
return Misc.getAttribute(node,XMLString.TEXT_KEY2);
}
// Collect a mark
private final static Node getRightNode(Node node) {
Node nextNode;
do {nextNode = node.getNextSibling();
if (nextNode!=null) { return nextNode; }
node = node.getParentNode();
} while (node!=null);
return null;
}
private final static String collectMark(Node node) {
StringBuffer buf = new StringBuffer();
String sId = Misc.getAttribute(node,XMLString.TEXT_ID);
node = getRightNode(node);
while (node!=null) {
if (node.getNodeType()==Node.TEXT_NODE) {
buf.append(node.getNodeValue());
node = getRightNode(node);
}
else if (node.getNodeType()==Node.ELEMENT_NODE) {
boolean bReady = false;
//String sNodeName = node.getNodeName();
if (sId.equals(Misc.getAttribute(node,XMLString.TEXT_ID))) {
node = null; // found the end mark
bReady = true;
}
else if (OfficeReader.isTextElement(node) &&
!OfficeReader.isNoteElement(node)) {
if (node.hasChildNodes()) {
node = node.getFirstChild(); bReady=true;
}
}
if (!bReady) { node=getRightNode(node); };
}
}
return buf.toString();
}
}

View file

@ -0,0 +1,126 @@
/************************************************************************
*
* ListCounter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2005 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2007-10-17)
*
*/
package writer2latex.office;
import writer2latex.util.*;
/**
* <p>This class produces labels for OOo lists/outlines (for xhtml
* and text, which cannot produce them on their own).</p>
*
*/
public class ListCounter {
private int nCounter[] = new int[11];
private String sNumFormat[] = new String[11];
private ListStyle style;
private int nLevel=1; // current level
public ListCounter() {
// Create a dummy counter
this.style = null;
for (int i=1; i<=10; i++) {
sNumFormat[i] = null;
}
}
public ListCounter(ListStyle style) {
this();
if (style!=null) {
this.style = style;
for (int i=1; i<=10; i++) {
sNumFormat[i] = style.getLevelProperty(i,XMLString.STYLE_NUM_FORMAT);
}
}
}
public ListCounter step(int nLevel) {
// Make sure no higher levels are zero
// This means that unlike eg. LaTeX, step(1).step(3) does not create
// the value 1.0.1 but rather 1.1.1
for (int i=1; i<nLevel; i++) {
if (nCounter[i]==0) { nCounter[i]=1; }
}
// Then step this level
nCounter[nLevel]++;
// Finally clear lower levels
if (nLevel<10) { restart(nLevel+1); }
this.nLevel = nLevel;
return this;
}
public ListCounter restart(int nLevel) {
restart(nLevel,0);
return this;
}
public ListCounter restart(int nLevel, int nValue) {
nCounter[nLevel] = nValue;
for (int i=nLevel+1; i<=10; i++) {
nCounter[i] = 0;
}
return this;
}
public int getValue(int nLevel) {
return nCounter[nLevel];
}
public int[] getValues() {
int[] nCounterSnapshot = new int[11];
System.arraycopy(nCounter,0,nCounterSnapshot,0,11);
return nCounterSnapshot;
}
public String getLabel() {
if (sNumFormat[nLevel]==null) return "";
int nLevels = Misc.getPosInteger(style.getLevelProperty(nLevel,
XMLString.TEXT_DISPLAY_LEVELS),1);
String sPrefix = style.getLevelProperty(nLevel,XMLString.STYLE_NUM_PREFIX);
String sSuffix = style.getLevelProperty(nLevel,XMLString.STYLE_NUM_SUFFIX);
String sLabel="";
if (sPrefix!=null) { sLabel+=sPrefix; }
for (int j=nLevel-nLevels+1; j<nLevel; j++) {
sLabel+=formatNumber(nCounter[j],sNumFormat[j],true)+".";
}
// TODO: Lettersync
sLabel+=formatNumber(nCounter[nLevel],sNumFormat[nLevel],true);
if (sSuffix!=null) { sLabel+=sSuffix; }
return sLabel;
}
// Utility method to generate number
private String formatNumber(int number,String sStyle,boolean bLetterSync) {
if ("a".equals(sStyle)) { return Misc.int2alph(number,bLetterSync); }
else if ("A".equals(sStyle)) { return Misc.int2Alph(number,bLetterSync); }
else if ("i".equals(sStyle)) { return Misc.int2roman(number); }
else if ("I".equals(sStyle)) { return Misc.int2Roman(number); }
else if ("1".equals(sStyle)) { return Misc.int2arabic(number); }
else return "";
}
}

View file

@ -0,0 +1,128 @@
/************************************************************************
*
* ListStyle.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002 by Henrik Just
*
* All Rights Reserved.
*
* Version 0.4 (2004-02-16)
*
*/
package writer2latex.office;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import writer2latex.util.Misc;
/** <p> Class representing a list style (including outline numbering) in OOo Writer</p>
*/
public class ListStyle extends OfficeStyle {
// the file format doesn't specify a maximum nesting level, but OOo
// currently supports 10
private static final int MAX_LEVEL = 10;
private PropertySet[] level;
private PropertySet[] levelStyle;
public ListStyle() {
level = new PropertySet[MAX_LEVEL+1];
levelStyle = new PropertySet[MAX_LEVEL+1];
for (int i=1; i<=MAX_LEVEL; i++) {
level[i] = new PropertySet();
levelStyle[i] = new PropertySet();
}
}
public String getLevelType(int i) {
if (i>=1 && i<=MAX_LEVEL) {
return level[i].getName();
}
else {
return null;
}
}
public boolean isNumber(int i) {
return XMLString.TEXT_LIST_LEVEL_STYLE_NUMBER.equals(level[i].getName());
}
public boolean isBullet(int i) {
return XMLString.TEXT_LIST_LEVEL_STYLE_BULLET.equals(level[i].getName());
}
public boolean isImage(int i) {
return XMLString.TEXT_LIST_LEVEL_STYLE_IMAGE.equals(level[i].getName());
}
public String getLevelProperty(int i, String sName) {
if (i>=1 && i<=MAX_LEVEL) {
return level[i].getProperty(sName);
}
else {
return null;
}
}
public String getLevelStyleProperty(int i, String sName) {
if (i>=1 && i<=MAX_LEVEL) {
return levelStyle[i].getProperty(sName);
}
else {
return null;
}
}
public void loadStyleFromDOM(Node node) {
super.loadStyleFromDOM(node);
// Collect level information from child elements:
if (node.hasChildNodes()){
NodeList nl = node.getChildNodes();
int nLen = nl.getLength();
for (int i = 0; i < nLen; i++ ) {
Node child=nl.item(i);
if (child.getNodeType()==Node.ELEMENT_NODE){
String sLevel = Misc.getAttribute(child,XMLString.TEXT_LEVEL);
if (sLevel!=null) {
int nLevel = Misc.getPosInteger(sLevel,1);
if (nLevel>=1 && nLevel<=MAX_LEVEL) {
level[nLevel].loadFromDOM(child);
// Also include style:properties
if (child.hasChildNodes()){
NodeList nl2 = child.getChildNodes();
int nLen2 = nl2.getLength();
for (int i2 = 0; i2 < nLen2; i2++ ) {
Node child2=nl2.item(i2);
if (child2.getNodeType()==Node.ELEMENT_NODE){
if (child2.getNodeName().equals(XMLString.STYLE_PROPERTIES)) {
levelStyle[nLevel].loadFromDOM(child2);
}
if (child2.getNodeName().equals(XMLString.STYLE_LIST_LEVEL_PROPERTIES)) { // oasis
levelStyle[nLevel].loadFromDOM(child2);
}
}
}
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,135 @@
/************************************************************************
*
* LoftReader.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-22)
*
*/
package writer2latex.office;
//import java.util.Hashtable;
//import java.util.Set;
import org.w3c.dom.Element;
//import org.w3c.dom.Node;
import writer2latex.util.Misc;
/**
* <p>The class reads a <code>text:illustration-index</code> or
* <code>text:table-index</code> element.</p>
*/
public class LoftReader {
private Element loftSource = null;
private Element indexBody = null;
private String sName = null; // (section) name for this lof/lot
private String sStyleName = null; // section style name
private boolean bUseCaption = true;
private String sCaptionSequenceName = null;
private boolean bIsByChapter = false; // default is document
private Element indexTitleTemplate = null;
private Element loftEntryTemplate = null;
private boolean bIsTableIndex;
/** <p>Initialize the LoftReader with a illustration/table index node
* @param onode a <code>text:*-index</code>
*/
public LoftReader(Element onode) {
bIsTableIndex = onode.getTagName().equals(XMLString.TEXT_TABLE_INDEX);
sName = Misc.getAttribute(onode,XMLString.TEXT_NAME);
sStyleName = Misc.getAttribute(onode,XMLString.TEXT_STYLE_NAME);
loftSource = bIsTableIndex ?
Misc.getChildByTagName(onode,XMLString.TEXT_TABLE_INDEX_SOURCE) :
Misc.getChildByTagName(onode,XMLString.TEXT_ILLUSTRATION_INDEX_SOURCE);
indexBody = Misc.getChildByTagName(onode,XMLString.TEXT_INDEX_BODY);
if (loftSource!=null) {
bUseCaption = !"false".equals(loftSource.getAttribute(XMLString.TEXT_USE_CAPTION));
sCaptionSequenceName = loftSource.getAttribute(XMLString.TEXT_CAPTION_SEQUENCE_NAME);
bIsByChapter = "chapter".equals(loftSource.getAttribute(XMLString.TEXT_INDEX_SCOPE));
indexTitleTemplate = Misc.getChildByTagName(loftSource,XMLString.TEXT_INDEX_TITLE_TEMPLATE);
loftEntryTemplate = bIsTableIndex ?
Misc.getChildByTagName(loftSource,XMLString.TEXT_TABLE_INDEX_ENTRY_TEMPLATE) :
Misc.getChildByTagName(loftSource,XMLString.TEXT_ILLUSTRATION_INDEX_ENTRY_TEMPLATE);
}
}
/** <p>Get the (section) name for this loft </p>
* @return the name of the loft
*/
public String getName() { return sName; }
/** <p>Get the (section) style name for this loft </p>
* @return name of the section style to use for this loft
*/
public String getStyleName() { return sStyleName; }
/** <p>Is this a table index or a figure index? </p>
* @return true if it's a table index
*/
public boolean isTableIndex() { return bIsTableIndex; }
/** <p>Is this loft by chapter? </p>
* @return true if the scope is a chapter only
*/
public boolean isByChapter() { return bIsByChapter; }
/** <p>Is this loft generated by captions? (otherwise: by object names)</p>
* @return true if we use captions
*/
public boolean useCaption() { return bUseCaption; }
/** <p>Get the sequence name to use for the caption</p>
* @return the name of the caption
*/
public String getCaptionSequenceName() { return sCaptionSequenceName; }
/** <p>Get the index title template for this loft</p>
* @return the <code>text:index-title-template</code> element, or null
*/
public Element getIndexTitleTemplate() { return indexTitleTemplate; }
/** <p>Get the entry template for this loft at a specific level</p>
* @param nLevel the outline level
* @return the <code>text:table-of-content-entry-template</code> element, or null
*/
public Element getLoftEntryTemplate(int nLevel) {
return loftEntryTemplate;
}
/** <p>Return the generated content of this loft, if available</p>
* @return the <code>text:index-body</code> element
*/
public Element getIndexBody() { return indexBody; }
}

View file

@ -0,0 +1,159 @@
/************************************************************************
*
* MIMETypes.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-24)
*
*/
package writer2latex.office;
/* Some helpers to handle the MIME types used by OOo
*/
public final class MIMETypes extends writer2latex.api.MIMETypes {
// OOo MIME types, taken from
// http://framework.openoffice.org/documentation/mimetypes/mimetypes.html
public static final String WRITER="application/vnd.sun.xml.writer";
public static final String CALC="application/vnd.sun.xml.calc";
public static final String IMPRESS="application/vnd.sun.xml.impress";
public static final String DRAW="application/vnd.sun.xml.draw";
public static final String CHART="application/vnd.sun.xml.chart";
public static final String MATH="application/vnd.sun.xml.math";
// OpenDocument MIME types (from spec)
public static final String ODT="application/vnd.oasis.opendocument.text";
public static final String ODS="application/vnd.oasis.opendocument.spreadsheet";
public static final String ODP="application/vnd.oasis.opendocument.presentation";
public static final String ODF="application/vnd.oasis.opendocument.formula";
// zip
public static final String ZIP="application/zip";
// Magic signatures for some binary files
public static final byte[] PNG_SIG = { (byte) 0x89, 0x50, 0x4e, 0x47 }; // .PNG
public static final byte[] JPEG_SIG = { (byte) 0xff, (byte) 0xd8, (byte) 0xff, (byte) 0xe0 };
public static final byte[] GIF87_SIG = { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }; // GIF87a
public static final byte[] GIF89_SIG = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }; // GIF89a
public static final byte[] TIFF_SIG = { 0x49, 0x49, 0x2A }; // II*
public static final byte[] BMP_SIG = { 0x42, 0x4d }; // BM
public static final byte[] WMF_SIG = { (byte) 0xd7, (byte) 0xcd, (byte) 0xc6, (byte) 0x9a };
public static final byte[] WMF30_SIG = { 1, 0, 9, 0 }; // Old WMF format, not reliable - see below
public static final byte[] EPS_SIG = { 0x25, 0x21 }; // %!
public static final byte[] SVM_SIG = { 0x56, 0x43, 0x4c, 0x4d, 0x54, 0x46 }; // VCLMTF
public static final byte[] ZIP_SIG = { 0x50, 0x4b, 0x03, 0x04 }; // PK..
// Preferred file extensions for some files
public static final String LATEX_EXT = ".tex";
public static final String BIBTEX_EXT = ".bib";
public static final String XHTML_EXT = ".html";
public static final String XHTML_MATHML_EXT = ".xhtml";
public static final String XHTML_MATHML_XSL_EXT = ".xml";
public static final String PNG_EXT = ".png";
public static final String JPEG_EXT = ".jpg"; // this is the default in graphicx.sty
public static final String GIF_EXT = ".gif";
public static final String TIFF_EXT = ".tif";
public static final String BMP_EXT = ".bmp";
public static final String WMF_EXT = ".wmf";
public static final String EPS_EXT = ".eps";
public static final String SVM_EXT = ".svm";
public static final String PDF_EXT = ".pdf";
private static final boolean isType(byte[] blob, byte[] sig) {
int n = sig.length;
for (int i=0; i<n; i++) {
if (blob[i]!=sig[i]) { return false; }
}
return true;
}
public static final String getMagicMIMEType(byte[] blob) {
if (isType(blob,PNG_SIG)) { return PNG; }
if (isType(blob,JPEG_SIG)) { return JPEG; }
if (isType(blob,GIF87_SIG)) { return GIF; }
if (isType(blob,GIF89_SIG)) { return GIF; }
if (isType(blob,TIFF_SIG)) { return TIFF; }
if (isType(blob,BMP_SIG)) { return BMP; }
if (isType(blob,WMF_SIG)) { return WMF; }
if (isType(blob,WMF30_SIG)) { return WMF; } // do not trust this..
if (isType(blob,EPS_SIG)) { return EPS; }
if (isType(blob,SVM_SIG)) { return SVM; }
if (isType(blob,ZIP_SIG)) { return ZIP; }
return "";
}
public static final String getFileExtension(String sMIME) {
if (PNG.equals(sMIME)) { return PNG_EXT; }
if (JPEG.equals(sMIME)) { return JPEG_EXT; }
if (GIF.equals(sMIME)) { return GIF_EXT; }
if (TIFF.equals(sMIME)) { return TIFF_EXT; }
if (BMP.equals(sMIME)) { return BMP_EXT; }
if (WMF.equals(sMIME)) { return WMF_EXT; }
if (EPS.equals(sMIME)) { return EPS_EXT; }
if (SVM.equals(sMIME)) { return SVM_EXT; }
if (PDF.equals(sMIME)) { return PDF_EXT; }
if (LATEX.equals(sMIME)) { return LATEX_EXT; }
if (BIBTEX.equals(sMIME)) { return BIBTEX_EXT; }
if (XHTML.equals(sMIME)) { return XHTML_EXT; }
if (XHTML_MATHML.equals(sMIME)) { return XHTML_MATHML_EXT; }
if (XHTML_MATHML_XSL.equals(sMIME)) { return XHTML_MATHML_XSL_EXT; }
return "";
}
public static boolean isVectorFormat(String sMIME) {
return WMF.equals(sMIME) || EPS.equals(sMIME) || SVM.equals(sMIME) || PDF.equals(sMIME);
}
}
/* Notes on old WMF format:
Found on math type objects: 1 0 - 9 0 - 0 3 - 90 1 - 0 0
Explanation from http://wvware.sourceforge.net/caolan/ora-wmf.html :
The standard Windows metafile header is 18 bytes in length and is structured as follows:
typedef struct _WindowsMetaHeader
{
WORD FileType; Type of metafile (0=memory, 1=disk)
WORD HeaderSize; Size of header in WORDS (always 9)
WORD Version; Version of Microsoft Windows used
DWORD FileSize; Total size of the metafile in WORDs
WORD NumOfObjects; Number of objects in the file
DWORD MaxRecordSize; The size of largest record in WORDs
WORD NumOfParams; Not Used (always 0)
} WMFHEAD;
FileType contains a value which indicates the location of the metafile data. A value of 0 indicates that the metafile is stored in memory, while a 1 indicates that it is stored on disk.
HeaderSize contains the size of the metafile header in 16-bit WORDs. This value is always 9.
Version stores the version number of Microsoft Windows that created the metafile. This value is always read in hexadecimal format. For example, in a metafile created by Windows 3.0 and 3.1, this item would have the value 0x0300.
FileSize specifies the total size of the metafile in 16-bit WORDs.
NumOfObjects specifies the number of objects that are in the metafile.
MaxRecordSize specifies the size of the largest record in the metafile in WORDs.
NumOfParams is not used and is set to a value of 0.
*/

View file

@ -0,0 +1,66 @@
/************************************************************************
*
* MasterPage.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2005 by Henrik Just
*
* All Rights Reserved.
*
* Version 0.5 (2005-10-10)
*
*/
package writer2latex.office;
import org.w3c.dom.Node;
import writer2latex.util.Misc;
/** <p> Class representing a master page in OOo Writer </p> */
public class MasterPage extends OfficeStyle {
private PropertySet properties = new PropertySet();
private Node header = null;
private Node headerLeft = null;
private Node footer = null;
private Node footerLeft = null;
public String getProperty(String sPropName) {
return properties.getProperty(sPropName);
}
public String getPageLayoutName() {
String sName = properties.getProperty(XMLString.STYLE_PAGE_MASTER_NAME);
if (sName==null) { // try oasis format
sName = properties.getProperty(XMLString.STYLE_PAGE_LAYOUT_NAME);
}
return sName;
}
public Node getHeader() { return header; }
public Node getHeaderLeft() { return headerLeft; }
public Node getFooter() { return footer; }
public Node getFooterLeft() { return footerLeft; }
public void loadStyleFromDOM(Node node) {
super.loadStyleFromDOM(node);
properties.loadFromDOM(node);
header = Misc.getChildByTagName(node,XMLString.STYLE_HEADER);
headerLeft = Misc.getChildByTagName(node,XMLString.STYLE_HEADER_LEFT);
footer = Misc.getChildByTagName(node,XMLString.STYLE_FOOTER);
footerLeft = Misc.getChildByTagName(node,XMLString.STYLE_FOOTER_LEFT);
}
}

View file

@ -0,0 +1,169 @@
/************************************************************************
*
* MetaData.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.office;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import writer2latex.util.*;
//import writer2latex.office.*;
import writer2latex.xmerge.OfficeDocument;
/**
* <p>This class represents the metadata of an OOo Writer document.</p>
*/
public class MetaData {
// Dublin Core
private String sTitle = "";
private String sCreator = "";
private String sInitialCreator = "";
private String sDate = "";
private String sDescription = "";
private String sLanguage = "";
private String sSubject = "";
// Keywords
private String sKeywords = "";
/** <p>Construct a new instance from an OOo Writer document.</p>
* @param oooDoc is the OOo document
*/
public MetaData(OfficeDocument oooDoc) {
// get the DOM (either package or flat)
Document dom = oooDoc.getMetaDOM();
if (dom==null) { dom = oooDoc.getContentDOM(); }
// get the office:meta element
NodeList list = dom.getElementsByTagName(XMLString.OFFICE_META);
if (list.getLength() == 0) { return; } // oops, no metadata - fails silently
Node meta = list.item(0);
if (!meta.hasChildNodes()) { return; }
// traverse the metadata
CSVList keywords = new CSVList(", ");
list = meta.getChildNodes();
int nLen = list.getLength();
for (int i=0; i<nLen; i++) {
Node child = list.item(i);
String sName = child.getNodeName();
if (XMLString.DC_TITLE.equals(sName)) {
sTitle = getContent(child);
}
if (XMLString.DC_CREATOR.equals(sName)) {
sCreator = getContent(child);
}
if (XMLString.DC_DATE.equals(sName)) {
sDate = getContent(child);
}
if (XMLString.DC_DESCRIPTION.equals(sName)) {
sDescription = getContent(child);
}
if (XMLString.DC_LANGUAGE.equals(sName)) {
sLanguage = getContent(child);
}
if (XMLString.DC_SUBJECT.equals(sName)) {
sSubject = getContent(child);
}
if (XMLString.META_INITIAL_CREATOR.equals(sName)) {
sInitialCreator = getContent(child);
}
if (XMLString.META_KEYWORD.equals(sName)) { // oasis
keywords.addValue(getContent(child));
}
if (XMLString.META_KEYWORDS.equals(sName)) {
// Old format: Keywords are contained within meta:keywords
if (child.hasChildNodes()) {
// traverse the keywords
NodeList keywordList = child.getChildNodes();
int nWordCount = keywordList.getLength();
for (int j=0; j<nWordCount; j++) {
Node grandchild = keywordList.item(j);
if (XMLString.META_KEYWORD.equals(grandchild.getNodeName())){
keywords.addValue(getContent(grandchild));
}
}
}
}
}
sKeywords = keywords.toString();
}
/** <p> Get the title of this document (may be null)</p>
* @return the title of the document
*/
public String getTitle() { return sTitle; }
/** <p> Get the creator of this document (may be null)</p>
* @return the creator of the document (or the initial creator if none is specified)
*/
public String getCreator() { return sCreator==null ? sInitialCreator : sCreator; }
/** <p> Get the initial creator of this document (may be null)</p>
* @return the initial creator of the document
*/
public String getInitialCreator() { return sInitialCreator; }
/** <p> Get the date of this document (may be null)</p>
* @return the date of the document
*/
public String getDate() { return sDate; }
/** <p> Get the description of this document (may be null)</p>
* @return the description of the document
*/
public String getDescription() { return sDescription; }
/** <p> Get the language of this document (may be null)</p>
* @return the language of the document
*/
public String getLanguage() { return sLanguage; }
/** <p> Get the subject of this document (may be null)</p>
* @return the subject of the document
*/
public String getSubject() { return sSubject; }
/** <p> Get the keywords of this document as a comma separated list (may be null)</p>
* @return the keywords of the document
*/
public String getKeywords() { return sKeywords; }
private String getContent(Node node) {
if (!node.hasChildNodes()) { return null; }
String s="";
NodeList list = node.getChildNodes();
int nLen = list.getLength();
for (int i=0; i<nLen; i++) {
if (list.item(i).getNodeType()==Node.TEXT_NODE) {
s+= list.item(i).getNodeValue();
}
}
return s;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,66 @@
/************************************************************************
*
* OfficeStyle.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2006 by Henrik Just
*
* All Rights Reserved.
*
* Version 0.5 (2006-11-23)
*
*/
package writer2latex.office;
import org.w3c.dom.Node;
import writer2latex.util.Misc;
/** <p> Abstract class representing a style in OOo </p> */
public abstract class OfficeStyle {
// These attributes are defined by OfficeStyleFamily upon collection of styles
protected String sName;
protected OfficeStyleFamily family;
protected boolean bAutomatic;
private String sDisplayName;
private String sParentName;
private String sListStyleName;
private String sMasterPageName;
public String getName() { return sName; }
public OfficeStyleFamily getFamily() { return family; }
public boolean isAutomatic() { return bAutomatic; }
public String getDisplayName() { return sDisplayName; }
public String getParentName() { return sParentName; }
public String getListStyleName() { return sListStyleName; }
public String getMasterPageName() { return sMasterPageName; }
public void loadStyleFromDOM(Node node){
sDisplayName = Misc.getAttribute(node,XMLString.STYLE_DISPLAY_NAME);
if (sDisplayName==null) { sDisplayName = sName; }
sParentName = Misc.getAttribute(node,XMLString.STYLE_PARENT_STYLE_NAME);
sListStyleName = Misc.getAttribute(node,XMLString.STYLE_LIST_STYLE_NAME);
sMasterPageName = Misc.getAttribute(node,XMLString.STYLE_MASTER_PAGE_NAME);
}
}

View file

@ -0,0 +1,137 @@
/************************************************************************
*
* OfficeStyleFamily.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-23)
*
*/
package writer2latex.office;
import org.w3c.dom.Node;
import java.util.Hashtable;
import java.util.Enumeration;
import writer2latex.util.Misc;
/** Container class representing a style family in OOo */
public class OfficeStyleFamily {
private Hashtable styles = new Hashtable();
private Class styleClass;
private Hashtable displayNames = new Hashtable();
private OfficeStyle defaultStyle = null;
/** Create a new OfficeStyleFamily based on a class
* @param styleClass the subclass of OfficeStyle used to represent styles
* in this family
*/
public OfficeStyleFamily(Class styleClass) {
this.styleClass = styleClass;
}
/** Define the default style for this family, ie. an unnamed style providing
* defaults for some style properties. This style cannot be found using
* getStyle or getStyleByDisplayName.
* @param style the new default style
*/
public void setDefaultStyle(OfficeStyle style) {
defaultStyle = style;
}
/** Get the default style for this family
* @return the default style, or null if none is defined
*/
public OfficeStyle getDefaultStyle() {
return defaultStyle;
}
/** Get a style by name
* @param sName the name of the style
* @return the style, or null if such a style does not exist
*/
public OfficeStyle getStyle(String sName) {
if (sName==null) { return null; }
else { return (OfficeStyle) styles.get(sName); }
}
/** Get a style by display name. Automatic styles does not have a display
* name, so only common styles can be retrieved with this method
* @param sDisplayName the display name of the style
* @return the style, or null if such a style does not exist
*/
public OfficeStyle getStyleByDisplayName(String sDisplayName) {
if (sDisplayName==null) { return null; }
else { return getStyle((String) displayNames.get(sDisplayName)); }
}
/** Get the display name for the style with the specified name.
* If this is an automatic style, the parent style is used
* @param sName the style name
* @return the display name, or null if the style does not exist
*/
public String getDisplayName(String sName) {
OfficeStyle style = getStyle(sName);
if (style==null) { return null; }
if (style.isAutomatic()) {
style = getStyle(style.getParentName());
if (style==null) { return null; }
}
return style.getDisplayName();
}
/** Get all named styles in the family (ie. excluding the default style)
* @return an enumeration of all styles represented by OfficeStyle objects
*/
public Enumeration getStylesEnumeration(){
return styles.elements();
}
/** Load a style from a DOM representation
* @param node the style:... node representing the style
* @param bAutomatic if true, the style is an automatic style
*/
public void loadStyleFromDOM(Node node, boolean bAutomatic) {
String sName = Misc.getAttribute(node,XMLString.STYLE_NAME);
if (sName!=null) {
try {
OfficeStyle style = (OfficeStyle) styleClass.newInstance();
style.sName=sName;
style.family=this;
style.bAutomatic=bAutomatic;
style.loadStyleFromDOM(node);
styles.put(sName,style);
if (!bAutomatic) {
// Create backlink from display name to name
displayNames.put(style.getDisplayName(),sName);
}
}
catch (InstantiationException e) {
// Will not happen if a proper class is passed in the constructor
}
catch (IllegalAccessException e) {
// Should also not happen
}
}
}
}

View file

@ -0,0 +1,13 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>The package writer2latex.office</title>
</head>
<body>
<p>A collection of classes that reads ODF files.</p>
<p>In general the DOM tree is used directly, but these classes provides a more
convenient interface to specific parts of the document.</p>
</body>
</html>

View file

@ -0,0 +1,91 @@
/************************************************************************
*
* PageLayout.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2007 by Henrik Just
*
* All Rights Reserved.
*
* Version 0.5 (2007-03-17)
*
*/
package writer2latex.office;
import org.w3c.dom.Node;
import writer2latex.util.Misc;
/** <p> Class representing a page master in OOo Writer. This is represented
* like other styles + a separate style for header and footer</p>
*/
public class PageLayout extends StyleWithProperties {
private String sPageUsage = null;
private boolean bHasHeaderStyle = false;
private PropertySet headerStyle = new PropertySet();
private boolean bHasFooterStyle = false;
private PropertySet footerStyle = new PropertySet();
public String getPageUsage() {
return sPageUsage;
}
public boolean hasHeaderStyle() { return bHasHeaderStyle; }
public String getHeaderProperty(String sPropName) {
return headerStyle.getProperty(sPropName);
}
public boolean hasFooterStyle() { return bHasFooterStyle; }
public String getFooterProperty(String sPropName) {
return footerStyle.getProperty(sPropName);
}
public void loadStyleFromDOM(Node node) {
super.loadStyleFromDOM(node);
sPageUsage = Misc.getAttribute(node,XMLString.STYLE_PAGE_USAGE);
Node hsNode = Misc.getChildByTagName(node,XMLString.STYLE_HEADER_STYLE);
if (hsNode!=null) {
Node hsProperties = Misc.getChildByTagName(hsNode,XMLString.STYLE_PROPERTIES);
if (hsProperties==null) { // oasis:
hsProperties = Misc.getChildByTagName(hsNode,XMLString.STYLE_HEADER_FOOTER_PROPERTIES);
}
if (hsProperties!=null) {
bHasHeaderStyle = true;
headerStyle.loadFromDOM(hsProperties);
}
}
Node fsNode = Misc.getChildByTagName(node,XMLString.STYLE_FOOTER_STYLE);
if (fsNode!=null) {
Node fsProperties = Misc.getChildByTagName(fsNode,XMLString.STYLE_PROPERTIES);
if (fsProperties==null) { // oasis:
fsProperties = Misc.getChildByTagName(fsNode,XMLString.STYLE_HEADER_FOOTER_PROPERTIES);
}
if (fsProperties!=null) {
bHasFooterStyle = true;
footerStyle.loadFromDOM(fsProperties);
}
}
}
}

View file

@ -0,0 +1,98 @@
/************************************************************************
*
* PropertySet.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2007 by Henrik Just
*
* All Rights Reserved.
*
* Version 0.5 (2007-03-17)
*
*/
package writer2latex.office;
import org.w3c.dom.Node;
import org.w3c.dom.NamedNodeMap;
import java.util.Enumeration;
import java.util.Hashtable;
/** <p> Class representing a set of style properties in OOo (actually this
is simply the set of attributes of an element). </p>
*/
public class PropertySet {
private Hashtable properties = new Hashtable();
private String sName;
public PropertySet() {
properties = new Hashtable();
sName="";
}
public String getProperty(String sPropName) {
if (sPropName!=null) {
String sValue = (String) properties.get(sPropName);
if (sValue!=null && sValue.endsWith("inch")) {
// Cut of inch to in
return sValue.substring(0,sValue.length()-2);
}
else {
return sValue;
}
}
else {
return null;
}
}
public String getName() { return sName; }
public void loadFromDOM(Node node) {
sName = node.getNodeName();
// read the attributes of the node, if any
if (node!=null) {
NamedNodeMap attrNodes = node.getAttributes();
if (attrNodes!=null) {
int nLen = attrNodes.getLength();
for (int i=0; i<nLen; i++){
Node attr = attrNodes.item(i);
properties.put(attr.getNodeName(),attr.getNodeValue());
}
}
}
}
public boolean containsProperty(String sProperty) {
return sProperty!=null && properties.containsKey(sProperty);
}
public void setProperty(String sProperty, String sValue){
properties.put(sProperty,sValue);
}
public String toString() {
String s="";
Enumeration keys = properties.keys();
while (keys.hasMoreElements()) {
String sKey = (String) keys.nextElement();
String sValue = (String) properties.get(sKey);
s += sKey+"="+sValue+" ";
}
return s;
}
}

View file

@ -0,0 +1,232 @@
/************************************************************************
*
* SVMReader.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2004 by Urban Widmark
*
* All Rights Reserved.
*
* Version 0.4 (2004-02-26)
*
*/
package writer2latex.office;
/** This class contains a static method to extract EPS data from an SVM file */
public final class SVMReader {
/* Notes on SVM file format:
Relevant OO 1.1.0 Source Files include:
tools/source/stream/vcompat.cxx
vcl/source/gdi/gdimtf.cxx
vcl/inc/gdimtf.hxx
vcl/source/gdi/metaact.cxx
vcl/inc/metaact.hxx
vcl/source/gdi/mapmod.cxx
vcl/inc/mapmod.hxx
tools/source/generic/fract.cxx
tools/inc/fract.hxx
tools/source/generic/gen.cxx
tools/inc/gen.hxx
VersionCompat (6 bytes)
version - 2 bytes
totalsize - 4 bytes
Point (Pair) (8 bytes)
X - 4 bytes (long)
Y - 4 bytes (long)
Fraction (8 bytes)
nNumerator - 4 bytes (long)
nDenominator - 4 bytes (long)
MapMode (6 + 2 + 8 + 8 + 8 + 1 = 33 bytes)
VersionCompat
meUnit - UINT16
maOrigin - Point
maScaleX - Fraction
maScaleY - Fraction
mbSimple - BOOL (1 byte)
Size (Pair) (8 bytes)
width - 4 bytes (long)
height - 4 bytes (long)
GfxLink (16 or 57 bytes)
VersionCompat
nType - 2 bytes
nSize - 4 bytes
nUserId - 4 bytes
aSize - Size (version >=2)
aMapMode - MapMode (version >=2)
MetaEPSAction
VersionCompat
maGfxLink - GfxLink
data[maGfxLink.nSize] - bytes
maPoint - Point
maSize - Size
maSubst - GDIMetaFile (alternative image?)
SVM file
"VCLMTF"
Compat - VersionCompat
nStmCompressMode - UINT32
aPrefMapMode - MapMode
aPrefSize - Size
count - UINT32
action[count] - MetaAction
Example header from an EPS image included in a Writer document:
00000000: 5643 4c4d 5446 0100 3100 0000 0000 0000 VCLMTF..1.......
00000010: 0100 1b00 0000 0800 0000 0000 0000 0000 ................
00000020: 0100 0000 0100 0000 0100 0000 0100 0000 ................
00000030: 0169 0100 00fd 0000 0001 0000 008f 0001 .i..............
00000040: 0096 3200 0002 0033 0000 0001 003f 3000 ..2....3.....?0.
00000050: 0000 0000 0000 0000 0000 0000 0001 001b ................
00000060: 0000 000a 0000 0000 0000 0000 0001 0000 ................
00000070: 0001 0000 0001 0000 0001 0000 0001 2521 ..............%!
5643 4c4d 5446 "VCLMTF" 0
0100 version 6
3100 0000 totalsize
0000 0000 compress 12
0100 aPrefMapMode.version 16
1b00 0000 aPrefMapMode.totalsize
0800 aPrefMapMode.meUnit
0000 0000 aPrefMapMode.maOrigin
0000 0000
0100 0000 aPrefMapMode.maScaleX
0100 0000
0100 0000 aPrefMapMode.maScaleY
0100 0000
01 aPrefMapMode.mbSimple
6901 0000 aPrefSize 49
fd00 0000
0100 0000 nCount 57
8f00 type == META_EPS_ACTION 61
0100 version
9632 0000 totalsize
0200 version
3300 0000 totalsize
0100 nType
3f30 0000 nSize 75
0000 0000 nUserId
0000 0000 aSize
0000 0000
0100
1b00 0000
0a00
0000 0000
0000 0000
0100 0000
0100 0000
0100 0000
0100 0000
01
Beginning of EPS data:
2521
Note that maPoint/maSize/maSubst are all after the EPS file.
*/
/** Determine if this SVM contains an EPS document and retrieve start and
* end positions if so.
*
* @param blob byte array containing SVM file
* @param offlen integer array to retrieve the offset into the SVM file
* (offlen[0]) and the length (offlen[1]) of the EPS
* document. If the method returns false, the array
* will be unchanged.
*
* @return returns true if the SVM contains an EPS document
*/
public static final boolean readSVM(byte[] blob, int[] offlen) {
int pos = 57;
int nCount = getInt(blob, pos);
pos += 4;
for (int i=0; i<nCount; i++) {
int type = getShort(blob, pos);
pos += 2;
// We only understand META_EPS_ACTION
if (type != 143)
return false;
pos += 6;
int version = getShort(blob, pos);
pos += 6; // version + totalsize
pos += 2; // nType
// This is the size of the EPS data.
int size = getInt(blob, pos);
pos += 8;
if (version >= 2)
pos += 41;
for (int j=0; j<MIMETypes.EPS_SIG.length; j++)
if (MIMETypes.EPS_SIG[j] != blob[pos + j])
return false;
offlen[0] = pos;
offlen[1] = size;
// For now we only understand files where the EPS entry is
// the first MetaAction
break;
}
return true;
}
private static int getInt(byte[] blob, int pos)
{
return
((blob[pos+0] & 0xff) << 0) +
((blob[pos+1] & 0xff) << 8) +
((blob[pos+2] & 0xff) << 16) +
((blob[pos+3] & 0xff) << 24);
}
private static int getShort(byte[] blob, int pos)
{
return
((blob[pos+0] & 0xff) << 0) +
((blob[pos+1] & 0xff) << 8);
}
}

View file

@ -0,0 +1,298 @@
/************************************************************************
*
* StyleWithProperties.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-11-22)
*/
package writer2latex.office;
//import org.w3c.dom.Element;
import org.w3c.dom.Node;
//import org.w3c.dom.NamedNodeMap;
//import java.util.Hashtable;
import writer2latex.util.Misc;
/** <p> Class representing a style in OOo which contains a style:properties
* element </p>
*/
public class StyleWithProperties extends OfficeStyle {
private final static int OLDPROPS = 0;
private final static int TEXT = 1;
private final static int PAR = 2;
private final static int SECTION = 3;
private final static int TABLE = 4;
private final static int COLUMN = 5;
private final static int ROW = 6;
private final static int CELL = 7;
private final static int GRAPHIC = 8;
private final static int PAGE = 9;
//private final static int DRAWPAGE = 10;
private final static int COUNT = 10;
private PropertySet[] properties = new PropertySet[COUNT];
private boolean bIsOldProps = false;
private PropertySet backgroundImageProperties = new PropertySet();
private int nColCount = 0;
private boolean bHasFootnoteSep = false;
private PropertySet footnoteSep = new PropertySet();
public StyleWithProperties() {
for (int i=0; i<COUNT; i++) {
properties[i] = new PropertySet();
}
}
public void loadStyleFromDOM(Node node) {
super.loadStyleFromDOM(node);
// read the properties of the style, if any
Node child = node.getFirstChild();
while (child!=null) {
if (child.getNodeType()==Node.ELEMENT_NODE) {
String sName = child.getNodeName();
if (XMLString.STYLE_PROPERTIES.equals(sName)) {
bIsOldProps = true; // style:properties identifies old format
loadPropertiesFromDOM(OLDPROPS,child);
}
else if (XMLString.STYLE_TEXT_PROPERTIES.equals(sName)) {
loadPropertiesFromDOM(TEXT,child);
}
else if (XMLString.STYLE_PARAGRAPH_PROPERTIES.equals(sName)) {
loadPropertiesFromDOM(PAR,child);
}
else if (XMLString.STYLE_SECTION_PROPERTIES.equals(sName)) {
loadPropertiesFromDOM(SECTION,child);
}
else if (XMLString.STYLE_TABLE_PROPERTIES.equals(sName)) {
loadPropertiesFromDOM(TABLE,child);
}
else if (XMLString.STYLE_TABLE_COLUMN_PROPERTIES.equals(sName)) {
loadPropertiesFromDOM(COLUMN,child);
}
else if (XMLString.STYLE_TABLE_ROW_PROPERTIES.equals(sName)) {
loadPropertiesFromDOM(ROW,child);
}
else if (XMLString.STYLE_TABLE_CELL_PROPERTIES.equals(sName)) {
loadPropertiesFromDOM(CELL,child);
}
else if (XMLString.STYLE_GRAPHIC_PROPERTIES.equals(sName)) {
loadPropertiesFromDOM(GRAPHIC,child);
}
else if (XMLString.STYLE_PAGE_LAYOUT_PROPERTIES.equals(sName)) {
loadPropertiesFromDOM(PAGE,child);
}
else if (XMLString.STYLE_DRAWING_PAGE_PROPERTIES.equals(sName)) {
loadPropertiesFromDOM(PAGE,child);
}
}
child = child.getNextSibling();
}
}
private void loadPropertiesFromDOM(int nIndex,Node node) {
properties[nIndex].loadFromDOM(node);
// Several property sets may contain these complex properties, but only one per style:
Node child = node.getFirstChild();
while (child!=null) {
if (child.getNodeType()==Node.ELEMENT_NODE) {
String sName = child.getNodeName();
if (XMLString.STYLE_BACKGROUND_IMAGE.equals(sName)) {
backgroundImageProperties.loadFromDOM(child);
}
else if (XMLString.STYLE_COLUMNS.equals(sName)) {
nColCount = Misc.getPosInteger(Misc.getAttribute(child,
XMLString.FO_COLUMN_COUNT),1);
// TODO: read individual columns
}
else if (XMLString.STYLE_FOOTNOTE_SEP.equals(sName)) {
bHasFootnoteSep = true;
footnoteSep.loadFromDOM(child);
}
}
child = child.getNextSibling();
}
}
protected String getProperty(int nIndex, String sName, boolean bInherit) {
int nRealIndex = bIsOldProps ? OLDPROPS : nIndex;
if (properties[nRealIndex].containsProperty(sName)) {
String sValue = properties[nRealIndex].getProperty(sName);
return Misc.truncateLength(sValue);
}
else if (bInherit && getParentName()!=null) {
StyleWithProperties parentStyle = (StyleWithProperties) family.getStyle(getParentName());
if (parentStyle!=null) {
return parentStyle.getProperty(nIndex,sName,bInherit);
}
}
return null; // no value
}
public String getTextProperty(String sName, boolean bInherit) {
return getProperty(TEXT,sName,bInherit);
}
public String getParProperty(String sName, boolean bInherit) {
return getProperty(PAR,sName,bInherit);
}
public String getSectionProperty(String sName, boolean bInherit) {
return getProperty(SECTION,sName,bInherit);
}
public String getTableProperty(String sName, boolean bInherit) {
return getProperty(TABLE,sName,bInherit);
}
public String getColumnProperty(String sName, boolean bInherit) {
return getProperty(COLUMN,sName,bInherit);
}
public String getRowProperty(String sName, boolean bInherit) {
return getProperty(ROW,sName,bInherit);
}
public String getCellProperty(String sName, boolean bInherit) {
return getProperty(CELL,sName,bInherit);
}
public String getGraphicProperty(String sName, boolean bInherit) {
return getProperty(GRAPHIC,sName,bInherit);
}
// TODO: Remove this method
public String getProperty(String sProperty, boolean bInherit){
String sValue;
for (int i=0; i<COUNT; i++) {
sValue = getProperty(i,sProperty,bInherit);
if (sValue!=null) { return sValue; }
}
return null; // no value
}
// TODO: Remove this method
public String getProperty(String sProperty){
return getProperty(sProperty,true);
}
protected String getAbsoluteProperty(int nIndex, String sProperty){
int nRealIndex = bIsOldProps ? OLDPROPS : nIndex;
if (properties[nRealIndex].containsProperty(sProperty)){
String sValue=(String) properties[nRealIndex].getProperty(sProperty);
if (sValue.endsWith("%")) {
StyleWithProperties parentStyle
= (StyleWithProperties) family.getStyle(getParentName());
if (parentStyle!=null) {
String sParentValue = parentStyle.getAbsoluteProperty(nIndex,sProperty);
if (sParentValue!=null) { return Misc.multiply(sValue,sParentValue); }
}
else if (getFamily()!=null && getFamily().getDefaultStyle()!=null) {
StyleWithProperties style = (StyleWithProperties) getFamily().getDefaultStyle();
String sDefaultValue=(String) style.getProperty(nIndex,sProperty,false);
if (sValue !=null) { return Misc.multiply(sValue,sDefaultValue); }
}
}
else {
return Misc.truncateLength(sValue);
}
}
else if (getParentName()!=null){
StyleWithProperties parentStyle
= (StyleWithProperties) family.getStyle(getParentName());
if (parentStyle!=null) {
return parentStyle.getAbsoluteProperty(nIndex,sProperty);
}
}
else if (getFamily()!=null && getFamily().getDefaultStyle()!=null) {
StyleWithProperties style = (StyleWithProperties) getFamily().getDefaultStyle();
String sValue=(String) style.getProperty(nIndex,sProperty,false);
if (sValue !=null) { return sValue; }
}
// no value!
return null;
}
public String getAbsoluteTextProperty(String sName) {
return getAbsoluteProperty(TEXT,sName);
}
public String getAbsoluteParProperty(String sName) {
return getAbsoluteProperty(PAR,sName);
}
public String getAbsoluteSectionProperty(String sName) {
return getAbsoluteProperty(SECTION,sName);
}
public String getAbsoluteTableProperty(String sName) {
return getAbsoluteProperty(TABLE,sName);
}
public String getAbsoluteColumnProperty(String sName) {
return getAbsoluteProperty(COLUMN,sName);
}
public String getAbsoluteRowProperty(String sName) {
return getAbsoluteProperty(ROW,sName);
}
public String getAbsoluteCellProperty(String sName) {
return getAbsoluteProperty(CELL,sName);
}
public String getAbsoluteGraphicProperty(String sName) {
return getAbsoluteProperty(GRAPHIC,sName);
}
// TODO: Remove this method
public String getAbsoluteProperty(String sProperty){
String sValue;
for (int i=0; i<COUNT; i++) {
sValue = getAbsoluteProperty(i,sProperty);
if (sValue!=null) { return sValue; }
}
return null; // no value
}
// Get a length property that defaults to 0cm
public String getAbsoluteLength(String sProperty) {
String s = getAbsoluteProperty(sProperty);
if (s==null) { return "0cm"; }
else { return s; }
}
public String getBackgroundImageProperty(String sName) {
return backgroundImageProperties.getProperty(sName);
}
public int getColCount() { return nColCount; }
public boolean hasFootnoteSep() { return bHasFootnoteSep; }
public String getFootnoteProperty(String sPropName) {
return footnoteSep.getProperty(sPropName);
}
}

View file

@ -0,0 +1,66 @@
/************************************************************************
*
* TableLine.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-09-04)
*
*/
package writer2latex.office;
import org.w3c.dom.Node;
import writer2latex.util.Misc;
/**
* <p> This class represents the properties of a row or column in a table</p>
*/
public class TableLine {
private String sStyleName;
private String sVisibility;
private String sDefaultCellStyleName;
private boolean bDisplay;
private boolean bHeader;
public TableLine(Node node, boolean bHeader, boolean bDisplay) {
// Node must be table:table-column or table:table-row
sStyleName = Misc.getAttribute(node,XMLString.TABLE_STYLE_NAME);
sVisibility = Misc.getAttribute(node,XMLString.TABLE_VISIBILITY);
if (sVisibility==null) { sVisibility = "visible"; }
sDefaultCellStyleName = Misc.getAttribute(node,XMLString.TABLE_DEFAULT_CELL_STYLE_NAME);
this.bDisplay = bDisplay;
this.bHeader = bHeader;
}
public String getStyleName() { return sStyleName; }
public String getVisibility() { return sVisibility; }
public boolean isCollapse() { return "collapse".equals(sVisibility); }
public boolean isFilter() { return "filter".equals(sVisibility); }
public String getDefaultCellStyleName() { return sDefaultCellStyleName; }
public boolean isDisplay() { return bDisplay; }
public boolean isHeader() { return bHeader; }
}

View file

@ -0,0 +1,122 @@
/************************************************************************
*
* TableRange.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2008 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2008-09-07)
*
*/
package writer2latex.office;
/**
* This class represent a table range within a table. A table range is defined
* as a rectangular area (such as a print range), possibly excluding filtered
* and hidden rows. A <code>TableView</code> can be derived from a table range,
* providing read access to the range.
*/
public class TableRange {
private TableReader reader;
private int nFirstRow;
private int nLastRow;
private int nFirstCol;
private int nLastCol;
private boolean bIncludeHidden;
private boolean bIncludeFiltered;
public TableRange(TableReader reader) {
this.reader = reader;
// The initial view is the complete table
nFirstRow = 0;
nLastRow = reader.getRowCount()-1;
nFirstCol = 0;
nLastCol = reader.getColCount()-1;
bIncludeHidden = true;
bIncludeFiltered = true;
}
public void setFirstRow(int nRow) {
// Adjust to a valid value (in 0..nLastRow)
if (nRow<0) { nFirstRow = 0; }
else if (nRow>nLastRow) { nFirstRow = nLastRow; }
else { nFirstRow = nRow; }
}
public int getFirstRow() {
return nFirstRow;
}
public void setLastRow(int nRow) {
// Adjust to a valid value (in nFirstRow..(nRowCount-1))
if (nRow<nFirstRow) { nLastRow = nFirstRow; }
else if (nRow>=reader.getRowCount()) { nLastRow = reader.getRowCount()-1; }
else { nLastRow = nRow; }
}
public int getLastRow() {
return nLastRow;
}
public void setFirstCol(int nCol) {
// Adjust to a valid value (in 0..nLastCol)
if (nCol<0) { nFirstCol = 0; }
else if (nCol>nLastCol) { nFirstCol = nLastCol; }
else { nFirstCol = nCol; }
}
public int getFirstCol() {
return nFirstCol;
}
public void setLastCol(int nCol) {
// Adjust to a valid value (in nFirstCol..(nColCount-1))
if (nCol<nFirstCol) { nLastCol = nFirstCol; }
else if (nCol>=reader.getColCount()) { nLastCol = reader.getColCount()-1; }
else { nLastCol = nCol; }
}
public int getLastCol() {
return nLastCol;
}
public void setIncludeHidden(boolean b) {
bIncludeHidden = b;
}
public boolean includeHidden() {
return bIncludeHidden;
}
public void setIncludeFiltered(boolean b) {
bIncludeFiltered = b;
}
public boolean includeFiltered() {
return bIncludeFiltered;
}
public TableView createTableView() {
return new TableView(reader, this);
}
}

View file

@ -0,0 +1,131 @@
/************************************************************************
*
* TableRangeParser.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2009 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.0 (2009-02-16)
*
*/
package writer2latex.office;
import writer2latex.util.Misc;
import writer2latex.util.SimpleInputBuffer;
/**
* This class parses a space separated list of table ranges. A table range is of the form
* <sheet name>.<column><row>:<sheet name>.<column><row>
* where the sheet name is quoted (single quotes) if it contains spaces
* the column is one or two uppercase letters (A-Z)
* the row is an integer.
* The sheet name is currently ignored, and so are any syntax errors
*/
public class TableRangeParser {
private SimpleInputBuffer inbuf;
public TableRangeParser(String s) {
inbuf = new SimpleInputBuffer(s);
inbuf.skipSpaces();
}
public boolean hasMoreRanges() {
return !inbuf.atEnd();
}
// returns { first col, first row, last col, last row }
public int[] getRange() {
if (!inbuf.atEnd()) {
int[] nFirst = parseAddress();
parseCharacter(':');
int[] nLast = parseAddress();
int[] nResult = { nFirst[0], nFirst[1], nLast[0], nLast[1] };
return nResult;
}
else {
return null;
}
}
private void parseCharacter(char c) {
// ignore the character if it's missing
if (inbuf.peekChar()==c) { inbuf.getChar(); }
inbuf.skipSpaces();
}
// returns { col, row }
private int[] parseAddress() {
parseSheetName();
parseCharacter('.');
int[] nResult = new int[2];
nResult[0] = parseColumn();
nResult[1] = parseRow();
inbuf.skipSpaces();
return nResult;
}
private void parseSheetName() {
if (inbuf.peekChar()=='.') {
// The sheet name may be omitted
}
else if (inbuf.peekChar()=='\'') {
// The sheet name must be quoted if it contains space, dots or apostrophes
inbuf.getChar(); // skip leading '
while (!inbuf.atEnd() && inbuf.peekChar()!='\'') {
inbuf.getChar();
if (inbuf.peekChar()=='\'' && inbuf.peekFollowingChar()=='\'') {
// Escaped '
inbuf.getChar(); inbuf.getChar();
}
}
inbuf.getChar(); // skip trailing '
}
else {
// Unquoted sheet name, ends with dot
while (!inbuf.atEnd() && inbuf.peekChar()!='.') {
inbuf.getChar();
}
}
inbuf.skipSpaces();
}
// Return column number, starting with 0
private int parseColumn() {
if (inbuf.peekChar()>='A' && inbuf.peekChar()<='Z') {
int nFirst = inbuf.getChar()-65;
if (inbuf.peekChar()>='A' && inbuf.peekChar()<='Z') {
int nSecond = inbuf.getChar()-65;
return 26*(nFirst+1)+nSecond;
}
else {
return nFirst;
}
}
else {
return 0; // syntax error
}
}
// Return row number, starting with 0
private int parseRow() {
return Misc.getPosInteger(inbuf.getInteger(),1)-1;
}
}

Some files were not shown because too many files have changed in this diff Show more