Started modifying project structure
This commit is contained in:
parent
4e16ed01c2
commit
1e4ee37f89
566 changed files with 3340 additions and 176 deletions
441
src/main/java/writer2latex/Application.java
Normal file
441
src/main/java/writer2latex/Application.java
Normal file
|
@ -0,0 +1,441 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* Application.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-01-09)
|
||||
*
|
||||
*/
|
||||
|
||||
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.HashSet;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Set;
|
||||
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>Command line 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>-html5</code>, <code>-xhtml</code>,
|
||||
<code>-xhtml+mathml</code>, <code>-epub</code>, <code>-epub3</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>-stylesheet[=]filename</code>
|
||||
* <li><code>-resource[=]filename[::media type]</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<String> configFileNames = new Vector<String>();
|
||||
private String sTemplateFileName = null;
|
||||
private String sStyleSheetFileName = null;
|
||||
private Set<String> resources = new HashSet<String>();
|
||||
private Hashtable<String,String> options = new Hashtable<String,String>();
|
||||
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 {
|
||||
//long time = System.currentTimeMillis();
|
||||
Application app = new Application();
|
||||
app.parseCommandLine(args);
|
||||
app.doConversion();
|
||||
//System.out.println("Total conversion time was "+(System.currentTimeMillis()-time)+" miliseconds");
|
||||
} 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 5a: 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 5b: Read style sheet
|
||||
if (sStyleSheetFileName!=null) {
|
||||
try {
|
||||
System.out.println("Reading style sheet "+sStyleSheetFileName);
|
||||
byte [] styleSheetBytes = Misc.inputStreamToByteArray(new FileInputStream(sStyleSheetFileName));
|
||||
converter.readStyleSheet(new ByteArrayInputStream(styleSheetBytes));
|
||||
}
|
||||
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 style sheet file!");
|
||||
System.out.println(" "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5c: Read style resources
|
||||
for (String sResource : resources) {
|
||||
String sMediaType;
|
||||
String sFileName;
|
||||
int nSeparator = sResource.indexOf("::");
|
||||
if (nSeparator>-1) {
|
||||
sFileName = sResource.substring(0,nSeparator);
|
||||
sMediaType = sResource.substring(nSeparator+2);
|
||||
}
|
||||
else {
|
||||
sFileName = sResource;
|
||||
sMediaType = null;
|
||||
}
|
||||
System.out.println("Reading resource file "+sFileName);
|
||||
try {
|
||||
byte [] resourceBytes = Misc.inputStreamToByteArray(new FileInputStream(sFileName));
|
||||
converter.readResource(new ByteArrayInputStream(resourceBytes),sFileName,sMediaType);
|
||||
} catch (IOException e) {
|
||||
System.out.println("--> Failed to read the resource 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<String> keys = options.keys();
|
||||
while (keys.hasMoreElements()) {
|
||||
String sKey = 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.out.println(" Please make sure the file is in OpenDocument format");
|
||||
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(" -xhtml11");
|
||||
System.out.println(" -xhtml+mathml");
|
||||
System.out.println(" -html5");
|
||||
System.out.println(" -epub");
|
||||
System.out.println(" -epub3");
|
||||
System.out.println(" -recurse");
|
||||
System.out.println(" -template[=]<template file>");
|
||||
System.out.println(" -stylesheet[=]<style sheet file>");
|
||||
System.out.println(" -resource[=]<resource file>[::<media type>]");
|
||||
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 ("-html5".equals(sArg)) { sTargetMIME = MIMETypes.HTML5; }
|
||||
else if ("-xhtml".equals(sArg)) { sTargetMIME = MIMETypes.XHTML; }
|
||||
else if ("-xhtml11".equals(sArg)) { sTargetMIME = MIMETypes.XHTML11; }
|
||||
else if ("-xhtml+mathml".equals(sArg)) { sTargetMIME = MIMETypes.XHTML_MATHML; }
|
||||
else if ("-epub".equals(sArg)) { sTargetMIME = MIMETypes.EPUB; }
|
||||
else if ("-epub3".equals(sArg)) { sTargetMIME = MIMETypes.EPUB3; }
|
||||
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 if ("-stylesheet".equals(sArg)) { sStyleSheetFileName = sArg2; }
|
||||
else if ("-resource".equals(sArg)) { resources.add(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");
|
||||
}
|
||||
|
||||
|
||||
}
|
93
src/main/java/writer2latex/BatchHandlerImpl.java
Normal file
93
src/main/java/writer2latex/BatchHandlerImpl.java
Normal file
|
@ -0,0 +1,93 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* BatchHandlerImpl.java
|
||||
*
|
||||
* Copyright: 2002-2008 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
95
src/main/java/writer2latex/api/BatchConverter.java
Normal file
95
src/main/java/writer2latex/api/BatchConverter.java
Normal file
|
@ -0,0 +1,95 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* BatchConverter.java
|
||||
*
|
||||
* Copyright: 2002-2008 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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);
|
||||
|
||||
}
|
76
src/main/java/writer2latex/api/BatchHandler.java
Normal file
76
src/main/java/writer2latex/api/BatchHandler.java
Normal file
|
@ -0,0 +1,76 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* BatchHandler.java
|
||||
*
|
||||
* Copyright: 2002-2008 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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();
|
||||
|
||||
}
|
120
src/main/java/writer2latex/api/ComplexOption.java
Normal file
120
src/main/java/writer2latex/api/ComplexOption.java
Normal file
|
@ -0,0 +1,120 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* Config.java
|
||||
*
|
||||
* Copyright: 2002-2009 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2009-11-19)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.api;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** A complex option is a set of named keys, each pointing to a set of named attributes
|
||||
*/
|
||||
public class ComplexOption {
|
||||
|
||||
private Map<String,Map<String,String>> options = new HashMap<String,Map<String,String>>();
|
||||
|
||||
/** Clear the contents of the set
|
||||
*
|
||||
*/
|
||||
public void clear() {
|
||||
options.clear();
|
||||
}
|
||||
|
||||
/** Remove an option from the set, if it exists
|
||||
*
|
||||
* @param sName the name of the key to remove
|
||||
*/
|
||||
public void remove(String sName) {
|
||||
if (options.containsKey(sName)) {
|
||||
options.remove(sName);
|
||||
}
|
||||
}
|
||||
|
||||
/** Define a key. If the key already exists, the old value will be replaced
|
||||
*
|
||||
* @param sName the name of the key. The name must be non-empty, otherwise the request will be ignored.
|
||||
* @param attributes
|
||||
*/
|
||||
public void put(String sName, Map<String,String> attributes) {
|
||||
if (sName!=null && sName.length()>0) {
|
||||
options.put(sName, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
/** Define a key using a <i>copy</i> of a the provided attributes.
|
||||
* If the key already exists, the old value will be replaced
|
||||
*
|
||||
* @param sName the name of the key. The name must be non-empty, otherwise the request will be ignored.
|
||||
* @param attributes
|
||||
*/
|
||||
public void copy(String sName, Map<String,String> attributes) {
|
||||
if (sName!=null && sName.length()>0) {
|
||||
Map<String,String> newAttributes = new HashMap<String,String>();
|
||||
for (String sAttrName : attributes.keySet()) {
|
||||
newAttributes.put(sAttrName, attributes.get(sAttrName));
|
||||
}
|
||||
put(sName, newAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the value belonging to a key
|
||||
*
|
||||
* @param sName the name of the key
|
||||
* @return the attributes, or null if the option doesn't exist
|
||||
*/
|
||||
public Map<String,String> get(String sName) {
|
||||
return options.get(sName);
|
||||
}
|
||||
|
||||
/** Copy all values from another <code>ComplexOption</code>
|
||||
* (overwrites existing values)
|
||||
* @param co another instance of <code>ComplexOption</code>
|
||||
*/
|
||||
public void copyAll(ComplexOption co) {
|
||||
for (String sName : co.keySet()) {
|
||||
copy(sName, co.get(sName));
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the names of all options that are currently defined by this complex option
|
||||
*
|
||||
* @return all names as a <code>Set</code>
|
||||
*/
|
||||
public Set<String> keySet() {
|
||||
return options.keySet();
|
||||
}
|
||||
|
||||
/** Test if this complex options contains a specific option name
|
||||
*
|
||||
* @param sName the name to test
|
||||
* @return true if the name exists
|
||||
*/
|
||||
public boolean containsKey(String sName) {
|
||||
return options.containsKey(sName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
105
src/main/java/writer2latex/api/Config.java
Normal file
105
src/main/java/writer2latex/api/Config.java
Normal file
|
@ -0,0 +1,105 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* Config.java
|
||||
*
|
||||
* Copyright: 2002-2009 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2009-09-22)
|
||||
*
|
||||
*/
|
||||
|
||||
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);
|
||||
|
||||
/** Get a complex option
|
||||
*
|
||||
* @param sName the name of the complex option
|
||||
* @return the option
|
||||
*/
|
||||
public ComplexOption getComplexOption(String sName);
|
||||
|
||||
}
|
||||
|
60
src/main/java/writer2latex/api/ContentEntry.java
Normal file
60
src/main/java/writer2latex/api/ContentEntry.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ContentEntry.java
|
||||
*
|
||||
* Copyright: 2002-2010 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2010-03-15)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.api;
|
||||
|
||||
/** This interface represents a content entry, that is a named reference
|
||||
* to a position within the output document.
|
||||
*/
|
||||
public interface ContentEntry {
|
||||
/** Get the outline level of this <code>ContentEntry</code>.
|
||||
* The top level is 1 (entries corresponding to indexes are considered
|
||||
* top level).
|
||||
* Note that intermediate levels may be missing (e.g. a heading of
|
||||
* level 3 may follow immediately after a heading of level 1).
|
||||
*
|
||||
* @return the outline level
|
||||
*/
|
||||
public int getLevel();
|
||||
|
||||
/** Get the title for this entry
|
||||
*
|
||||
* @return the title
|
||||
*/
|
||||
public String getTitle();
|
||||
|
||||
/** Get the file associated with the entry
|
||||
*
|
||||
* @return the output file
|
||||
*/
|
||||
public OutputFile getFile();
|
||||
|
||||
/** Get the name of a target within the file, if any
|
||||
*
|
||||
* @return the target name, or null if no target is needed
|
||||
*/
|
||||
public String getTarget();
|
||||
|
||||
}
|
153
src/main/java/writer2latex/api/Converter.java
Normal file
153
src/main/java/writer2latex/api/Converter.java
Normal file
|
@ -0,0 +1,153 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* Converter.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2014-08-28)
|
||||
*
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
/** Read a style sheet to <em>include</em> with the converted document.
|
||||
* The format of the style sheet depends on the <code>Converter</code>
|
||||
* implementation.
|
||||
*
|
||||
* @param is an <code>InputStream</code> from which to read the style sheet
|
||||
* @throws IOException if some exception occurs while reading the style sheet
|
||||
*/
|
||||
public void readStyleSheet(InputStream is) throws IOException;
|
||||
|
||||
/** Read a style sheet to <em>include</em> with the converted document.
|
||||
* The format of the style sheet depends on the <code>Converter</code>
|
||||
* implementation.
|
||||
*
|
||||
* @param file a file from which to read the style sheet
|
||||
* @throws IOException if the file does not exist or some exception occurs
|
||||
* while reading the style sheet
|
||||
*/
|
||||
public void readStyleSheet(File file) throws IOException;
|
||||
|
||||
/** Read a resource to <em>include</em> with the converted document.
|
||||
* A resource can be any (binary) file and will be placed in the same directory as
|
||||
* the style sheet
|
||||
*
|
||||
* @param is an <code>InputStream</code> from which to read the resource
|
||||
* @param sFileName the file name to use for the resource
|
||||
* @param sMediaType the media type of the resource, if null the media type will be guessed from the file name
|
||||
* @throws IOException if some exception occurs while reading the resource
|
||||
*/
|
||||
public void readResource(InputStream is, String sFileName, String sMediaType) throws IOException;
|
||||
|
||||
/** Read a style sheet to <em>include</em> with the converted document.
|
||||
* A resource can be any (binary) file and will be placed in the same directory as
|
||||
* the style sheet
|
||||
*
|
||||
* @param file a file from which to read the style sheet
|
||||
* @param sFileName the file name to use for the resource
|
||||
* @param sMediaType the media type of the resource, if null the media type will be guessed from the file name
|
||||
* @throws IOException if the file does not exist or some exception occurs
|
||||
* while reading the resource
|
||||
*/
|
||||
public void readResource(File file, String sFileName, String sMediaType) 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;
|
||||
|
||||
/** Convert a document
|
||||
*
|
||||
* @param dom a DOM tree representing the document as flat XML
|
||||
* @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)
|
||||
* @param bDestructive set to true if the converter is allowed to remove contents from the DOM tree (to save memory)
|
||||
* @return a <code>ConverterResult</code> containing the converted document
|
||||
* @throws IOException if some exception occurs while reading the document
|
||||
*/
|
||||
public ConverterResult convert(org.w3c.dom.Document dom, String sTargetFileName, boolean bDestructive)
|
||||
throws IOException;
|
||||
|
||||
}
|
143
src/main/java/writer2latex/api/ConverterFactory.java
Normal file
143
src/main/java/writer2latex/api/ConverterFactory.java
Normal file
|
@ -0,0 +1,143 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ConverterFactory.java
|
||||
*
|
||||
* Copyright: 2002-2018 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 2.0 (2018-03-06)
|
||||
*
|
||||
*/
|
||||
|
||||
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 = "1.9.1";
|
||||
private static final String DATE = "2018-03-06";
|
||||
|
||||
/** Return the Writer2LaTeX version in the form
|
||||
* (major version).(minor version).(patch level)<br/>
|
||||
* Development versions have an odd minor version number
|
||||
* @return the version number
|
||||
*/
|
||||
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/xhtml11</code> for XHTML 1.1 format
|
||||
* Note that this is <em>not</em> the recommended media type for XHTML 1.1
|
||||
* (see http://www.w3.org/TR/xhtml-media-types/), but it is used internally
|
||||
* by Writer2xhtml to distinguish from XHTML+MathML</li>
|
||||
* <li><code>application/xhtml+xml</code> for XHTML+MathML</li>
|
||||
* <li><code>text/html5</code> for HTML5 documents
|
||||
* Note that this is <em>not</em> the recommended media type for HTML5
|
||||
* (see http://wiki.whatwg.org/), but it is used internally
|
||||
* by Writer2xhtml to distinguish from HTML5</li>
|
||||
* <li><code>application/epub+zip</code></li> for EPUB format</li>
|
||||
* <li><code>epub3</code> for EPUB 3 format</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.XHTML11.equals(sMIME)) {
|
||||
converter = createInstance("writer2latex.xhtml.Xhtml11Converter");
|
||||
}
|
||||
else if (MIMETypes.XHTML_MATHML.equals(sMIME)) {
|
||||
converter = createInstance("writer2latex.xhtml.XhtmlMathMLConverter");
|
||||
}
|
||||
else if (MIMETypes.HTML5.equals(sMIME)) {
|
||||
converter = createInstance("writer2latex.xhtml.Html5Converter");
|
||||
}
|
||||
else if (MIMETypes.EPUB3.equals(sMIME)) {
|
||||
converter = createInstance("writer2latex.epub.EPUB3Converter");
|
||||
}
|
||||
else if (MIMETypes.EPUB.equals(sMIME)) {
|
||||
converter = createInstance("writer2latex.epub.EPUBConverter");
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
131
src/main/java/writer2latex/api/ConverterResult.java
Normal file
131
src/main/java/writer2latex/api/ConverterResult.java
Normal file
|
@ -0,0 +1,131 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ConverterResult.java
|
||||
*
|
||||
* Copyright: 2002-2011 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2011-07-20)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/** 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
|
||||
* Deprecated as of Writer2LaTeX 1.2: The master document is always the first document
|
||||
* returned by the <code>iterator</code>
|
||||
*
|
||||
* @return <code>OutputFile</code> the master document
|
||||
*/
|
||||
@Deprecated public OutputFile getMasterDocument();
|
||||
|
||||
/** Gets an <code>Iterator</code> to access all files in the
|
||||
* <code>ConverterResult</code>. The iterator will return the master documents first
|
||||
* in logical order (starting with the primary master document)
|
||||
* @return an <code>Iterator</code> of all files
|
||||
*/
|
||||
public Iterator<OutputFile> iterator();
|
||||
|
||||
/** Get the meta data associated with the source document
|
||||
* @return the meta data
|
||||
*/
|
||||
public MetaData getMetaData();
|
||||
|
||||
/** Get the content table (based on headings) for this <code>ConverterResult</code>
|
||||
*
|
||||
* @return list view of the content
|
||||
*/
|
||||
public List<ContentEntry> getContent();
|
||||
|
||||
/** Get the entry which contains the table page
|
||||
*
|
||||
* @return the entry or null if there is no title page
|
||||
*/
|
||||
public ContentEntry getTitlePageFile();
|
||||
|
||||
/** Get the entry which contains the start of the actual text (the first chapter, or simply the start of
|
||||
* the document if there are no headings)
|
||||
*
|
||||
* @return the entry
|
||||
*/
|
||||
public ContentEntry getTextFile();
|
||||
|
||||
/** Get the entry which contains the table of contents
|
||||
*
|
||||
* @return the entry or null if a table of content does not exist
|
||||
*/
|
||||
public ContentEntry getTocFile();
|
||||
|
||||
/** Get the entry which contains the list of tables
|
||||
*
|
||||
* @return the entry or null if a list of tables does not exist
|
||||
*/
|
||||
public ContentEntry getLotFile();
|
||||
|
||||
/** Get the entry which contains the list of figures
|
||||
*
|
||||
* @return the entry or null if a list of figures does not exist
|
||||
*/
|
||||
public ContentEntry getLofFile();
|
||||
|
||||
/** Get the entry which contains the alphabetical index
|
||||
*
|
||||
* @return the entry or null if an alphabetical index does not exist
|
||||
*/
|
||||
public ContentEntry getIndexFile();
|
||||
|
||||
/** Get the entry which contains the bibliography
|
||||
*
|
||||
* @return the entry or null if a bibliography does not exist
|
||||
*/
|
||||
public ContentEntry getBibliographyFile();
|
||||
|
||||
/** Get the entry which contains the cover (which usually will contain a cover image)
|
||||
*
|
||||
* @return the entry or null if a cover does not exist
|
||||
*/
|
||||
public ContentEntry getCoverFile();
|
||||
|
||||
/** Get the entry which contains the actual cover image
|
||||
*
|
||||
* @return the entry or null if a cover image does not exist
|
||||
*/
|
||||
public ContentEntry getCoverImageFile();
|
||||
|
||||
|
||||
|
||||
/** 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;
|
||||
|
||||
}
|
57
src/main/java/writer2latex/api/GraphicConverter.java
Normal file
57
src/main/java/writer2latex/api/GraphicConverter.java
Normal file
|
@ -0,0 +1,57 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* GraphicConverter.java
|
||||
*
|
||||
* Copyright: 2002-2008 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
140
src/main/java/writer2latex/api/IndexPageEntry.java
Normal file
140
src/main/java/writer2latex/api/IndexPageEntry.java
Normal file
|
@ -0,0 +1,140 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* IndexPageEntry.java
|
||||
*
|
||||
* Copyright: 2002-2009 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
62
src/main/java/writer2latex/api/MIMETypes.java
Normal file
62
src/main/java/writer2latex/api/MIMETypes.java
Normal file
|
@ -0,0 +1,62 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* MIMETypes.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-01-09)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.api;
|
||||
|
||||
/* Some helpers to handle the MIME types used by OOo and Writer2LaTeX
|
||||
*/
|
||||
|
||||
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 EMF="image/x-emf";
|
||||
public static final String WMF="image/x-wmf";
|
||||
public static final String EPS="image/x-eps";
|
||||
public static final String SVG="image/svg+xml";
|
||||
// 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";
|
||||
|
||||
// Destination formats
|
||||
public static final String XHTML="text/html";
|
||||
/** This is a fake MIME type, for internal use only */
|
||||
public static final String XHTML11="application/xhtml11";
|
||||
public static final String XHTML_MATHML="application/xhtml+xml";
|
||||
/** This is a fake MIME type, for internal use only */
|
||||
public static final String HTML5="text/html5";
|
||||
public static final String EPUB="application/epub+zip";
|
||||
/** This is not a MIME type either */
|
||||
public static final String EPUB3="epub3";
|
||||
public static final String LATEX="application/x-latex";
|
||||
public static final String BIBTEX="application/x-bibtex";
|
||||
public static final String TEXT="text";
|
||||
|
||||
}
|
81
src/main/java/writer2latex/api/MetaData.java
Normal file
81
src/main/java/writer2latex/api/MetaData.java
Normal file
|
@ -0,0 +1,81 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* MetaData.java
|
||||
*
|
||||
* Copyright: 2002-2010 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2010-12-15)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.api;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/** This interface provides access to the predefined meta data of the
|
||||
* source document (currently incomplete)
|
||||
*/
|
||||
public interface MetaData {
|
||||
/** Get the title of the source document
|
||||
*
|
||||
* @return the title (may return an empty string)
|
||||
*/
|
||||
public String getTitle();
|
||||
|
||||
/** Get the subject of the source document
|
||||
*
|
||||
* @return the subject (may return an empty string)
|
||||
*/
|
||||
public String getSubject();
|
||||
|
||||
/** Get the keywords of the source document
|
||||
*
|
||||
* @return the keywords as a comma separated list (may return an empty string)
|
||||
*/
|
||||
public String getKeywords();
|
||||
|
||||
/** Get the description of the source document
|
||||
*
|
||||
* @return the description (may return an empty string)
|
||||
*/
|
||||
public String getDescription();
|
||||
|
||||
/** Get the creator of the source document (or the initial creator if none is specified)
|
||||
*
|
||||
* @return the creator (may return an empty string)
|
||||
*/
|
||||
public String getCreator();
|
||||
|
||||
/** Get the (main) language of the document
|
||||
*
|
||||
* @return the language
|
||||
*/
|
||||
public String getLanguage();
|
||||
|
||||
/** Get the date of the source document
|
||||
*
|
||||
* @return the date (may return an empty string)
|
||||
*/
|
||||
public String getDate();
|
||||
|
||||
/** Get the user-defined meta data
|
||||
*
|
||||
* @return the user-defined meta data as a name-value map
|
||||
*/
|
||||
public Map<String,String> getUserDefinedMetaData();
|
||||
}
|
70
src/main/java/writer2latex/api/OutputFile.java
Normal file
70
src/main/java/writer2latex/api/OutputFile.java
Normal file
|
@ -0,0 +1,70 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* OutputFile.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-05-05)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.api;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/** An <code>OutputFile</code> represents 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();
|
||||
|
||||
/** Get the MIME type of the <code>OutputFile</code>.
|
||||
*
|
||||
* @return string representation of the MIME type
|
||||
*/
|
||||
public String getMIMEType();
|
||||
|
||||
/** Test whether this document is part of the main document flow (master documents) or
|
||||
* an auxiliary document
|
||||
*
|
||||
* @return true if this document is a master document
|
||||
*/
|
||||
public boolean isMasterDocument();
|
||||
|
||||
/** Test whether this document contains mathematical formulas
|
||||
*
|
||||
* @return true if the document contains formulas
|
||||
*/
|
||||
public boolean containsMath();
|
||||
}
|
13
src/main/java/writer2latex/api/Package.html
Normal file
13
src/main/java/writer2latex/api/Package.html
Normal 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>
|
60
src/main/java/writer2latex/api/StarMathConverter.java
Normal file
60
src/main/java/writer2latex/api/StarMathConverter.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* StarMathConverter.java
|
||||
*
|
||||
* Copyright: 2002-2008 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
181
src/main/java/writer2latex/base/BatchConverterBase.java
Normal file
181
src/main/java/writer2latex/base/BatchConverterBase.java
Normal file
|
@ -0,0 +1,181 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* BatchConverterBase.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2014-08-27)
|
||||
*
|
||||
*/
|
||||
|
||||
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.iterator().next().getFileName();
|
||||
}
|
||||
|
||||
|
||||
}
|
292
src/main/java/writer2latex/base/BibliographyGenerator.java
Normal file
292
src/main/java/writer2latex/base/BibliographyGenerator.java
Normal file
|
@ -0,0 +1,292 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* BibliographyGenerator.java
|
||||
*
|
||||
* Copyright: 2002-2018 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2018-03-06)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.base;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import writer2latex.office.OfficeReader;
|
||||
import writer2latex.office.XMLString;
|
||||
import writer2latex.util.Misc;
|
||||
import writer2latex.util.StringComparator;
|
||||
|
||||
/** This class is used to generate bibliographic references and a bibliography.
|
||||
* Bibliographies are generated from a list of items (text:bibliograpy-mark), a global configuration
|
||||
* (text:bibliography-configuration) and a formatting template (text:bibliography-source)
|
||||
*/
|
||||
public abstract class BibliographyGenerator {
|
||||
|
||||
// Bibliography configuration data
|
||||
private String sPrefix = "[";
|
||||
private String sSuffix = "]";
|
||||
|
||||
// The sorted list of bibliography marks
|
||||
private List<Element> bibMarkList = new ArrayList<Element>();
|
||||
|
||||
// Map from key to label
|
||||
private Map<String,String> bibMarkLabel = new HashMap<String,String>();
|
||||
|
||||
// Flag to identify numbering
|
||||
private boolean bNumberedEntries = false;
|
||||
|
||||
// Flag to identify truncation of templates
|
||||
private boolean bSkipKey = false;
|
||||
|
||||
/** Create a new bibliography generator based on a bibliography configuration and a list of bibliography marks
|
||||
*
|
||||
* @param ofr the office reader used to access the source document
|
||||
* @param bSkipKey set to true if the key should be excluded when applying templates
|
||||
*/
|
||||
protected BibliographyGenerator(OfficeReader ofr, boolean bSkipKey) {
|
||||
this.bSkipKey = bSkipKey;
|
||||
|
||||
Element bibConfig = ofr.getBibliographyConfiguration();
|
||||
if (bibConfig!=null) {
|
||||
if (bibConfig.hasAttribute(XMLString.TEXT_PREFIX)) {
|
||||
sPrefix = bibConfig.getAttribute(XMLString.TEXT_PREFIX);
|
||||
}
|
||||
if (bibConfig.hasAttribute(XMLString.TEXT_SUFFIX)) {
|
||||
sSuffix = bibConfig.getAttribute(XMLString.TEXT_SUFFIX);
|
||||
}
|
||||
}
|
||||
|
||||
collectBibMarks(ofr.getBibliographyMarks());
|
||||
sortBibMarks(bibConfig);
|
||||
createLabels(bibConfig);
|
||||
}
|
||||
|
||||
// Collect the bibliography marks from the raw list, removing any duplicates
|
||||
private void collectBibMarks(List<Element> bibMarks) {
|
||||
Set<String> keys = new HashSet<String>();
|
||||
for (Element bibMark : bibMarks) {
|
||||
String sKey = bibMark.getAttribute(XMLString.TEXT_IDENTIFIER);
|
||||
if (!keys.contains(sKey)) {
|
||||
bibMarkList.add(bibMark);
|
||||
keys.add(sKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the bibliography marks based on the settings in the bibliography configuration
|
||||
private void sortBibMarks(Element bibConfig) {
|
||||
if (bibConfig!=null && "false".equals(bibConfig.getAttribute(XMLString.TEXT_SORT_BY_POSITION))) {
|
||||
// Get the sort algorithm
|
||||
//String sSortAlgorithm = "alphanumeric";
|
||||
//if (bibConfig.hasAttribute(XMLString.TEXT_SORT_ALGORITHM)) {
|
||||
// sSortAlgorithm = bibConfig.getAttribute(XMLString.TEXT_SORT_ALGORITHM);
|
||||
//}
|
||||
|
||||
// Get the sort keys
|
||||
List<String> sortKeys = new ArrayList<String>();
|
||||
List<Boolean> sortAscending = new ArrayList<Boolean>();
|
||||
Node child = bibConfig.getFirstChild();
|
||||
while (child!=null) {
|
||||
if (child.getNodeType()==Node.ELEMENT_NODE && child.getNodeName().equals(XMLString.TEXT_SORT_KEY)) {
|
||||
String sKey = Misc.getAttribute(child, XMLString.TEXT_KEY);
|
||||
if (sKey!=null) {
|
||||
sortKeys.add(sKey);
|
||||
sortAscending.add(!"false".equals(Misc.getAttribute(child, XMLString.TEXT_SORT_ASCENDING)));
|
||||
}
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
|
||||
// Sort the list
|
||||
Comparator<Element> comparator = new StringComparator<Element>(
|
||||
Misc.getAttribute(bibConfig,XMLString.FO_LANGUAGE),
|
||||
Misc.getAttribute(bibConfig, XMLString.FO_COUNTRY)) {
|
||||
private List<String> sortKeys = null;
|
||||
private List<Boolean> sortAscending = null;
|
||||
|
||||
Comparator<Element> setSortKeys(List<String> sortKeys, List<Boolean> sortAscending) {
|
||||
this.sortKeys = sortKeys;
|
||||
this.sortAscending = sortAscending;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int compare(Element a, Element b) {
|
||||
int nCount = sortKeys.size();
|
||||
for (int i=0; i<nCount; i++) {
|
||||
String sWorda = a.getAttribute("text:"+sortKeys.get(i));
|
||||
String sWordb = b.getAttribute("text:"+sortKeys.get(i));
|
||||
int nCompare = getCollator().compare(sWorda, sWordb)*(sortAscending.get(i) ? 1 : -1);
|
||||
if (nCompare!=0) { return nCompare; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}.setSortKeys(sortKeys, sortAscending);
|
||||
|
||||
Collections.sort(bibMarkList, comparator);
|
||||
}
|
||||
}
|
||||
|
||||
private void createLabels(Element bibConfig) {
|
||||
bNumberedEntries = bibConfig!=null && "true".equals(bibConfig.getAttribute(XMLString.TEXT_NUMBERED_ENTRIES));
|
||||
int nCount = bibMarkList.size();
|
||||
for (int i=0; i<nCount; i++) {
|
||||
Element item = bibMarkList.get(i);
|
||||
String sKey = item.getAttribute(XMLString.TEXT_IDENTIFIER);
|
||||
if (bNumberedEntries) {
|
||||
bibMarkLabel.put(sKey, Integer.toString(i+1));
|
||||
}
|
||||
else {
|
||||
bibMarkLabel.put(sKey, sKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Get all labels used in the bibliography
|
||||
*
|
||||
* @return the set of labels
|
||||
*/
|
||||
protected Collection<String> getLabels() {
|
||||
return bibMarkLabel.values();
|
||||
}
|
||||
|
||||
/** Check whether entries are numbered rather than labeled with the key
|
||||
*
|
||||
* @return true if the entries are numbered
|
||||
*/
|
||||
protected boolean isNumberedEntries() {
|
||||
return bNumberedEntries;
|
||||
}
|
||||
|
||||
/** Get citation text for a reference to the bibliography
|
||||
*
|
||||
* @param sKey the key of the bibliography item
|
||||
* @return the citation text to be shown in the document
|
||||
*/
|
||||
public String generateCitation(String sKey) {
|
||||
return sPrefix+bibMarkLabel.get(sKey)+sSuffix;
|
||||
}
|
||||
|
||||
/** Generate a bibliography
|
||||
*
|
||||
* @param bibSource a text:bibliography-source element
|
||||
*/
|
||||
protected void generateBibliography(Element bibSource) {
|
||||
Map<String,Element> bibEntryTemplate = collectTemplates(bibSource);
|
||||
for (Element bibMark : bibMarkList) {
|
||||
String sKey = bibMark.getAttribute(XMLString.TEXT_IDENTIFIER);
|
||||
String sType = bibMark.getAttribute(XMLString.TEXT_BIBLIOGRAPHY_TYPE);
|
||||
if (bibEntryTemplate.containsKey(sType)) {
|
||||
Element template = bibEntryTemplate.get(sType);
|
||||
String sStyleName = template.getAttribute(XMLString.TEXT_STYLE_NAME);
|
||||
insertBibliographyItem(sStyleName,sKey);
|
||||
applyTemplate(template,bibMark);
|
||||
}
|
||||
else { // Use a default template (identical with the default template in LO)
|
||||
String sAuthor = bibMark.getAttribute(XMLString.TEXT_AUTHOR);
|
||||
String sTitle = bibMark.getAttribute(XMLString.TEXT_TITLE);
|
||||
String sYear = bibMark.getAttribute(XMLString.TEXT_YEAR);
|
||||
insertBibliographyItem(null,sKey);
|
||||
if (!bSkipKey) {
|
||||
insertBibliographyItemElement(null,bibMarkLabel.get(sKey));
|
||||
insertBibliographyItemElement(null,": ");
|
||||
}
|
||||
insertBibliographyItemElement(null,sAuthor);
|
||||
insertBibliographyItemElement(null,", ");
|
||||
insertBibliographyItemElement(null,sTitle);
|
||||
insertBibliographyItemElement(null,", ");
|
||||
insertBibliographyItemElement(null,sYear);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String,Element> collectTemplates(Element bibSource) {
|
||||
Map<String,Element> bibEntryTemplate = new HashMap<String,Element>();
|
||||
Node child = bibSource.getFirstChild();
|
||||
while (child!=null) {
|
||||
if (child.getNodeType()==Node.ELEMENT_NODE
|
||||
&& child.getNodeName().equals(XMLString.TEXT_BIBLIOGRAPHY_ENTRY_TEMPLATE)) {
|
||||
String sType = Misc.getAttribute(child, XMLString.TEXT_BIBLIOGRAPHY_TYPE);
|
||||
if (sType!=null) {
|
||||
bibEntryTemplate.put(sType, (Element)child);
|
||||
}
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
return bibEntryTemplate;
|
||||
}
|
||||
|
||||
private void applyTemplate(Element template, Element bibMark) {
|
||||
boolean bSkip = bSkipKey;
|
||||
Node child = template.getFirstChild();
|
||||
while (child!=null) {
|
||||
if (child.getNodeType()==Node.ELEMENT_NODE) {
|
||||
if (child.getNodeName().equals(XMLString.TEXT_INDEX_ENTRY_BIBLIOGRAPHY)) {
|
||||
String sField = Misc.getAttribute(child, XMLString.TEXT_BIBLIOGRAPHY_DATA_FIELD);
|
||||
if (sField!=null) {
|
||||
String sValue = bibMark.getAttribute("text:"+sField);
|
||||
if (sField.equals("identifier")) {
|
||||
sValue = bibMarkLabel.get(sValue);
|
||||
}
|
||||
else {
|
||||
bSkip = false;
|
||||
}
|
||||
if (!bSkip) {
|
||||
String sElementStyleName = Misc.getAttribute(child,XMLString.TEXT_STYLE_NAME);
|
||||
insertBibliographyItemElement(sElementStyleName,sValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (child.getNodeName().equals(XMLString.TEXT_INDEX_ENTRY_SPAN)) {
|
||||
if (!bSkip) {
|
||||
String sValue = Misc.getPCDATA(child);
|
||||
String sElementStyleName = Misc.getAttribute(child,XMLString.TEXT_STYLE_NAME);
|
||||
insertBibliographyItemElement(sElementStyleName,sValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
}
|
||||
|
||||
/** Insert a new bibliography item
|
||||
*
|
||||
* @param sStyleName a paragraph style to apply to the item
|
||||
* @param sKey the key of the bibliography item
|
||||
*/
|
||||
protected abstract void insertBibliographyItem(String sStyleName, String sKey);
|
||||
|
||||
/** Insert an element of a bibliography item
|
||||
*
|
||||
* @param sStyleName a character style to apply to the element
|
||||
* @param sText the element text
|
||||
*/
|
||||
protected abstract void insertBibliographyItemElement(String sStyleName, String sText);
|
||||
|
||||
}
|
179
src/main/java/writer2latex/base/BinaryGraphicsDocument.java
Normal file
179
src/main/java/writer2latex/base/BinaryGraphicsDocument.java
Normal file
|
@ -0,0 +1,179 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* BinaryGraphicsDocument.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-05-05)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.base;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import writer2latex.api.OutputFile;
|
||||
|
||||
|
||||
/** This class is used to represent a binary graphics document to be included in the converter result.
|
||||
* I may also represent a linked image, which should <em>not</em> be included (and will produce an empty file
|
||||
* if it is).
|
||||
*/
|
||||
public class BinaryGraphicsDocument implements OutputFile {
|
||||
|
||||
private String sFileName;
|
||||
private String sMimeType;
|
||||
|
||||
private boolean bAcceptedFormat;
|
||||
|
||||
private boolean bRecycled = false;
|
||||
|
||||
// Data for an embedded image
|
||||
private byte[] blob = null;
|
||||
private int nOff = 0;
|
||||
private int nLen = 0;
|
||||
|
||||
/**Constructs a new graphics document.
|
||||
* Until data is added using the <code>read</code> methods, the document is considered a link to
|
||||
* the image given by the file name.
|
||||
*
|
||||
* @param sFileName The name or URL of the <code>GraphicsDocument</code>.
|
||||
* @param sMimeType the MIME type of the document
|
||||
*/
|
||||
public BinaryGraphicsDocument(String sFileName, String sMimeType) {
|
||||
this.sFileName = sFileName;
|
||||
this.sMimeType = sMimeType;
|
||||
bAcceptedFormat = false; // or rather "don't know"
|
||||
}
|
||||
|
||||
/** Construct a new graphics document which is a recycled version of the supplied one.
|
||||
* This implies that all information is identical, but the recycled version does not contain any data.
|
||||
* This is for images that are used more than once in the document.
|
||||
*
|
||||
* @param bgd the source document
|
||||
*/
|
||||
public BinaryGraphicsDocument(BinaryGraphicsDocument bgd) {
|
||||
this.sFileName = bgd.getFileName();
|
||||
this.sMimeType = bgd.getMIMEType();
|
||||
this.bAcceptedFormat = bgd.isAcceptedFormat();
|
||||
this.bRecycled = true;
|
||||
}
|
||||
|
||||
/** Is this graphics document recycled?
|
||||
*
|
||||
* @return true if this is the case
|
||||
*/
|
||||
public boolean isRecycled() {
|
||||
return bRecycled;
|
||||
}
|
||||
|
||||
/** Set image contents to a byte array
|
||||
*
|
||||
* @param data the image data
|
||||
* @param bIsAcceptedFormat flag to indicate that the format of the image is acceptable for the converter
|
||||
*/
|
||||
public void setData(byte[] data, boolean bIsAcceptedFormat) {
|
||||
setData(data,0,data.length,bIsAcceptedFormat);
|
||||
}
|
||||
|
||||
/** Set image contents to part of a byte array
|
||||
*
|
||||
* @param data the image data
|
||||
* @param nOff the offset into the byte array
|
||||
* @param nLen the number of bytes to use
|
||||
* @param bIsAcceptedFormat flag to indicate that the format of the image is acceptable for the converter
|
||||
*/
|
||||
public void setData(byte[] data, int nOff, int nLen, boolean bIsAcceptedFormat) {
|
||||
this.blob = data;
|
||||
this.nOff = nOff;
|
||||
this.nLen = nLen;
|
||||
this.bAcceptedFormat = bIsAcceptedFormat;
|
||||
}
|
||||
|
||||
/** Does this <code>BinaryGraphicsDocument</code> represent a linked image?
|
||||
*
|
||||
* @return true if so
|
||||
*/
|
||||
public boolean isLinked() {
|
||||
return blob==null && !bRecycled;
|
||||
}
|
||||
|
||||
/** Is this image in an acceptable format for the converter?
|
||||
*
|
||||
* @return true if so (always returns false for linked images)
|
||||
*/
|
||||
public boolean isAcceptedFormat() {
|
||||
return bAcceptedFormat;
|
||||
}
|
||||
|
||||
/** Get the data of the image
|
||||
*
|
||||
* @return the image data as a byte array - or null if this is a linked image
|
||||
*/
|
||||
public byte[] getData() {
|
||||
return blob;
|
||||
}
|
||||
|
||||
// Implement OutputFile
|
||||
|
||||
/** Writes out the content to the specified <code>OutputStream</code>.
|
||||
* Linked images will not write any data.
|
||||
*
|
||||
* @param os <code>OutputStream</code> to write out the content.
|
||||
*
|
||||
* @throws IOException If any I/O error occurs.
|
||||
*/
|
||||
public void write(OutputStream os) throws IOException {
|
||||
if (blob!=null) {
|
||||
os.write(blob, nOff, nLen);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the document name or URL</p>
|
||||
*
|
||||
* @return The document name or URL
|
||||
*/
|
||||
public String getFileName() {
|
||||
return sFileName;
|
||||
}
|
||||
|
||||
/** Get the MIME type of the document.
|
||||
*
|
||||
* @return The MIME type or null if this is unknown
|
||||
*/
|
||||
public String getMIMEType() {
|
||||
return sMimeType;
|
||||
}
|
||||
|
||||
/** Is this document a master document?
|
||||
*
|
||||
* @return false - a graphics file is never a master document
|
||||
*/
|
||||
public boolean isMasterDocument() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Does this document contain formulas?
|
||||
*
|
||||
* @return false - a graphics file does not contain formulas
|
||||
*/
|
||||
public boolean containsMath() {
|
||||
return false;
|
||||
}
|
||||
}
|
43
src/main/java/writer2latex/base/BooleanOption.java
Normal file
43
src/main/java/writer2latex/base/BooleanOption.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* BooleanOption.java
|
||||
*
|
||||
* Copyright: 2002-2008 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
187
src/main/java/writer2latex/base/ConfigBase.java
Normal file
187
src/main/java/writer2latex/base/ConfigBase.java
Normal file
|
@ -0,0 +1,187 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ConfigBase.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2014-08-26)
|
||||
*
|
||||
*/
|
||||
|
||||
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 java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.DOMImplementation;
|
||||
|
||||
import writer2latex.api.ComplexOption;
|
||||
|
||||
public abstract class ConfigBase implements writer2latex.api.Config {
|
||||
|
||||
protected abstract int getOptionCount();
|
||||
protected abstract String getDefaultConfigPath();
|
||||
|
||||
protected Option[] options;
|
||||
protected Map<String,ComplexOption> optionGroups;
|
||||
|
||||
public ConfigBase() {
|
||||
options = new Option[getOptionCount()];
|
||||
optionGroups = new HashMap<String,ComplexOption>();
|
||||
}
|
||||
|
||||
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 ComplexOption getComplexOption(String sGroup) {
|
||||
return optionGroups.get(sGroup);
|
||||
}
|
||||
|
||||
// The subclass may use this method to define option groups
|
||||
protected ComplexOption addComplexOption(String sGroup) {
|
||||
optionGroups.put(sGroup, new ComplexOption());
|
||||
return optionGroups.get(sGroup);
|
||||
}
|
||||
|
||||
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 {
|
||||
DOMDocument doc = new DOMDocument("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.length()>0) { 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 {
|
||||
DOMDocument doc = new DOMDocument("config",".xml");
|
||||
Document dom = null;
|
||||
try {
|
||||
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = builderFactory.newDocumentBuilder();
|
||||
DOMImplementation domImpl = builder.getDOMImplementation();
|
||||
dom = domImpl.createDocument("","config",null);
|
||||
} catch (ParserConfigurationException e) {
|
||||
// This will not happen
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
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);
|
||||
|
||||
}
|
||||
|
59
src/main/java/writer2latex/base/ContentEntryImpl.java
Normal file
59
src/main/java/writer2latex/base/ContentEntryImpl.java
Normal file
|
@ -0,0 +1,59 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ConverterResultImpl.java
|
||||
*
|
||||
* Copyright: 2002-2010 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2010-03-24)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.base;
|
||||
|
||||
import writer2latex.api.ContentEntry;
|
||||
import writer2latex.api.OutputFile;
|
||||
|
||||
public class ContentEntryImpl implements ContentEntry {
|
||||
private String sTitle;
|
||||
private int nLevel;
|
||||
private OutputFile file;
|
||||
private String sTarget;
|
||||
|
||||
public ContentEntryImpl(String sTitle, int nLevel, OutputFile file, String sTarget) {
|
||||
this.sTitle = sTitle;
|
||||
this.nLevel = nLevel;
|
||||
this.file = file;
|
||||
this.sTarget = sTarget;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return sTitle;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return nLevel;
|
||||
}
|
||||
|
||||
public OutputFile getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public String getTarget() {
|
||||
return sTarget;
|
||||
}
|
||||
}
|
183
src/main/java/writer2latex/base/ConverterBase.java
Normal file
183
src/main/java/writer2latex/base/ConverterBase.java
Normal file
|
@ -0,0 +1,183 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ConverterBase.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2014-09-03)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.base;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import writer2latex.api.GraphicConverter;
|
||||
import writer2latex.api.Converter;
|
||||
import writer2latex.api.ConverterResult;
|
||||
import writer2latex.api.OutputFile;
|
||||
import writer2latex.office.EmbeddedObject;
|
||||
import writer2latex.office.MetaData;
|
||||
import writer2latex.office.OfficeDocument;
|
||||
import writer2latex.office.OfficeReader;
|
||||
import writer2latex.office.XMLString;
|
||||
import writer2latex.util.Misc;
|
||||
|
||||
/**<p>Abstract base implementation of <code>writer2latex.api.Converter</code></p>
|
||||
*/
|
||||
public abstract class ConverterBase implements Converter {
|
||||
|
||||
public enum TexMathsStyle {inline, display, latex};
|
||||
|
||||
// Helper
|
||||
protected GraphicConverter graphicConverter;
|
||||
|
||||
// The source document
|
||||
protected OfficeDocument odDoc;
|
||||
protected OfficeReader ofr;
|
||||
protected MetaData metaData;
|
||||
protected ImageConverter imageConverter;
|
||||
|
||||
// The output file(s)
|
||||
protected String sTargetFileName;
|
||||
protected ConverterResultImpl converterResult;
|
||||
|
||||
// Constructor
|
||||
public ConverterBase() {
|
||||
graphicConverter = null;
|
||||
converterResult = new ConverterResultImpl();
|
||||
}
|
||||
|
||||
// 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 { }
|
||||
|
||||
// Provide a do noting fallback method
|
||||
public void readStyleSheet(InputStream is) throws IOException { }
|
||||
|
||||
// Provide a do noting fallback method
|
||||
public void readStyleSheet(File file) throws IOException { }
|
||||
|
||||
// Provide a do noting fallback method
|
||||
public void readResource(InputStream is, String sFileName, String sMediaType) throws IOException { }
|
||||
|
||||
// Provide a do noting fallback method
|
||||
public void readResource(File file, String sFileName, String sMediaType) 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();
|
||||
odDoc.read(is);
|
||||
return convert(sTargetFileName,true);
|
||||
}
|
||||
|
||||
public ConverterResult convert(org.w3c.dom.Document dom, String sTargetFileName, boolean bDestructive) throws IOException {
|
||||
// Read document
|
||||
odDoc = new OfficeDocument();
|
||||
odDoc.read(dom);
|
||||
return convert(sTargetFileName,bDestructive);
|
||||
}
|
||||
|
||||
private ConverterResult convert(String sTargetFileName, boolean bDestructive) throws IOException {
|
||||
ofr = new OfficeReader(odDoc,false);
|
||||
metaData = new MetaData(odDoc);
|
||||
imageConverter = new ImageConverter(ofr,bDestructive,true);
|
||||
imageConverter.setGraphicConverter(graphicConverter);
|
||||
|
||||
// Prepare output
|
||||
this.sTargetFileName = sTargetFileName;
|
||||
converterResult.reset();
|
||||
|
||||
converterResult.setMetaData(metaData);
|
||||
if (metaData.getLanguage()==null || metaData.getLanguage().length()==0) {
|
||||
metaData.setLanguage(ofr.getMajorityLanguage());
|
||||
}
|
||||
|
||||
convertInner();
|
||||
|
||||
return converterResult;
|
||||
}
|
||||
|
||||
// The subclass must provide the implementation
|
||||
public abstract void convertInner() throws IOException;
|
||||
|
||||
public MetaData getMetaData() { return metaData; }
|
||||
|
||||
public ImageConverter getImageCv() { return imageConverter; }
|
||||
|
||||
public void addDocument(OutputFile doc) { converterResult.addDocument(doc); }
|
||||
|
||||
public EmbeddedObject getEmbeddedObject(String sHref) {
|
||||
return odDoc.getEmbeddedObject(sHref);
|
||||
}
|
||||
|
||||
/** Get a TexMaths equation from a draw:frame (PNG formula) or draw:g element (SVG)
|
||||
* Such an element is a TexMaths equation if it contains an svg:title element with content "TexMaths"
|
||||
* The actual formula is the content of an svg:desc element
|
||||
*
|
||||
* @param node the draw:frame or draw:g element to check
|
||||
* @return the TexMaths equation, or null if this is not a TexMaths equation
|
||||
*/
|
||||
public Element getTexMathsEquation(Element node) {
|
||||
Element svgTitle = Misc.getChildByTagName(node, XMLString.SVG_TITLE);
|
||||
if (svgTitle!=null && "TexMaths".equals(Misc.getPCDATA(svgTitle))) {
|
||||
return Misc.getChildByTagName(node, XMLString.SVG_DESC);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public TexMathsStyle getTexMathsStyle(String s) {
|
||||
String[] sContent = s.split("\u00a7");
|
||||
if (sContent.length>=3) { // we only need 3 items of 6
|
||||
if ("display".equals(sContent[1])) {
|
||||
return TexMathsStyle.display;
|
||||
}
|
||||
else if ("latex".equals(sContent[1]) || "text".equals(sContent[1])) { // text is for OOoLaTeX
|
||||
return TexMathsStyle.latex;
|
||||
}
|
||||
}
|
||||
return TexMathsStyle.inline;
|
||||
}
|
||||
|
||||
public String getTexMathsEquation(String s) {
|
||||
String[] sContent = s.split("\u00a7");
|
||||
if (sContent.length>=3) { // we only need 3 items of 6
|
||||
return sContent[2];
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
282
src/main/java/writer2latex/base/ConverterResultImpl.java
Normal file
282
src/main/java/writer2latex/base/ConverterResultImpl.java
Normal file
|
@ -0,0 +1,282 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ConverterResultImpl.java
|
||||
*
|
||||
* Copyright: 2002-2011 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2011-07-20)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.base;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
import java.util.Iterator;
|
||||
|
||||
import writer2latex.api.ConverterResult;
|
||||
import writer2latex.api.MetaData;
|
||||
import writer2latex.api.OutputFile;
|
||||
import writer2latex.api.ContentEntry;
|
||||
|
||||
/** <code>ConverterResultImpl</code> is a straightforward implementation of <code>ConverterResult</code>
|
||||
*/
|
||||
public class ConverterResultImpl implements ConverterResult {
|
||||
|
||||
private List<OutputFile> files;
|
||||
|
||||
private List<ContentEntry> content;
|
||||
private ContentEntry titlePageFile;
|
||||
private ContentEntry textFile;
|
||||
private ContentEntry tocFile;
|
||||
private ContentEntry lofFile;
|
||||
private ContentEntry lotFile;
|
||||
private ContentEntry indexFile;
|
||||
private ContentEntry bibliographyFile;
|
||||
private ContentEntry coverFile;
|
||||
private ContentEntry coverImageFile;
|
||||
|
||||
private MetaData metaData = null;
|
||||
|
||||
private int nMasterCount;
|
||||
|
||||
/** Construct a new <code>ConverterResultImpl</code> with empty content
|
||||
*/
|
||||
public ConverterResultImpl() {
|
||||
reset();
|
||||
}
|
||||
|
||||
/** Resets all data. This empties all <code>OutputFile</code> and <code>ContentEntry</code> objects
|
||||
* objects from this class. This allows reuse of a <code>ConvertResult</code> object.
|
||||
*/
|
||||
public void reset() {
|
||||
files = new Vector<OutputFile>();
|
||||
content = new Vector<ContentEntry>();
|
||||
titlePageFile = null;
|
||||
textFile = null;
|
||||
tocFile = null;
|
||||
lofFile = null;
|
||||
lotFile = null;
|
||||
indexFile = null;
|
||||
bibliographyFile = null;
|
||||
coverImageFile = null;
|
||||
metaData = null;
|
||||
nMasterCount = 0;
|
||||
}
|
||||
|
||||
/** Adds an <code>OutputFile</code> to the list
|
||||
*
|
||||
* @param file The <code>OutputFile</code> to add.
|
||||
*/
|
||||
public void addDocument(OutputFile file) {
|
||||
if (file.isMasterDocument()) {
|
||||
files.add(nMasterCount++, file);
|
||||
}
|
||||
else {
|
||||
files.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the first master document
|
||||
*
|
||||
* @return <code>OutputFile</code> the master document
|
||||
*/
|
||||
public OutputFile getMasterDocument() {
|
||||
return files.size()>0 ? files.get(0) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an <code>Iterator</code> to access the <code>List</code>
|
||||
* of <code>OutputFile</code> objects
|
||||
*
|
||||
* @return The <code>Iterator</code> to access the
|
||||
* <code>List</code> of <code>OutputFile</code> objects.
|
||||
*/
|
||||
public Iterator<OutputFile> iterator() {
|
||||
return files.iterator();
|
||||
}
|
||||
|
||||
/** Add an entry to the "external" table of contents
|
||||
*
|
||||
* @param entry the entry to add
|
||||
*/
|
||||
public void addContentEntry(ContentEntry entry) {
|
||||
content.add(entry);
|
||||
}
|
||||
|
||||
public List<ContentEntry> getContent() {
|
||||
return Collections.unmodifiableList(content);
|
||||
}
|
||||
|
||||
/** Define the entry which contains the title page
|
||||
*
|
||||
* @param entry the entry
|
||||
*/
|
||||
public void setTitlePageFile(ContentEntry entry) {
|
||||
titlePageFile = entry;
|
||||
}
|
||||
|
||||
public ContentEntry getTitlePageFile() {
|
||||
return titlePageFile;
|
||||
}
|
||||
|
||||
/** Define the entry which contains the main text file
|
||||
*
|
||||
* @param entry the entry
|
||||
*/
|
||||
public void setTextFile(ContentEntry entry) {
|
||||
textFile = entry;
|
||||
}
|
||||
|
||||
public ContentEntry getTextFile() {
|
||||
return textFile;
|
||||
}
|
||||
|
||||
/** Define the entry which contains the table of contents
|
||||
*
|
||||
* @param entry the entry
|
||||
*/
|
||||
public void setTocFile(ContentEntry entry) {
|
||||
tocFile = entry;
|
||||
}
|
||||
|
||||
public ContentEntry getTocFile() {
|
||||
return tocFile;
|
||||
}
|
||||
|
||||
/** Define the entry which contains the list of figures
|
||||
*
|
||||
* @param entry the entry
|
||||
*/
|
||||
public void setLofFile(ContentEntry entry) {
|
||||
lofFile = entry;
|
||||
}
|
||||
|
||||
public ContentEntry getLofFile() {
|
||||
return lofFile;
|
||||
}
|
||||
|
||||
/** Define the entry which contains the list of tables
|
||||
*
|
||||
* @param entry the entry
|
||||
*/
|
||||
public void setLotFile(ContentEntry entry) {
|
||||
lotFile = entry;
|
||||
}
|
||||
|
||||
public ContentEntry getLotFile() {
|
||||
return lotFile;
|
||||
}
|
||||
|
||||
/** Define the entry which contains the alphabetical index
|
||||
*
|
||||
* @param entry the entry
|
||||
*/
|
||||
public void setIndexFile(ContentEntry entry) {
|
||||
indexFile = entry;
|
||||
}
|
||||
|
||||
public ContentEntry getIndexFile() {
|
||||
return indexFile;
|
||||
}
|
||||
|
||||
/** Define the entry which contains the bibliography
|
||||
*
|
||||
* @param entry the entry
|
||||
*/
|
||||
public void setBibliographyFile(ContentEntry entry) {
|
||||
bibliographyFile = entry;
|
||||
}
|
||||
|
||||
public ContentEntry getBibliographyFile() {
|
||||
return bibliographyFile;
|
||||
}
|
||||
|
||||
/** Define the entry which contains the cover
|
||||
*
|
||||
* @param entry the entry
|
||||
*/
|
||||
public void setCoverFile(ContentEntry entry) {
|
||||
coverFile = entry;
|
||||
}
|
||||
|
||||
public ContentEntry getCoverFile() {
|
||||
return coverFile;
|
||||
}
|
||||
|
||||
/** Define the entry which contains the cover image
|
||||
*
|
||||
* @param entry the entry
|
||||
*/
|
||||
public void setCoverImageFile(ContentEntry entry) {
|
||||
coverImageFile = entry;
|
||||
}
|
||||
|
||||
public ContentEntry getCoverImageFile() {
|
||||
return coverImageFile;
|
||||
}
|
||||
|
||||
/** Set the meta data of this <code>ConverterResult</code>
|
||||
*
|
||||
* @param metaData the meta data
|
||||
*/
|
||||
public void setMetaData(MetaData metaData) {
|
||||
this.metaData = metaData;
|
||||
}
|
||||
|
||||
/** Get the meta data of this <code>ConverterResult</code>
|
||||
*
|
||||
* @return the meta data
|
||||
*/
|
||||
public MetaData getMetaData() {
|
||||
return metaData;
|
||||
}
|
||||
|
||||
/** Write all files to a given directory
|
||||
*
|
||||
* @param dir the directory to use
|
||||
*/
|
||||
public void write(File dir) throws IOException {
|
||||
if (dir!=null && !dir.exists()) throw new IOException("Directory does not exist");
|
||||
Iterator<OutputFile> docEnum = iterator();
|
||||
while (docEnum.hasNext()) {
|
||||
OutputFile docOut = docEnum.next();
|
||||
String sDirName = "";
|
||||
String sFileName = docOut.getFileName();
|
||||
File subdir = dir;
|
||||
int nSlash = sFileName.indexOf("/");
|
||||
if (nSlash>-1) {
|
||||
sDirName = sFileName.substring(0,nSlash);
|
||||
sFileName = sFileName.substring(nSlash+1);
|
||||
subdir = new File(dir,sDirName);
|
||||
if (!subdir.exists()) { subdir.mkdir(); }
|
||||
}
|
||||
File outfile = new File (subdir,sFileName);
|
||||
FileOutputStream fos = new FileOutputStream(outfile);
|
||||
docOut.write(fos);
|
||||
fos.flush();
|
||||
fos.close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
369
src/main/java/writer2latex/base/DOMDocument.java
Normal file
369
src/main/java/writer2latex/base/DOMDocument.java
Normal file
|
@ -0,0 +1,369 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* DOMDocument.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-05-05)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.base;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
//import org.xml.sax.SAXParseException;
|
||||
|
||||
import writer2latex.api.OutputFile;
|
||||
|
||||
/**
|
||||
* This class represents XML-based documents. It is loosely based on a class from the former xmerge project
|
||||
* from OOo.
|
||||
*/
|
||||
public class DOMDocument implements OutputFile {
|
||||
|
||||
/** Factory for <code>DocumentBuilder</code> objects. */
|
||||
private static DocumentBuilderFactory factory =
|
||||
DocumentBuilderFactory.newInstance();
|
||||
|
||||
/** DOM <code>Document</code> of content.xml. */
|
||||
private Document contentDoc = null;
|
||||
|
||||
/** DOM <code>Document</code> of styles.xml. */
|
||||
//private Document styleDoc = null;
|
||||
|
||||
private String documentName = null;
|
||||
private String fileName = null;
|
||||
private String fileExt = null;
|
||||
|
||||
/** Resources object. */
|
||||
//private Resources res = null;
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*
|
||||
* @param name <code>Document</code> name.
|
||||
* @param ext <code>Document</code> extension.
|
||||
*/
|
||||
public DOMDocument(String name,String ext)
|
||||
{
|
||||
this(name,ext,true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file extension of the <code>Document</code>
|
||||
* represented.
|
||||
*
|
||||
* @return file extension of the <code>Document</code>.
|
||||
*/
|
||||
protected String getFileExtension() {
|
||||
return fileExt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor with arguments to set <code>namespaceAware</code>
|
||||
* and <code>validating</code> flags.
|
||||
*
|
||||
* @param name <code>Document</code> name (may or may not
|
||||
* contain extension).
|
||||
* @param ext <code>Document</code> extension.
|
||||
* @param namespaceAware Value for <code>namespaceAware</code> flag.
|
||||
* @param validating Value for <code>validating</code> flag.
|
||||
*/
|
||||
public DOMDocument(String name, String ext,boolean namespaceAware, boolean validating) {
|
||||
|
||||
//res = Resources.getInstance();
|
||||
factory.setValidating(validating);
|
||||
factory.setNamespaceAware(namespaceAware);
|
||||
this.fileExt = ext;
|
||||
this.documentName = trimDocumentName(name);
|
||||
this.fileName = documentName + getFileExtension();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes the file extension from the <code>Document</code>
|
||||
* name.
|
||||
*
|
||||
* @param name Full <code>Document</code> name with extension.
|
||||
*
|
||||
* @return Name of <code>Document</code> without the extension.
|
||||
*/
|
||||
private String trimDocumentName(String name) {
|
||||
String temp = name.toLowerCase();
|
||||
String ext = getFileExtension();
|
||||
|
||||
if (temp.endsWith(ext)) {
|
||||
// strip the extension
|
||||
int nlen = name.length();
|
||||
int endIndex = nlen - ext.length();
|
||||
name = name.substring(0,endIndex);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a DOM <code>Document</code> object of the document content
|
||||
* file. Note that a content DOM is not created when the constructor
|
||||
* is called. So, either the <code>read</code> method or the
|
||||
* <code>initContentDOM</code> method will need to be called ahead
|
||||
* on this object before calling this method.
|
||||
*
|
||||
* @return DOM <code>Document</code> object.
|
||||
*/
|
||||
public Document getContentDOM() {
|
||||
|
||||
return contentDoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Content of the <code>Document</code> to the contents of the
|
||||
* supplied <code>Node</code> list.
|
||||
*
|
||||
* @param newDom DOM <code>Document</code> object.
|
||||
*/
|
||||
public void setContentDOM( Node newDom) {
|
||||
contentDoc=(Document)newDom;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the name of the <code>Document</code>.
|
||||
*
|
||||
* @return The name of <code>Document</code>.
|
||||
*/
|
||||
public String getName() {
|
||||
|
||||
return documentName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the file name of the <code>Document</code>, possibly
|
||||
* with the standard extension.
|
||||
*
|
||||
* @return The file name of <code>Document</code>.
|
||||
*/
|
||||
public String getFileName() {
|
||||
|
||||
return fileName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read the Office <code>Document</code> from the specified
|
||||
* <code>InputStream</code>.
|
||||
*
|
||||
* @param is Office document <code>InputStream</code>.
|
||||
*
|
||||
* @throws IOException If any I/O error occurs.
|
||||
*/
|
||||
public void read(InputStream is) throws IOException {
|
||||
DocumentBuilder builder = null;
|
||||
try {
|
||||
builder = factory.newDocumentBuilder();
|
||||
} catch (ParserConfigurationException ex) {
|
||||
throw new IOException(ex.getMessage());
|
||||
}
|
||||
try {
|
||||
contentDoc= builder.parse(is);
|
||||
} catch (SAXException ex) {
|
||||
throw new IOException(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out content to the supplied <code>OutputStream</code>.
|
||||
* (with pretty printing)
|
||||
* @param os XML <code>OutputStream</code>.
|
||||
* @throws IOException If any I/O error occurs.
|
||||
*/
|
||||
public void write(OutputStream os) throws IOException {
|
||||
OutputStreamWriter osw = new OutputStreamWriter(os,"UTF-8");
|
||||
osw.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
|
||||
write(getContentDOM().getDocumentElement(),0,osw);
|
||||
osw.flush();
|
||||
osw.close();
|
||||
}
|
||||
|
||||
// Write nodes; we only need element, text and comment nodes
|
||||
private void write(Node node, int nLevel, OutputStreamWriter osw) throws IOException {
|
||||
short nType = node.getNodeType();
|
||||
switch (nType) {
|
||||
case Node.ELEMENT_NODE:
|
||||
if (node.hasChildNodes()) {
|
||||
// Block pretty print from this node?
|
||||
NodeList list = node.getChildNodes();
|
||||
int nLen = list.getLength();
|
||||
boolean bBlockPrettyPrint = false;
|
||||
if (nLevel>=0) {
|
||||
for (int i = 0; i < nLen; i++) {
|
||||
bBlockPrettyPrint |= list.item(i).getNodeType()==Node.TEXT_NODE;
|
||||
}
|
||||
}
|
||||
// Print start tag
|
||||
if (nLevel>=0) { writeSpaces(nLevel,osw); }
|
||||
osw.write("<"+node.getNodeName());
|
||||
writeAttributes(node,osw);
|
||||
osw.write(">");
|
||||
if (nLevel>=0 && !bBlockPrettyPrint) { osw.write("\n"); }
|
||||
// Print children
|
||||
for (int i = 0; i < nLen; i++) {
|
||||
int nNextLevel;
|
||||
if (bBlockPrettyPrint || nLevel<0) { nNextLevel=-1; }
|
||||
else { nNextLevel=nLevel+1; }
|
||||
write(list.item(i),nNextLevel,osw);
|
||||
}
|
||||
// Print end tag
|
||||
if (nLevel>=0 && !bBlockPrettyPrint) { writeSpaces(nLevel,osw); }
|
||||
osw.write("</"+node.getNodeName()+">");
|
||||
if (nLevel>=0) { osw.write("\n"); }
|
||||
}
|
||||
else { // empty element
|
||||
if (nLevel>=0) { writeSpaces(nLevel,osw); }
|
||||
osw.write("<"+node.getNodeName());
|
||||
writeAttributes(node,osw);
|
||||
osw.write(" />");
|
||||
if (nLevel>=0) { osw.write("\n"); }
|
||||
}
|
||||
break;
|
||||
case Node.TEXT_NODE:
|
||||
write(node.getNodeValue(),osw);
|
||||
break;
|
||||
case Node.COMMENT_NODE:
|
||||
if (nLevel>=0) { writeSpaces(nLevel,osw); }
|
||||
osw.write("<!-- ");
|
||||
write(node.getNodeValue(),osw);
|
||||
osw.write(" -->");
|
||||
if (nLevel>=0) { osw.write("\n"); }
|
||||
}
|
||||
}
|
||||
|
||||
private void writeAttributes(Node node, OutputStreamWriter osw) throws IOException {
|
||||
NamedNodeMap attr = node.getAttributes();
|
||||
int nLen = attr.getLength();
|
||||
for (int i=0; i<nLen; i++) {
|
||||
Node item = attr.item(i);
|
||||
osw.write(" ");
|
||||
write(item.getNodeName(),osw);
|
||||
osw.write("=\"");
|
||||
write(item.getNodeValue(),osw);
|
||||
osw.write("\"");
|
||||
}
|
||||
}
|
||||
|
||||
private void writeSpaces(int nCount, OutputStreamWriter osw) throws IOException {
|
||||
for (int i=0; i<nCount; i++) { osw.write(" "); }
|
||||
}
|
||||
|
||||
private void write(String s, OutputStreamWriter osw) throws IOException {
|
||||
int nLen = s.length();
|
||||
char c;
|
||||
for (int i=0; i<nLen; i++) {
|
||||
c = s.charAt(i);
|
||||
switch (c) {
|
||||
case ('<'): osw.write("<"); break;
|
||||
case ('>'): osw.write(">"); break;
|
||||
case ('&'): osw.write("&"); break;
|
||||
case ('"'): osw.write("""); break;
|
||||
case ('\''): osw.write( "'"); break;
|
||||
default: osw.write(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new DOM <code>Document</code> with the content
|
||||
* containing minimum XML tags.
|
||||
*
|
||||
* @throws IOException If any I/O error occurs.
|
||||
*/
|
||||
public final void initContentDOM() throws IOException {
|
||||
contentDoc = createDOM("");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a new DOM <code>Document</code> containing minimum
|
||||
* OpenOffice XML tags.</p>
|
||||
*
|
||||
* <p>This method uses the subclass
|
||||
* <code>getOfficeClassAttribute</code> method to get the
|
||||
* attribute for <i>office:class</i>.</p>
|
||||
*
|
||||
* @param rootName root name of <code>Document</code>.
|
||||
*
|
||||
* @throws IOException If any I/O error occurs.
|
||||
*/
|
||||
private final Document createDOM(String rootName) throws IOException {
|
||||
|
||||
Document doc = null;
|
||||
|
||||
try {
|
||||
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
doc = builder.newDocument();
|
||||
|
||||
} catch (ParserConfigurationException ex) {
|
||||
// This will not happen
|
||||
System.err.println("Error:"+ ex);
|
||||
throw new IOException(ex);
|
||||
}
|
||||
|
||||
Element root = (Element) doc.createElement(rootName);
|
||||
doc.appendChild(root);
|
||||
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
// We need these because we implement OutputFile
|
||||
public String getMIMEType() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public boolean isMasterDocument() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean containsMath() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
354
src/main/java/writer2latex/base/ImageConverter.java
Normal file
354
src/main/java/writer2latex/base/ImageConverter.java
Normal file
|
@ -0,0 +1,354 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ImageConverter.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2014-11-18)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.base;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import writer2latex.api.GraphicConverter;
|
||||
import writer2latex.office.EmbeddedBinaryObject;
|
||||
import writer2latex.office.EmbeddedObject;
|
||||
import writer2latex.office.MIMETypes;
|
||||
import writer2latex.office.OfficeReader;
|
||||
import writer2latex.office.SVMReader;
|
||||
import writer2latex.office.XMLString;
|
||||
import writer2latex.util.Misc;
|
||||
|
||||
/** This class extracts and converts images from an office document.
|
||||
* The images are returned as <code>BinaryGraphicsDocument</code>.
|
||||
* The image converter can be configured as destructive. In this case, the returned
|
||||
* graphics documents will contain the only reference to the image (the original data
|
||||
* will be removed).
|
||||
*/
|
||||
public final class ImageConverter {
|
||||
private OfficeReader ofr;
|
||||
private boolean bDestructive;
|
||||
|
||||
// Data for file name generation
|
||||
private String sBaseFileName = "";
|
||||
private String sSubDirName = "";
|
||||
private int nImageCount = 0;
|
||||
private NumberFormat formatter;
|
||||
|
||||
// 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<String> acceptedFormats = new HashSet<String>();
|
||||
|
||||
// In the package format, the same image file may be used more than once in the document
|
||||
// Hence we keep information of all documents for potential
|
||||
private HashMap<String,BinaryGraphicsDocument> recycledImages = new HashMap<String,BinaryGraphicsDocument>();
|
||||
|
||||
/** Construct a new <code>ImageConverter</code> referring to a specific document
|
||||
*
|
||||
* @param ofr the office reader to use
|
||||
* @param bExtractEPS set true if EPS content should be extracted from SVM files
|
||||
*/
|
||||
public ImageConverter(OfficeReader ofr, boolean bDestructive, boolean bExtractEPS) {
|
||||
this.ofr = ofr;
|
||||
this.bDestructive = bDestructive;
|
||||
this.bExtractEPS = bExtractEPS;
|
||||
this.formatter = new DecimalFormat("000");
|
||||
}
|
||||
|
||||
/** Define the base file name to use for generating file names
|
||||
*
|
||||
* @param sBaseFileName the base file name
|
||||
*/
|
||||
public void setBaseFileName(String sBaseFileName) {
|
||||
this.sBaseFileName = sBaseFileName;
|
||||
}
|
||||
|
||||
/** Define the name of a sub directory to prepend to file names
|
||||
*
|
||||
* @param sSubDirName the sub directory
|
||||
*/
|
||||
public void setUseSubdir(String sSubDirName) {
|
||||
this.sSubDirName = sSubDirName+"/";
|
||||
}
|
||||
|
||||
/** Specify that the <code>ImageConverter</code> should return an image even if it was not possible
|
||||
* to convert it to an acceptable format.
|
||||
*
|
||||
* @param b true if other formats should be accepted
|
||||
*/
|
||||
public void setAcceptOtherFormats(boolean b) {
|
||||
bAcceptOtherFormats = b;
|
||||
}
|
||||
|
||||
/** Define the default format for raster graphics
|
||||
*
|
||||
* @param sMime the MIME type of the default raster format
|
||||
*/
|
||||
public void setDefaultFormat(String sMime) {
|
||||
addAcceptedFormat(sMime);
|
||||
sDefaultFormat = sMime;
|
||||
}
|
||||
|
||||
/** Define the default format for vector graphics
|
||||
*
|
||||
* @param sMime the MIME type for the default vector format
|
||||
*/
|
||||
public void setDefaultVectorFormat(String sMime) {
|
||||
addAcceptedFormat(sMime);
|
||||
sDefaultVectorFormat = sMime;
|
||||
}
|
||||
|
||||
/** Define an accepted graphics format
|
||||
*
|
||||
* @param sMime the MIME type of the format
|
||||
*/
|
||||
public void addAcceptedFormat(String sMime) {
|
||||
acceptedFormats.add(sMime);
|
||||
}
|
||||
|
||||
/** Is a given format accepted?
|
||||
*
|
||||
* @param sMime the MIME type to query
|
||||
* @return true if this is an accepted format
|
||||
*/
|
||||
private boolean isAcceptedFormat(String sMime) {
|
||||
return acceptedFormats.contains(sMime);
|
||||
}
|
||||
|
||||
/** Define the <code>GraphicConverter</code> to use for image conversion
|
||||
*
|
||||
* @param gcv the graphics converter
|
||||
*/
|
||||
public void setGraphicConverter(GraphicConverter gcv) {
|
||||
this.gcv = gcv;
|
||||
}
|
||||
|
||||
/** Get an image from a <code>draw:image</code> element. If the converter is destructive, the returned
|
||||
* <code>BinaryGraphicsDocument</code> will hold the only reference to the image data (the original
|
||||
* data will be removed).
|
||||
*
|
||||
* @param node the image element
|
||||
* @return a document containing the (converted) image, or null if it was not possible to read the image
|
||||
* or convert it to an accepted format
|
||||
*/
|
||||
public BinaryGraphicsDocument getImage(Element node) {
|
||||
String sName = sSubDirName+sBaseFileName+formatter.format(++nImageCount);
|
||||
BinaryGraphicsDocument bgd = getImage(node,sName);
|
||||
if (bgd!=null) {
|
||||
if (!bgd.isAcceptedFormat() || (sDefaultVectorFormat!=null && !sDefaultVectorFormat.equals(bgd.getMIMEType()))) {
|
||||
// We may have better luck with an alternative image
|
||||
Element sibling = getAlternativeImage(node);
|
||||
if (sibling!=null) {
|
||||
BinaryGraphicsDocument altBgd = getImage(sibling,sName);
|
||||
if (altBgd!=null && altBgd.isAcceptedFormat()) {
|
||||
if (!bgd.isAcceptedFormat() ||
|
||||
(sDefaultVectorFormat!=null && !sDefaultVectorFormat.equals(bgd.getMIMEType()) &&
|
||||
sDefaultVectorFormat.equals(altBgd.getMIMEType()))) {
|
||||
bgd = altBgd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bgd==null || bgd.isLinked() || bgd.isRecycled()) {
|
||||
// The file name was not used
|
||||
nImageCount--;
|
||||
}
|
||||
return bgd;
|
||||
}
|
||||
|
||||
private BinaryGraphicsDocument getImage(Element node, String sName) {
|
||||
assert(XMLString.DRAW_IMAGE.equals(node.getTagName()));
|
||||
|
||||
// Image data
|
||||
String sExt = null;
|
||||
String sMIME = null;
|
||||
byte[] blob = null;
|
||||
String sId = null;
|
||||
|
||||
// First try to extract the image using the xlink:href attribute
|
||||
if (node.hasAttribute(XMLString.XLINK_HREF)) {
|
||||
String sHref = node.getAttribute(XMLString.XLINK_HREF);
|
||||
if (sHref.length()>0) {
|
||||
// We may have seen this image before, return the recycled version
|
||||
if (recycledImages.containsKey(sHref)) {
|
||||
return recycledImages.get(sHref);
|
||||
}
|
||||
// Image may be embedded in package:
|
||||
String sPath = sHref;
|
||||
if (sPath.startsWith("#")) { sPath = sPath.substring(1); }
|
||||
if (sPath.startsWith("./")) { sPath = sPath.substring(2); }
|
||||
EmbeddedObject obj = ofr.getEmbeddedObject(sPath);
|
||||
if (obj!=null && obj instanceof EmbeddedBinaryObject) {
|
||||
EmbeddedBinaryObject object = (EmbeddedBinaryObject) obj;
|
||||
blob = object.getBinaryData();
|
||||
sMIME = object.getType();
|
||||
if (sMIME.length()==0) {
|
||||
// If the manifest provides a media type, trust that
|
||||
// Otherwise determine it by byte inspection
|
||||
sMIME = MIMETypes.getMagicMIMEType(blob);
|
||||
}
|
||||
sExt = MIMETypes.getFileExtension(sMIME);
|
||||
if (bDestructive) {
|
||||
object.dispose();
|
||||
}
|
||||
// We got an image, define ID for recycling
|
||||
sId = sHref;
|
||||
}
|
||||
else {
|
||||
// This is a linked image
|
||||
// TODO: Add option to download image from the URL?
|
||||
String sFileName = ofr.fixRelativeLink(sHref);
|
||||
BinaryGraphicsDocument bgd
|
||||
= new BinaryGraphicsDocument(sFileName,null);
|
||||
return bgd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no suitable xlink:href attribute, the image must be contained in an office:binary-element as base64
|
||||
if (blob==null) {
|
||||
Node obd = Misc.getChildByTagName(node,XMLString.OFFICE_BINARY_DATA);
|
||||
if (obd!=null) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
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());
|
||||
blob = DatatypeConverter.parseBase64Binary(buf.toString());
|
||||
// We may have seen this image before, return the recycled version
|
||||
String sId1 = createId(blob);
|
||||
if (recycledImages.containsKey(sId1)) {
|
||||
return recycledImages.get(sId1);
|
||||
}
|
||||
sMIME = MIMETypes.getMagicMIMEType(blob);
|
||||
sExt = MIMETypes.getFileExtension(sMIME);
|
||||
if (bDestructive) {
|
||||
node.removeChild(obd);
|
||||
}
|
||||
// We got an image, define ID for recycling
|
||||
sId = sId1;
|
||||
}
|
||||
else {
|
||||
// There is no image data
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Is this an EPS file embedded in an SVM file?
|
||||
// (This case is obsolete, but kept for the sake of old documents)
|
||||
if (bExtractEPS && MIMETypes.SVM.equals(sMIME)) {
|
||||
// Look for postscript:
|
||||
int[] offlen = new int[2];
|
||||
if (SVMReader.readSVM(blob,offlen)) {
|
||||
String sFileName = sName+MIMETypes.EPS_EXT;
|
||||
BinaryGraphicsDocument bgd
|
||||
= new BinaryGraphicsDocument(sFileName, MIMETypes.EPS);
|
||||
bgd.setData(blob,offlen[0],offlen[1],true);
|
||||
return bgd;
|
||||
}
|
||||
}
|
||||
|
||||
// We have an embedded image.
|
||||
|
||||
// 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) {
|
||||
byte[] newBlob = null;
|
||||
String sTargetMIME = null;
|
||||
|
||||
if (MIMETypes.isVectorFormat(sMIME) && sDefaultVectorFormat!=null &&
|
||||
gcv.supportsConversion(sMIME,sDefaultVectorFormat,false,false)) {
|
||||
// Try vector format first
|
||||
newBlob = gcv.convert(blob, sMIME, sTargetMIME=sDefaultVectorFormat);
|
||||
}
|
||||
if (newBlob==null && gcv.supportsConversion(sMIME,sDefaultFormat,false,false)) {
|
||||
// Then try bitmap format
|
||||
newBlob = gcv.convert(blob,sMIME,sTargetMIME=sDefaultFormat);
|
||||
}
|
||||
|
||||
if (newBlob!=null) {
|
||||
// Conversion successful - create new data
|
||||
blob = newBlob;
|
||||
sMIME = sTargetMIME;
|
||||
sExt = MIMETypes.getFileExtension(sMIME);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the result
|
||||
if (isAcceptedFormat(sMIME) || bAcceptOtherFormats) {
|
||||
String sFileName = sName+sExt;
|
||||
BinaryGraphicsDocument bgd = new BinaryGraphicsDocument(sFileName,sMIME);
|
||||
bgd.setData(blob,isAcceptedFormat(sMIME));
|
||||
if (sId!=null) {
|
||||
recycledImages.put(sId, new BinaryGraphicsDocument(bgd));
|
||||
}
|
||||
return bgd;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Element getAlternativeImage(Element node) {
|
||||
Node sibling = node.getNextSibling();
|
||||
if (sibling!=null && Misc.isElement(sibling, XMLString.DRAW_IMAGE)) {
|
||||
return (Element) sibling;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a fingerprint of a blob. The fingerprint concatenates the MD5 hash with the first 10 bytes of the blob.
|
||||
private String createId(byte[] blob) {
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// This would be surprising
|
||||
return null;
|
||||
}
|
||||
return DatatypeConverter.printHexBinary(md.digest(blob))
|
||||
+DatatypeConverter.printHexBinary(Arrays.copyOf(blob, 10));
|
||||
}
|
||||
|
||||
}
|
39
src/main/java/writer2latex/base/IntegerOption.java
Normal file
39
src/main/java/writer2latex/base/IntegerOption.java
Normal file
|
@ -0,0 +1,39 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* IntegerOption.java
|
||||
*
|
||||
* Copyright: 2002-2008 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
44
src/main/java/writer2latex/base/Option.java
Normal file
44
src/main/java/writer2latex/base/Option.java
Normal file
|
@ -0,0 +1,44 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* Option.java
|
||||
*
|
||||
* Copyright: 2002-2008 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
17
src/main/java/writer2latex/base/Package.html
Normal file
17
src/main/java/writer2latex/base/Package.html
Normal 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>
|
167
src/main/java/writer2latex/bibtex/BibTeXDocument.java
Normal file
167
src/main/java/writer2latex/bibtex/BibTeXDocument.java
Normal file
|
@ -0,0 +1,167 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* BibTeXDocument.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-07-01)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.bibtex;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import writer2latex.api.ConverterFactory;
|
||||
import writer2latex.api.MIMETypes;
|
||||
import writer2latex.api.OutputFile;
|
||||
import writer2latex.latex.LaTeXConfig;
|
||||
import writer2latex.latex.i18n.ClassicI18n;
|
||||
import writer2latex.latex.i18n.I18n;
|
||||
import writer2latex.util.ExportNameCollection;
|
||||
import writer2latex.office.BibMark;
|
||||
import writer2latex.office.BibMark.EntryType;
|
||||
import writer2latex.office.OfficeReader;
|
||||
|
||||
/** Class representing a BibTeX document
|
||||
*/
|
||||
public class BibTeXDocument implements OutputFile {
|
||||
private static final String FILE_EXTENSION = ".bib";
|
||||
|
||||
private String sName;
|
||||
private Hashtable<String, BibMark> entries = new Hashtable<String, BibMark>();
|
||||
private ExportNameCollection exportNames = new ExportNameCollection("",true,"_-:");
|
||||
private I18n i18n;
|
||||
|
||||
private boolean bIsMaster;
|
||||
|
||||
/** Constructs a new BibTeX Document based on an office document
|
||||
*
|
||||
* @param sName The name of the document
|
||||
* @param bIsMaster is this a master document?
|
||||
* @param ofr the office document
|
||||
*/
|
||||
public BibTeXDocument(String sName, boolean bIsMaster, OfficeReader ofr) {
|
||||
this.sName = sName;
|
||||
this.bIsMaster = bIsMaster;
|
||||
loadEntries(ofr);
|
||||
// Use default config (only ascii, no extra font packages)
|
||||
i18n = new ClassicI18n(new LaTeXConfig());
|
||||
}
|
||||
|
||||
private void loadEntries(OfficeReader ofr) {
|
||||
List<Element> bibMarks = ofr.getBibliographyMarks();
|
||||
for (Element bibMark : bibMarks) {
|
||||
BibMark entry = new BibMark(bibMark);
|
||||
entries.put(entry.getIdentifier(),entry);
|
||||
exportNames.addName(entry.getIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
// Methods to query the content
|
||||
|
||||
/** Test whether or not this BibTeX document contains any entries
|
||||
*
|
||||
* @return true if there is one or more entries in the document
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return entries.size()==0;
|
||||
}
|
||||
|
||||
/** Get export name for an identifier
|
||||
*
|
||||
* @param sIdentifier the identifier
|
||||
* @return the export name
|
||||
*/
|
||||
public String getExportName(String sIdentifier) {
|
||||
return exportNames.getExportName(sIdentifier);
|
||||
}
|
||||
|
||||
/** Returns the document name without file extension
|
||||
*
|
||||
* @return the document name without file extension
|
||||
*/
|
||||
public String getName() {
|
||||
return sName;
|
||||
}
|
||||
|
||||
// Implement writer2latex.api.OutputFile
|
||||
|
||||
public String getFileName() {
|
||||
return new String(sName + FILE_EXTENSION);
|
||||
}
|
||||
|
||||
public String getMIMEType() {
|
||||
return MIMETypes.BIBTEX;
|
||||
}
|
||||
|
||||
public boolean isMasterDocument() {
|
||||
return bIsMaster;
|
||||
}
|
||||
|
||||
public boolean containsMath() {
|
||||
return false;
|
||||
}
|
||||
|
||||
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://writer2latex.sourceforge.net for more info.\n");
|
||||
osw.write("\n");
|
||||
Enumeration<BibMark> enumeration = entries.elements();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
BibMark entry = enumeration.nextElement();
|
||||
osw.write("@");
|
||||
osw.write(entry.getEntryType().toUpperCase());
|
||||
osw.write("{");
|
||||
osw.write(exportNames.getExportName(entry.getIdentifier()));
|
||||
osw.write(",\n");
|
||||
for (EntryType entryType : EntryType.values()) {
|
||||
String sValue = entry.getField(entryType);
|
||||
if (sValue!=null) {
|
||||
if (entryType==EntryType.author || entryType==EntryType.editor) {
|
||||
// OOo uses ; to separate authors and editors - BibTeX uses and
|
||||
sValue = sValue.replaceAll(";" , " and ");
|
||||
}
|
||||
osw.write(" ");
|
||||
osw.write(BibTeXEntryMap.getFieldName(entryType).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();
|
||||
}
|
||||
|
||||
}
|
109
src/main/java/writer2latex/bibtex/BibTeXEntryMap.java
Normal file
109
src/main/java/writer2latex/bibtex/BibTeXEntryMap.java
Normal file
|
@ -0,0 +1,109 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* BibTeXEntryMap.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2014-12-16)
|
||||
*
|
||||
*/
|
||||
package writer2latex.bibtex;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import writer2latex.office.BibMark.EntryType;
|
||||
|
||||
/**
|
||||
* This class provides static methods to map the the entry types of an ODF
|
||||
* bibliography mark to and from BibTeX field names
|
||||
*
|
||||
*/
|
||||
public class BibTeXEntryMap {
|
||||
private static Map<EntryType, String> bibTeXFields = null;
|
||||
private static Map<String, EntryType> entryTypes = null;
|
||||
|
||||
private static void createMaps() {
|
||||
// Note the BibTeX fileds key and crossref are not supported in ODF
|
||||
bibTeXFields = new HashMap<EntryType, String>();
|
||||
bibTeXFields.put(EntryType.address, "address");
|
||||
bibTeXFields.put(EntryType.annote, "annote");
|
||||
bibTeXFields.put(EntryType.author, "author");
|
||||
bibTeXFields.put(EntryType.booktitle, "booktitle");
|
||||
bibTeXFields.put(EntryType.chapter, "chapter");
|
||||
bibTeXFields.put(EntryType.edition, "edition");
|
||||
bibTeXFields.put(EntryType.editor, "editor");
|
||||
bibTeXFields.put(EntryType.howpublished, "howpublished");
|
||||
bibTeXFields.put(EntryType.institution, "institution");
|
||||
bibTeXFields.put(EntryType.journal, "journal");
|
||||
bibTeXFields.put(EntryType.month, "month");
|
||||
bibTeXFields.put(EntryType.note, "note");
|
||||
bibTeXFields.put(EntryType.number, "number");
|
||||
bibTeXFields.put(EntryType.organizations, "organization");
|
||||
bibTeXFields.put(EntryType.pages, "pages");
|
||||
bibTeXFields.put(EntryType.publisher, "publisher");
|
||||
bibTeXFields.put(EntryType.school, "school");
|
||||
bibTeXFields.put(EntryType.series, "series");
|
||||
bibTeXFields.put(EntryType.title, "title");
|
||||
bibTeXFields.put(EntryType.report_type, "type");
|
||||
bibTeXFields.put(EntryType.volume, "volume");
|
||||
bibTeXFields.put(EntryType.year, "year");
|
||||
bibTeXFields.put(EntryType.url, "url");
|
||||
bibTeXFields.put(EntryType.custom1, "custom1");
|
||||
bibTeXFields.put(EntryType.custom2, "custom2");
|
||||
bibTeXFields.put(EntryType.custom3, "custom3");
|
||||
bibTeXFields.put(EntryType.custom4, "custom4");
|
||||
bibTeXFields.put(EntryType.custom5, "custom5");
|
||||
bibTeXFields.put(EntryType.isbn, "isbn");
|
||||
|
||||
entryTypes = new HashMap<String, EntryType>();
|
||||
for (EntryType entryType : bibTeXFields.keySet()) {
|
||||
entryTypes.put(bibTeXFields.get(entryType), entryType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return BibTeX field name corresponding to and entry type
|
||||
*
|
||||
* @param entryType
|
||||
* the entry type
|
||||
* @return the BibTeX field name, or null if there is no corresponding
|
||||
* BibTeX field
|
||||
*/
|
||||
public static final String getFieldName(EntryType entryType) {
|
||||
if (bibTeXFields == null) {
|
||||
createMaps();
|
||||
}
|
||||
return bibTeXFields.containsKey(entryType) ? bibTeXFields.get(entryType) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return entry type corresponding to a BibTeX field
|
||||
*
|
||||
* @param sFieldName
|
||||
* the BibTeX field name
|
||||
* @return the entry type, or null if there is no corresponding entry type
|
||||
*/
|
||||
public static final EntryType getEntryType(String sFieldName) {
|
||||
if (bibTeXFields == null) {
|
||||
createMaps();
|
||||
}
|
||||
sFieldName = sFieldName.toLowerCase();
|
||||
return entryTypes.containsKey(sFieldName) ? entryTypes.get(sFieldName) : null;
|
||||
}
|
||||
}
|
67
src/main/java/writer2latex/bibtex/Converter.java
Normal file
67
src/main/java/writer2latex/bibtex/Converter.java
Normal file
|
@ -0,0 +1,67 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* Converter.java
|
||||
*
|
||||
* Copyright: 2001-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-06-22)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.bibtex;
|
||||
|
||||
import writer2latex.api.Config;
|
||||
import writer2latex.base.ConverterBase;
|
||||
import writer2latex.latex.LaTeXConfig;
|
||||
import writer2latex.util.Misc;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** This class exports bibliographic information from an OpenDocument text file to a BibTeX data file
|
||||
*/
|
||||
public final class Converter extends ConverterBase {
|
||||
|
||||
// Implement converter API
|
||||
|
||||
// TODO: Doesn't really use the configuration - should use some fake config
|
||||
private LaTeXConfig config;
|
||||
|
||||
public Converter() {
|
||||
super();
|
||||
config = new LaTeXConfig();
|
||||
}
|
||||
|
||||
public Config getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
// Extend converter base
|
||||
|
||||
/** Convert the document into BibTeX format.</p>
|
||||
*
|
||||
* @throws IOException If any I/O error occurs.
|
||||
*/
|
||||
@Override public void convertInner() throws IOException {
|
||||
sTargetFileName = Misc.trimDocumentName(sTargetFileName,".bib");
|
||||
|
||||
BibTeXDocument bibDoc = new BibTeXDocument(sTargetFileName,true,ofr);
|
||||
|
||||
converterResult.addDocument(bibDoc);
|
||||
}
|
||||
|
||||
}
|
14
src/main/java/writer2latex/bibtex/Package.html
Normal file
14
src/main/java/writer2latex/bibtex/Package.html
Normal 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>
|
76
src/main/java/writer2latex/epub/ContainerWriter.java
Normal file
76
src/main/java/writer2latex/epub/ContainerWriter.java
Normal file
|
@ -0,0 +1,76 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ContainerWriter.java
|
||||
*
|
||||
* Copyright: 2001-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* version 1.4 (2014-08-26)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.epub;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.w3c.dom.DOMImplementation;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.DocumentType;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import writer2latex.base.DOMDocument;
|
||||
|
||||
/** This class creates the required META-INF/container.xml file for an EPUB package
|
||||
* (see http://www.idpf.org/ocf/ocf1.0/download/ocf10.htm).
|
||||
*/
|
||||
public class ContainerWriter extends DOMDocument {
|
||||
|
||||
public ContainerWriter() {
|
||||
super("container", "xml");
|
||||
|
||||
// create DOM
|
||||
Document contentDOM = null;
|
||||
try {
|
||||
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = builderFactory.newDocumentBuilder();
|
||||
DOMImplementation domImpl = builder.getDOMImplementation();
|
||||
DocumentType doctype = domImpl.createDocumentType("container","","");
|
||||
contentDOM = domImpl.createDocument("urn:oasis:names:tc:opendocument:xmlns:container","container",doctype);
|
||||
}
|
||||
catch (ParserConfigurationException t) { // this should never happen
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
|
||||
// Populate the DOM tree
|
||||
Element container = contentDOM.getDocumentElement();
|
||||
container.setAttribute("version", "1.0");
|
||||
container.setAttribute("xmlns","urn:oasis:names:tc:opendocument:xmlns:container");
|
||||
|
||||
Element rootfiles = contentDOM.createElement("rootfiles");
|
||||
container.appendChild(rootfiles);
|
||||
|
||||
Element rootfile = contentDOM.createElement("rootfile");
|
||||
rootfile.setAttribute("full-path", "OEBPS/book.opf");
|
||||
rootfile.setAttribute("media-type", "application/oebps-package+xml");
|
||||
rootfiles.appendChild(rootfile);
|
||||
|
||||
setContentDOM(contentDOM);
|
||||
}
|
||||
|
||||
}
|
64
src/main/java/writer2latex/epub/EPUB3Converter.java
Normal file
64
src/main/java/writer2latex/epub/EPUB3Converter.java
Normal file
|
@ -0,0 +1,64 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* EPUB3Converter.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* version 1.6 (2015-01-15)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.epub;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import writer2latex.api.ConverterResult;
|
||||
import writer2latex.base.ConverterResultImpl;
|
||||
import writer2latex.xhtml.Html5Converter;
|
||||
|
||||
|
||||
/** This class converts an OpenDocument file to an EPUB 3 document.
|
||||
*/
|
||||
public final class EPUB3Converter extends Html5Converter {
|
||||
|
||||
// Constructor
|
||||
public EPUB3Converter() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override public ConverterResult convert(InputStream is, String sTargetFileName) throws IOException {
|
||||
setOPS(true);
|
||||
ConverterResult xhtmlResult = super.convert(is, "chapter");
|
||||
return createPackage(xhtmlResult,sTargetFileName);
|
||||
}
|
||||
|
||||
@Override public ConverterResult convert(org.w3c.dom.Document dom, String sTargetFileName, boolean bDestructive) throws IOException {
|
||||
setOPS(true);
|
||||
ConverterResult xhtmlResult = super.convert(dom, "chapter", bDestructive);
|
||||
return createPackage(xhtmlResult,sTargetFileName);
|
||||
}
|
||||
|
||||
private ConverterResult createPackage(ConverterResult xhtmlResult, String sTargetFileName) {
|
||||
ConverterResultImpl epubResult = new ConverterResultImpl();
|
||||
epubResult.addDocument(new EPUBWriter(xhtmlResult,sTargetFileName,3,getXhtmlConfig()));
|
||||
epubResult.setMetaData(xhtmlResult.getMetaData());
|
||||
return epubResult;
|
||||
}
|
||||
|
||||
}
|
64
src/main/java/writer2latex/epub/EPUBConverter.java
Normal file
64
src/main/java/writer2latex/epub/EPUBConverter.java
Normal file
|
@ -0,0 +1,64 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* EPUBConverter.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* version 1.6 (2015-01-15)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.epub;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import writer2latex.api.ConverterResult;
|
||||
import writer2latex.base.ConverterResultImpl;
|
||||
import writer2latex.xhtml.Xhtml11Converter;
|
||||
|
||||
|
||||
/** This class converts an OpenDocument file to an EPUB document.
|
||||
*/
|
||||
public final class EPUBConverter extends Xhtml11Converter {
|
||||
|
||||
// Constructor
|
||||
public EPUBConverter() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override public ConverterResult convert(InputStream is, String sTargetFileName) throws IOException {
|
||||
setOPS(true);
|
||||
ConverterResult xhtmlResult = super.convert(is, "chapter");
|
||||
return createPackage(xhtmlResult,sTargetFileName);
|
||||
}
|
||||
|
||||
@Override public ConverterResult convert(org.w3c.dom.Document dom, String sTargetFileName, boolean bDestructive) throws IOException {
|
||||
setOPS(true);
|
||||
ConverterResult xhtmlResult = super.convert(dom, "chapter", bDestructive);
|
||||
return createPackage(xhtmlResult,sTargetFileName);
|
||||
}
|
||||
|
||||
private ConverterResult createPackage(ConverterResult xhtmlResult, String sTargetFileName) {
|
||||
ConverterResultImpl epubResult = new ConverterResultImpl();
|
||||
epubResult.addDocument(new EPUBWriter(xhtmlResult,sTargetFileName,2,getXhtmlConfig()));
|
||||
epubResult.setMetaData(xhtmlResult.getMetaData());
|
||||
return epubResult;
|
||||
}
|
||||
|
||||
}
|
152
src/main/java/writer2latex/epub/EPUBWriter.java
Normal file
152
src/main/java/writer2latex/epub/EPUBWriter.java
Normal file
|
@ -0,0 +1,152 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* EPUBWriter.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* version 1.6 (2015-06-24)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.epub;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Iterator;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import writer2latex.api.ConverterResult;
|
||||
import writer2latex.api.OutputFile;
|
||||
import writer2latex.util.Misc;
|
||||
import writer2latex.xhtml.XhtmlConfig;
|
||||
|
||||
/** This class repackages an XHTML document into EPUB format.
|
||||
* Some filenames are hard wired in this implementation: The main directory is OEBPS and
|
||||
* the OPF and NCX files are book.opf and book.ncx respectively; finally the EPUB 3 navigation
|
||||
* document is nav.xhtml
|
||||
*/
|
||||
public class EPUBWriter implements OutputFile {
|
||||
|
||||
private static final byte[] mimeBytes = { 'a', 'p', 'p', 'l', 'i', 'c', 'a', 't', 'i', 'o', 'n', '/',
|
||||
'e', 'p', 'u', 'b', '+', 'z', 'i', 'p'};
|
||||
|
||||
private ConverterResult xhtmlResult;
|
||||
private String sFileName;
|
||||
private int nVersion;
|
||||
private XhtmlConfig config;
|
||||
|
||||
/** Create a new <code>EPUBWriter</code> based on a <code>ConverterResult</code>.
|
||||
*
|
||||
* @param xhtmlResult the converter result to repackage
|
||||
* @param sFileName the file name to use for the result
|
||||
* @param nVersion the EPUB version number. Can be either 2 or 3, other values are interpreted as 2.
|
||||
* @param config the configuration to use
|
||||
*/
|
||||
public EPUBWriter(ConverterResult xhtmlResult, String sFileName, int nVersion, XhtmlConfig config) {
|
||||
this.xhtmlResult = xhtmlResult;
|
||||
this.sFileName = Misc.removeExtension(sFileName);
|
||||
this.nVersion = nVersion;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
// Implement OutputFile
|
||||
|
||||
public String getFileName() {
|
||||
return sFileName+".epub";
|
||||
}
|
||||
|
||||
public String getMIMEType() {
|
||||
return "application/epub+zip";
|
||||
}
|
||||
|
||||
public boolean isMasterDocument() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean containsMath() {
|
||||
// We don't really care about this
|
||||
return nVersion==3;
|
||||
}
|
||||
|
||||
public void write(OutputStream os) throws IOException {
|
||||
ZipOutputStream zos = new ZipOutputStream(os);
|
||||
|
||||
// Write uncompressed MIME type as first entry
|
||||
ZipEntry mimeEntry = new ZipEntry("mimetype");
|
||||
mimeEntry.setMethod(ZipEntry.STORED);
|
||||
mimeEntry.setCrc(0x2CAB616F);
|
||||
mimeEntry.setSize(mimeBytes.length);
|
||||
zos.putNextEntry(mimeEntry);
|
||||
zos.write(mimeBytes, 0, mimeBytes.length);
|
||||
zos.closeEntry();
|
||||
|
||||
// Write container entry next
|
||||
OutputFile containerWriter = new ContainerWriter();
|
||||
ZipEntry containerEntry = new ZipEntry("META-INF/container.xml");
|
||||
zos.putNextEntry(containerEntry);
|
||||
writeZipEntry(containerWriter,zos);
|
||||
zos.closeEntry();
|
||||
|
||||
// Then manifest
|
||||
OPFWriter manifest = new OPFWriter(xhtmlResult,sFileName,nVersion,config);
|
||||
ZipEntry manifestEntry = new ZipEntry("OEBPS/book.opf");
|
||||
zos.putNextEntry(manifestEntry);
|
||||
writeZipEntry(manifest,zos);
|
||||
zos.closeEntry();
|
||||
|
||||
// And content table
|
||||
if (nVersion==3) {
|
||||
OutputFile navigation = new NavigationWriter(xhtmlResult);
|
||||
ZipEntry navigationEntry = new ZipEntry("OEBPS/nav.xhtml");
|
||||
zos.putNextEntry(navigationEntry);
|
||||
writeZipEntry(navigation,zos);
|
||||
zos.closeEntry();
|
||||
}
|
||||
if (nVersion!=3 || config.includeNCX()) {
|
||||
OutputFile ncx = new NCXWriter(xhtmlResult, manifest.getUid());
|
||||
ZipEntry ncxEntry = new ZipEntry("OEBPS/book.ncx");
|
||||
zos.putNextEntry(ncxEntry);
|
||||
writeZipEntry(ncx,zos);
|
||||
zos.closeEntry();
|
||||
}
|
||||
|
||||
// Finally XHTML content
|
||||
Iterator<OutputFile> iter = xhtmlResult.iterator();
|
||||
while (iter.hasNext()) {
|
||||
OutputFile file = iter.next();
|
||||
ZipEntry entry = new ZipEntry("OEBPS/"+file.getFileName());
|
||||
zos.putNextEntry(entry);
|
||||
writeZipEntry(file, zos);
|
||||
zos.closeEntry();
|
||||
}
|
||||
|
||||
zos.close();
|
||||
}
|
||||
|
||||
private void writeZipEntry(OutputFile file, ZipOutputStream zos) throws IOException {
|
||||
// Unfortunately we cannot simply do file.write(zos) because the write method of OutputFile
|
||||
// closes the OutputStream. Hence this suboptimal solution
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
file.write(baos);
|
||||
byte[] content = baos.toByteArray();
|
||||
zos.write(content, 0, content.length);
|
||||
}
|
||||
|
||||
}
|
162
src/main/java/writer2latex/epub/NCXWriter.java
Normal file
162
src/main/java/writer2latex/epub/NCXWriter.java
Normal file
|
@ -0,0 +1,162 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* NCXWriter.java
|
||||
*
|
||||
* Copyright: 2001-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* version 1.4 (2014-08-26)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.epub;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.w3c.dom.DOMImplementation;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.DocumentType;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import writer2latex.api.ContentEntry;
|
||||
import writer2latex.api.ConverterResult;
|
||||
import writer2latex.base.DOMDocument;
|
||||
import writer2latex.util.Misc;
|
||||
|
||||
/** This class creates the required NXC file for an EPUB document
|
||||
* (see http://www.idpf.org/2007/opf/OPF_2.0_final_spec.html#Section2.4).
|
||||
*/
|
||||
public class NCXWriter extends DOMDocument {
|
||||
|
||||
public NCXWriter(ConverterResult cr, String sUUID) {
|
||||
super("book", "ncx");
|
||||
|
||||
// create DOM
|
||||
Document contentDOM = null;
|
||||
try {
|
||||
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = builderFactory.newDocumentBuilder();
|
||||
DOMImplementation domImpl = builder.getDOMImplementation();
|
||||
DocumentType doctype = domImpl.createDocumentType("ncx","","");
|
||||
contentDOM = domImpl.createDocument("http://www.daisy.org/z3986/2005/ncx/","ncx",doctype);
|
||||
}
|
||||
catch (ParserConfigurationException t) { // this should never happen
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
|
||||
// Populate the DOM tree
|
||||
Element ncx = contentDOM.getDocumentElement();
|
||||
ncx.setAttribute("version", "2005-1");
|
||||
ncx.setAttribute("xml:lang", cr.getMetaData().getLanguage());
|
||||
ncx.setAttribute("xmlns","http://www.daisy.org/z3986/2005/ncx/");
|
||||
|
||||
// The head has four required meta data items
|
||||
Element head = contentDOM.createElement("head");
|
||||
ncx.appendChild(head);
|
||||
|
||||
Element uid = contentDOM.createElement("meta");
|
||||
uid.setAttribute("name","dtb:uid");
|
||||
uid.setAttribute("content", sUUID);
|
||||
head.appendChild(uid);
|
||||
|
||||
Element depth = contentDOM.createElement("meta");
|
||||
depth.setAttribute("name","dtb:depth");
|
||||
// Setting the content attribute later
|
||||
head.appendChild(depth);
|
||||
|
||||
Element totalPageCount = contentDOM.createElement("meta");
|
||||
totalPageCount.setAttribute("name","dtb:totalPageCount");
|
||||
totalPageCount.setAttribute("content", "0");
|
||||
head.appendChild(totalPageCount);
|
||||
|
||||
Element maxPageNumber = contentDOM.createElement("meta");
|
||||
maxPageNumber.setAttribute("name","dtb:maxPageNumber");
|
||||
maxPageNumber.setAttribute("content", "0");
|
||||
head.appendChild(maxPageNumber);
|
||||
|
||||
// The ncx must contain a docTitle element
|
||||
Element docTitle = contentDOM.createElement("docTitle");
|
||||
ncx.appendChild(docTitle);
|
||||
Element docTitleText = contentDOM.createElement("text");
|
||||
docTitle.appendChild(docTitleText);
|
||||
docTitleText.appendChild(contentDOM.createTextNode(cr.getMetaData().getTitle()));
|
||||
|
||||
// Build the navMap from the content table in the converter result
|
||||
Element navMap = contentDOM.createElement("navMap");
|
||||
ncx.appendChild(navMap);
|
||||
|
||||
Element currentContainer = ncx;
|
||||
int nCurrentLevel = 0;
|
||||
int nCurrentEntryLevel = 0; // This may differ from nCurrentLevel if the heading levels "jump" in then document
|
||||
int nDepth = 0;
|
||||
int nPlayOrder = 0;
|
||||
|
||||
Iterator<ContentEntry> content = cr.getContent().iterator();
|
||||
while (content.hasNext()) {
|
||||
ContentEntry entry = content.next();
|
||||
int nEntryLevel = Math.max(entry.getLevel(), 1);
|
||||
|
||||
if (nEntryLevel<nCurrentLevel) {
|
||||
// Return to higher level
|
||||
for (int i=nEntryLevel; i<nCurrentLevel; i++) {
|
||||
currentContainer = (Element) currentContainer.getParentNode();
|
||||
}
|
||||
nCurrentLevel = nEntryLevel;
|
||||
}
|
||||
else if (nEntryLevel>nCurrentEntryLevel) {
|
||||
// To lower level (always one step; a jump from e.g. heading 1 to heading 3 in the document
|
||||
// is considered an error)
|
||||
currentContainer = (Element) currentContainer.getLastChild();
|
||||
nCurrentLevel++;
|
||||
}
|
||||
|
||||
nCurrentEntryLevel = nEntryLevel;
|
||||
|
||||
Element navPoint = contentDOM.createElement("navPoint");
|
||||
navPoint.setAttribute("playOrder", Integer.toString(++nPlayOrder));
|
||||
navPoint.setAttribute("id", "text"+nPlayOrder);
|
||||
currentContainer.appendChild(navPoint);
|
||||
|
||||
Element navLabel = contentDOM.createElement("navLabel");
|
||||
navPoint.appendChild(navLabel);
|
||||
Element navLabelText = contentDOM.createElement("text");
|
||||
navLabel.appendChild(navLabelText);
|
||||
navLabelText.appendChild(contentDOM.createTextNode(entry.getTitle()));
|
||||
|
||||
Element navPointContent = contentDOM.createElement("content");
|
||||
String sHref = Misc.makeHref(entry.getFile().getFileName());
|
||||
if (entry.getTarget()!=null) { sHref+="#"+entry.getTarget(); }
|
||||
navPointContent.setAttribute("src", sHref);
|
||||
navPoint.appendChild(navPointContent);
|
||||
|
||||
nDepth = Math.max(nDepth, nCurrentLevel);
|
||||
}
|
||||
|
||||
if (nDepth==0) {
|
||||
// TODO: We're in trouble: The document has no headings
|
||||
}
|
||||
|
||||
depth.setAttribute("content", Integer.toString(nDepth));
|
||||
|
||||
setContentDOM(contentDOM);
|
||||
|
||||
}
|
||||
}
|
130
src/main/java/writer2latex/epub/NavigationWriter.java
Normal file
130
src/main/java/writer2latex/epub/NavigationWriter.java
Normal file
|
@ -0,0 +1,130 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* NavigationWriter.java
|
||||
*
|
||||
* Copyright: 2001-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* version 1.6 (2015-04-21)
|
||||
*
|
||||
*/
|
||||
package writer2latex.epub;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.w3c.dom.DOMImplementation;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.DocumentType;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import writer2latex.api.ContentEntry;
|
||||
import writer2latex.api.ConverterResult;
|
||||
import writer2latex.base.DOMDocument;
|
||||
import writer2latex.util.Misc;
|
||||
|
||||
/** This class writes the EPUB Navigation Document as defined in EPUB 3
|
||||
*/
|
||||
public class NavigationWriter extends DOMDocument {
|
||||
|
||||
public NavigationWriter(ConverterResult cr) {
|
||||
super("nav", "xhtml");
|
||||
|
||||
// create DOM
|
||||
Document contentDOM = null;
|
||||
try {
|
||||
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = builderFactory.newDocumentBuilder();
|
||||
DOMImplementation domImpl = builder.getDOMImplementation();
|
||||
DocumentType doctype = domImpl.createDocumentType("xhtml","","");
|
||||
contentDOM = domImpl.createDocument("http://www.w3.org/1999/xhtml","html",doctype);
|
||||
}
|
||||
catch (ParserConfigurationException t) { // this should never happen
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
|
||||
// Populate the DOM tree
|
||||
Element doc = contentDOM.getDocumentElement();
|
||||
doc.setAttribute("xmlns","http://www.w3.org/1999/xhtml");
|
||||
doc.setAttribute("xmlns:epub","http://www.idpf.org/2007/ops");
|
||||
doc.setAttribute("xml:lang", cr.getMetaData().getLanguage());
|
||||
doc.setAttribute("lang", cr.getMetaData().getLanguage());
|
||||
|
||||
// Create and populate the head
|
||||
Element head = contentDOM.createElement("head");
|
||||
doc.appendChild(head);
|
||||
|
||||
Element title = contentDOM.createElement("title");
|
||||
head.appendChild(title);
|
||||
title.appendChild(contentDOM.createTextNode("EPUB 3 Navigation Document"));
|
||||
// Or use the document title cr.getMetaData().getTitle()?
|
||||
|
||||
// Create the body
|
||||
Element body = contentDOM.createElement("body");
|
||||
doc.appendChild(body);
|
||||
|
||||
// Create nav element
|
||||
Element nav = contentDOM.createElement("nav");
|
||||
nav.setAttribute("epub:type", "toc");
|
||||
body.appendChild(nav);
|
||||
|
||||
// Populate the nav element from the content table in the converter result
|
||||
Element currentContainer = body;
|
||||
int nCurrentLevel = 0;
|
||||
int nCurrentEntryLevel = 0; // This may differ from nCurrentLevel if the heading levels "jump" in then document
|
||||
Iterator<ContentEntry> content = cr.getContent().iterator();
|
||||
while (content.hasNext()) {
|
||||
ContentEntry entry = content.next();
|
||||
int nEntryLevel = Math.max(entry.getLevel(), 1);
|
||||
|
||||
if (nEntryLevel<nCurrentLevel) {
|
||||
// Return to higher level
|
||||
for (int i=nEntryLevel; i<nCurrentLevel; i++) {
|
||||
currentContainer = (Element) currentContainer.getParentNode().getParentNode();
|
||||
}
|
||||
nCurrentLevel = nEntryLevel;
|
||||
}
|
||||
else if (nEntryLevel>nCurrentEntryLevel) {
|
||||
// To lower level (always one step; a jump from e.g. heading 1 to heading 3 in the document
|
||||
// is considered an error)
|
||||
currentContainer = (Element) currentContainer.getLastChild();
|
||||
Element ol = contentDOM.createElement("ol");
|
||||
currentContainer.appendChild(ol);
|
||||
currentContainer = ol;
|
||||
nCurrentLevel++;
|
||||
}
|
||||
|
||||
nCurrentEntryLevel = nEntryLevel;
|
||||
|
||||
// Create the actual toc entry
|
||||
Element li = contentDOM.createElement("li");
|
||||
currentContainer.appendChild(li);
|
||||
Element a = contentDOM.createElement("a");
|
||||
li.appendChild(a);
|
||||
String sHref = Misc.makeHref(entry.getFile().getFileName());
|
||||
if (entry.getTarget()!=null) { sHref+="#"+entry.getTarget(); }
|
||||
a.setAttribute("href", sHref);
|
||||
a.appendChild(contentDOM.createTextNode(entry.getTitle()));
|
||||
}
|
||||
|
||||
setContentDOM(contentDOM);
|
||||
}
|
||||
|
||||
}
|
419
src/main/java/writer2latex/epub/OPFWriter.java
Normal file
419
src/main/java/writer2latex/epub/OPFWriter.java
Normal file
|
@ -0,0 +1,419 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* OPFWriter.java
|
||||
*
|
||||
* Copyright: 2001-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* version 1.6 (2015-06-24)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.epub;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.w3c.dom.DOMImplementation;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.DocumentType;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import writer2latex.api.ContentEntry;
|
||||
import writer2latex.api.ConverterResult;
|
||||
import writer2latex.api.OutputFile;
|
||||
import writer2latex.base.DOMDocument;
|
||||
import writer2latex.util.Misc;
|
||||
import writer2latex.xhtml.XhtmlConfig;
|
||||
|
||||
/** This class writes an OPF-file for an EPUB document (see http://www.idpf.org/2007/opf/OPF_2.0_final_spec.html).
|
||||
*/
|
||||
public class OPFWriter extends DOMDocument {
|
||||
private String sUID=null;
|
||||
|
||||
public OPFWriter(ConverterResult cr, String sFileName, int nVersion, XhtmlConfig config) {
|
||||
super("book", "opf");
|
||||
|
||||
// create DOM
|
||||
Document contentDOM = null;
|
||||
try {
|
||||
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = builderFactory.newDocumentBuilder();
|
||||
DOMImplementation domImpl = builder.getDOMImplementation();
|
||||
DocumentType doctype = domImpl.createDocumentType("package","","");
|
||||
contentDOM = domImpl.createDocument("http://www.idpf.org/2007/opf","package",doctype);
|
||||
}
|
||||
catch (ParserConfigurationException t) { // this should never happen
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
|
||||
// Populate the DOM tree
|
||||
Element pack = contentDOM.getDocumentElement();
|
||||
if (nVersion==3) {
|
||||
pack.setAttribute("version", "3.0");
|
||||
}
|
||||
else {
|
||||
pack.setAttribute("version", "2.0");
|
||||
}
|
||||
pack.setAttribute("xmlns","http://www.idpf.org/2007/opf");
|
||||
pack.setAttribute("unique-identifier", "BookId");
|
||||
|
||||
// Meta data, at least dc:title, dc:language and dc:identifier are required by the specification
|
||||
// For EPUB 3, also dcterms:modified is required
|
||||
Element metadata = contentDOM.createElement("metadata");
|
||||
metadata.setAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/");
|
||||
if (nVersion!=3) {
|
||||
metadata.setAttribute("xmlns:opf", "http://www.idpf.org/2007/opf");
|
||||
}
|
||||
pack.appendChild(metadata);
|
||||
|
||||
// Title and language (required; use file name if title is empty)
|
||||
String sTitle = cr.getMetaData().getTitle();
|
||||
appendElement(contentDOM, metadata, "dc:title", sTitle.trim().length()>0 ? sTitle : sFileName);
|
||||
appendElement(contentDOM, metadata, "dc:language", cr.getMetaData().getLanguage());
|
||||
|
||||
// Modification (required in EPUB 3)
|
||||
if (nVersion==3) {
|
||||
appendElement(contentDOM, metadata, "meta", getCurrentDateTime())
|
||||
.setAttribute("property", "dcterms:modified");
|
||||
}
|
||||
|
||||
// Subject and keywords in ODF both map to Dublin core subjects
|
||||
if (cr.getMetaData().getSubject().length()>0) {
|
||||
appendElement(contentDOM, metadata, "dc:subject", cr.getMetaData().getSubject());
|
||||
}
|
||||
if (cr.getMetaData().getKeywords().length()>0) {
|
||||
String[] sKeywords = cr.getMetaData().getKeywords().split(",");
|
||||
for (String sKeyword : sKeywords) {
|
||||
appendElement(contentDOM, metadata, "dc:subject", sKeyword.trim());
|
||||
}
|
||||
}
|
||||
if (cr.getMetaData().getDescription().length()>0) {
|
||||
appendElement(contentDOM, metadata, "dc:description", cr.getMetaData().getDescription());
|
||||
}
|
||||
|
||||
// User defined meta data
|
||||
// The identifier, creator, contributor and date has an optional attribute and there may be multiple instances of
|
||||
// the latter three. The key must be in the form name[id][.attribute]
|
||||
// where the id is some unique id amongst the instances with the same name
|
||||
// Furthermore the instances will be sorted on the id
|
||||
// Thus you can have e.g. creator1.aut="John Doe" and creator2.aut="Jane Doe", and "John Doe" will be the first author
|
||||
boolean bHasIdentifier = false;
|
||||
boolean bHasCreator = false;
|
||||
boolean bHasDate = false;
|
||||
// First rearrange the user-defined meta data
|
||||
Map<String,String> userDefinedMetaData = cr.getMetaData().getUserDefinedMetaData();
|
||||
Map<String,String[]> dc = new HashMap<String,String[]>();
|
||||
for (String sKey : userDefinedMetaData.keySet()) {
|
||||
if (sKey.length()>0) {
|
||||
String[] sValue = new String[2];
|
||||
sValue[0] = userDefinedMetaData.get(sKey);
|
||||
String sNewKey;
|
||||
int nDot = sKey.indexOf(".");
|
||||
if (nDot>0) {
|
||||
sNewKey = sKey.substring(0, nDot).toLowerCase();
|
||||
sValue[1] = sKey.substring(nDot+1);
|
||||
}
|
||||
else {
|
||||
sNewKey = sKey.toLowerCase();
|
||||
sValue[1] = null;
|
||||
}
|
||||
dc.put(sNewKey, sValue);
|
||||
}
|
||||
}
|
||||
// Then export it
|
||||
String[] sKeys = Misc.sortStringSet(dc.keySet());
|
||||
for (String sKey : sKeys) {
|
||||
String sValue = dc.get(sKey)[0];
|
||||
String sAttributeValue = dc.get(sKey)[1];
|
||||
if (sKey.startsWith("identifier")) {
|
||||
Element identifier = appendElement(contentDOM, metadata, "dc:identifier", sValue);
|
||||
if (!bHasIdentifier) { // The first identifier is the unique ID
|
||||
identifier.setAttribute("id", "BookId");
|
||||
sUID = sValue;
|
||||
}
|
||||
if (sAttributeValue!=null) {
|
||||
if (nVersion==3) {
|
||||
Element meta = appendElement(contentDOM, metadata, "meta", sAttributeValue);
|
||||
meta.setAttribute("refines", "#BookId");
|
||||
meta.setAttribute("property", "identifier-type");
|
||||
}
|
||||
else {
|
||||
identifier.setAttribute("opf:scheme", sAttributeValue);
|
||||
}
|
||||
}
|
||||
bHasIdentifier = true;
|
||||
}
|
||||
else if (sKey.startsWith("creator")) {
|
||||
Element creator = appendElement(contentDOM, metadata, "dc:creator", sValue);
|
||||
creator.setAttribute("id", sKey);
|
||||
if (nVersion==3) {
|
||||
Element fileas = appendElement(contentDOM, metadata, "meta", fileAs(sValue));
|
||||
fileas.setAttribute("refines", "#"+sKey);
|
||||
fileas.setAttribute("property", "file-as");
|
||||
if (sAttributeValue!=null) {
|
||||
Element role = appendElement(contentDOM, metadata, "meta", sAttributeValue);
|
||||
role.setAttribute("refines", "#"+sKey);
|
||||
role.setAttribute("property", "role");
|
||||
}
|
||||
}
|
||||
else {
|
||||
creator.setAttribute("opf:file-as", fileAs(sValue));
|
||||
if (sAttributeValue!=null) {
|
||||
creator.setAttribute("opf:role", sAttributeValue);
|
||||
}
|
||||
}
|
||||
bHasCreator = true;
|
||||
}
|
||||
else if (sKey.startsWith("contributor")) {
|
||||
Element contributor = appendElement(contentDOM, metadata, "dc:contributor", sValue);
|
||||
contributor.setAttribute("id", sKey);
|
||||
if (nVersion==3) {
|
||||
Element fileas = appendElement(contentDOM, metadata, "meta", fileAs(sValue));
|
||||
fileas.setAttribute("refines", "#"+sKey);
|
||||
fileas.setAttribute("property", "file-as");
|
||||
if (sAttributeValue!=null) {
|
||||
Element role = appendElement(contentDOM, metadata, "meta", sAttributeValue);
|
||||
role.setAttribute("refines", "#"+sKey);
|
||||
role.setAttribute("property", "role");
|
||||
}
|
||||
}
|
||||
else {
|
||||
contributor.setAttribute("opf:file-as", fileAs(sValue));
|
||||
if (sAttributeValue!=null) {
|
||||
contributor.setAttribute("opf:role", sAttributeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sKey.startsWith("date")) {
|
||||
Element date = appendElement(contentDOM, metadata, "dc:date", sValue);
|
||||
date.setAttribute("id", sKey);
|
||||
if (nVersion==3) {
|
||||
if (sAttributeValue!=null) {
|
||||
Element event = appendElement(contentDOM, metadata, "meta", sAttributeValue);
|
||||
event.setAttribute("refines", "#"+sKey);
|
||||
event.setAttribute("property", "event");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (sAttributeValue!=null) {
|
||||
date.setAttribute("opf:event", sAttributeValue);
|
||||
}
|
||||
}
|
||||
bHasDate = true;
|
||||
}
|
||||
// Remaining properties must be unique and has not attributes, hence
|
||||
else if (sAttributeValue==null) {
|
||||
if ("publisher".equals(sKey)) {
|
||||
appendElement(contentDOM, metadata, "dc:publisher", sValue);
|
||||
}
|
||||
else if ("type".equals(sKey)) {
|
||||
appendElement(contentDOM, metadata, "dc:type", sValue);
|
||||
}
|
||||
else if ("format".equals(sKey)) {
|
||||
appendElement(contentDOM, metadata, "dc:format", sValue);
|
||||
}
|
||||
else if ("source".equals(sKey)) {
|
||||
appendElement(contentDOM, metadata, "dc:source", sValue);
|
||||
}
|
||||
else if ("relation".equals(sKey)) {
|
||||
appendElement(contentDOM, metadata, "dc:relation", sValue);
|
||||
}
|
||||
else if ("coverage".equals(sKey)) {
|
||||
appendElement(contentDOM, metadata, "dc:coverage", sValue);
|
||||
}
|
||||
else if ("rights".equals(sKey)) {
|
||||
appendElement(contentDOM, metadata, "dc:rights", sValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back values for identifier, creator and date
|
||||
if (!bHasIdentifier) {
|
||||
// Create a universal unique ID
|
||||
sUID = UUID.randomUUID().toString();
|
||||
Element identifier = appendElement(contentDOM, metadata, "dc:identifier", sUID);
|
||||
identifier.setAttribute("id", "BookId");
|
||||
if (nVersion==3) {
|
||||
Element meta = appendElement(contentDOM, metadata, "meta", "UUID");
|
||||
meta.setAttribute("refines", "#BookId");
|
||||
meta.setAttribute("property", "identifier-type");
|
||||
}
|
||||
else {
|
||||
identifier.setAttribute("opf:scheme", "UUID");
|
||||
}
|
||||
}
|
||||
if (!bHasCreator && cr.getMetaData().getCreator().length()>0) {
|
||||
Element creator = appendElement(contentDOM, metadata, "dc:creator", cr.getMetaData().getCreator());
|
||||
creator.setAttribute("id", "creator");
|
||||
if (nVersion==3) {
|
||||
Element fileas = appendElement(contentDOM, metadata, "meta", fileAs(cr.getMetaData().getCreator()));
|
||||
fileas.setAttribute("refines", "#creator");
|
||||
fileas.setAttribute("property", "file-as");
|
||||
}
|
||||
else {
|
||||
creator.setAttribute("opf:file-as", fileAs(cr.getMetaData().getCreator()));
|
||||
}
|
||||
}
|
||||
if (!bHasDate && cr.getMetaData().getDate().length()>0) {
|
||||
// TODO: Support meta:creation-date?
|
||||
appendElement(contentDOM, metadata, "dc:date", Misc.dateOnly(cr.getMetaData().getDate()));
|
||||
}
|
||||
|
||||
// Manifest must contain references to all the files in the XHTML converter result
|
||||
// Spine should contain references to all the master documents within the converter result
|
||||
Element manifest = contentDOM.createElement("manifest");
|
||||
pack.appendChild(manifest);
|
||||
|
||||
Element spine = contentDOM.createElement("spine");
|
||||
if (nVersion!=3 || config.includeNCX()) { // Use old NCX file for navigation
|
||||
spine.setAttribute("toc", "ncx");
|
||||
}
|
||||
pack.appendChild(spine);
|
||||
|
||||
int nMasterCount = 0;
|
||||
int nResourceCount = 0;
|
||||
Iterator<OutputFile> iterator = cr.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
OutputFile file = iterator.next();
|
||||
Element item = contentDOM.createElement("item");
|
||||
manifest.appendChild(item);
|
||||
item.setAttribute("href",Misc.makeHref(file.getFileName()));
|
||||
item.setAttribute("media-type", file.getMIMEType());
|
||||
// Treat cover as recommended by Threepress consulting (http://blog.threepress.org/2009/11/20/best-practices-in-epub-cover-images/)
|
||||
if (cr.getCoverFile()!=null && cr.getCoverFile().getFile()==file) {
|
||||
item.setAttribute("id", "cover");
|
||||
|
||||
Element itemref = contentDOM.createElement("itemref");
|
||||
itemref.setAttribute("idref", "cover");
|
||||
itemref.setAttribute("linear", "no"); // maybe problematic
|
||||
spine.appendChild(itemref);
|
||||
}
|
||||
else if (cr.getCoverImageFile()!=null && cr.getCoverImageFile().getFile()==file) {
|
||||
item.setAttribute("id", "cover-image");
|
||||
|
||||
Element meta = contentDOM.createElement("meta");
|
||||
meta.setAttribute("name", "cover");
|
||||
meta.setAttribute("content", "cover-image");
|
||||
metadata.appendChild(meta);
|
||||
}
|
||||
else if (file.isMasterDocument()) {
|
||||
String sId = "text"+(++nMasterCount);
|
||||
item.setAttribute("id", sId);
|
||||
|
||||
if (nVersion==3 && file.containsMath()) {
|
||||
item.setAttribute("properties","mathml");
|
||||
}
|
||||
|
||||
Element itemref = contentDOM.createElement("itemref");
|
||||
itemref.setAttribute("idref", sId);
|
||||
spine.appendChild(itemref);
|
||||
}
|
||||
else {
|
||||
item.setAttribute("id", "resource"+(++nResourceCount));
|
||||
}
|
||||
}
|
||||
|
||||
if (nVersion==3) { // Include the new Navigation Document
|
||||
Element item = contentDOM.createElement("item");
|
||||
item.setAttribute("href", "nav.xhtml");
|
||||
item.setAttribute("media-type", "application/xhtml+xml");
|
||||
item.setAttribute("id", "nav");
|
||||
item.setAttribute("properties", "nav");
|
||||
manifest.appendChild(item);
|
||||
}
|
||||
if (nVersion!=3 || config.includeNCX()) { // Include old NCX file
|
||||
Element item = contentDOM.createElement("item");
|
||||
item.setAttribute("href", "book.ncx");
|
||||
item.setAttribute("media-type", "application/x-dtbncx+xml");
|
||||
item.setAttribute("id", "ncx");
|
||||
manifest.appendChild(item);
|
||||
}
|
||||
|
||||
// The guide may contain references to some fundamental structural components
|
||||
Element guide = contentDOM.createElement("guide");
|
||||
pack.appendChild(guide);
|
||||
addGuideReference(contentDOM,guide,"cover",cr.getCoverFile());
|
||||
addGuideReference(contentDOM,guide,"title-page",cr.getTitlePageFile());
|
||||
addGuideReference(contentDOM,guide,"text",cr.getTextFile());
|
||||
addGuideReference(contentDOM,guide,"toc",cr.getTocFile());
|
||||
addGuideReference(contentDOM,guide,"index",cr.getIndexFile());
|
||||
addGuideReference(contentDOM,guide,"loi",cr.getLofFile());
|
||||
addGuideReference(contentDOM,guide,"lot",cr.getLotFile());
|
||||
addGuideReference(contentDOM,guide,"bibliography",cr.getBibliographyFile());
|
||||
|
||||
setContentDOM(contentDOM);
|
||||
}
|
||||
|
||||
/** Get the unique ID associated with this EPUB document (either collected from the user-defined
|
||||
* meta data or a generated UUID)
|
||||
*
|
||||
* @return the ID
|
||||
*/
|
||||
public String getUid() {
|
||||
return sUID;
|
||||
}
|
||||
|
||||
private String fileAs(String sName) {
|
||||
int nSpace = sName.lastIndexOf(' ');
|
||||
if (nSpace>-1) {
|
||||
return sName.substring(nSpace+1).trim()+", "+sName.substring(0, nSpace).trim();
|
||||
}
|
||||
else {
|
||||
return sName.trim();
|
||||
}
|
||||
}
|
||||
|
||||
private Element appendElement(Document contentDOM, Element node, String sTagName, String sContent) {
|
||||
Element child = contentDOM.createElement(sTagName);
|
||||
node.appendChild(child);
|
||||
child.appendChild(contentDOM.createTextNode(sContent));
|
||||
return child;
|
||||
}
|
||||
|
||||
private void addGuideReference(Document contentDOM, Element guide, String sType, ContentEntry entry) {
|
||||
if (entry!=null) {
|
||||
Element reference = contentDOM.createElement("reference");
|
||||
reference.setAttribute("type", sType);
|
||||
reference.setAttribute("title", entry.getTitle());
|
||||
String sHref = Misc.makeHref(entry.getFile().getFileName());
|
||||
if (entry.getTarget()!=null) { sHref+="#"+entry.getTarget(); }
|
||||
reference.setAttribute("href", sHref);
|
||||
guide.appendChild(reference);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current date and time in the required format
|
||||
private String getCurrentDateTime() {
|
||||
Date date = Calendar.getInstance().getTime();
|
||||
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
return formatter.format(date);
|
||||
}
|
||||
|
||||
}
|
13
src/main/java/writer2latex/epub/Package.html
Normal file
13
src/main/java/writer2latex/epub/Package.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>The package writer2latex.epub</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>This package contains EPUB specific code.</p>
|
||||
<p>It contains a <code>writerlatex.api.Converter</code> implementation for
|
||||
conversion into EPUB.</p>
|
||||
</body>
|
||||
</html>
|
260
src/main/java/writer2latex/latex/BibConverter.java
Normal file
260
src/main/java/writer2latex/latex/BibConverter.java
Normal file
|
@ -0,0 +1,260 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* BibConverter.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-07-27)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import writer2latex.base.BibliographyGenerator;
|
||||
import writer2latex.bibtex.BibTeXDocument;
|
||||
|
||||
import writer2latex.latex.i18n.ClassicI18n;
|
||||
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 handles bibliographic citations and the bibliography. The result depends on these
|
||||
* configuration options:
|
||||
* <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
|
||||
* a thebibliography environment
|
||||
* <li><code>bibtex_style</code> If BibTeX is used, this style will be applied
|
||||
* </ul>
|
||||
* The citations will always be exported as \cite commands.
|
||||
*/
|
||||
class BibConverter extends ConverterHelper {
|
||||
|
||||
private BibTeXDocument bibDoc = null;
|
||||
private boolean bUseBibTeX;
|
||||
private String sBibTeXEncoding = null;
|
||||
private String sDocumentEncoding = null;
|
||||
|
||||
/** Construct a new BibConverter.
|
||||
*
|
||||
* @param config the configuration to use
|
||||
* @param palette the ConverterPalette to use
|
||||
*/
|
||||
BibConverter(OfficeReader ofr,LaTeXConfig config, ConverterPalette palette) {
|
||||
super(ofr,config,palette);
|
||||
|
||||
// We need to create a BibTeX document except if we are using external BibTeX files
|
||||
if (!(config.useBibtex() && config.externalBibtexFiles().length()>0)) {
|
||||
bibDoc = new BibTeXDocument(palette.getOutFileName(),false,ofr);
|
||||
}
|
||||
|
||||
// We need to use a different encoding for the BibTeX files
|
||||
if (config.externalBibtexFiles().length()>0) {
|
||||
int nBibTeXEncoding = config.getBibtexEncoding();
|
||||
int nDocumentEncoding = config.getInputencoding();
|
||||
if (config.getBackend()!=LaTeXConfig.XETEX && nBibTeXEncoding>-1 && nBibTeXEncoding!=nDocumentEncoding) {
|
||||
sBibTeXEncoding = ClassicI18n.writeInputenc(nBibTeXEncoding);
|
||||
sDocumentEncoding = ClassicI18n.writeInputenc(nDocumentEncoding);
|
||||
}
|
||||
}
|
||||
|
||||
// We need to export it
|
||||
bUseBibTeX = config.useBibtex();
|
||||
}
|
||||
|
||||
/** Export the bibliography directly as a thebibliography environment (as an alternative to using BibTeX)
|
||||
*/
|
||||
private class ThebibliographyGenerator extends BibliographyGenerator {
|
||||
// The bibliography is the be inserted in this LaTeX document portion with that context
|
||||
private LaTeXDocumentPortion ldp;
|
||||
private Context context;
|
||||
|
||||
// The current bibliography item is to formatted with this before/after pair with that context
|
||||
private BeforeAfter itemBa = null;
|
||||
private Context itemContext = null;
|
||||
|
||||
ThebibliographyGenerator(OfficeReader ofr) {
|
||||
super(ofr,true);
|
||||
}
|
||||
|
||||
void handleBibliography(Element bibliography, LaTeXDocumentPortion ldp, Context context) {
|
||||
this.ldp = ldp;
|
||||
this.context = context;
|
||||
|
||||
String sWidestLabel = "";
|
||||
Collection<String> labels = getLabels();
|
||||
for (String sLabel : labels) {
|
||||
if (sLabel.length()>=sWidestLabel.length()) {
|
||||
sWidestLabel = sLabel;
|
||||
}
|
||||
}
|
||||
|
||||
ldp.append("\\begin{thebibliography}{").append(sWidestLabel).append("}\n");
|
||||
generateBibliography(bibliography);
|
||||
endBibliographyItem();
|
||||
ldp.append("\\end{thebibliography}\n");
|
||||
}
|
||||
|
||||
@Override protected void insertBibliographyItem(String sStyleName, String sKey) {
|
||||
endBibliographyItem();
|
||||
|
||||
itemBa = new BeforeAfter();
|
||||
itemContext = (Context) context.clone();
|
||||
|
||||
// Apply paragraph style (character formatting only)
|
||||
StyleWithProperties style = ofr.getParStyle(sStyleName);
|
||||
if (style!=null) {
|
||||
palette.getI18n().applyLanguage(style,true,true,itemBa);
|
||||
palette.getCharSc().applyFont(style,true,true,itemBa,itemContext);
|
||||
if (itemBa.getBefore().length()>0) {
|
||||
itemBa.add(" ","");
|
||||
itemBa.enclose("{", "}");
|
||||
}
|
||||
}
|
||||
|
||||
// Convert item
|
||||
ldp.append(itemBa.getBefore());
|
||||
palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(style));
|
||||
|
||||
ldp.append("\\bibitem");
|
||||
if (!isNumberedEntries()) {
|
||||
ldp.append("[").append(bibDoc.getExportName(sKey)).append("]");
|
||||
}
|
||||
ldp.append("{").append(bibDoc.getExportName(sKey)).append("} ");
|
||||
}
|
||||
|
||||
private void endBibliographyItem() {
|
||||
if (itemBa!=null) {
|
||||
palette.getI18n().popSpecialTable();
|
||||
ldp.append(itemBa.getAfter()).append("\n");
|
||||
itemBa = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override protected void insertBibliographyItemElement(String sStyleName, String sText) {
|
||||
BeforeAfter ba = new BeforeAfter();
|
||||
Context elementContext = (Context) itemContext.clone();
|
||||
|
||||
// Apply character style
|
||||
StyleWithProperties style = ofr.getTextStyle(sStyleName);
|
||||
palette.getCharSc().applyTextStyle(sStyleName,ba,elementContext);
|
||||
|
||||
// Convert text
|
||||
ldp.append(ba.getBefore());
|
||||
palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(style));
|
||||
ldp.append(palette.getI18n().convert(sText, false, elementContext.getLang()));
|
||||
palette.getI18n().popSpecialTable();
|
||||
ldp.append(ba.getAfter());
|
||||
}
|
||||
}
|
||||
|
||||
/** Append declarations needed by the <code>BibConverter</code> to the preamble.
|
||||
*
|
||||
* @param pack the LaTeXDocumentPortion to which declarations of packages (\\usepackage) should be added.
|
||||
* @param decl the LaTeXDocumentPortion to which other declarations should be added.
|
||||
*/
|
||||
void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
|
||||
// Currently nothing
|
||||
}
|
||||
|
||||
/** Process a bibliography
|
||||
*
|
||||
* @param node A text:bibliography element
|
||||
* @param ldp the LaTeXDocumentPortion to which LaTeX code should be added
|
||||
* @param oc the current context
|
||||
*/
|
||||
void handleBibliography(Element node, LaTeXDocumentPortion ldp, Context oc) {
|
||||
if (!config.noIndex()) {
|
||||
if (config.useBibtex()) { // Export using BibTeX
|
||||
handleBibliographyAsBibTeX(ldp);
|
||||
}
|
||||
else { // Export as thebibliography environment
|
||||
ThebibliographyGenerator bibCv = new ThebibliographyGenerator(ofr);
|
||||
Element source = Misc.getChildByTagName(node,XMLString.TEXT_BIBLIOGRAPHY_SOURCE);
|
||||
bibCv.handleBibliography(source, ldp, oc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleBibliographyAsBibTeX(LaTeXDocumentPortion ldp) {
|
||||
// Use the style given in the configuration
|
||||
ldp.append("\\bibliographystyle{")
|
||||
.append(config.bibtexStyle())
|
||||
.append("}").nl();
|
||||
|
||||
// Use BibTeX file from configuration, or exported BibTeX file
|
||||
// TODO: For XeTeX, probably use \XeTeXdefaultencoding?
|
||||
if (config.externalBibtexFiles().length()>0) {
|
||||
if (sBibTeXEncoding!=null) {
|
||||
ldp.append("\\inputencoding{").append(sBibTeXEncoding).append("}").nl();
|
||||
}
|
||||
ldp.append("\\bibliography{")
|
||||
.append(config.externalBibtexFiles())
|
||||
.append("}").nl();
|
||||
if (sBibTeXEncoding!=null) {
|
||||
ldp.append("\\inputencoding{").append(sDocumentEncoding).append("}").nl();
|
||||
}
|
||||
}
|
||||
else {
|
||||
ldp.append("\\bibliography{")
|
||||
.append(bibDoc.getName())
|
||||
.append("}").nl();
|
||||
}
|
||||
}
|
||||
|
||||
/** Process a Bibliography Mark
|
||||
* @param node a text:bibliography-mark element
|
||||
* @param ldp the LaTeXDocumentPortion to which LaTeX code should be added
|
||||
* @param oc the current context
|
||||
*/
|
||||
void handleBibliographyMark(Element node, LaTeXDocumentPortion ldp, Context oc) {
|
||||
String sIdentifier = node.getAttribute(XMLString.TEXT_IDENTIFIER);
|
||||
if (sIdentifier!=null) {
|
||||
// Use original citation if using external files; stripped if exporting BibTeX
|
||||
ldp.append("\\cite{")
|
||||
.append(config.externalBibtexFiles().length()==0 ? bibDoc.getExportName(sIdentifier) : sIdentifier)
|
||||
.append("}");
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the BibTeX document, if any (that is if the document contains bibliographic data <em>and</em>
|
||||
* the configuration does not specify external BibTeX files)
|
||||
*
|
||||
* @return the BiBTeXDocument, or null if no BibTeX file is needed
|
||||
*/
|
||||
BibTeXDocument getBibTeXDocument() {
|
||||
if (bUseBibTeX && bibDoc!=null && !bibDoc.isEmpty()) {
|
||||
return bibDoc;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
229
src/main/java/writer2latex/latex/BlockConverter.java
Normal file
229
src/main/java/writer2latex/latex/BlockConverter.java
Normal file
|
@ -0,0 +1,229 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* BlockConverter.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-04-15)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import writer2latex.latex.util.Context;
|
||||
import writer2latex.latex.util.StyleMap;
|
||||
import writer2latex.office.OfficeReader;
|
||||
import writer2latex.office.XMLString;
|
||||
import writer2latex.util.Misc;
|
||||
|
||||
/**
|
||||
* This class handles basic block content, such as 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
|
||||
palette.getListCv().handleList(child,ldp,ic);
|
||||
}
|
||||
|
||||
else if (sTagName.equals(XMLString.TEXT_UNORDERED_LIST)) {
|
||||
palette.getListCv().handleList(child,ldp,ic);
|
||||
}
|
||||
|
||||
else if (sTagName.equals(XMLString.TEXT_ORDERED_LIST)) {
|
||||
palette.getListCv().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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
162
src/main/java/writer2latex/latex/CaptionConverter.java
Normal file
162
src/main/java/writer2latex/latex/CaptionConverter.java
Normal file
|
@ -0,0 +1,162 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* CaptionConverter.java
|
||||
*
|
||||
* Copyright: 2002-2008 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
508
src/main/java/writer2latex/latex/CharStyleConverter.java
Normal file
508
src/main/java/writer2latex/latex/CharStyleConverter.java
Normal file
|
@ -0,0 +1,508 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* CharStyleConverter.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-06-30)
|
||||
*
|
||||
*/
|
||||
|
||||
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<String, String> fontDecls = new Hashtable<String, String>();
|
||||
|
||||
// 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("\\providecommand\\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));
|
||||
context.setVerbatim(sm.getVerbatim(sDisplayName));
|
||||
context.setNoLineBreaks(sm.getVerbatim(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 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+"}{"+Calc.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;
|
||||
}
|
||||
|
||||
}
|
201
src/main/java/writer2latex/latex/ColorConverter.java
Normal file
201
src/main/java/writer2latex/latex/ColorConverter.java
Normal file
|
@ -0,0 +1,201 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ColorConverter.java
|
||||
*
|
||||
* Copyright: 2002-2008 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
42
src/main/java/writer2latex/latex/ContentHandlingOption.java
Normal file
42
src/main/java/writer2latex/latex/ContentHandlingOption.java
Normal file
|
@ -0,0 +1,42 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ContentHandlingOption.java
|
||||
*
|
||||
* Copyright: 2002-2008 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
}
|
47
src/main/java/writer2latex/latex/ConverterHelper.java
Normal file
47
src/main/java/writer2latex/latex/ConverterHelper.java
Normal file
|
@ -0,0 +1,47 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ConverterHelper.java
|
||||
*
|
||||
* Copyright: 2002-2016 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-06-20)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex;
|
||||
|
||||
import writer2latex.office.OfficeReader;
|
||||
|
||||
/**
|
||||
* <p>This is an abstract superclass for converter helpers.</p>
|
||||
*/
|
||||
abstract class ConverterHelper {
|
||||
|
||||
OfficeReader ofr;
|
||||
LaTeXConfig config;
|
||||
ConverterPalette palette;
|
||||
|
||||
ConverterHelper(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
|
||||
this.ofr = ofr;
|
||||
this.config = config;
|
||||
this.palette = palette;
|
||||
}
|
||||
|
||||
abstract void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl);
|
||||
|
||||
}
|
298
src/main/java/writer2latex/latex/ConverterPalette.java
Normal file
298
src/main/java/writer2latex/latex/ConverterPalette.java
Normal file
|
@ -0,0 +1,298 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ConverterPalette.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-04-14)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import writer2latex.api.Config;
|
||||
import writer2latex.api.ConverterFactory;
|
||||
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.util.CSVList;
|
||||
import writer2latex.util.ExportNameCollection;
|
||||
import writer2latex.util.Misc;
|
||||
import writer2latex.office.MIMETypes;
|
||||
import writer2latex.office.StyleWithProperties;
|
||||
import writer2latex.office.XMLString;
|
||||
|
||||
/** This class converts a Writer XML file to a LaTeX file
|
||||
*/
|
||||
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 PageStyleConverter pageSc;
|
||||
private BlockConverter blockCv;
|
||||
private ParConverter parCv;
|
||||
private HeadingConverter headingCv;
|
||||
private IndexConverter indexCv;
|
||||
private BibConverter bibCv;
|
||||
private SectionConverter sectionCv;
|
||||
private TableConverter tableCv;
|
||||
private ListConverter listCv;
|
||||
private NoteConverter noteCv;
|
||||
private CaptionConverter captionCv;
|
||||
private InlineConverter inlineCv;
|
||||
private FieldConverter fieldCv;
|
||||
private DrawConverter drawCv;
|
||||
private MathConverter mathCv;
|
||||
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 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 ListConverter getListCv() { return listCv; }
|
||||
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 MathConverter getMathCv() { return mathCv; }
|
||||
public Info getInfo() { return info; }
|
||||
|
||||
|
||||
// fill out inner converter method
|
||||
public void convertInner() throws IOException {
|
||||
sTargetFileName = Misc.trimDocumentName(sTargetFileName,".tex");
|
||||
String sSafeTargetFileName = new ExportNameCollection(true).getExportName(sTargetFileName);
|
||||
imageConverter.setBaseFileName(sSafeTargetFileName+"-img");
|
||||
if (config.saveImagesInSubdir()) {
|
||||
imageConverter.setUseSubdir(sSafeTargetFileName+"-img");
|
||||
}
|
||||
|
||||
// Set graphics formats depending on backend
|
||||
if (config.getBackend()==LaTeXConfig.PDFTEX || config.getBackend()==LaTeXConfig.XETEX) {
|
||||
imageConverter.setDefaultFormat(MIMETypes.PNG);
|
||||
imageConverter.setDefaultVectorFormat(MIMETypes.PDF);
|
||||
imageConverter.addAcceptedFormat(MIMETypes.JPEG);
|
||||
}
|
||||
else if (config.getBackend()==LaTeXConfig.DVIPS) {
|
||||
imageConverter.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);
|
||||
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);
|
||||
listCv = new ListConverter(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);
|
||||
mathCv = new MathConverter(ofr,config,this);
|
||||
info = new Info(ofr,config,this);
|
||||
|
||||
// Create master document and add this
|
||||
this.texDoc = new LaTeXDocument(sTargetFileName,config.getWrapLinesAfter(),true);
|
||||
if (config.getBackend()!=LaTeXConfig.XETEX) {
|
||||
texDoc.setEncoding(ClassicI18n.writeJavaEncoding(config.getInputencoding()));
|
||||
}
|
||||
else {
|
||||
texDoc.setEncoding("UTF-8");
|
||||
|
||||
}
|
||||
converterResult.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);
|
||||
noteCv.appendDeclarations(packages,declarations);
|
||||
charSc.appendDeclarations(packages,declarations);
|
||||
headingCv.appendDeclarations(packages,declarations);
|
||||
parCv.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);
|
||||
listCv.appendDeclarations(packages,declarations);
|
||||
captionCv.appendDeclarations(packages,declarations);
|
||||
inlineCv.appendDeclarations(packages,declarations);
|
||||
fieldCv.appendDeclarations(packages,declarations);
|
||||
drawCv.appendDeclarations(packages,declarations);
|
||||
mathCv.appendDeclarations(packages,declarations);
|
||||
|
||||
// Add custom preamble
|
||||
String sCustomPreamble = config.getCustomPreamble();
|
||||
if (sCustomPreamble.length()>0) {
|
||||
declarations.append(sCustomPreamble).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!=null) {
|
||||
createMeta("date",Misc.dateOnly(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://writer2latex.sourceforge.net 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) {
|
||||
converterResult.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();
|
||||
}
|
||||
|
||||
|
||||
}
|
454
src/main/java/writer2latex/latex/DrawConverter.java
Normal file
454
src/main/java/writer2latex/latex/DrawConverter.java
Normal file
|
@ -0,0 +1,454 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* DrawConverter.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2014-09-05)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import writer2latex.base.BinaryGraphicsDocument;
|
||||
import writer2latex.latex.util.BeforeAfter;
|
||||
import writer2latex.latex.util.Context;
|
||||
import writer2latex.office.EmbeddedObject;
|
||||
import writer2latex.office.EmbeddedXMLObject;
|
||||
import writer2latex.office.MIMETypes;
|
||||
import writer2latex.office.OfficeReader;
|
||||
import writer2latex.office.XMLString;
|
||||
import writer2latex.util.CSVList;
|
||||
import writer2latex.util.Calc;
|
||||
import writer2latex.util.Misc;
|
||||
|
||||
/**
|
||||
* <p>This class handles draw elements.</p>
|
||||
*/
|
||||
public class DrawConverter extends ConverterHelper {
|
||||
|
||||
private boolean bNeedGraphicx = false;
|
||||
|
||||
// Keep track of floating frames (images, text boxes...)
|
||||
private Stack<LinkedList<Element>> floatingFramesStack = new Stack<LinkedList<Element>>();
|
||||
|
||||
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<Element>());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Process the first child of a draw:frame
|
||||
public void handleDrawElement(Element node, LaTeXDocumentPortion ldp, Context oc) {
|
||||
// node must be an element 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)) {
|
||||
if (!palette.getMathCv().handleTexMathsEquation(node,ldp)) {
|
||||
// OpenDocument: Get the actual draw element in the frame
|
||||
handleDrawElement(Misc.getFirstChildElement(node),ldp,oc);
|
||||
}
|
||||
}
|
||||
else if (sName.equals(XMLString.DRAW_G)) {
|
||||
if (!palette.getMathCv().handleTexMathsEquation(node,ldp)) {
|
||||
// Shapes are currently not supported
|
||||
ldp.append("[Warning: Draw object ignored]");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Other drawing objects 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 formuladoc = ((EmbeddedXMLObject) object).getContentDOM();
|
||||
Element formula = Misc.getChildByTagName(formuladoc,XMLString.MATH); // Since OOo3.2
|
||||
if (formula==null) {
|
||||
formula = Misc.getChildByTagName(formuladoc,XMLString.MATH_MATH);
|
||||
}
|
||||
String sLaTeX = palette.getMathCv().convert(formula);
|
||||
if (!" ".equals(sLaTeX)) { // ignore empty formulas
|
||||
ldp.append(" $")
|
||||
.append(sLaTeX)
|
||||
.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); // Since OOo 3.2
|
||||
if (formula==null) {
|
||||
formula = Misc.getChildByTagName(node,XMLString.MATH_MATH);
|
||||
}
|
||||
if (formula!=null) {
|
||||
ldp.append(" $")
|
||||
.append(palette.getMathCv().convert(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
|
||||
if (config.floatFigures()) {
|
||||
// Inside floats we don't want the extra glue added by the center environment
|
||||
ba.add("\\centering\n","\n");
|
||||
}
|
||||
else {
|
||||
// Outside a float we certainly want it
|
||||
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(sName,"|graphic",ldp);
|
||||
String sAnchor = frame.getAttribute(XMLString.TEXT_ANCHOR_TYPE);
|
||||
|
||||
//if (oc.isInFrame() || "as-char".equals(sAnchor)) {
|
||||
if ("as-char".equals(sAnchor)) {
|
||||
handleDrawImageAsChar(node,ldp,oc);
|
||||
}
|
||||
else {
|
||||
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;
|
||||
|
||||
BinaryGraphicsDocument bgd = palette.getImageCv().getImage(node);
|
||||
if (bgd!=null) {
|
||||
if (!bgd.isLinked()) { // embedded image
|
||||
if (!bgd.isRecycled()) { palette.addDocument(bgd); }
|
||||
sFileName = bgd.getFileName();
|
||||
String sMIME = bgd.getMIMEType();
|
||||
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)));
|
||||
}
|
||||
else { // linked image
|
||||
sFileName = bgd.getFileName();
|
||||
String sExt = Misc.getFileExtension(sFileName).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 {
|
||||
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 = Calc.truncateLength(frame.getAttribute(XMLString.SVG_WIDTH));
|
||||
String sHeight = Calc.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(sName,"|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 {
|
||||
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 = Calc.truncateLength(getFrame(node).getAttribute(XMLString.SVG_WIDTH));
|
||||
if (!bIsCaption) {
|
||||
ldp.append("\\begin{minipage}{").append(sWidth).append("}").nl();
|
||||
}
|
||||
floatingFramesStack.push(new LinkedList<Element>());
|
||||
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<Element> floatingFrames = 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();
|
||||
}
|
||||
|
||||
}
|
986
src/main/java/writer2latex/latex/FieldConverter.java
Normal file
986
src/main/java/writer2latex/latex/FieldConverter.java
Normal file
|
@ -0,0 +1,986 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* FieldConverter.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-06-23)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex;
|
||||
|
||||
//import java.io.UnsupportedEncodingException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import writer2latex.latex.util.Context;
|
||||
import writer2latex.latex.util.HeadingMap;
|
||||
import writer2latex.office.ListStyle;
|
||||
import writer2latex.office.OfficeReader;
|
||||
import writer2latex.office.XMLString;
|
||||
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 (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 {
|
||||
|
||||
// Identify Zotero items
|
||||
private static final String ZOTERO_ITEM = "ZOTERO_ITEM";
|
||||
// Identify JabRef items
|
||||
private static final String JABREF_ITEM = "JR_cite";
|
||||
|
||||
// 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<String, Node> seqDecl = new Hashtable<String, Node>();
|
||||
// first usage of sequence (maps name->text:sequence element)
|
||||
private Hashtable<String, Element> seqFirst = new Hashtable<String, Element>();
|
||||
|
||||
private Vector<Element> postponedReferenceMarks = new Vector<Element>();
|
||||
private Vector<Element> postponedBookmarks = new Vector<Element>();
|
||||
|
||||
private boolean bUseHyperref = false;
|
||||
private boolean bUsesPageCount = false;
|
||||
private boolean bUsesTitleref = false;
|
||||
private boolean bConvertZotero = false;
|
||||
private boolean bConvertJabRef = false;
|
||||
private boolean bIncludeOriginalCitations = false;
|
||||
private boolean bUseNatbib = false;
|
||||
|
||||
public FieldConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
|
||||
super(ofr,config,palette);
|
||||
// hyperref.sty is not compatible with titleref.sty:
|
||||
bUseHyperref = config.useHyperref() && !config.useTitleref();
|
||||
bConvertZotero = config.useBibtex() && config.zoteroBibtexFiles().length()>0;
|
||||
bConvertJabRef = config.useBibtex() && config.jabrefBibtexFiles().length()>0;
|
||||
bIncludeOriginalCitations = config.includeOriginalCitations();
|
||||
bUseNatbib = config.useBibtex() && config.useNatbib();
|
||||
}
|
||||
|
||||
/** <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 natbib
|
||||
if (config.useBibtex() && config.useNatbib()) {
|
||||
pack.append("\\usepackage");
|
||||
if (config.getNatbibOptions().length()>0) {
|
||||
pack.append("[").append(config.getNatbibOptions()).append("]");
|
||||
}
|
||||
pack.append("{natbib}").nl();
|
||||
}
|
||||
// use lastpage.sty
|
||||
if (bUsesPageCount) {
|
||||
pack.append("\\usepackage{lastpage}").nl();
|
||||
}
|
||||
|
||||
// use titleref.sty
|
||||
if (bUsesTitleref) {
|
||||
pack.append("\\usepackage{titleref}").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<String> names = seqFirst.keys();
|
||||
while (names.hasMoreElements()) {
|
||||
// Get first text:sequence element
|
||||
String sName = names.nextElement();
|
||||
Element first = 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(ListConverter.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 ("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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to handle this reference name as a Zotero reference, return true on success
|
||||
private boolean handleZoteroReferenceName(String sName, LaTeXDocumentPortion ldp, Context oc) {
|
||||
// First parse the reference name:
|
||||
// A Zotero reference name has the form ZOTERO_ITEM <json object> <identifier> with a single space separating the items
|
||||
// The identifier is a unique identifier for the reference and is not used here
|
||||
if (sName.startsWith(ZOTERO_ITEM)) {
|
||||
int nObjectStart = sName.indexOf('{');
|
||||
int nObjectEnd = sName.lastIndexOf('}');
|
||||
if (nObjectStart>-1 && nObjectEnd>-1 && nObjectStart<nObjectEnd) {
|
||||
String sJsonObject = sName.substring(nObjectStart, nObjectEnd+1);
|
||||
JSONObject jo = null;
|
||||
try {
|
||||
jo = new JSONObject(sJsonObject);
|
||||
} catch (JSONException e) {
|
||||
return false;
|
||||
}
|
||||
// Successfully parsed the reference, now generate the code
|
||||
// (we don't expect any errors and ignore them, if they happen anyway)
|
||||
|
||||
// Sort key (purpose? currently ignored)
|
||||
/*boolean bSort = true;
|
||||
try {
|
||||
bSort = jo.getBoolean("sort");
|
||||
}
|
||||
catch (JSONException e) {
|
||||
}*/
|
||||
|
||||
JSONArray citationItemsArray = null;
|
||||
try { // The value is an array of objects, one for each source in this citation
|
||||
citationItemsArray = jo.getJSONArray("citationItems");
|
||||
}
|
||||
catch (JSONException e) {
|
||||
}
|
||||
|
||||
if (citationItemsArray!=null) {
|
||||
int nCitationCount = citationItemsArray.length();
|
||||
|
||||
if (bUseNatbib) {
|
||||
if (nCitationCount>1) {
|
||||
// For multiple citations, use \citetext, otherwise we cannot add individual prefixes and suffixes
|
||||
// TODO: If no prefixes or suffixes exist, it's safe to combine the citations
|
||||
ldp.append("\\citetext{");
|
||||
}
|
||||
|
||||
for (int nIndex=0; nIndex<nCitationCount; nIndex++) {
|
||||
|
||||
JSONObject citationItems = null;
|
||||
try { // Each citation is represented as an object
|
||||
citationItems = citationItemsArray.getJSONObject(nIndex);
|
||||
}
|
||||
catch (JSONException e) {
|
||||
}
|
||||
|
||||
if (citationItems!=null) {
|
||||
if (nIndex>0) {
|
||||
ldp.append("; "); // Separate multiple citations in this reference
|
||||
}
|
||||
|
||||
// Citation items
|
||||
String sURI = "";
|
||||
boolean bSuppressAuthor = false;
|
||||
String sPrefix = "";
|
||||
String sSuffix = "";
|
||||
String sLocator = "";
|
||||
String sLocatorType = "";
|
||||
|
||||
try { // The URI seems to be an array with a single string value(?)
|
||||
sURI = citationItems.getJSONArray("uri").getString(0);
|
||||
}
|
||||
catch (JSONException e) {
|
||||
}
|
||||
|
||||
try { // SuppressAuthor is a boolean value
|
||||
bSuppressAuthor = citationItems.getBoolean("suppressAuthor");
|
||||
}
|
||||
catch (JSONException e) {
|
||||
}
|
||||
|
||||
try { // Prefix is a string value
|
||||
sPrefix = citationItems.getString("prefix");
|
||||
}
|
||||
catch (JSONException e) {
|
||||
}
|
||||
|
||||
try { // Suffix is a string value
|
||||
sSuffix = citationItems.getString("suffix");
|
||||
}
|
||||
catch (JSONException e) {
|
||||
}
|
||||
|
||||
try { // Locator is a string value, e.g. a page number
|
||||
sLocator = citationItems.getString("locator");
|
||||
}
|
||||
catch (JSONException e) {
|
||||
}
|
||||
|
||||
try {
|
||||
// LocatorType is a string value, e.g. book, verse, page (missing locatorType means page)
|
||||
sLocatorType = citationItems.getString("locatorType");
|
||||
}
|
||||
catch (JSONException e) {
|
||||
}
|
||||
|
||||
// Adjust locator type (empty locator type means "page")
|
||||
// TODO: Handle other locator types (localize and abbreviate): Currently the internal name (e.g. book) is used.
|
||||
if (sLocator.length()>0 && sLocatorType.length()==0) {
|
||||
// A locator of the form <number><other characters><number> is interpreted as several pages
|
||||
if (Pattern.compile("[0-9]+[^0-9]+[0-9]+").matcher(sLocator).find()) {
|
||||
sLocatorType = "pp.";
|
||||
}
|
||||
else {
|
||||
sLocatorType = "p.";
|
||||
}
|
||||
}
|
||||
|
||||
// Insert command. TODO: Evaluate this
|
||||
if (nCitationCount>1) { // Use commands without parentheses
|
||||
if (bSuppressAuthor) { ldp.append("\\citeyear"); }
|
||||
else { ldp.append("\\citet"); }
|
||||
}
|
||||
else {
|
||||
if (bSuppressAuthor) { ldp.append("\\citeyearpar"); }
|
||||
else { ldp.append("\\citep"); }
|
||||
}
|
||||
|
||||
if (sPrefix.length()>0) {
|
||||
ldp.append("[").append(palette.getI18n().convert(sPrefix,true,oc.getLang())).append("]");
|
||||
}
|
||||
|
||||
if (sPrefix.length()>0 || sSuffix.length()>0 || sLocatorType.length()>0 || sLocator.length()>0) {
|
||||
// Note that we need to include an empty suffix if there's a prefix!
|
||||
ldp.append("[")
|
||||
.append(palette.getI18n().convert(sSuffix,true,oc.getLang()))
|
||||
.append(palette.getI18n().convert(sLocatorType,true,oc.getLang()));
|
||||
if (sLocatorType.length()>0 && sLocator.length()>0) {
|
||||
ldp.append("~");
|
||||
}
|
||||
ldp.append(palette.getI18n().convert(sLocator,true,oc.getLang()))
|
||||
.append("]");
|
||||
}
|
||||
|
||||
ldp.append("{");
|
||||
int nSlash = sURI.lastIndexOf('/');
|
||||
if (nSlash>0) {
|
||||
ldp.append(sURI.substring(nSlash+1));
|
||||
}
|
||||
else {
|
||||
ldp.append(sURI);
|
||||
}
|
||||
ldp.append("}");
|
||||
}
|
||||
}
|
||||
|
||||
if (nCitationCount>1) { // End the \citetext command
|
||||
ldp.append("}");
|
||||
}
|
||||
}
|
||||
else { // natbib is not available, use simple \cite command
|
||||
ldp.append("\\cite{");
|
||||
for (int nIndex=0; nIndex<nCitationCount; nIndex++) {
|
||||
JSONObject citationItems = null;
|
||||
try { // Each citation is represented as an object
|
||||
citationItems = citationItemsArray.getJSONObject(nIndex);
|
||||
}
|
||||
catch (JSONException e) {
|
||||
}
|
||||
|
||||
if (citationItems!=null) {
|
||||
if (nIndex>0) {
|
||||
ldp.append(","); // Separate multiple citations in this reference
|
||||
}
|
||||
|
||||
// Citation items
|
||||
String sURI = "";
|
||||
|
||||
try { // The URI seems to be an array with a single string value(?)
|
||||
sURI = citationItems.getJSONArray("uri").getString(0);
|
||||
}
|
||||
catch (JSONException e) {
|
||||
}
|
||||
|
||||
int nSlash = sURI.lastIndexOf('/');
|
||||
if (nSlash>0) {
|
||||
ldp.append(sURI.substring(nSlash+1));
|
||||
}
|
||||
else {
|
||||
ldp.append(sURI);
|
||||
}
|
||||
}
|
||||
}
|
||||
ldp.append("}");
|
||||
}
|
||||
|
||||
oc.setInZoteroJabRefText(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to handle this reference name as a JabRef reference, return true on success
|
||||
private boolean handleJabRefReferenceName(String sName, LaTeXDocumentPortion ldp, Context oc) {
|
||||
// First parse the reference name:
|
||||
// A JabRef reference name has the form JR_cite<m>_<n>_<identifiers> where
|
||||
// m is a sequence number to ensure unique citations (may be empty)
|
||||
// n=1 for (Author date) and n=2 for Author (date) citations
|
||||
// identifiers is a comma separated list of BibTeX keys
|
||||
if (sName.startsWith(JABREF_ITEM)) {
|
||||
String sRemains = sName.substring(JABREF_ITEM.length());
|
||||
int nUnderscore = sRemains.indexOf('_');
|
||||
if (nUnderscore>-1) {
|
||||
sRemains = sRemains.substring(nUnderscore+1);
|
||||
if (sRemains.length()>2) {
|
||||
String sCommand;
|
||||
if (bUseNatbib) {
|
||||
if (sRemains.charAt(0)=='1') {
|
||||
sCommand = "\\citep";
|
||||
}
|
||||
else {
|
||||
sCommand = "\\citet";
|
||||
}
|
||||
}
|
||||
else {
|
||||
sCommand = "\\cite";
|
||||
}
|
||||
ldp.append(sCommand).append("{").append(sRemains.substring(2)).append("}");
|
||||
}
|
||||
}
|
||||
oc.setInZoteroJabRefText(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String shortenRefname(String s) {
|
||||
// For Zotero items, use the trailing unique identifier
|
||||
if (s.startsWith(ZOTERO_ITEM)) {
|
||||
int nLast = s.lastIndexOf(' ');
|
||||
if (nLast>0) {
|
||||
return s.substring(nLast+1);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/** <p>Process a reference mark end (text:reference-mark-end 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 handleReferenceMarkEnd(Element node, LaTeXDocumentPortion ldp, Context oc) {
|
||||
// Nothing to do, except to mark that this ends any Zotero/JabRef citation
|
||||
oc.setInZoteroJabRefText(false);
|
||||
if (bIncludeOriginalCitations) { // Protect space after comment
|
||||
ldp.append("{}");
|
||||
}
|
||||
}
|
||||
|
||||
/** <p>Process a reference mark (text:reference-mark or text:reference-mark-start 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()) {
|
||||
String sName = node.getAttribute(XMLString.TEXT_NAME);
|
||||
// Zotero and JabRef (mis)uses reference marks to store citations, so check this first
|
||||
if (sName!=null && (!bConvertZotero || !handleZoteroReferenceName(sName, ldp, oc))
|
||||
&& (!bConvertJabRef || !handleJabRefReferenceName(sName, ldp, oc))) {
|
||||
// Plain reference mark
|
||||
// Note: Always include \label here, even when it's not used
|
||||
ldp.append("\\label{ref:"+refnames.getExportName(shortenRefname(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(shortenRefname(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(shortenRefname(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 if (("number".equals(sFormat) || "number-no-superior".equals(sFormat) || "number-all-superior".equals(sFormat)) &&
|
||||
(ofr.bookmarkInHeading(sName) || ofr.bookmarkInList(sName))) {
|
||||
ListStyle style=null;
|
||||
int nLevel = 0;
|
||||
String sPrefix=null;
|
||||
String sSuffix=null;
|
||||
// Only convert the prefix and suffix if it is converted at the reference target
|
||||
if (ofr.bookmarkInHeading(sName)) {
|
||||
if (config.formatting()>=LaTeXConfig.CONVERT_MOST) {
|
||||
style = ofr.getOutlineStyle();
|
||||
}
|
||||
nLevel = ofr.getBookmarkHeadingLevel(sName);
|
||||
}
|
||||
else {
|
||||
if (config.formatting()>=LaTeXConfig.CONVERT_BASIC) {
|
||||
style = ofr.getListStyle(ofr.getBookmarkListStyle(sName));
|
||||
}
|
||||
nLevel = ofr.getBookmarkListLevel(sName);
|
||||
}
|
||||
if (style!=null) {
|
||||
sPrefix = style.getLevelProperty(nLevel, XMLString.STYLE_NUM_PREFIX);
|
||||
sSuffix = style.getLevelProperty(nLevel, XMLString.STYLE_NUM_SUFFIX);
|
||||
}
|
||||
if (sPrefix!=null) ldp.append(palette.getI18n().convert(sPrefix,false,oc.getLang()));
|
||||
ldp.append("\\ref{bkm:").append(bookmarknames.getExportName(sName)).append("}");
|
||||
if (sSuffix!=null) ldp.append(palette.getI18n().convert(sSuffix,false,oc.getLang()));
|
||||
}
|
||||
else { // use current value
|
||||
palette.getInlineCv().traversePCDATA(node,ldp,oc);
|
||||
}
|
||||
}
|
||||
|
||||
/** Do we have any pending reference marks or bookmarks, that may be inserted in this context?
|
||||
*
|
||||
* @param oc the context to verify against
|
||||
* @return true if there are pending marks
|
||||
*/
|
||||
public boolean hasPendingReferenceMarks(Context oc) {
|
||||
return !oc.isInSection() && !oc.isInCaption() && !oc.isVerbatim() &&
|
||||
postponedReferenceMarks.size()+postponedBookmarks.size()>0;
|
||||
}
|
||||
|
||||
/** <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(postponedReferenceMarks.get(i),ldp,oc);
|
||||
}
|
||||
postponedReferenceMarks.clear();
|
||||
// Type out all postponed bookmarks
|
||||
n = postponedBookmarks.size();
|
||||
for (int i=0; i<n; i++) {
|
||||
handleBookmark(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 (OfficeReader.getTextContent(node).trim().equals(sHref)) {
|
||||
// The link text equals the url
|
||||
ldp.append("\\url{")
|
||||
.append(escapeHref(sHref,oc.isInFootnote()))
|
||||
.append("}");
|
||||
}
|
||||
else {
|
||||
ldp.append("\\href{")
|
||||
.append(escapeHref(sHref,oc.isInFootnote()))
|
||||
.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 the argument to a href, we have to escape or encode certain characters
|
||||
private String escapeHref(String s, boolean bInFootnote) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int i=0; i<s.length(); i++) {
|
||||
if (bInFootnote && s.charAt(i)=='#') { buf.append("\\#"); }
|
||||
else if (bInFootnote && s.charAt(i)=='%') { buf.append("\\%"); }
|
||||
// The following should not occur in an URL (see RFC1738), but just to be sure we encode them
|
||||
else if (s.charAt(i)=='\\') { buf.append("\\%5C"); }
|
||||
else if (s.charAt(i)=='{') { buf.append("\\%7B"); }
|
||||
else if (s.charAt(i)=='}') { buf.append("\\%7D"); }
|
||||
// hyperref.sty deals safely with other characters
|
||||
else { buf.append(s.charAt(i)); }
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
}
|
431
src/main/java/writer2latex/latex/HeadingConverter.java
Normal file
431
src/main/java/writer2latex/latex/HeadingConverter.java
Normal file
|
@ -0,0 +1,431 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* HeadingConverter.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-04-14)
|
||||
*
|
||||
*/
|
||||
|
||||
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.office.ListStyle;
|
||||
import writer2latex.office.OfficeReader;
|
||||
import writer2latex.office.StyleWithProperties;
|
||||
import writer2latex.office.XMLString;
|
||||
import writer2latex.util.Calc;
|
||||
import writer2latex.util.Misc;
|
||||
|
||||
/* 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];
|
||||
|
||||
// Display hidden text?
|
||||
private boolean bDisplayHiddenText = false;
|
||||
|
||||
/** Constructs a new <code>HeadingConverter</code>.
|
||||
*/
|
||||
public HeadingConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
|
||||
super(ofr,config,palette);
|
||||
this.bDisplayHiddenText = config.displayHiddenText();
|
||||
}
|
||||
|
||||
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 style
|
||||
String sStyleName = node.getAttribute(XMLString.TEXT_STYLE_NAME);
|
||||
StyleWithProperties style = ofr.getParStyle(sStyleName);
|
||||
|
||||
// Check for hidden text
|
||||
if (!bDisplayHiddenText && style!=null && "none".equals(style.getProperty(XMLString.TEXT_DISPLAY))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the level
|
||||
int nLevel = ofr.isOpenDocument() ?
|
||||
Misc.getPosInteger(Misc.getAttribute(node, XMLString.TEXT_OUTLINE_LEVEL),1) :
|
||||
Misc.getPosInteger(Misc.getAttribute(node, XMLString.TEXT_LEVEL),1);
|
||||
boolean bUnNumbered = "true".equals(Misc.getAttribute(node,XMLString.TEXT_IS_LIST_HEADER));
|
||||
|
||||
// Get the heading map
|
||||
HeadingMap hm = config.getHeadingMap();
|
||||
|
||||
if (nLevel<=hm.getMaxLevel()) {
|
||||
// Always push the font used
|
||||
palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(style));
|
||||
|
||||
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 (bUnNumbered) {
|
||||
ldp.append("*");
|
||||
}
|
||||
else if (baHardChar.getBefore().length()>0 || containsElements(node)) {
|
||||
// If this heading contains formatting, add optional argument:
|
||||
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);
|
||||
|
||||
// Get margin parameters (using first line indent as left margin)
|
||||
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 sTextIndent = style.getAbsoluteLength(XMLString.FO_TEXT_INDENT);
|
||||
|
||||
// Seems that we should *not* override the paragraph style
|
||||
/*ListStyle outline = ofr.getOutlineStyle();
|
||||
if (outline.isNewType(i)) {
|
||||
String sNumFormat = ListStyleConverter.numFormat(outline.getLevelProperty(i,XMLString.STYLE_NUM_FORMAT));
|
||||
if (sNumFormat!=null && !"".equals(sNumFormat)) {
|
||||
// It there's a numbering, override left margins with the value from the outline
|
||||
sMarginLeft = outline.getLevelStyleProperty(i, XMLString.FO_MARGIN_LEFT);
|
||||
if (sMarginLeft==null) { sMarginLeft = "0cm"; }
|
||||
sTextIndent = outline.getLevelStyleProperty(i, XMLString.FO_TEXT_INDENT);
|
||||
if (sTextIndent==null) { sTextIndent = "0cm"; }
|
||||
}
|
||||
}*/
|
||||
|
||||
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();
|
||||
}
|
||||
// Note: Use first line as left indent (cannot have separate first line indent)
|
||||
ldp.append("\\renewcommand\\").append(sSecName)
|
||||
.append("{\\@startsection{").append(sSecName).append("}{"+hm.getLevel(i))
|
||||
.append("}{"+Calc.add(sMarginLeft,sTextIndent)+"}{");
|
||||
// Suppress indentation after heading? currently not..
|
||||
// ldp.append("-");
|
||||
ldp.append(sMarginTop)
|
||||
.append("}{")
|
||||
.append(Calc.isZero(sMarginBottom) ? "0.1mm" : 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] = ListConverter.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 sSpaceChar="";
|
||||
String sDistance=null;
|
||||
if (outline.isNewType(i)) {
|
||||
String sFormat = outline.getLevelStyleProperty(i, XMLString.TEXT_LABEL_FOLLOWED_BY);
|
||||
if ("listtab".equals(sFormat)) {
|
||||
String sMarginLeft="0cm";
|
||||
String sTextIndent="0cm";
|
||||
if (sHeadingStyles[i]!=null) {
|
||||
StyleWithProperties style = ofr.getParStyle(sHeadingStyles[i]);
|
||||
if (style!=null) {
|
||||
sMarginLeft = style.getAbsoluteLength(XMLString.FO_MARGIN_LEFT);
|
||||
sTextIndent = style.getAbsoluteLength(XMLString.FO_TEXT_INDENT);
|
||||
}
|
||||
}
|
||||
// Seems that we should *not* override the paragraph style
|
||||
/*String sMarginLeft = outline.getLevelStyleProperty(i, XMLString.FO_MARGIN_LEFT);
|
||||
if (sMarginLeft==null) { sMarginLeft = "0cm"; }
|
||||
String sTextIndent = outline.getLevelStyleProperty(i, XMLString.FO_TEXT_INDENT);
|
||||
if (sTextIndent==null) { sTextIndent = "0cm"; }*/
|
||||
String sTabPos = outline.getLevelStyleProperty(i, XMLString.TEXT_LIST_TAB_STOP_POSITION);
|
||||
if (sTabPos==null) { sTabPos = "0cm"; }
|
||||
sDistance = Calc.sub(sTabPos, Calc.add(sMarginLeft, sTextIndent));
|
||||
}
|
||||
else if ("space".equals(sFormat)) {
|
||||
sSpaceChar="\\ ";
|
||||
}
|
||||
}
|
||||
else {
|
||||
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 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"; }
|
||||
}
|
||||
String sLabelWidth = null;
|
||||
if (outline.isNewType(i)) {
|
||||
String sFormat = outline.getLevelStyleProperty(i, XMLString.TEXT_LABEL_FOLLOWED_BY);
|
||||
if ("listtab".equals(sFormat) || sAlignmentChar=="r") {
|
||||
sLabelWidth="0cm";
|
||||
}
|
||||
}
|
||||
else {
|
||||
sLabelWidth = outline.getLevelStyleProperty(i,XMLString.TEXT_MIN_LABEL_WIDTH);
|
||||
}
|
||||
// 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("\\protect\\makebox[").append(sLabelWidth).append("][").append(sAlignmentChar).append("]{");
|
||||
}
|
||||
ldp.append(baText.getBefore())
|
||||
.append(sPrefix!=null ? palette.getI18n().convert(sPrefix,false,"en") : "")
|
||||
.append("#1")
|
||||
.append(sSuffix!=null ? palette.getI18n().convert(sSuffix,false,"en") : "")
|
||||
.append(sSpaceChar)
|
||||
.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) ||
|
||||
child.getNodeName().startsWith(XMLString.TEXT_BOOKMARK))) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
251
src/main/java/writer2latex/latex/IndexConverter.java
Normal file
251
src/main/java/writer2latex/latex/IndexConverter.java
Normal file
|
@ -0,0 +1,251 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* IndexConverter.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2014-09-18)
|
||||
*
|
||||
*/
|
||||
|
||||
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<Element> postponedIndexMarks = new Vector<Element>();
|
||||
|
||||
/** <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);
|
||||
}
|
||||
}
|
||||
|
||||
/** Do we have any pending index marks, that may be inserted in this context?
|
||||
*
|
||||
* @param oc the context to verify against
|
||||
* @return true if there are pending index marks
|
||||
*/
|
||||
public boolean hasPendingIndexMarks(Context oc) {
|
||||
return !oc.isInSection() && !oc.isInCaption() && !oc.isVerbatim() &&
|
||||
postponedIndexMarks.size()>0;
|
||||
}
|
||||
|
||||
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(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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
73
src/main/java/writer2latex/latex/Info.java
Normal file
73
src/main/java/writer2latex/latex/Info.java
Normal file
|
@ -0,0 +1,73 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* Info.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-06-21)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import writer2latex.util.Misc;
|
||||
import writer2latex.office.OfficeReader;
|
||||
import writer2latex.office.StyleWithProperties;
|
||||
import writer2latex.office.XMLString;
|
||||
|
||||
|
||||
/** This class creates various information to the user about the conversion.
|
||||
*/
|
||||
class Info extends ConverterHelper {
|
||||
|
||||
@Override public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
|
||||
// Currently nothing
|
||||
}
|
||||
|
||||
Info(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
|
||||
super(ofr,config,palette);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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("\"");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
740
src/main/java/writer2latex/latex/InlineConverter.java
Normal file
740
src/main/java/writer2latex/latex/InlineConverter.java
Normal file
|
@ -0,0 +1,740 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* InlineConverter.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2014-09-19)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
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 {
|
||||
|
||||
// Display hidden text?
|
||||
private boolean bDisplayHiddenText = false;
|
||||
|
||||
private boolean bIncludeOriginalCitations = false;
|
||||
private String sTabstop = "\\ \\ ";
|
||||
private boolean bHasPdfannotation = false;
|
||||
|
||||
public InlineConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
|
||||
super(ofr,config,palette);
|
||||
bIncludeOriginalCitations = config.includeOriginalCitations();
|
||||
// Get custom code for tab stops
|
||||
if (config.getTabstop().length()>0) {
|
||||
sTabstop = config.getTabstop();
|
||||
}
|
||||
this.bDisplayHiddenText = config.displayHiddenText();
|
||||
}
|
||||
|
||||
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 several text:span elements
|
||||
*
|
||||
*/
|
||||
private void handleTextSpans(Element[] nodes, LaTeXDocumentPortion ldp, Context oc) {
|
||||
if (oc.isMathMode()) {
|
||||
for (Element node : nodes) {
|
||||
handleTextSpanMath(node, ldp, oc);
|
||||
}
|
||||
}
|
||||
else {
|
||||
handleTextSpanText(ldp, oc, nodes);
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle a text:span element
|
||||
*/
|
||||
public void handleTextSpan(Element node, LaTeXDocumentPortion ldp, Context oc) {
|
||||
if (oc.isMathMode()) { handleTextSpanMath(node, ldp, oc); }
|
||||
else { handleTextSpanText(ldp, oc, node); }
|
||||
}
|
||||
|
||||
private void handleTextSpanMath(Element node, LaTeXDocumentPortion ldp, Context oc) {
|
||||
// TODO: Handle a selection of formatting attributes: color, superscript...
|
||||
String sStyleName = node.getAttribute(XMLString.TEXT_STYLE_NAME);
|
||||
StyleWithProperties style = ofr.getTextStyle(sStyleName);
|
||||
|
||||
// Check for hidden text
|
||||
if (!bDisplayHiddenText && style!=null && "none".equals(style.getProperty(XMLString.TEXT_DISPLAY))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Handle several spans.
|
||||
// If the converted formatting happens to be identical (e.g. \textbf{...}), the spans will be merged.
|
||||
private void handleTextSpanText(LaTeXDocumentPortion ldp, Context oc, Element... nodes) {
|
||||
// The current formatting
|
||||
BeforeAfter baCurrent = new BeforeAfter();
|
||||
for (Element node : nodes) {
|
||||
String sStyleName = node.getAttribute(XMLString.TEXT_STYLE_NAME);
|
||||
StyleWithProperties style = ofr.getTextStyle(sStyleName);
|
||||
|
||||
// First check for hidden text
|
||||
if (bDisplayHiddenText || style==null || !"none".equals(style.getProperty(XMLString.TEXT_DISPLAY))) {
|
||||
// Then check for strict handling of styles
|
||||
String sDisplayName = ofr.getTextStyles().getDisplayName(sStyleName);
|
||||
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();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// We do want to convert this span :-)
|
||||
|
||||
// Always push the font used
|
||||
palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(ofr.getTextStyle(sStyleName)));
|
||||
|
||||
// Apply the style
|
||||
BeforeAfter ba = new BeforeAfter();
|
||||
Context ic = (Context) oc.clone();
|
||||
// Don't style it if
|
||||
// - we're already within a verbatim environment
|
||||
// - a {foot|end}note is the only content
|
||||
// - there is no content
|
||||
// - this is an automatic style in header/footer (name clash problem, only in package format)
|
||||
if (!oc.isVerbatim() && !onlyNote(node) && OfficeReader.getCharacterCount(node)>0
|
||||
&& !(ofr.isPackageFormat() && (style!=null && style.isAutomatic()) && oc.isInHeaderFooter())) {
|
||||
palette.getCharSc().applyTextStyle(sStyleName,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 area for footnotes
|
||||
if ((ba.getBefore().length()>0 || ba.getAfter().length()>0) && !ic.isInFootnote()) {
|
||||
ic.setNoFootnotes(true);
|
||||
}
|
||||
|
||||
// Merge spans? If the formatting of this span differs from the previous span, we will close the
|
||||
// previous span and start a new one
|
||||
if (!ba.getBefore().equals(baCurrent.getBefore()) || !ba.getAfter().equals(baCurrent.getAfter())) {
|
||||
ldp.append(baCurrent.getAfter());
|
||||
ldp.append(ba.getBefore());
|
||||
baCurrent = ba;
|
||||
}
|
||||
|
||||
traverseInlineText(node,ldp,ic);
|
||||
|
||||
// In the special case of pending footnotes, index marks and reference marks, we will close the span now.
|
||||
// Otherwise we will wait and see
|
||||
if (palette.getNoteCv().hasPendingFootnotes(oc)
|
||||
|| palette.getIndexCv().hasPendingIndexMarks(oc)
|
||||
|| palette.getFieldCv().hasPendingReferenceMarks(oc)) {
|
||||
ldp.append(baCurrent.getAfter());
|
||||
baCurrent = new BeforeAfter();
|
||||
}
|
||||
|
||||
// Flush any pending footnotes, index marks and reference marks
|
||||
if (!ic.isInFootnote()) { palette.getNoteCv().flushFootnotes(ldp,oc); }
|
||||
palette.getFieldCv().flushReferenceMarks(ldp,oc);
|
||||
palette.getIndexCv().flushIndexMarks(ldp,oc);
|
||||
|
||||
// finally pop the special table
|
||||
palette.getI18n().popSpecialTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
ldp.append(baCurrent.getAfter());
|
||||
}
|
||||
|
||||
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) {
|
||||
if (oc.isInZoteroJabRefText()) {
|
||||
if (bIncludeOriginalCitations) { // Include original citation as a comment
|
||||
ldp.append("%")
|
||||
.append(palette.getI18n().convert(s, false, oc.getLang()))
|
||||
.nl();
|
||||
}
|
||||
}
|
||||
else { // Normal text
|
||||
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 {
|
||||
// Collect further spans
|
||||
Vector<Element> spans = new Vector<Element>();
|
||||
|
||||
Node remember;
|
||||
boolean bContinue = false;
|
||||
do {
|
||||
spans.add((Element)childNode);
|
||||
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)))
|
||||
bContinue = true;
|
||||
}
|
||||
} while(bContinue);
|
||||
childNode = remember;
|
||||
|
||||
handleTextSpans(spans.toArray(new Element[spans.size()]),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_MARK_END)) {
|
||||
palette.getFieldCv().handleReferenceMarkEnd(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
|
||||
Element creator = null;
|
||||
Element date = null;
|
||||
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();
|
||||
}
|
||||
else if (Misc.isElement(child, XMLString.DC_CREATOR)) {
|
||||
creator = (Element) child;
|
||||
}
|
||||
else if (Misc.isElement(child, XMLString.DC_DATE)) {
|
||||
date = (Element) child;
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
if (creator!=null) {
|
||||
ldp.append("%");
|
||||
traversePlainInlineText(creator, ldp, oc);
|
||||
ldp.nl();
|
||||
}
|
||||
if (date!=null) {
|
||||
ldp.append("%")
|
||||
.append(Misc.formatDate(OfficeReader.getTextContent(date), palette.getI18n().getDefaultLanguage(), null))
|
||||
.nl();
|
||||
}
|
||||
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("{");
|
||||
Element creator = null;
|
||||
Element date = null;
|
||||
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;
|
||||
}
|
||||
else if (Misc.isElement(child, XMLString.DC_CREATOR)) {
|
||||
creator = (Element) child;
|
||||
}
|
||||
else if (Misc.isElement(child, XMLString.DC_DATE)) {
|
||||
date = (Element) child;
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
if (creator!=null) {
|
||||
if (!bFirst) ldp.append(" - ");
|
||||
traversePlainInlineText(creator, ldp, oc);
|
||||
}
|
||||
if (date!=null) {
|
||||
if (creator!=null) ldp.append(", ");
|
||||
else if (!bFirst) ldp.append(" ");
|
||||
ldp.append(Misc.formatDate(OfficeReader.getTextContent(date), palette.getI18n().getDefaultLanguage(), null));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
750
src/main/java/writer2latex/latex/LaTeXConfig.java
Normal file
750
src/main/java/writer2latex/latex/LaTeXConfig.java
Normal file
|
@ -0,0 +1,750 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* LaTeXConfig.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-07-23)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import writer2latex.api.ComplexOption;
|
||||
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.latex.util.StyleMapItem;
|
||||
import writer2latex.util.Misc;
|
||||
|
||||
public class LaTeXConfig extends writer2latex.base.ConfigBase {
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// I. Define items needed by ConfigBase
|
||||
|
||||
protected int getOptionCount() { return 73; }
|
||||
protected String getDefaultConfigPath() { return "/writer2latex/latex/config/"; }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// II. Override getter and setter methods for simple options in order to:
|
||||
// - Treat the custom preamble like a regular option, even though the xml representation is different
|
||||
// - Be backwards compatible (renamed the option keep_image_size)
|
||||
|
||||
@Override public void setOption(String sName,String sValue) {
|
||||
if (sName.equals("custom-preamble")) {
|
||||
sCustomPreamble = sValue;
|
||||
}
|
||||
else {
|
||||
// this option has been renamed:
|
||||
if (sName.equals("keep_image_size")) { sName = "original_image_size"; }
|
||||
super.setOption(sName, sValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public String getOption(String sName) {
|
||||
if (sName.equals("custom-preamble")) {
|
||||
return sCustomPreamble;
|
||||
}
|
||||
else {
|
||||
return super.getOption(sName);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// III. Declare all constants
|
||||
|
||||
// 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;
|
||||
public static final int CONVERT_GEOMETRY = 6;
|
||||
|
||||
// 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
|
||||
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_TITLESEC = 19;
|
||||
private static final int USE_TITLETOC = 20;
|
||||
private static final int USE_HYPERREF = 21;
|
||||
private static final int USE_CAPTION = 22;
|
||||
private static final int USE_LONGTABLE = 23;
|
||||
private static final int USE_SUPERTABULAR = 24;
|
||||
private static final int USE_TABULARY = 25;
|
||||
private static final int USE_ENDNOTES = 26;
|
||||
private static final int USE_ULEM = 27;
|
||||
private static final int USE_LASTPAGE = 28;
|
||||
private static final int USE_TITLEREF = 29;
|
||||
private static final int USE_BIBTEX = 30;
|
||||
private static final int BIBTEX_STYLE = 31;
|
||||
private static final int EXTERNAL_BIBTEX_FILES = 32;
|
||||
private static final int BIBTEX_ENCODING = 33;
|
||||
private static final int ZOTERO_BIBTEX_FILES = 34;
|
||||
private static final int JABREF_BIBTEX_FILES = 35;
|
||||
private static final int INCLUDE_ORIGINAL_CITATIONS = 36;
|
||||
private static final int USE_NATBIB = 37;
|
||||
private static final int NATBIB_OPTIONS = 38;
|
||||
private static final int FONT = 39;
|
||||
private static final int FORMATTING = 40;
|
||||
private static final int PAGE_FORMATTING = 41;
|
||||
private static final int OTHER_STYLES = 42;
|
||||
private static final int IMAGE_CONTENT = 43;
|
||||
private static final int TABLE_CONTENT = 44;
|
||||
private static final int TABLE_FIRST_HEAD_STYLE = 45;
|
||||
private static final int TABLE_HEAD_STYLE = 46;
|
||||
private static final int TABLE_FOOT_STYLE = 47;
|
||||
private static final int TABLE_LAST_FOOT_STYLE = 48;
|
||||
private static final int IGNORE_HARD_PAGE_BREAKS = 49;
|
||||
private static final int IGNORE_HARD_LINE_BREAKS = 50;
|
||||
private static final int IGNORE_EMPTY_PARAGRAPHS =51;
|
||||
private static final int IGNORE_DOUBLE_SPACES = 52;
|
||||
private static final int DISPLAY_HIDDEN_TEXT = 53;
|
||||
private static final int ALIGN_FRAMES = 54;
|
||||
private static final int FLOAT_FIGURES = 55;
|
||||
private static final int FLOAT_TABLES = 56;
|
||||
private static final int FLOAT_OPTIONS = 57;
|
||||
private static final int FIGURE_SEQUENCE_NAME = 58;
|
||||
private static final int TABLE_SEQUENCE_NAME = 59;
|
||||
private static final int IMAGE_OPTIONS = 60;
|
||||
private static final int REMOVE_GRAPHICS_EXTENSION = 61;
|
||||
private static final int ORIGINAL_IMAGE_SIZE = 62;
|
||||
private static final int SIMPLE_TABLE_LIMIT = 63;
|
||||
private static final int NOTES = 64;
|
||||
private static final int METADATA = 65;
|
||||
private static final int TABSTOP = 66;
|
||||
private static final int WRAP_LINES_AFTER = 67;
|
||||
private static final int SPLIT_LINKED_SECTIONS = 68;
|
||||
private static final int SPLIT_TOPLEVEL_SECTIONS = 69;
|
||||
private static final int SAVE_IMAGES_IN_SUBDIR = 70;
|
||||
private static final int OLD_MATH_COLORS = 71;
|
||||
private static final int DEBUG = 72;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// IV. Our options data
|
||||
|
||||
private ComplexOption headingMap;
|
||||
private ComplexOption parMap;
|
||||
private ComplexOption parBlockMap;
|
||||
private ComplexOption listMap;
|
||||
private ComplexOption listItemMap;
|
||||
private ComplexOption textMap;
|
||||
private ComplexOption textAttrMap;
|
||||
private ComplexOption stringReplace;
|
||||
private ComplexOption mathSymbols;
|
||||
private String sCustomPreamble = "";
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// V. The rather long constructor setting all defaults
|
||||
|
||||
/** Construct a new <code>LaTeXConfig</code> with default values for all options
|
||||
*/
|
||||
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_TITLESEC] = new BooleanOption("use_titlesec","false");
|
||||
options[USE_TITLETOC] = new BooleanOption("use_titletoc","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_BIBTEX] = new BooleanOption("use_bibtex","false");
|
||||
options[BIBTEX_STYLE] = new Option("bibtex_style","plain");
|
||||
options[EXTERNAL_BIBTEX_FILES] = new Option("external_bibtex_files","");
|
||||
options[BIBTEX_ENCODING] = new IntegerOption("bibtex_encoding","document") {
|
||||
public void setString(String sValue) {
|
||||
super.setString(sValue);
|
||||
if ("document".equals(sValue)) { nValue = -1; }
|
||||
else { nValue = ClassicI18n.readInputenc(sValue); }
|
||||
}
|
||||
};
|
||||
options[ZOTERO_BIBTEX_FILES] = new Option("zotero_bibtex_files","");
|
||||
options[JABREF_BIBTEX_FILES] = new Option("jabref_bibtex_files","");
|
||||
options[INCLUDE_ORIGINAL_CITATIONS] = new BooleanOption("include_original_citations","false");
|
||||
options[USE_NATBIB] = new BooleanOption("use_natbib","false");
|
||||
options[NATBIB_OPTIONS] = new Option("natbib_options","");
|
||||
options[FONT] = new Option("font","default");
|
||||
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 ("convert_geometry".equals(sValue)) nValue = CONVERT_GEOMETRY;
|
||||
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[TABLE_FIRST_HEAD_STYLE] = new Option("table_first_head_style","");
|
||||
options[TABLE_HEAD_STYLE] = new Option("table_head_style","");
|
||||
options[TABLE_FOOT_STYLE] = new Option("table_foot_style","");
|
||||
options[TABLE_LAST_FOOT_STYLE] = new Option("table_last_foot_style","");
|
||||
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[DISPLAY_HIDDEN_TEXT] = new BooleanOption("display_hidden_text","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","120") {
|
||||
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[OLD_MATH_COLORS] = new BooleanOption("old_math_colors","false");
|
||||
options[DEBUG] = new BooleanOption("debug","false");
|
||||
|
||||
// Complex options - heading map
|
||||
headingMap = addComplexOption("heading-map");
|
||||
Map<String,String> attr = new HashMap<String,String>();
|
||||
attr.put("name", "section");
|
||||
attr.put("level", "1");
|
||||
headingMap.put("1", attr);
|
||||
|
||||
attr = new HashMap<String,String>();
|
||||
attr.put("name", "subsection");
|
||||
attr.put("level", "2");
|
||||
headingMap.put("2", attr);
|
||||
|
||||
attr = new HashMap<String,String>();
|
||||
attr.put("name", "subsubsection");
|
||||
attr.put("level", "3");
|
||||
headingMap.put("3", attr);
|
||||
|
||||
attr = new HashMap<String,String>();
|
||||
attr.put("name", "paragraph");
|
||||
attr.put("level", "4");
|
||||
headingMap.put("4", attr);
|
||||
|
||||
attr = new HashMap<String,String>();
|
||||
attr.put("name", "subparagraph");
|
||||
attr.put("level", "5");
|
||||
headingMap.put("5", attr);
|
||||
|
||||
// Complex options - style maps
|
||||
parMap = addComplexOption("paragraph-map");
|
||||
parBlockMap = addComplexOption("paragraph-block-map");
|
||||
listMap = addComplexOption("list-map");
|
||||
listItemMap = addComplexOption("listitem-map");
|
||||
textMap = addComplexOption("text-map");
|
||||
textAttrMap = addComplexOption("text-attribute-map");
|
||||
|
||||
// Complex options - string replace
|
||||
stringReplace=addComplexOption("string-replace");
|
||||
|
||||
// Standard string replace:
|
||||
// Fix french spacing; replace nonbreaking space
|
||||
// right before em-dash, !, ?, : and ; (babel handles this)
|
||||
attr = new HashMap<String,String>();
|
||||
attr.put("fontenc", "any");
|
||||
attr.put("latex-code", " \u2014");
|
||||
stringReplace.put("\u00A0\u2014",attr);
|
||||
|
||||
attr = new HashMap<String,String>();
|
||||
attr.put("fontenc", "any");
|
||||
attr.put("latex-code", " !");
|
||||
stringReplace.put("\u00A0!",attr);
|
||||
|
||||
attr = new HashMap<String,String>();
|
||||
attr.put("fontenc", "any");
|
||||
attr.put("latex-code", " ?");
|
||||
stringReplace.put("\u00A0?",attr);
|
||||
|
||||
attr = new HashMap<String,String>();
|
||||
attr.put("fontenc", "any");
|
||||
attr.put("latex-code", " :");
|
||||
stringReplace.put("\u00A0:",attr);
|
||||
|
||||
attr = new HashMap<String,String>();
|
||||
attr.put("fontenc", "any");
|
||||
attr.put("latex-code", " ;");
|
||||
stringReplace.put("\u00A0;",attr);
|
||||
|
||||
// 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"));
|
||||
|
||||
// Complex options - math user defined symbols
|
||||
mathSymbols = addComplexOption("math-symbol-map");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// VI. Provide methods to fill in the gaps in the supers read and write methods
|
||||
|
||||
protected void readInner(Element elm) {
|
||||
if (elm.getTagName().equals("heading-map")) {
|
||||
// Unlike other complex options, a heading map is completely replaced
|
||||
headingMap.clear();
|
||||
Node child = elm.getFirstChild();
|
||||
while (child!=null) {
|
||||
if (child.getNodeType()==Node.ELEMENT_NODE) {
|
||||
Element childElm = (Element) child;
|
||||
if (childElm.getTagName().equals("heading-level-map")) {
|
||||
if (childElm.hasAttribute("writer-level")) {
|
||||
Map<String,String> attr = new HashMap<String,String>();
|
||||
attr.put("name",childElm.getAttribute("name"));
|
||||
attr.put("level",childElm.getAttribute("level"));
|
||||
headingMap.put(childElm.getAttribute("writer-level"), attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
}
|
||||
else 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");
|
||||
}
|
||||
|
||||
Map<String,String> attr = new HashMap<String,String>();
|
||||
attr.put("before", elm.getAttribute("before"));
|
||||
attr.put("after", elm.getAttribute("after"));
|
||||
|
||||
if ("paragraph".equals(sFamily)) {
|
||||
if (elm.hasAttribute("line-break")) { attr.put("line-break", elm.getAttribute("line-break")); }
|
||||
if (elm.hasAttribute("break-after")) { attr.put("break-after", elm.getAttribute("break-after")); }
|
||||
if (elm.hasAttribute("verbatim")) { attr.put("verbatim", elm.getAttribute("verbatim")); }
|
||||
parMap.put(sName, attr);
|
||||
}
|
||||
if ("paragraph-block".equals(sFamily)) {
|
||||
attr.put("next", elm.getAttribute("next"));
|
||||
if (elm.hasAttribute("verbatim")) { attr.put("verbatim", elm.getAttribute("verbatim")); }
|
||||
parBlockMap.put(sName, attr);
|
||||
}
|
||||
else if ("list".equals(sFamily)) {
|
||||
listMap.put(sName, attr);
|
||||
}
|
||||
else if ("listitem".equals(sFamily)) {
|
||||
listItemMap.put(sName, attr);
|
||||
}
|
||||
else if ("text".equals(sFamily)) {
|
||||
if (elm.hasAttribute("verbatim")) { attr.put("verbatim", elm.getAttribute("verbatim")); }
|
||||
textMap.put(sName, attr);
|
||||
}
|
||||
else if ("text-attribute".equals(sFamily)) {
|
||||
textAttrMap.put(sName, attr);
|
||||
}
|
||||
}
|
||||
else if (elm.getTagName().equals("string-replace")) {
|
||||
String sInput = elm.getAttribute("input");
|
||||
Map<String,String> attributes = new HashMap<String,String>();
|
||||
attributes.put("latex-code", elm.getAttribute("latex-code"));
|
||||
if (elm.hasAttribute("fontenc") && elm.getAttribute("fontenc").length()>0) {
|
||||
// The fontenc attribute is optional
|
||||
attributes.put("fontenc", elm.getAttribute("fontenc"));
|
||||
}
|
||||
else {
|
||||
attributes.put("fontenc", "any");
|
||||
}
|
||||
stringReplace.put(sInput,attributes);
|
||||
}
|
||||
else if (elm.getTagName().equals("math-symbol-map")) {
|
||||
String sName = elm.getAttribute("name");
|
||||
Map<String,String> attr = new HashMap<String,String>();
|
||||
attr.put("latex", elm.getAttribute("latex"));
|
||||
mathSymbols.put(sName, attr);
|
||||
}
|
||||
else if (elm.getTagName().equals("custom-preamble")) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
Node child = elm.getFirstChild();
|
||||
while (child!=null) {
|
||||
if (child.getNodeType()==Node.TEXT_NODE) {
|
||||
buf.append(child.getNodeValue());
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
sCustomPreamble = buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeInner(Document dom) {
|
||||
// Write heading map
|
||||
int nMaxLevel = 0;
|
||||
while (nMaxLevel<10 && headingMap.get(Integer.toString(nMaxLevel+1))!=null) { nMaxLevel++; }
|
||||
|
||||
Element hmNode = dom.createElement("heading-map");
|
||||
// This attribute is not used anymore, but we keep it for backwards compatibility
|
||||
hmNode.setAttribute("max-level",Integer.toString(nMaxLevel));
|
||||
dom.getDocumentElement().appendChild(hmNode);
|
||||
for (int i=1; i<=nMaxLevel; i++) {
|
||||
Element hlmNode = dom.createElement("heading-level-map");
|
||||
String sWriterLevel = Integer.toString(i);
|
||||
hlmNode.setAttribute("writer-level",sWriterLevel);
|
||||
Map<String,String> attr = headingMap.get(sWriterLevel);
|
||||
hlmNode.setAttribute("name",attr.get("name"));
|
||||
hlmNode.setAttribute("level",attr.get("level"));
|
||||
hmNode.appendChild(hlmNode);
|
||||
}
|
||||
|
||||
// Write style maps
|
||||
writeStyleMap(dom,parMap,"paragraph");
|
||||
writeStyleMap(dom,parBlockMap,"paragraph-block");
|
||||
writeStyleMap(dom,listMap,"list");
|
||||
writeStyleMap(dom,listItemMap,"listitem");
|
||||
writeStyleMap(dom,textMap,"text");
|
||||
writeStyleMap(dom,textAttrMap,"text-attribute");
|
||||
|
||||
// Write string replace
|
||||
Set<String> inputStrings = stringReplace.keySet();
|
||||
for (String sInput : inputStrings) {
|
||||
Map<String,String> attributes = stringReplace.get(sInput);
|
||||
Element srNode = dom.createElement("string-replace");
|
||||
srNode.setAttribute("input",sInput);
|
||||
srNode.setAttribute("latex-code",attributes.get("latex-code"));
|
||||
srNode.setAttribute("fontenc",attributes.get("fontenc"));
|
||||
dom.getDocumentElement().appendChild(srNode);
|
||||
}
|
||||
|
||||
// Write math symbol map
|
||||
for (String sName : mathSymbols.keySet()) {
|
||||
String sLatex = mathSymbols.get(sName).get("latex");
|
||||
Element msNode = dom.createElement("math-symbol-map");
|
||||
msNode.setAttribute("name",sName);
|
||||
msNode.setAttribute("latex",sLatex);
|
||||
dom.getDocumentElement().appendChild(msNode);
|
||||
}
|
||||
|
||||
// Write custom preamble
|
||||
Element cp = dom.createElement("custom-preamble");
|
||||
cp.appendChild(dom.createTextNode( sCustomPreamble));
|
||||
dom.getDocumentElement().appendChild(cp);
|
||||
}
|
||||
|
||||
private void writeStyleMap(Document dom, ComplexOption co, String sFamily) {
|
||||
for (String sName : co.keySet()) {
|
||||
Map<String,String> attr = co.get(sName);
|
||||
Element smNode = dom.createElement("style-map");
|
||||
smNode.setAttribute("name",sName);
|
||||
smNode.setAttribute("family",sFamily);
|
||||
smNode.setAttribute("before",attr.containsKey("before") ? attr.get("before") : "");
|
||||
smNode.setAttribute("after",attr.containsKey("after") ? attr.get("after") : "");
|
||||
if (attr.containsKey("next")) {
|
||||
smNode.setAttribute("next",attr.get("next"));
|
||||
}
|
||||
if (attr.containsKey("line-break")) {
|
||||
smNode.setAttribute("line-break",attr.get("line-break"));
|
||||
}
|
||||
if (attr.containsKey("break-after")) {
|
||||
smNode.setAttribute("break-after", attr.get("break-after"));
|
||||
}
|
||||
if (attr.containsKey("verbatim")) {
|
||||
smNode.setAttribute("verbatim",attr.get("verbatim"));
|
||||
}
|
||||
dom.getDocumentElement().appendChild(smNode);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// VII. Convenience accessor methods
|
||||
|
||||
public HeadingMap getHeadingMap() {
|
||||
int nMaxLevel = 0;
|
||||
while (nMaxLevel<10 && headingMap.get(Integer.toString(nMaxLevel+1))!=null) { nMaxLevel++; }
|
||||
|
||||
HeadingMap map = new HeadingMap(nMaxLevel);
|
||||
for (int i=1; i<=nMaxLevel; i++) {
|
||||
String sWriterLevel = Integer.toString(i);
|
||||
Map<String,String> attr = headingMap.get(sWriterLevel);
|
||||
String sName = attr.get("name");
|
||||
int nLevel = Misc.getPosInteger(attr.get("level"),0);
|
||||
map.setLevelData(i, sName, nLevel);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// Get style maps
|
||||
public StyleMap getParStyleMap() { return getStyleMap(parMap); }
|
||||
public StyleMap getParBlockStyleMap() { return getStyleMap(parBlockMap); }
|
||||
public StyleMap getListStyleMap() { return getStyleMap(listMap); }
|
||||
public StyleMap getListItemStyleMap() { return getStyleMap(listItemMap); }
|
||||
public StyleMap getTextAttributeStyleMap() { return getStyleMap(textAttrMap); }
|
||||
public StyleMap getTextStyleMap() { return getStyleMap(textMap); }
|
||||
|
||||
private StyleMap getStyleMap(ComplexOption co) {
|
||||
StyleMap map = new StyleMap();
|
||||
for (String sName : co.keySet()) {
|
||||
Map<String,String> attr = co.get(sName);
|
||||
String sBefore = attr.containsKey("before") ? attr.get("before") : "";
|
||||
String sAfter = attr.containsKey("after") ? attr.get("after") : "";
|
||||
String sNext = attr.containsKey("next") ? attr.get("next") : "";
|
||||
boolean bLineBreak = !"false".equals(attr.get("line-break"));
|
||||
int nBreakAfter = StyleMapItem.PAR;
|
||||
String sBreakAfter = attr.get("break-after");
|
||||
if ("none".equals(sBreakAfter)) { nBreakAfter = StyleMapItem.NONE; }
|
||||
else if ("line".equals(sBreakAfter)) { nBreakAfter = StyleMapItem.LINE; }
|
||||
boolean bVerbatim = "true".equals(attr.get("verbatim"));
|
||||
map.put(sName, sBefore, sAfter, sNext, bLineBreak, nBreakAfter, bVerbatim);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// Return current string replace as a trie
|
||||
public ReplacementTrie getStringReplace() {
|
||||
ReplacementTrie trie = new ReplacementTrie();
|
||||
for (String sInput : stringReplace.keySet()) {
|
||||
Map<String,String> attributes = stringReplace.get(sInput);
|
||||
String sLaTeXCode = attributes.get("latex-code");
|
||||
String sFontenc = attributes.get("fontenc");
|
||||
trie.put(sInput,sLaTeXCode!=null ? sLaTeXCode : "",
|
||||
ClassicI18n.readFontencs(sFontenc!=null ? sFontenc : "any"));
|
||||
}
|
||||
return trie;
|
||||
}
|
||||
|
||||
// Get the math symbols as a simple Map
|
||||
public Map<String, String> getMathSymbols() {
|
||||
Map<String,String> map = new HashMap<String,String>();
|
||||
for (String sName : mathSymbols.keySet()) {
|
||||
String sLatex = mathSymbols.get(sName).get("latex");
|
||||
map.put(sName, sLatex);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// Get the custom preamble
|
||||
public String getCustomPreamble() { return sCustomPreamble; }
|
||||
|
||||
// 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 useTitlesec() { return ((BooleanOption) options[USE_TITLESEC]).getValue(); }
|
||||
public boolean useTitletoc() { return ((BooleanOption) options[USE_TITLETOC]).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 useBibtex() { return ((BooleanOption) options[USE_BIBTEX]).getValue(); }
|
||||
public String bibtexStyle() { return options[BIBTEX_STYLE].getString(); }
|
||||
public String externalBibtexFiles() { return options[EXTERNAL_BIBTEX_FILES].getString(); }
|
||||
public int getBibtexEncoding() { return ((IntegerOption) options[BIBTEX_ENCODING]).getValue(); }
|
||||
public String zoteroBibtexFiles() { return options[ZOTERO_BIBTEX_FILES].getString(); }
|
||||
public String jabrefBibtexFiles() { return options[JABREF_BIBTEX_FILES].getString(); }
|
||||
public boolean includeOriginalCitations() { return ((BooleanOption) options[INCLUDE_ORIGINAL_CITATIONS]).getValue(); }
|
||||
public boolean useNatbib() { return ((BooleanOption) options[USE_NATBIB]).getValue(); }
|
||||
public String getNatbibOptions() { return options[NATBIB_OPTIONS].getString(); }
|
||||
|
||||
// Formatting options
|
||||
public String getFont() { return options[FONT].getString(); }
|
||||
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 String getTableFirstHeadStyle() { return options[TABLE_FIRST_HEAD_STYLE].getString(); }
|
||||
public String getTableHeadStyle() { return options[TABLE_HEAD_STYLE].getString(); }
|
||||
public String getTableFootStyle() { return options[TABLE_FOOT_STYLE].getString(); }
|
||||
public String getTableLastFootStyle() { return options[TABLE_LAST_FOOT_STYLE].getString(); }
|
||||
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(); }
|
||||
public boolean displayHiddenText() { return ((BooleanOption) options[DISPLAY_HIDDEN_TEXT]).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(); }
|
||||
|
||||
// Compatibility options
|
||||
public boolean oldMathColors() { return ((BooleanOption) options[OLD_MATH_COLORS]).getValue(); }
|
||||
}
|
||||
|
154
src/main/java/writer2latex/latex/LaTeXDocument.java
Normal file
154
src/main/java/writer2latex/latex/LaTeXDocument.java
Normal file
|
@ -0,0 +1,154 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* LaTeXDocument.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-05-05)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex;
|
||||
|
||||
import writer2latex.api.MIMETypes;
|
||||
import writer2latex.api.OutputFile;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
/**
|
||||
* <p>Class representing a LaTeX document.</p>
|
||||
*
|
||||
*/
|
||||
public class LaTeXDocument implements OutputFile {
|
||||
private static final String FILE_EXTENSION = ".tex";
|
||||
|
||||
private String sName;
|
||||
|
||||
private boolean bIsMaster;
|
||||
|
||||
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
|
||||
* @param bIsMaster true if this is a master document
|
||||
*/
|
||||
public LaTeXDocument(String sName,int nWrap,boolean bIsMaster) {
|
||||
this.nWrap = nWrap;
|
||||
this.sName = trimDocumentName(sName);
|
||||
this.bIsMaster = bIsMaster;
|
||||
contents = new LaTeXDocumentPortion(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* <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);
|
||||
}
|
||||
|
||||
public String getMIMEType() {
|
||||
return MIMETypes.LATEX;
|
||||
}
|
||||
|
||||
public boolean isMasterDocument() {
|
||||
return bIsMaster;
|
||||
}
|
||||
|
||||
public boolean containsMath() {
|
||||
// We don't use this information currently
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <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;
|
||||
}
|
||||
|
||||
}
|
||||
|
242
src/main/java/writer2latex/latex/LaTeXDocumentPortion.java
Normal file
242
src/main/java/writer2latex/latex/LaTeXDocumentPortion.java
Normal file
|
@ -0,0 +1,242 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* LaTeXDocumentPortion.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2014-09-19)
|
||||
*
|
||||
*/
|
||||
|
||||
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<Object> nodes; // The collection of all nodes in this portion
|
||||
|
||||
private StringBuilder 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?
|
||||
|
||||
/** Construct a new empty <code>LaTeXDocumentPortion</code>
|
||||
*
|
||||
* @param bWrap set to true if lines may be wrapped on writing
|
||||
*/
|
||||
public LaTeXDocumentPortion(boolean bWrap){
|
||||
this.bWrap = bWrap;
|
||||
nodes = new Vector<Object>();
|
||||
curText = new StringBuilder();
|
||||
bEmpty = true;
|
||||
}
|
||||
|
||||
/** Add another portion to the end of this portion
|
||||
*
|
||||
* @param ldp The <code>LaTeXDocuemtPortion</code> to add
|
||||
* @return a reference to this <code>LaTeXDocumentPortion</code> (not the appended one)
|
||||
*/
|
||||
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 StringBuilder();
|
||||
bEmpty = true;
|
||||
}
|
||||
nodes.add(ldp);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add a string to the end of this portion
|
||||
*
|
||||
* @param s the string to add
|
||||
* @return a reference to this <code>LaTeXDocumentPortion</code>
|
||||
*/
|
||||
public LaTeXDocumentPortion append(String s){
|
||||
curText.append(s);
|
||||
bEmpty = false; // even if this is the empty string!
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add an integer to the end of this portion
|
||||
*
|
||||
* @param n the integer to add
|
||||
* @return a reference to this <code>LaTeXDocumentPortion</code>
|
||||
*/
|
||||
public LaTeXDocumentPortion append(int n){
|
||||
curText.append(n);
|
||||
bEmpty = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add a newline to the end of this portion
|
||||
*
|
||||
* @return a reference to this <code>LaTeXDocumentPortion</code>
|
||||
*/
|
||||
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 StringBuilder to the output */
|
||||
private void writeBuffer(StringBuilder 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 StringBuilder to the output without wrap */
|
||||
private void writeBuffer(StringBuilder 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
|
||||
*
|
||||
* @param osw an <code>OutputStreamWriter</code> to write to
|
||||
* @param nLineLen the line length after which automatic line breaks should occur if allowed (nLineLen=0 means no wrap)
|
||||
* @param sNewline the newline character(s) to use
|
||||
* @throws IOException if an exception occurs writing to to osw
|
||||
*/
|
||||
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((StringBuilder) nodes.get(i),osw,nLineLen,sNewline);
|
||||
}
|
||||
else {
|
||||
writeBuffer((StringBuilder) 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 LaTeXDocumentPortion as a string
|
||||
*
|
||||
* @return a string representation of the <code>LaTeXDocumentPortion</code>
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
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((StringBuilder) nodes.get(i));
|
||||
}
|
||||
}
|
||||
if (!bEmpty) { // write current node as well
|
||||
buf.append(curText.toString());
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
588
src/main/java/writer2latex/latex/ListConverter.java
Normal file
588
src/main/java/writer2latex/latex/ListConverter.java
Normal file
|
@ -0,0 +1,588 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ListConverter.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-04-14)
|
||||
*
|
||||
*/
|
||||
package writer2latex.latex;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
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.office.ListStyle;
|
||||
import writer2latex.office.OfficeReader;
|
||||
import writer2latex.office.XMLString;
|
||||
import writer2latex.util.Calc;
|
||||
import writer2latex.util.Misc;
|
||||
|
||||
public class ListConverter extends StyleConverter {
|
||||
boolean bNeedSaveEnumCounter = false;
|
||||
private Hashtable<String, String[]> listStyleLevelNames = new Hashtable<String, String[]>();
|
||||
|
||||
|
||||
/** Construct a new <code>ListConverter</code>
|
||||
*/
|
||||
public ListConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
|
||||
super(ofr,config,palette);
|
||||
}
|
||||
|
||||
@Override 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> 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();
|
||||
if ("true".equals(node.getAttribute(XMLString.TEXT_CONTINUE_NUMBERING))) { ic.setInContinuedList(true); }
|
||||
|
||||
// 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();
|
||||
applyListStyle(bOrdered,ba,ic);
|
||||
|
||||
// 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()) {
|
||||
palette.getBlockCv().traverseBlockText(node,ldp,oc);
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply the style
|
||||
BeforeAfter ba = new BeforeAfter();
|
||||
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,oc);
|
||||
|
||||
// export the list item (note the special treatment of lists in tables)
|
||||
if (ba.getBefore().length()>0) {
|
||||
ldp.append(ba.getBefore());
|
||||
if (config.formatting()>=LaTeXConfig.CONVERT_MOST && !oc.isInTable()) { ldp.nl(); }
|
||||
}
|
||||
palette.getBlockCv().traverseBlockText(node,ldp,oc);
|
||||
if (ba.getAfter().length()>0 || oc.isInTable()) { 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;
|
||||
}
|
||||
|
||||
// Convert style information
|
||||
/** <p>Apply a list style to an ordered or unordered list.</p> */
|
||||
private void applyListStyle(boolean bOrdered, BeforeAfter ba, Context oc) {
|
||||
// Step 1. We may have a style map, this always takes precedence
|
||||
String sDisplayName = ofr.getListStyles().getDisplayName(oc.getListStyleName());
|
||||
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(oc.getListStyleName());
|
||||
if (style==null || config.formatting()<=LaTeXConfig.IGNORE_MOST) {
|
||||
if (oc.getListLevel()<=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
|
||||
// (for list in tables this is the maximum formatting we export)
|
||||
if (config.formatting()==LaTeXConfig.CONVERT_BASIC ||
|
||||
(config.formatting()>=LaTeXConfig.CONVERT_MOST && oc.isInTable())) {
|
||||
if (oc.getListLevel()==1) {
|
||||
if (!listStyleLevelNames.containsKey(oc.getListStyleName())) {
|
||||
createListStyleLabels(oc.getListStyleName());
|
||||
}
|
||||
ba.add("\\liststyle"+styleNames.getExportName(getDisplayName(oc.getListStyleName()))+"\n","");
|
||||
}
|
||||
if (oc.getListLevel()<=4) {
|
||||
String sCounterName = listStyleLevelNames.get(oc.getListStyleName())[oc.getListLevel()];
|
||||
if (oc.isInContinuedList() && style.isNumber(oc.getListLevel())) {
|
||||
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 (oc.isInContinuedList() && style.isNumber(oc.getListLevel())) {
|
||||
ba.add("\n\\setcounter{"+sCounterName+"}{\\value{saveenum}}","");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Step 4: Export with formatting, as "Writer style" custom lists
|
||||
if (oc.getListLevel()<=4) { // TODO: Max level should not be fixed
|
||||
if (!styleNames.containsName(getDisplayName(oc.getListStyleName()))) {
|
||||
createListStyle(oc.getListStyleName());
|
||||
}
|
||||
String sTeXName="list"+styleNames.getExportName(getDisplayName(oc.getListStyleName()))
|
||||
+"level"+Misc.int2roman(oc.getListLevel());
|
||||
if (!oc.isInContinuedList() && style.isNumber(oc.getListLevel())) {
|
||||
int nStartValue = Misc.getPosInteger(style.getLevelProperty(oc.getListLevel(),XMLString.TEXT_START_VALUE),1)-1;
|
||||
// Note that we need a blank line after certain constructions to get proper indentation
|
||||
ba.add("\n\\setcounter{"+sTeXName+"}{"+Integer.toString(nStartValue)+"}\n","");
|
||||
}
|
||||
ba.add("\\begin{"+sTeXName+"}","\\end{"+sTeXName+"}");
|
||||
}
|
||||
}
|
||||
|
||||
/** <p>Apply a list style to a list item.</p> */
|
||||
private void applyListItemStyle(String sStyleName, int nLevel, boolean bHeader,
|
||||
boolean bRestart, int nStartValue, BeforeAfter ba, Context oc) {
|
||||
// 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)
|
||||
// (for list in tables this is the maximum formatting we export)
|
||||
if (config.formatting()==LaTeXConfig.CONVERT_BASIC ||
|
||||
(config.formatting()>=LaTeXConfig.CONVERT_MOST && oc.isInTable())) {
|
||||
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++) {
|
||||
// The alignment of the label works the same for old and new format
|
||||
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"; }
|
||||
}
|
||||
|
||||
if (style.isNewType(i)) {
|
||||
// The new type from ODT 1.2 is somewhat weird; we take it step by step
|
||||
|
||||
// Fist the list style defines a left margin (leftskip) and a first line indent (parindent)
|
||||
// to *replace* the values from the paragraph style
|
||||
String sMarginLeft = style.getLevelStyleProperty(i, XMLString.FO_MARGIN_LEFT);
|
||||
if (sMarginLeft==null) { sMarginLeft = "0cm"; }
|
||||
String sTextIndent = style.getLevelStyleProperty(i, XMLString.FO_TEXT_INDENT);
|
||||
if (sTextIndent==null) { sTextIndent = "0cm"; }
|
||||
|
||||
// Generate the LaTeX code to replace these values
|
||||
String sDefWriterlistleftskip = "\\def\\writerlistleftskip{\\setlength\\leftskip{"+sMarginLeft+"}}";
|
||||
String sDefWriterlistparindent = "\\def\\writerlistparindent{\\setlength\\parindent{"+sTextIndent+"}}";
|
||||
|
||||
// Next we have three types of label format: listtab, space, nothing
|
||||
String sFormat = style.getLevelStyleProperty(i, XMLString.TEXT_LABEL_FOLLOWED_BY);
|
||||
|
||||
// Generate LaTeX code to typeset the label, followed by a space character if required
|
||||
String sTheLabel = "\\label"+sLevelName[i]+("space".equals(sFormat) ? "\\ " : "");
|
||||
|
||||
if ("listtab".equals(sFormat) || sAlignmentChar=="r") {
|
||||
// In these cases we typeset the label aligned at a zero width box (rather than as an integrated part of the text)
|
||||
sTheLabel = "\\makebox[0cm][" + sAlignmentChar + "]{"+sTheLabel+"}";
|
||||
|
||||
if ("listtab".equals(sFormat)) {
|
||||
// In the tab case we must the calculate the hspace to put *after* the zero width box
|
||||
// This defines the position of an additional tab stop, which really means the start position of the text *after* the label
|
||||
String sTabPos = style.getLevelStyleProperty(i, XMLString.TEXT_LIST_TAB_STOP_POSITION);
|
||||
if (sTabPos==null) { sTabPos = "0cm"; }
|
||||
sTheLabel += "\\hspace{"+Calc.sub(sTabPos, Calc.add(sMarginLeft, sTextIndent))+"}";
|
||||
}
|
||||
}
|
||||
|
||||
// We are now ready to declare the list style
|
||||
declarations.append("\\newenvironment{").append(sLevelName[i]).append("}{")
|
||||
// Initialize hooks
|
||||
.append(sDefWriterlistleftskip)
|
||||
.append("\\def\\writerlistparindent{}")
|
||||
.append("\\def\\writerlistlabel{}")
|
||||
// Redefine \item
|
||||
.append("\\def\\item{")
|
||||
// The new parindent is the position of the label
|
||||
.append(sDefWriterlistparindent)
|
||||
.append("\\def\\writerlistlabel{");
|
||||
if (style.isNumber(i)) {
|
||||
declarations.append("\\stepcounter{").append(sLevelName[i]).append("}");
|
||||
}
|
||||
declarations.append(sTheLabel).append("\\writerlistremovelabel}}}{}").nl();
|
||||
}
|
||||
else {
|
||||
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);
|
||||
declarations
|
||||
.append("\\newenvironment{")
|
||||
.append(sLevelName[i]).append("}{")
|
||||
.append("\\def\\writerlistleftskip{\\addtolength\\leftskip{")
|
||||
.append(Calc.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(palette.getI18n().convert(sPrefix,false,"en")); }
|
||||
ldp.append("\\the").append(sName[i]);
|
||||
if (sSuffix!=null) { ldp.append(palette.getI18n().convert(sSuffix,false,"en")); }
|
||||
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) {
|
||||
String sFontName = palette.getCharSc().getFontName(style.getLevelProperty(i,XMLString.TEXT_STYLE_NAME));
|
||||
palette.getI18n().pushSpecialTable(sFontName);
|
||||
// Bullets are usually symbols, so this should be OK:
|
||||
ldp.append(palette.getI18n().convert(sBullet,false,"en"));
|
||||
palette.getI18n().popSpecialTable();
|
||||
}
|
||||
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; }
|
||||
}
|
||||
|
||||
}
|
401
src/main/java/writer2latex/latex/MathConverter.java
Normal file
401
src/main/java/writer2latex/latex/MathConverter.java
Normal file
|
@ -0,0 +1,401 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* MathConverter.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2014-09-03)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import writer2latex.base.ConverterBase.TexMathsStyle;
|
||||
import writer2latex.office.EmbeddedObject;
|
||||
import writer2latex.office.EmbeddedXMLObject;
|
||||
import writer2latex.office.MIMETypes;
|
||||
import writer2latex.office.OfficeReader;
|
||||
import writer2latex.office.StyleWithProperties;
|
||||
import writer2latex.office.TableReader;
|
||||
import writer2latex.office.XMLString;
|
||||
import writer2latex.util.Misc;
|
||||
|
||||
/**
|
||||
* This ConverterHelper converts mathematical content to LaTeX.
|
||||
* It works slightly different than the other helpers: A number of elements may or may not
|
||||
* have content that should be converted to math. Thus the methods offered first examines
|
||||
* the content. If it turns out to be a mathematical formula, it is converted. Otherwise
|
||||
* nothing is done, and the method returns false.
|
||||
* Mathematical content may be MathML (with StarMath annotation), TexMaths or (the now obsolete) OOoLaTeX
|
||||
*/
|
||||
public final class MathConverter extends ConverterHelper {
|
||||
|
||||
private StarMathConverter smc;
|
||||
|
||||
private boolean bContainsFormulas = false;
|
||||
private boolean bAddParAfterDisplay = false;
|
||||
|
||||
private boolean bNeedTexMathsPreamble = false;
|
||||
private boolean bNeedOOoLaTeXPreamble = false;
|
||||
|
||||
private Element theEquation = null;
|
||||
private Element theSequence = null;
|
||||
|
||||
public MathConverter(OfficeReader ofr,LaTeXConfig config, ConverterPalette palette) {
|
||||
super(ofr,config,palette);
|
||||
smc = new StarMathConverter(palette.getI18n(),config);
|
||||
bAddParAfterDisplay = config.formatting()>=LaTeXConfig.CONVERT_MOST;
|
||||
}
|
||||
|
||||
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
|
||||
if (bContainsFormulas) {
|
||||
smc.appendDeclarations(pack,decl);
|
||||
}
|
||||
if (bNeedTexMathsPreamble) {
|
||||
// The preamble may be stored as a user defined property (newline is represented as paragraph sign)
|
||||
Map<String,String> props = palette.getMetaData().getUserDefinedMetaData();
|
||||
if (props.containsKey("TexMathsPreamble")) {
|
||||
decl.append("% TexMaths preamble\n")
|
||||
.append(props.get("TexMathsPreamble").replace('\u00a7', '\n'));
|
||||
}
|
||||
}
|
||||
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: Replace with a method "handleEquation"
|
||||
public String convert(Element 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.SEMANTICS); // Since OOo 3.2
|
||||
if (semantics==null) {
|
||||
semantics = Misc.getChildByTagName(formula,XMLString.MATH_SEMANTICS);
|
||||
}
|
||||
if (semantics!=null) {
|
||||
Node annotation = Misc.getChildByTagName(semantics,XMLString.ANNOTATION); // Since OOo 3.2
|
||||
if (annotation==null) {
|
||||
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)
|
||||
if (formula.hasChildNodes()) {
|
||||
return "\\text{Warning: No StarMath annotation}";
|
||||
}
|
||||
else { // empty formula
|
||||
return " ";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Try to convert a draw:frame or draw:g element as an (inline) TexMaths or OOoLaTeX equation
|
||||
*
|
||||
* @param node the element containing the equation (draw:frame or draw:g)
|
||||
* @param ldp the LaTeXDocumentPortion to contain the converted equation
|
||||
*
|
||||
* @return true if this elements happen to be a TexMaths equation, otherwise false
|
||||
*/
|
||||
public boolean handleTexMathsEquation(Element node, LaTeXDocumentPortion ldp) {
|
||||
String sLaTeX = null;
|
||||
Element equation = palette.getTexMathsEquation(node);
|
||||
if (equation!=null) {
|
||||
sLaTeX = Misc.getPCDATA(equation);
|
||||
if (sLaTeX!=null) { bNeedTexMathsPreamble = true; }
|
||||
}
|
||||
else { // Try OOoLaTeX
|
||||
// The LaTeX code is embedded in a custom style attribute:
|
||||
StyleWithProperties style = ofr.getFrameStyle(Misc.getAttribute(node, XMLString.DRAW_STYLE_NAME));
|
||||
if (style!=null) {
|
||||
sLaTeX = style.getProperty("OOoLatexArgs");
|
||||
if (sLaTeX!=null) { bNeedOOoLaTeXPreamble = true; }
|
||||
}
|
||||
}
|
||||
if (sLaTeX!=null) {
|
||||
// Format is <point size>X<mode>X<TeX code>X<format>X<resolution>X<transparency>
|
||||
// where X is a paragraph sign
|
||||
switch (palette.getTexMathsStyle(sLaTeX)) {
|
||||
case inline:
|
||||
ldp.append("$").append(palette.getTexMathsEquation(sLaTeX)).append("$");
|
||||
break;
|
||||
case display:
|
||||
ldp.append("$\\displaystyle ").append(palette.getTexMathsEquation(sLaTeX)).append("$");
|
||||
break;
|
||||
case latex:
|
||||
ldp.append(palette.getTexMathsEquation(sLaTeX));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Try to convert a table as a display equation:
|
||||
* A 1 row by 2 columns table in which each cell contains exactly one paragraph,
|
||||
* the left cell contains exactly one formula and the right cell contains exactly
|
||||
* one sequence number is treated as a (numbered) display equation.
|
||||
* This happens to coincide with the AutoText provided with OOo Writer :-)
|
||||
* @param table the table reader
|
||||
* @param ldp the LaTeXDocumentPortion to contain the converted equation
|
||||
* @return true if the conversion was successful, false if the table
|
||||
* did not represent a display equation
|
||||
*/
|
||||
public boolean handleDisplayEquation(TableReader table, LaTeXDocumentPortion ldp) {
|
||||
if (table.getRowCount()==1 && table.getColCount()==2 &&
|
||||
OfficeReader.isSingleParagraph(table.getCell(0, 0)) && OfficeReader.isSingleParagraph(table.getCell(0, 1)) ) {
|
||||
// Table of the desired form
|
||||
if (parseDisplayEquation(Misc.getFirstChildElement(table.getCell(0, 0))) && theEquation!=null && theSequence==null) {
|
||||
// Found equation in first cell
|
||||
Element myEquation = theEquation;
|
||||
if (parseDisplayEquation(Misc.getFirstChildElement(table.getCell(0, 1))) && theEquation==null && theSequence!=null) {
|
||||
// Found sequence in second cell
|
||||
handleDisplayEquation(myEquation, theSequence, ldp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**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 (possibly with formatting).
|
||||
* @param node the paragraph
|
||||
* @param ldp the LaTeXDocumentPortion to contain the converted equation
|
||||
* @return true if the conversion was successful, false if the paragraph
|
||||
* did not contain a display equation
|
||||
*/
|
||||
public boolean handleDisplayEquation(Element node, LaTeXDocumentPortion ldp) {
|
||||
if (parseDisplayEquation(node) && theEquation!=null) {
|
||||
handleDisplayEquation(theEquation, theSequence, ldp);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDisplayEquation(Element equation, Element sequence, LaTeXDocumentPortion ldp) {
|
||||
boolean bTexMaths = equation.getTagName().equals(XMLString.SVG_DESC);
|
||||
TexMathsStyle style = TexMathsStyle.inline;
|
||||
String sLaTeX;
|
||||
if (bTexMaths) {
|
||||
// TeXMaths equation
|
||||
sLaTeX = palette.getTexMathsEquation(Misc.getPCDATA(equation));
|
||||
style = palette.getTexMathsStyle(Misc.getPCDATA(equation));
|
||||
if (sLaTeX!=null) { bNeedTexMathsPreamble = true; }
|
||||
}
|
||||
else {
|
||||
// MathML equation
|
||||
sLaTeX = convert(equation);
|
||||
}
|
||||
if (sLaTeX!=null && !" ".equals(sLaTeX)) { // ignore empty formulas
|
||||
if (!bTexMaths || style!=TexMathsStyle.latex) {
|
||||
// Unfortunately we can't do numbered equations for TexMaths equations with latex style
|
||||
if (sequence!=null) {
|
||||
// Numbered equation
|
||||
ldp.append("\\begin{equation}");
|
||||
palette.getFieldCv().handleSequenceLabel(sequence,ldp);
|
||||
if (bTexMaths && style==TexMathsStyle.inline) {
|
||||
ldp.append("\\textstyle ");
|
||||
}
|
||||
ldp.nl()
|
||||
.append(sLaTeX).nl()
|
||||
.append("\\end{equation}").nl();
|
||||
}
|
||||
else {
|
||||
// Unnumbered equation
|
||||
ldp.append("\\begin{equation*}");
|
||||
if (bTexMaths && style==TexMathsStyle.inline) {
|
||||
ldp.append("\\textstyle ");
|
||||
}
|
||||
ldp.nl()
|
||||
.append(sLaTeX).nl()
|
||||
.append("\\end{equation*}").nl();
|
||||
}
|
||||
}
|
||||
else {
|
||||
ldp.append(sLaTeX).nl();
|
||||
}
|
||||
if (bAddParAfterDisplay) { ldp.nl(); }
|
||||
}
|
||||
}
|
||||
|
||||
/** Determine whether or not a paragraph contains a display equation.
|
||||
* A paragraph is a display equation if it contains a single formula and no text content except whitespace
|
||||
* and an optional sequence number which may be in brackets.
|
||||
* As a side effect, this method keeps a reference to the equation and the sequence number
|
||||
*
|
||||
* @param node the paragraph
|
||||
* @return true if this is a display equation
|
||||
*/
|
||||
private boolean parseDisplayEquation(Node node) {
|
||||
theEquation = null;
|
||||
theSequence = null;
|
||||
return doParseDisplayEquation(node);
|
||||
}
|
||||
|
||||
private boolean doParseDisplayEquation(Node node) {
|
||||
Node child = node.getFirstChild();
|
||||
while (child!=null) {
|
||||
if (Misc.isElement(child)) {
|
||||
Element elm = (Element) child;
|
||||
String sName = elm.getTagName();
|
||||
// First check for MathML or TexMaths equation
|
||||
Element equation = getMathmlEquation(elm);
|
||||
if (equation==null) {
|
||||
equation = palette.getTexMathsEquation(elm);
|
||||
}
|
||||
|
||||
if (equation!=null) {
|
||||
if (theEquation==null) {
|
||||
theEquation = equation;
|
||||
}
|
||||
else { // two or more equations -> not a display
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (XMLString.TEXT_SEQUENCE.equals(sName)) {
|
||||
if (theSequence==null) {
|
||||
theSequence = elm;
|
||||
}
|
||||
else { // two sequence numbers -> not a display
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (XMLString.TEXT_SPAN.equals(sName)) {
|
||||
if (!doParseDisplayEquation(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 if (XMLString.TEXT_SOFT_PAGE_BREAK.equals(sName)) { // since ODF 1.1
|
||||
// Soft page breaks 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;
|
||||
}
|
||||
|
||||
/** Get a MathML formula from a draw:frame
|
||||
*
|
||||
* @param node the draw:frame
|
||||
* @return the MathML element, or null if this is not a MathML formula
|
||||
*/
|
||||
private Element getMathmlEquation(Element node) {
|
||||
if (node.getTagName().equals(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();
|
||||
Element formula = Misc.getChildByTagName(formuladoc,XMLString.MATH); // Since OOo 3.2
|
||||
if (formula==null) {
|
||||
formula = Misc.getChildByTagName(formuladoc,XMLString.MATH_MATH);
|
||||
}
|
||||
return formula;
|
||||
}
|
||||
catch (org.xml.sax.SAXException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (java.io.IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // flat XML, object is contained in node
|
||||
Element formula = Misc.getChildByTagName(node,XMLString.MATH); // Since OOo 3.2
|
||||
if (formula==null) {
|
||||
formula = Misc.getChildByTagName(node,XMLString.MATH_MATH);
|
||||
}
|
||||
return formula;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
400
src/main/java/writer2latex/latex/NoteConverter.java
Normal file
400
src/main/java/writer2latex/latex/NoteConverter.java
Normal file
|
@ -0,0 +1,400 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* NoteConverter.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-04-15)
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: Get the styles for footnotes and endnotes and use Context.resetFormattingFromStyle...
|
||||
|
||||
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<Element> postponedFootnotes = new LinkedList<Element>();
|
||||
|
||||
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("}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Do we have any pending footnotes, that may be inserted in this context?
|
||||
*
|
||||
* @param oc the context to verify against
|
||||
* @return true if there are pending footnotes
|
||||
*/
|
||||
public boolean hasPendingFootnotes(Context oc) {
|
||||
return !oc.isNoFootnotes() && postponedFootnotes.size()>0;
|
||||
}
|
||||
|
||||
/** 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(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(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);
|
||||
|
||||
// Headings inside footnotes are considere a mistake and exported as ordinary paragraphs
|
||||
if (nodeName.equals(XMLString.TEXT_H) || nodeName.equals(XMLString.TEXT_P)) {
|
||||
StyleWithProperties style = ofr.getParStyle(node.getAttribute(XMLString.TEXT_STYLE_NAME));
|
||||
oc.resetFormattingFromStyle(style);
|
||||
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.getListCv().handleList(child,ldp,oc);
|
||||
}
|
||||
|
||||
if (nodeName.equals(XMLString.TEXT_ORDERED_LIST)) {
|
||||
palette.getListCv().handleList(child,ldp,oc);
|
||||
}
|
||||
|
||||
if (nodeName.equals(XMLString.TEXT_UNORDERED_LIST)) {
|
||||
palette.getListCv().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(ListConverter.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();
|
||||
}
|
||||
|
||||
|
||||
}
|
16
src/main/java/writer2latex/latex/Package.html
Normal file
16
src/main/java/writer2latex/latex/Package.html
Normal 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>
|
596
src/main/java/writer2latex/latex/PageStyleConverter.java
Normal file
596
src/main/java/writer2latex/latex/PageStyleConverter.java
Normal file
|
@ -0,0 +1,596 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* PageStyleConverter.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-04-15)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex;
|
||||
|
||||
import java.util.Enumeration;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import writer2latex.util.CSVList;
|
||||
import writer2latex.util.Calc;
|
||||
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 || config.pageFormatting()==LaTeXConfig.CONVERT_GEOMETRY) 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 || config.pageFormatting()==LaTeXConfig.CONVERT_GEOMETRY) { return; }
|
||||
|
||||
Context context = new Context();
|
||||
context.resetFormattingFromStyle(ofr.getDefaultParStyle());
|
||||
context.setInHeaderFooter(true);
|
||||
|
||||
|
||||
Enumeration<OfficeStyle> 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(ListConverter.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(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 && config.pageFormatting()!=LaTeXConfig.CONVERT_GEOMETRY) { 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<OfficeStyle> 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 && Calc.isLessThan(sHeadHeight,sThisHeadHeight)) {
|
||||
sHeadHeight = sThisHeadHeight;
|
||||
}
|
||||
String sThisHeadSep = layout.getHeaderProperty(XMLString.FO_MARGIN_BOTTOM);
|
||||
if (sThisHeadSep!=null && Calc.isLessThan(sHeadSep,sThisHeadSep)) {
|
||||
sHeadSep = sThisHeadSep;
|
||||
}
|
||||
bIncludeHead = true;
|
||||
}
|
||||
if (layout.hasFooterStyle()) {
|
||||
String sThisFootHeight = layout.getFooterProperty(XMLString.FO_MIN_HEIGHT);
|
||||
if (sThisFootHeight!=null && Calc.isLessThan(sFootHeight,sThisFootHeight)) {
|
||||
sFootHeight = sThisFootHeight;
|
||||
}
|
||||
String sThisFootSep = layout.getFooterProperty(XMLString.FO_MARGIN_TOP);
|
||||
if (sThisFootSep!=null && Calc.isLessThan(sFootSep,sThisFootSep)) {
|
||||
sFootSep = sThisFootSep;
|
||||
}
|
||||
bIncludeFoot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Define 12pt as minimum height (the source may specify 0pt..)
|
||||
if (bIncludeHead && Calc.isLessThan(sHeadHeight,"12pt")) {
|
||||
sHeadHeight = "12pt";
|
||||
}
|
||||
if (bIncludeFoot && Calc.isLessThan(sFootHeight,"12pt")) {
|
||||
sFootHeight = "12pt";
|
||||
}
|
||||
|
||||
String sFootSkip = Calc.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 = Calc.sub(sPaperHeight,sMarginTop);
|
||||
sTextHeight = Calc.sub(sTextHeight,sHeadHeight);
|
||||
sTextHeight = Calc.sub(sTextHeight,sHeadSep);
|
||||
sTextHeight = Calc.sub(sTextHeight,sFootSkip);
|
||||
sTextHeight = Calc.sub(sTextHeight,sMarginBottom);
|
||||
String sTextWidth = Calc.sub(sPaperWidth,sMarginLeft);
|
||||
sTextWidth = Calc.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(Calc.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 = Calc.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 Calc.isLessThan(Calc.abs(Calc.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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
586
src/main/java/writer2latex/latex/ParConverter.java
Normal file
586
src/main/java/writer2latex/latex/ParConverter.java
Normal file
|
@ -0,0 +1,586 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ParConverter.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2014-09-08)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import writer2latex.latex.util.BeforeAfter;
|
||||
import writer2latex.latex.util.Context;
|
||||
import writer2latex.latex.util.StyleMapItem;
|
||||
import writer2latex.latex.util.StyleMap;
|
||||
import writer2latex.office.OfficeReader;
|
||||
import writer2latex.office.StyleWithProperties;
|
||||
import writer2latex.office.XMLString;
|
||||
import writer2latex.util.Calc;
|
||||
|
||||
/* <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;
|
||||
|
||||
// Display hidden text?
|
||||
private boolean bDisplayHiddenText = false;
|
||||
|
||||
/** <p>Constructs a new <code>ParConverter</code>.</p>
|
||||
*/
|
||||
public ParConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) {
|
||||
super(ofr,config,palette);
|
||||
this.bDisplayHiddenText = config.displayHiddenText();
|
||||
}
|
||||
|
||||
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:p element node containing the paragraph
|
||||
* @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) {
|
||||
// Check for display equation (except in table cells)
|
||||
if ((!oc.isInTable()) && palette.getMathCv().handleDisplayEquation(node,ldp)) { return; }
|
||||
|
||||
// Get the style for this paragraph
|
||||
String sStyleName = node.getAttribute(XMLString.TEXT_STYLE_NAME);
|
||||
StyleWithProperties style = ofr.getParStyle(sStyleName);
|
||||
String sDisplayName = ofr.getParStyles().getDisplayName(sStyleName);
|
||||
|
||||
// Check for hidden text
|
||||
if (!bDisplayHiddenText && style!=null && "none".equals(style.getProperty(XMLString.TEXT_DISPLAY))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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();
|
||||
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
|
||||
int nBreakAfter;
|
||||
BeforeAfter ba = new BeforeAfter();
|
||||
if (oc.isInTable()) {
|
||||
nBreakAfter = applyCellParStyle(sStyleName,ba,ic,OfficeReader.getCharacterCount(node)==0,bLastInBlock);
|
||||
}
|
||||
else {
|
||||
nBreakAfter = 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 line break if desired
|
||||
if (nBreakAfter!=StyleMapItem.NONE) { ldp.nl(); }
|
||||
// Add a blank line except within verbatim and last in a block, and if desired by style map
|
||||
if (!bLastInBlock && !ic.isVerbatim() && !ic.isInSimpleTable() && nBreakAfter==StyleMapItem.PAR) { 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 int 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
|
||||
|
||||
context.setVerbatim(false);
|
||||
|
||||
int nBreakAfter = bLastInBlock ? StyleMapItem.NONE : StyleMapItem.PAR;
|
||||
|
||||
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(" ",""); }
|
||||
}
|
||||
}
|
||||
nBreakAfter = StyleMapItem.NONE;
|
||||
}
|
||||
else if (config.getParStyleMap().contains(ofr.getParStyles().getDisplayName(sName))) {
|
||||
// We have a style map in the configuration
|
||||
StyleMap sm = config.getParStyleMap();
|
||||
String sDisplayName = ofr.getParStyles().getDisplayName(sName);
|
||||
String sBefore = sm.getBefore(sDisplayName);
|
||||
String sAfter = sm.getAfter(sDisplayName);
|
||||
ba.add(sBefore, sAfter);
|
||||
// Add line breaks inside?
|
||||
if (sm.getLineBreak(sDisplayName)) {
|
||||
if (sBefore.length()>0) { ba.add("\n",""); }
|
||||
if (sAfter.length()>0 && !"}".equals(sAfter)) { ba.add("","\n"); }
|
||||
}
|
||||
nBreakAfter = sm.getBreakAfter(sDisplayName);
|
||||
if (sm.getVerbatim(sDisplayName)) { context.setVerbatim(true); }
|
||||
}
|
||||
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}"); }
|
||||
nBreakAfter = StyleMapItem.LINE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Export character formatting + alignment only
|
||||
BeforeAfter baPar = new BeforeAfter();
|
||||
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)) { baPar.add("\\centering\\arraybslash",""); }
|
||||
else if ("end".equals(sTextAlign)) { baPar.add("\\raggedleft\\arraybslash",""); }
|
||||
bNeedArrayBslash = true;
|
||||
}
|
||||
else if (bLastInBlock) { // no \par needed
|
||||
if ("center".equals(sTextAlign)) { baPar.add("\\centering",""); }
|
||||
else if ("end".equals(sTextAlign)) { baPar.add("\\raggedleft",""); }
|
||||
}
|
||||
else {
|
||||
if ("center".equals(sTextAlign)) { baPar.add("\\centering","\\par"); }
|
||||
else if ("end".equals(sTextAlign)) { baPar.add("\\raggedleft","\\par"); }
|
||||
}
|
||||
palette.getI18n().applyLanguage(style,true,true,baText);
|
||||
palette.getCharSc().applyFont(style,true,true,baText,context);
|
||||
}
|
||||
|
||||
// Group the contents if this is not the last paragraph in the cell
|
||||
boolean bIsGrouped = false;
|
||||
if ((!baPar.isEmpty() || !baText.isEmpty()) && !bLastInBlock) {
|
||||
ba.add("{","}");
|
||||
bIsGrouped = true;
|
||||
}
|
||||
ba.add(baPar);
|
||||
// Group the text formatting in any case (supertabular needs this)
|
||||
if (!baText.isEmpty() && !bIsGrouped) {
|
||||
ba.add("{", "}");
|
||||
}
|
||||
ba.add(baText);
|
||||
if (ba.getBefore().length()>0) { ba.add(" ",""); }
|
||||
}
|
||||
|
||||
// Update context
|
||||
StyleWithProperties style = ofr.getParStyle(sName);
|
||||
if (style!=null) {
|
||||
context.updateFormattingFromStyle(style);
|
||||
}
|
||||
return nBreakAfter;
|
||||
}
|
||||
|
||||
|
||||
/** <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 int applyParStyle(String sName, BeforeAfter ba, Context context, boolean bNoTextPar) {
|
||||
return applyParStyle(sName,ba,context,bNoTextPar,true);
|
||||
}
|
||||
|
||||
private int applyParStyle(String sName, BeforeAfter ba, Context context, boolean bNoTextPar, boolean bBreakInside) {
|
||||
// No style specified?
|
||||
if (sName==null) { return StyleMapItem.PAR; }
|
||||
|
||||
/*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*/
|
||||
int nBreakAfter = StyleMapItem.PAR;
|
||||
|
||||
if (bNoTextPar && (config.formatting()==LaTeXConfig.CONVERT_BASIC || config.formatting()==LaTeXConfig.IGNORE_MOST) ) {
|
||||
//TODO: If there is a style map, we should respect that despite the fact that the paragraph is empty
|
||||
// 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}"); nBreakAfter = StyleMapItem.LINE; }
|
||||
else if ("end".equals(sTextAlign)) { ba.add("{\\raggedleft ","\\par}"); nBreakAfter = StyleMapItem.LINE; }
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 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"); }
|
||||
}
|
||||
nBreakAfter = styleMap.getBreakAfter(sName);
|
||||
}
|
||||
|
||||
// Update context
|
||||
StyleWithProperties style = ofr.getParStyle(sName);
|
||||
if (style!=null) {
|
||||
context.updateFormattingFromStyle(style);
|
||||
}
|
||||
context.setVerbatim(styleMap.getVerbatim(sName));
|
||||
|
||||
return nBreakAfter;
|
||||
}
|
||||
|
||||
/** <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.getBreakAfter(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.getBreakAfter(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,styleMap.getBreakAfter(sParentName), 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=Calc.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 = Calc.multiply("10%",sMarginTop);
|
||||
if (Calc.length2px(sRubberMarginTop).equals("0")) { sRubberMarginTop="1pt"; }
|
||||
String sRubberMarginBottom = Calc.multiply("10%",sMarginBottom);
|
||||
if (Calc.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);
|
||||
}
|
||||
|
||||
}
|
224
src/main/java/writer2latex/latex/SectionConverter.java
Normal file
224
src/main/java/writer2latex/latex/SectionConverter.java
Normal file
|
@ -0,0 +1,224 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* SectionConverter.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2014-09-08)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
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;
|
||||
|
||||
// Display hidden text?
|
||||
private boolean bDisplayHiddenText = 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);
|
||||
this.bDisplayHiddenText = config.displayHiddenText();
|
||||
}
|
||||
|
||||
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
|
||||
if (bNeedMulticol) { pack.append("\\usepackage{multicol}").nl(); }
|
||||
}
|
||||
|
||||
// Handle a section as a Zotero bibliography
|
||||
private boolean handleZoteroBibliography(Element node, LaTeXDocumentPortion ldp, Context oc) {
|
||||
String sName = node.getAttribute(XMLString.TEXT_NAME);
|
||||
if (config.useBibtex() && config.zoteroBibtexFiles().length()>0 && sName.startsWith("ZOTERO_BIBL")) {
|
||||
// This section is a Zotero bibliography, and the user wishes to handle it as such
|
||||
// A Zotero bibliography name has the form ZOTERO_BIBL <json object> <identifier> with a single space separating the items
|
||||
// The identifier is a unique identifier for the bibliography and is not used here
|
||||
if (!config.noIndex()) {
|
||||
// Parse the name (errors are ignored) and add \nocite commands as appropriate
|
||||
int nObjectStart = sName.indexOf('{');
|
||||
int nObjectEnd = sName.lastIndexOf('}');
|
||||
if (nObjectStart>-1 && nObjectEnd>-1 && nObjectStart<nObjectEnd) {
|
||||
String sJsonObject = sName.substring(nObjectStart, nObjectEnd+1);
|
||||
JSONObject jo = null;
|
||||
try {
|
||||
jo = new JSONObject(sJsonObject);
|
||||
} catch (JSONException e) {
|
||||
}
|
||||
if (jo!=null) {
|
||||
JSONArray uncited = null;
|
||||
try {
|
||||
uncited = jo.getJSONArray("uncited");
|
||||
}
|
||||
catch (JSONException e) {
|
||||
}
|
||||
if (uncited!=null) {
|
||||
int nCount = uncited.length();
|
||||
if (nCount>0) {
|
||||
ldp.append("\\nocite{");
|
||||
for (int nIndex=0; nIndex<nCount; nIndex++) {
|
||||
if (nIndex>0) {
|
||||
ldp.append(",");
|
||||
}
|
||||
String sURI = null;
|
||||
try { // Each item is an array containing a single string
|
||||
sURI = uncited.getJSONArray(nIndex).getString(0);
|
||||
}
|
||||
catch (JSONException e) {
|
||||
}
|
||||
if (sURI!=null) {
|
||||
int nSlash = sURI.lastIndexOf('/');
|
||||
if (nSlash>0) { ldp.append(sURI.substring(nSlash+1)); }
|
||||
else { ldp.append(sURI); }
|
||||
}
|
||||
}
|
||||
ldp.append("}").nl();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the BibTeX style and files given in the configuration
|
||||
ldp.append("\\bibliographystyle{").append(config.bibtexStyle()).append("}").nl()
|
||||
.append("\\bibliography{").append(config.zoteroBibtexFiles()).append("}").nl();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle a section as a JabRef bibliography
|
||||
private boolean handleJabRefBibliography(Element node, LaTeXDocumentPortion ldp, Context oc) {
|
||||
String sName = node.getAttribute(XMLString.TEXT_NAME);
|
||||
if (config.useBibtex() && config.jabrefBibtexFiles().length()>0 && sName.equals("JR_bib")) {
|
||||
// This section is a JabRef bibliography, and the user wishes to handle it as such
|
||||
// A JabRef bibliography is identified by the name JR_bib
|
||||
// Use the BibTeX style and files given in the configuration
|
||||
ldp.append("\\bibliographystyle{").append(config.bibtexStyle()).append("}").nl()
|
||||
.append("\\bibliography{").append(config.jabrefBibtexFiles()).append("}").nl();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** <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) {
|
||||
// Unlike headings, paragraphs and spans, text:display is not attached to the style:
|
||||
if (!bDisplayHiddenText && "none".equals(Misc.getAttribute(node,XMLString.TEXT_DISPLAY))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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(),false);
|
||||
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();
|
||||
}
|
||||
// Zotero or JabRef might have generated this section as a bibliograhy:
|
||||
if (!handleZoteroBibliography(node,sectionLdp,ic) && !handleJabRefBibliography(node,sectionLdp,ic)) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
1737
src/main/java/writer2latex/latex/StarMathConverter.java
Normal file
1737
src/main/java/writer2latex/latex/StarMathConverter.java
Normal file
File diff suppressed because it is too large
Load diff
50
src/main/java/writer2latex/latex/StyleConverter.java
Normal file
50
src/main/java/writer2latex/latex/StyleConverter.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* StyleConverter.java
|
||||
*
|
||||
* Copyright: 2002-2008 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
491
src/main/java/writer2latex/latex/TableConverter.java
Normal file
491
src/main/java/writer2latex/latex/TableConverter.java
Normal file
|
@ -0,0 +1,491 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* TableConverter.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2014-09-03)
|
||||
*
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
enum RowType {
|
||||
FIRST_HEAD, HEAD, BODY, FOOT, LAST_FOOT;
|
||||
}
|
||||
|
||||
/** <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 = <string></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 RowType[] rowTypes;
|
||||
|
||||
// Return the paragraph style of the first paragraph/heading within this block text
|
||||
private String getFirstParStyle(Element node) {
|
||||
Node child = node.getFirstChild();
|
||||
while (child!=null) {
|
||||
if (Misc.isElement(child, XMLString.TEXT_P) || Misc.isElement(child, XMLString.TEXT_H)) {
|
||||
String sStyleName = Misc.getAttribute(child, XMLString.TEXT_STYLE_NAME);
|
||||
if (sStyleName!=null) {
|
||||
StyleWithProperties style = ofr.getParStyle(sStyleName);
|
||||
if (style!=null) {
|
||||
if (style.isAutomatic()) {
|
||||
sStyleName = style.getParentName();
|
||||
}
|
||||
return ofr.getParStyles().getDisplayName(sStyleName);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else if (OfficeReader.isTextElement(child)) {
|
||||
return getFirstParStyle((Element)child);
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean hasRowType(RowType test) {
|
||||
for (RowType type : rowTypes) {
|
||||
if (type==test) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (palette.getMathCv().handleDisplayEquation(table,ldp)) { return; }
|
||||
|
||||
// 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,config.floatTables() && !ic.isInFrame() && !table.isSubTable());
|
||||
|
||||
// Identify the row types
|
||||
rowTypes = new RowType[table.getRowCount()];
|
||||
for (int nRow=0; nRow<table.getRowCount(); nRow++) {
|
||||
// First collect the row type as defined in the document
|
||||
if (nRow<table.getFirstBodyRow()) {
|
||||
rowTypes[nRow] = RowType.HEAD;
|
||||
}
|
||||
else {
|
||||
rowTypes[nRow] = RowType.BODY;
|
||||
}
|
||||
if (formatter.isLongtable() || formatter.isSupertabular()) {
|
||||
// Then override with user defined row types where applicable
|
||||
// (but only for multipage tables)
|
||||
// The row type is determined from the first paragraph in the first cell
|
||||
String sStyleName = getFirstParStyle(table.getCell(nRow, 0));
|
||||
if (sStyleName!=null) {
|
||||
if (sStyleName.equals(config.getTableFirstHeadStyle())) {
|
||||
rowTypes[nRow] = RowType.FIRST_HEAD;
|
||||
}
|
||||
else if (sStyleName.equals(config.getTableHeadStyle())) {
|
||||
rowTypes[nRow] = RowType.HEAD;
|
||||
}
|
||||
else if (sStyleName.equals(config.getTableFootStyle())) {
|
||||
rowTypes[nRow] = RowType.FOOT;
|
||||
}
|
||||
else if (sStyleName.equals(config.getTableLastFootStyle())) {
|
||||
rowTypes[nRow] = RowType.LAST_FOOT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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("\\tablefirsthead{");
|
||||
if (hasRowType(RowType.FIRST_HEAD)) {
|
||||
handleRows(ldp,oc,RowType.FIRST_HEAD,true,false);
|
||||
}
|
||||
else {
|
||||
handleRows(ldp,oc,RowType.HEAD,true,false);
|
||||
}
|
||||
ldp.append("}\n");
|
||||
ldp.append("\\tablehead{");
|
||||
handleRows(ldp,oc,RowType.HEAD,true,false);
|
||||
ldp.append("}\n");
|
||||
|
||||
// Table foot
|
||||
ldp.append("\\tabletail{");
|
||||
handleRows(ldp,oc,RowType.FOOT,true,true);
|
||||
ldp.append("}\n");
|
||||
ldp.append("\\tablelasttail{");
|
||||
if (hasRowType(RowType.LAST_FOOT)) {
|
||||
handleRows(ldp,oc,RowType.LAST_FOOT,true,true);
|
||||
}
|
||||
else {
|
||||
handleRows(ldp,oc,RowType.FOOT,true,true);
|
||||
}
|
||||
ldp.append("}\n");
|
||||
|
||||
// The table body
|
||||
handleHyperTarget(ldp);
|
||||
ldp.append(baTable.getBefore()).nl();
|
||||
handleRows(ldp,oc,RowType.BODY,true,!hasRowType(RowType.FOOT) && !hasRowType(RowType.LAST_FOOT));
|
||||
ldp.nl().append(baTable.getAfter()).nl();
|
||||
|
||||
ldp.append(baTableAlign.getAfter());
|
||||
}
|
||||
|
||||
private void handleLongtable(LaTeXDocumentPortion ldp, Context oc) {
|
||||
handleHyperTarget(ldp);
|
||||
ldp.append(baTable.getBefore()).nl();
|
||||
|
||||
// First head
|
||||
if (caption!=null && bCaptionAbove) {
|
||||
// If there's a caption above, we must use \endfirsthead
|
||||
// and have to repeat the head if there's no first head
|
||||
handleCaption("\\caption",ldp,oc);
|
||||
ldp.append("\\\\").nl();
|
||||
if (hasRowType(RowType.FIRST_HEAD)) {
|
||||
handleRows(ldp,oc,RowType.FIRST_HEAD,true,true);
|
||||
}
|
||||
else {
|
||||
handleRows(ldp,oc,RowType.HEAD,true,true);
|
||||
}
|
||||
ldp.nl().append("\\endfirsthead").nl();
|
||||
}
|
||||
else if (hasRowType(RowType.FIRST_HEAD)) {
|
||||
// Otherwise we only need it if the table contains a first head
|
||||
handleRows(ldp,oc,RowType.FIRST_HEAD,true,true);
|
||||
ldp.nl().append("\\endfirsthead").nl();
|
||||
}
|
||||
|
||||
// Head
|
||||
handleRows(ldp,oc,RowType.HEAD,true,true);
|
||||
ldp.nl().append("\\endhead").nl();
|
||||
|
||||
// Foot
|
||||
handleRows(ldp,oc,RowType.FOOT,false,true);
|
||||
ldp.nl().append("\\endfoot").nl();
|
||||
|
||||
// Last foot
|
||||
if (caption!=null && !bCaptionAbove) {
|
||||
// If there's a caption below, we must use \endlastfoot
|
||||
// and have to repeat the foot if there's no last foot
|
||||
if (hasRowType(RowType.LAST_FOOT)) {
|
||||
handleRows(ldp,oc,RowType.LAST_FOOT,false,true);
|
||||
ldp.nl();
|
||||
}
|
||||
else if (hasRowType(RowType.FOOT)){
|
||||
handleRows(ldp,oc,RowType.FOOT,false,true);
|
||||
ldp.nl();
|
||||
}
|
||||
handleCaption("\\caption",ldp,oc);
|
||||
ldp.append("\\endlastfoot").nl();
|
||||
}
|
||||
else if (hasRowType(RowType.LAST_FOOT)) {
|
||||
// Otherwise we only need it if the table contains a last foot
|
||||
handleRows(ldp,oc,RowType.LAST_FOOT,false,true);
|
||||
ldp.nl().append("\\endlastfoot").nl();
|
||||
}
|
||||
|
||||
// Body
|
||||
handleRows(ldp,oc,RowType.BODY,!hasRowType(RowType.HEAD) && !hasRowType(RowType.FIRST_HEAD),true);
|
||||
|
||||
ldp.nl().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();
|
||||
handleRows(ldp,oc,RowType.HEAD,true,true);
|
||||
ldp.nl();
|
||||
handleRows(ldp,oc,RowType.BODY,!hasRowType(RowType.HEAD),true);
|
||||
ldp.append(baTable.getAfter()).nl();
|
||||
|
||||
// Caption below
|
||||
if (caption!=null && !bCaptionAbove) {
|
||||
handleCaption("\\caption",ldp,oc);
|
||||
}
|
||||
|
||||
ldp.nl().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();
|
||||
handleRows(ldp,oc,RowType.HEAD,true,true);
|
||||
ldp.nl();
|
||||
handleRows(ldp,oc,RowType.BODY,!hasRowType(RowType.HEAD),true);
|
||||
ldp.nl().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 handleRows(LaTeXDocumentPortion ldp, Context oc, RowType rowType, boolean bLineBefore, boolean bLineAfter) {
|
||||
int nRowCount = table.getRowCount();
|
||||
int nColCount = table.getColCount();
|
||||
boolean bFirst = true;
|
||||
boolean bProtect = false; // Do we need to protect '['?
|
||||
int nPreviousRow = -1;
|
||||
for (int nRow=0; nRow<nRowCount; nRow++) {
|
||||
if (rowTypes[nRow]==rowType) {
|
||||
// Add interrow material from previous row, if any
|
||||
if (nPreviousRow>-1) {
|
||||
String sInterRowMaterial = formatter.getInterrowMaterial(nPreviousRow+1);
|
||||
ldp.append(sInterRowMaterial).nl();
|
||||
if (sInterRowMaterial.length()>0) { bProtect=false; }
|
||||
}
|
||||
nPreviousRow = nRow;
|
||||
|
||||
// If it's the first row, add top interrow material
|
||||
if (bFirst && bLineBefore) {
|
||||
String sInter = formatter.getInterrowMaterial(nRow);
|
||||
if (sInter.length()>0) { ldp.append(sInter).nl(); }
|
||||
bFirst=false;
|
||||
}
|
||||
// Export columns in this row
|
||||
LaTeXDocumentPortion rowLdp = new LaTeXDocumentPortion(true);
|
||||
Context icRow = (Context) oc.clone();
|
||||
BeforeAfter baRow = new BeforeAfter();
|
||||
formatter.applyRowStyle(nRow,baRow,icRow);
|
||||
if (!baRow.isEmpty()) {
|
||||
rowLdp.append(baRow.getBefore());
|
||||
if (!formatter.isSimple()) { rowLdp.nl(); }
|
||||
}
|
||||
int nCol = 0;
|
||||
while (nCol<nColCount) {
|
||||
Element cell = (Element) table.getCell(nRow,nCol);
|
||||
if (cell!=null) {
|
||||
if (XMLString.TABLE_TABLE_CELL.equals(cell.getNodeName())) {
|
||||
Context icCell = (Context) icRow.clone();
|
||||
BeforeAfter baCell = new BeforeAfter();
|
||||
formatter.applyCellStyle(nRow,nCol,baCell,icCell);
|
||||
rowLdp.append(baCell.getBefore());
|
||||
if (nCol==nColCount-1) { icCell.setInLastTableColumn(true); }
|
||||
palette.getBlockCv().traverseBlockText(cell,rowLdp,icCell);
|
||||
rowLdp.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()) { rowLdp.append(" & "); }
|
||||
else { rowLdp.append(" &").nl(); }
|
||||
}
|
||||
nCol+=nColSpan;
|
||||
}
|
||||
else { // Non-existing cell, ignore (assuming it is a trailing cell)
|
||||
nCol++;
|
||||
}
|
||||
}
|
||||
rowLdp.append("\\\\");
|
||||
// We have to translate the row to a string to avoid extra newlines and to see the first characters
|
||||
String sRowLdp = rowLdp.toString();
|
||||
// Protect leading [
|
||||
if (bProtect && ((sRowLdp.length()>0 && sRowLdp.charAt(0)=='[') || sRowLdp.startsWith("\n["))) {
|
||||
ldp.append("{}");
|
||||
}
|
||||
ldp.append(sRowLdp);
|
||||
bProtect = true;
|
||||
}
|
||||
}
|
||||
// Add interrow material from last row, if required
|
||||
if (nPreviousRow>-1 && bLineAfter) {
|
||||
ldp.append(formatter.getInterrowMaterial(nPreviousRow+1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
473
src/main/java/writer2latex/latex/TableFormatter.java
Normal file
473
src/main/java/writer2latex/latex/TableFormatter.java
Normal file
|
@ -0,0 +1,473 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* TableFormatter.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-04-15)
|
||||
*
|
||||
*/
|
||||
|
||||
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 (cell!=null && 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);
|
||||
if (cell!=null) {
|
||||
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);
|
||||
}
|
||||
else { // Non-existing cell, treat as empty cell with no borders
|
||||
nCol++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
|
||||
}
|
||||
|
||||
/** 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>
|
||||
* @param ba the <code>BeforeAfter</code> to contain the table code
|
||||
* @param baAlign the <code>BeforeAfter</code> to contain the alignment code, if it's separate
|
||||
* @param bInFloat true if the table should be floating
|
||||
*/
|
||||
public void applyTableStyle(BeforeAfter ba, BeforeAfter baAlign, boolean bInFloat) {
|
||||
// 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()) {
|
||||
if (bInFloat & !bIsSupertabular) {
|
||||
// Inside a float we don't want the extra glue added by the flushleft/center/flushright environment
|
||||
switch (cAlign) {
|
||||
case 'c' : baAlign.add("\\centering\n", ""); break;
|
||||
case 'r' : baAlign.add("\\raggedleft\n", ""); break;
|
||||
case 'l' : baAlign.add("\\raggedright\n", "");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// But outside floats we do want it
|
||||
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{"+Calc.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
|
||||
StringBuilder buf = new StringBuilder();
|
||||
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 = Calc.add(sTotalColumnWidth,sColumnWidth[i]);
|
||||
}
|
||||
sTotalColumnWidth = Calc.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);
|
||||
|
||||
}
|
||||
|
||||
}
|
78
src/main/java/writer2latex/latex/config/clean.xml
Normal file
78
src/main/java/writer2latex/latex/config/clean.xml
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?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="display_hidden_text" 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>
|
||||
<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="} \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=" \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>
|
||||
|
8
src/main/java/writer2latex/latex/config/default.xml
Normal file
8
src/main/java/writer2latex/latex/config/default.xml
Normal 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/>
|
||||
|
44
src/main/java/writer2latex/latex/config/pdfprint.xml
Normal file
44
src/main/java/writer2latex/latex/config/pdfprint.xml
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?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="false" />
|
||||
<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="display_hidden_text" 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>
|
||||
|
51
src/main/java/writer2latex/latex/config/pdfscreen.xml
Normal file
51
src/main/java/writer2latex/latex/config/pdfscreen.xml
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?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="false" />
|
||||
<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="display_hidden_text" 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>
|
||||
|
||||
<!-- 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>
|
78
src/main/java/writer2latex/latex/config/ultraclean.xml
Normal file
78
src/main/java/writer2latex/latex/config/ultraclean.xml
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?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="display_hidden_text" 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>
|
||||
<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="} \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=" \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>
|
||||
|
773
src/main/java/writer2latex/latex/i18n/ClassicI18n.java
Normal file
773
src/main/java/writer2latex/latex/i18n/ClassicI18n.java
Normal file
|
@ -0,0 +1,773 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ClassicI18n.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-05-12)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex.i18n;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Stack;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
|
||||
import writer2latex.util.CSVList;
|
||||
import writer2latex.latex.LaTeXConfig;
|
||||
import writer2latex.latex.LaTeXDocumentPortion;
|
||||
import writer2latex.latex.ConverterPalette;
|
||||
import writer2latex.latex.util.BeforeAfter;
|
||||
import writer2latex.office.OfficeReader;
|
||||
import writer2latex.office.StyleWithProperties;
|
||||
import writer2latex.office.XMLString;
|
||||
|
||||
/** 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;
|
||||
}
|
||||
}
|
||||
|
||||
private static Hashtable<String,String> babelLanguages; // mappings iso->babel language
|
||||
|
||||
static {
|
||||
babelLanguages = new Hashtable<String,String>();
|
||||
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("nb", "norsk"); // latin1
|
||||
babelLanguages.put("nn", "nynorsk"); // 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?
|
||||
}
|
||||
|
||||
// End of static part of I18n!
|
||||
|
||||
// **** Global variables ****
|
||||
|
||||
// Unicode translation
|
||||
private Hashtable<String,UnicodeTable> tableSet; // all tables
|
||||
private UnicodeTable table; // currently active table (top of stack)
|
||||
private Stack<UnicodeTable> 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
|
||||
|
||||
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<String,UnicodeTable>();
|
||||
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<UnicodeTable>();
|
||||
tableStack.push(tableSet.get("root"));
|
||||
table = 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) {
|
||||
useInputenc(pack);
|
||||
useSymbolFonts(pack);
|
||||
useTextFonts(pack);
|
||||
useBabel(pack);
|
||||
}
|
||||
|
||||
private void useInputenc(LaTeXDocumentPortion ldp) {
|
||||
ldp.append("\\usepackage[")
|
||||
.append(writeInputenc(config.getInputencoding()))
|
||||
.append("]{inputenc}").nl();
|
||||
}
|
||||
|
||||
private void useBabel(LaTeXDocumentPortion ldp) {
|
||||
// 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<String> langiter = languages.iterator();
|
||||
while (langiter.hasNext()) {
|
||||
String sLang = langiter.next();
|
||||
if (!sLang.equals(sDefaultLanguage)) {
|
||||
if ("el".equals(sLang) && this.polytonicGreek()) {
|
||||
babelopt.addValue("polutonikogreek");
|
||||
}
|
||||
else {
|
||||
String sBabelLang = getBabelLanguage(sLang);
|
||||
if (sBabelLang!=null) {
|
||||
babelopt.addValue(sBabelLang);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The default language must be the last one
|
||||
if (sDefaultLanguage!=null) {
|
||||
if ("el".equals(sDefaultLanguage) && this.polytonicGreek()) {
|
||||
babelopt.addValue("polutonikogreek");
|
||||
}
|
||||
else {
|
||||
String sBabelLang = getBabelLanguage(sDefaultLanguage);
|
||||
if (sBabelLang!=null) {
|
||||
babelopt.addValue(sBabelLang);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!babelopt.isEmpty()) {
|
||||
ldp.append("\\usepackage[")
|
||||
.append(babelopt.toString())
|
||||
.append("]{babel}").nl();
|
||||
// For Polish we must undefine \lll which is later defined by ams
|
||||
if (languages.contains("pl")) {
|
||||
ldp.append("\\let\\lll\\undefined").nl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void useSymbolFonts(LaTeXDocumentPortion ldp) {
|
||||
if (config.useTipa()) {
|
||||
ldp.append("\\usepackage[noenc]{tipa}").nl()
|
||||
.append("\\usepackage{tipx}").nl();
|
||||
}
|
||||
|
||||
// Has to avoid some nameclashes
|
||||
if (config.useBbding()) {
|
||||
ldp.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();
|
||||
}
|
||||
|
||||
if (config.useIfsym()) {
|
||||
ldp.append("\\usepackage[geometry,weather,misc,clock]{ifsym}").nl();
|
||||
}
|
||||
|
||||
if (config.usePifont()) { ldp.append("\\usepackage{pifont}").nl(); }
|
||||
|
||||
if (config.useEurosym()) { ldp.append("\\usepackage{eurosym}").nl(); }
|
||||
|
||||
// Always use amsmath
|
||||
ldp.append("\\usepackage{amsmath}").nl();
|
||||
|
||||
// wasysym *must* be loaded between amsmath and amsfonts!
|
||||
if (config.useWasysym()) {
|
||||
ldp.append("\\usepackage{wasysym}").nl();
|
||||
}
|
||||
|
||||
// Always use amssymb, amsfonts, textcomp (always!)
|
||||
ldp.append("\\usepackage{amssymb,amsfonts,textcomp}").nl();
|
||||
}
|
||||
|
||||
private void useTextFonts(LaTeXDocumentPortion ldp) {
|
||||
// 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");
|
||||
ldp.append("\\usepackage[").append(fontencs.toString())
|
||||
.append("]{fontenc}").nl();
|
||||
|
||||
// use font package(s)
|
||||
useFontPackages(ldp);
|
||||
}
|
||||
|
||||
private void useFontPackages(LaTeXDocumentPortion ldp) {
|
||||
String sFont = config.getFont();
|
||||
// Sources:
|
||||
// A Survey of Free Math Fonts for TeX and LaTeX, Stephen G. Hartke 2006
|
||||
if ("cmbright".equals(sFont)) { // Computer Modern Bright designed by Walter A. Schmidt
|
||||
ldp.append("\\usepackage{cmbright}").nl();
|
||||
}
|
||||
else if ("ccfonts".equals(sFont)) { // Concrete designed by Donald E. Knuth
|
||||
ldp.append("\\usepackage{ccfonts}").nl();
|
||||
}
|
||||
else if ("ccfonts-euler".equals(sFont)) { // Concrete with Euler math fonts
|
||||
ldp.append("\\usepackage{ccfonts,eulervm}").nl();
|
||||
}
|
||||
else if ("iwona".equals(sFont)) { // Iwona
|
||||
ldp.append("\\usepackage[math]{iwona}").nl();
|
||||
}
|
||||
else if ("kurier".equals(sFont)) { // Kurier
|
||||
ldp.append("\\usepackage[math]{kurier}").nl();
|
||||
}
|
||||
else if ("anttor".equals(sFont)) { // Antykwa Torunska
|
||||
ldp.append("\\usepackage[math]{anttor}").nl();
|
||||
}
|
||||
else if ("kmath-kerkis".equals(sFont)) { // Kerkis
|
||||
ldp.append("\\usepackage{kmath,kerkis}");
|
||||
}
|
||||
else if ("fouriernc".equals(sFont)) { // New Century Schoolbook + Fourier
|
||||
ldp.append("\\usepackage{fouriernc}");
|
||||
}
|
||||
else if ("pxfonts".equals(sFont)) { // Palatino + pxfonts math
|
||||
ldp.append("\\usepackage{pxfonts}");
|
||||
}
|
||||
else if ("mathpazo".equals(sFont)) { // Palatino + Pazo math
|
||||
ldp.append("\\usepackage{mathpazo}");
|
||||
}
|
||||
else if ("mathpple".equals(sFont)) { // Palatino + Euler
|
||||
ldp.append("\\usepackage{mathpple}");
|
||||
}
|
||||
else if ("txfonts".equals(sFont)) { // Times + txfonts math
|
||||
ldp.append("\\usepackage[varg]{txfonts}");
|
||||
}
|
||||
else if ("mathptmx".equals(sFont)) { // Times + Symbol
|
||||
ldp.append("\\usepackage{mathptmx}");
|
||||
}
|
||||
else if ("arev".equals(sFont)) { // Arev Sans + Arev math
|
||||
ldp.append("\\usepackage{arev}");
|
||||
}
|
||||
else if ("charter-mathdesign".equals(sFont)) { // Bitstream Charter + Math Design
|
||||
ldp.append("\\usepackage[charter]{mathdesign}");
|
||||
}
|
||||
else if ("utopia-mathdesign".equals(sFont)) { // Utopia + Math Design
|
||||
ldp.append("\\usepackage[utopia]{mathdesign}");
|
||||
}
|
||||
else if ("fourier".equals(sFont)) { // Utopia + Fourier
|
||||
ldp.append("\\usepackage{fourier}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Apply a language language
|
||||
* @param style the OOo style to read attributes from
|
||||
* @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 = tableSet.get(sName);
|
||||
if (table==null) { table = tableSet.get("root"); }
|
||||
}
|
||||
tableStack.push(table);
|
||||
}
|
||||
|
||||
/** Pop a font from the font stack
|
||||
*/
|
||||
public void popSpecialTable() {
|
||||
tableStack.pop();
|
||||
table = 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); }
|
||||
StringBuilder buf=new StringBuilder();
|
||||
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, StringBuilder buf, int nFontenc) {
|
||||
int nCurFontenc = nFontenc;
|
||||
ucparser.reset(table,s,nStart,nEnd);
|
||||
boolean bIsFirst = true; // Protect all dangerous characters at the start
|
||||
char cProtect = '\u0000'; // Current character to protect
|
||||
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));
|
||||
cProtect = '\u0000';
|
||||
}
|
||||
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 (-, ', `)
|
||||
char cProtectThis = table.getProtectChar(c);
|
||||
if (cProtectThis!='\u0000' && (cProtectThis==cProtect || bIsFirst)) {
|
||||
buf.append("{}");
|
||||
}
|
||||
cProtect = cProtectThis;
|
||||
|
||||
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
|
||||
|
||||
cProtect = table.getProtectChar(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));
|
||||
}
|
||||
|
||||
bIsFirst = false;
|
||||
}
|
||||
|
||||
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 babelLanguages.get(sLang);
|
||||
}
|
||||
else {
|
||||
return null; // Unknown language
|
||||
}
|
||||
}
|
||||
|
||||
// **** 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;
|
||||
}
|
||||
|
||||
}
|
142
src/main/java/writer2latex/latex/i18n/I18n.java
Normal file
142
src/main/java/writer2latex/latex/i18n/I18n.java
Normal file
|
@ -0,0 +1,142 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* I18n.java
|
||||
*
|
||||
* Copyright: 2002-2011 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2011-05-09)
|
||||
*
|
||||
*/
|
||||
|
||||
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 ****
|
||||
|
||||
// The office reader
|
||||
protected OfficeReader ofr;
|
||||
|
||||
// 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 sDefaultCTLLanguage; // The default CTL ISO language to use
|
||||
protected String sDefaultCTLCountry; // The default CTL ISO country to use
|
||||
protected String sDefaultLanguage; // The default LCG ISO language to use
|
||||
protected String sDefaultCountry; // The default LCG ISO country to use
|
||||
protected HashSet<String> languages = new HashSet<String>(); // All LCG 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
|
||||
this.ofr = ofr;
|
||||
|
||||
// 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);
|
||||
sDefaultCountry = style.getProperty(XMLString.FO_COUNTRY);
|
||||
sDefaultCTLLanguage = style.getProperty(XMLString.STYLE_LANGUAGE_COMPLEX);
|
||||
sDefaultCTLCountry = style.getProperty(XMLString.STYLE_COUNTRY_COMPLEX);
|
||||
}
|
||||
}
|
||||
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 attributes from
|
||||
* @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);
|
||||
|
||||
/** Get the default language (either the document language or the most used language)
|
||||
*
|
||||
* @return the default language
|
||||
*/
|
||||
public String getDefaultLanguage() {
|
||||
return sDefaultLanguage;
|
||||
}
|
||||
|
||||
/** Get the default country
|
||||
*
|
||||
* @return the default country
|
||||
*/
|
||||
public String getDefaultCountry() {
|
||||
return sDefaultCountry;
|
||||
}
|
||||
}
|
16
src/main/java/writer2latex/latex/i18n/Package.html
Normal file
16
src/main/java/writer2latex/latex/i18n/Package.html
Normal 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>
|
216
src/main/java/writer2latex/latex/i18n/Polyglossia.java
Normal file
216
src/main/java/writer2latex/latex/i18n/Polyglossia.java
Normal file
|
@ -0,0 +1,216 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* XeTeXI18n.java
|
||||
*
|
||||
* Copyright: 2002-2010 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2010-12-15)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex.i18n;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class Polyglossia {
|
||||
private static Map<String,String> languageMap;
|
||||
private static Map<String,String> variantMap;
|
||||
|
||||
static {
|
||||
languageMap = new HashMap<String,String>();
|
||||
languageMap.put("am", "amharic");
|
||||
languageMap.put("ar", "arabic");
|
||||
languageMap.put("ast", "asturian");
|
||||
languageMap.put("bg", "bulgarian");
|
||||
languageMap.put("bn", "bengali");
|
||||
languageMap.put("br", "breton");
|
||||
languageMap.put("ca", "catalan");
|
||||
languageMap.put("cop", "coptic");
|
||||
languageMap.put("cs", "czech");
|
||||
languageMap.put("cy", "welsh");
|
||||
languageMap.put("da", "danish");
|
||||
languageMap.put("de", "german");
|
||||
languageMap.put("dsb", "lsorbian");
|
||||
languageMap.put("dv", "divehi");
|
||||
languageMap.put("el", "greek");
|
||||
languageMap.put("en", "english");
|
||||
languageMap.put("eo", "esperanto");
|
||||
languageMap.put("es", "spanish");
|
||||
languageMap.put("et", "estonian");
|
||||
languageMap.put("eu", "basque");
|
||||
languageMap.put("fa", "farsi");
|
||||
languageMap.put("fi", "finnish");
|
||||
languageMap.put("fr", "french");
|
||||
languageMap.put("ga", "irish");
|
||||
languageMap.put("gd", "scottish");
|
||||
languageMap.put("gl", "galician");
|
||||
languageMap.put("grc", "greek");
|
||||
languageMap.put("he", "hebrew");
|
||||
languageMap.put("hi", "hindi");
|
||||
languageMap.put("hr", "croatian");
|
||||
languageMap.put("hsb", "usorbian");
|
||||
languageMap.put("hu", "magyar");
|
||||
languageMap.put("hy", "armenian");
|
||||
languageMap.put("id", "bahasai"); // Bahasa Indonesia
|
||||
languageMap.put("ie", "interlingua");
|
||||
languageMap.put("is", "icelandic");
|
||||
languageMap.put("it", "italian");
|
||||
languageMap.put("la", "latin");
|
||||
languageMap.put("lo", "lao");
|
||||
languageMap.put("lt", "lithuanian");
|
||||
languageMap.put("lv", "latvian");
|
||||
languageMap.put("ml", "malayalam");
|
||||
languageMap.put("mr", "marathi");
|
||||
languageMap.put("ms", "bahasam"); // Bahasa Melayu
|
||||
languageMap.put("nb", "norsk");
|
||||
languageMap.put("nl", "dutch");
|
||||
languageMap.put("nn", "nynorsk");
|
||||
languageMap.put("oc", "occitan");
|
||||
languageMap.put("pl", "polish");
|
||||
languageMap.put("pt", "portuges");
|
||||
languageMap.put("pt-BR", "brazilian");
|
||||
languageMap.put("ro", "romanian");
|
||||
languageMap.put("ru", "russian");
|
||||
languageMap.put("sa", "sanskrit");
|
||||
languageMap.put("sk", "slovak");
|
||||
languageMap.put("sl", "slovenian");
|
||||
languageMap.put("sq", "albanian");
|
||||
languageMap.put("sr", "serbian");
|
||||
languageMap.put("sv", "swedish");
|
||||
languageMap.put("syr", "syriac");
|
||||
languageMap.put("ta", "tamil");
|
||||
languageMap.put("te", "telugu");
|
||||
languageMap.put("th", "thai");
|
||||
languageMap.put("tk", "turkmen");
|
||||
languageMap.put("tr", "turkish");
|
||||
languageMap.put("uk", "ukrainian");
|
||||
languageMap.put("ur", "urdu");
|
||||
languageMap.put("vi", "vietnamese");
|
||||
// TODO: Which language is samin?? One guess could be sami with the n for north?
|
||||
//languageMap.put("??", "samin");
|
||||
|
||||
variantMap = new HashMap<String,String>();
|
||||
// English variants
|
||||
variantMap.put("en-US", "american");
|
||||
variantMap.put("en-GB", "british");
|
||||
variantMap.put("en-AU", "australian");
|
||||
variantMap.put("en-NZ", "newzealand");
|
||||
// Greek variants
|
||||
variantMap.put("el", "monotonic");
|
||||
variantMap.put("grc", "ancient"); // Supported in OOo since 3.2
|
||||
}
|
||||
|
||||
private static String getEntry(Map<String,String> map, String sLocale, String sLang) {
|
||||
if (map.containsKey(sLocale)) {
|
||||
return map.get(sLocale);
|
||||
}
|
||||
else if (map.containsKey(sLang)) {
|
||||
return map.get(sLang);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// This ended the static part of Polyglossia
|
||||
|
||||
private Set<String> languages = new HashSet<String>();
|
||||
private List<String> declarations = new ArrayList<String>();
|
||||
private Map<String,String[]> commands = new HashMap<String,String[]>();
|
||||
|
||||
/** <p>Get the declarations for the applied languages, in the form</p>
|
||||
* <p><code>\\usepackage{polyglossia}</code></p>
|
||||
* <p><code>\\setdefaultlanguage{language1}</code></p>
|
||||
* <p><code>\\setotherlanguage{language2}</code></p>
|
||||
* <p><code>\\setotherlanguage{language3}</code></p>
|
||||
* <p><code>...</code></p>
|
||||
*
|
||||
* @return the declarations as a string array
|
||||
*/
|
||||
public String[] getDeclarations() {
|
||||
return declarations.toArray(new String[declarations.size()]);
|
||||
}
|
||||
|
||||
/** <p>Add the given locale to the list of applied locales and return definitions for applying the
|
||||
* language to a text portion:</p>
|
||||
* <ul>
|
||||
* <li>A command of the forn <code>\textlanguage[variant=languagevariant]</code></li>
|
||||
* <li>An environment in the form
|
||||
* <code>\begin{language}[variant=languagevariant]</code>...<code>\end{language}</code></li>
|
||||
* </ul>
|
||||
* <p>The first applied language is the default language</p>
|
||||
*
|
||||
* @param sLang The language
|
||||
* @param sCountry The country (may be null)
|
||||
* @return a string array containing definitions to apply the language: Entry 0 contains a command
|
||||
* and Entry 1 and 2 contains an environment
|
||||
*/
|
||||
public String[] applyLanguage(String sLang, String sCountry) {
|
||||
String sLocale = sCountry!=null ? sLang+"-"+sCountry : sLang;
|
||||
if (commands.containsKey(sLocale)) {
|
||||
return commands.get(sLocale);
|
||||
}
|
||||
else {
|
||||
// Get the Polyglossia language and variant
|
||||
String sPolyLang = getEntry(languageMap,sLocale,sLang);
|
||||
if (sPolyLang!=null) {
|
||||
String sVariant = getEntry(variantMap,sLocale,sLang);
|
||||
if (sVariant!=null) {
|
||||
sVariant = "[variant="+sVariant+"]";
|
||||
}
|
||||
else {
|
||||
sVariant = "";
|
||||
}
|
||||
|
||||
if (languages.size()==0) {
|
||||
// First language, load Polyglossia and make the language default
|
||||
declarations.add("\\usepackage{polyglossia}");
|
||||
declarations.add("\\setdefaultlanguage"+sVariant+"{"+sPolyLang+"}");
|
||||
languages.add(sPolyLang);
|
||||
sVariant = ""; // Do not apply variant directly
|
||||
}
|
||||
else if (!languages.contains(sPolyLang)) {
|
||||
// New language, add to declarations
|
||||
declarations.add("\\setotherlanguage"+sVariant+"{"+sPolyLang+"}");
|
||||
languages.add(sPolyLang);
|
||||
sVariant = ""; // Do not apply variant directly
|
||||
}
|
||||
|
||||
String[] sCommand = new String[3];
|
||||
sCommand[0] = "\\text"+sPolyLang+sVariant;
|
||||
if ("arabic".equals(sPolyLang)) { sPolyLang="Arabic"; }
|
||||
sCommand[1] = "\\begin{"+sPolyLang+"}"+sVariant;
|
||||
sCommand[2] = "\\end{"+sPolyLang+"}";
|
||||
commands.put(sLocale, sCommand);
|
||||
return sCommand;
|
||||
}
|
||||
else {
|
||||
// Unknown language
|
||||
String[] sCommand = new String[3];
|
||||
sCommand[0] = "";
|
||||
sCommand[1] = "";
|
||||
sCommand[2] = "";
|
||||
commands.put(sLocale, sCommand);
|
||||
return sCommand;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
60
src/main/java/writer2latex/latex/i18n/ReplacementTrie.java
Normal file
60
src/main/java/writer2latex/latex/i18n/ReplacementTrie.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ReplacementTrie.java
|
||||
*
|
||||
* Copyright: 2002-2009 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2009-09-20)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex.i18n;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/** 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 Set<String> getInputStrings() {
|
||||
HashSet<String> strings = new HashSet<String>();
|
||||
collectStrings(strings,"");
|
||||
return strings;
|
||||
}
|
||||
|
||||
|
||||
}
|
139
src/main/java/writer2latex/latex/i18n/ReplacementTrieNode.java
Normal file
139
src/main/java/writer2latex/latex/i18n/ReplacementTrieNode.java
Normal file
|
@ -0,0 +1,139 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ReplacementTrieNode.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2014-09-24)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex.i18n;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/** 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);
|
||||
}
|
||||
}
|
||||
|
||||
protected void collectStrings(Set<String> strings, String sPrefix) {
|
||||
ReplacementTrieNode child = this.getFirstChild();
|
||||
while (child!=null) {
|
||||
if (child.getLaTeXCode()!=null) {
|
||||
strings.add(sPrefix+child.getLetter());
|
||||
}
|
||||
child.collectStrings(strings, sPrefix+child.getLetter());
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
51
src/main/java/writer2latex/latex/i18n/UnicodeCharacter.java
Normal file
51
src/main/java/writer2latex/latex/i18n/UnicodeCharacter.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* UnicodeCharacter.java
|
||||
*
|
||||
* Copyright: 2002-2010 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2010-05-11)
|
||||
*
|
||||
*/
|
||||
|
||||
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
|
||||
char cProtect; // This character is represented by this character which may produce unwanted ligatures (-, ', `)
|
||||
|
||||
protected Object clone() {
|
||||
UnicodeCharacter uc = new UnicodeCharacter();
|
||||
uc.nType = this.nType;
|
||||
uc.sMath = this.sMath;
|
||||
uc.sText = this.sText;
|
||||
uc.nFontencs = this.nFontencs;
|
||||
uc.cProtect = this.cProtect;
|
||||
return uc;
|
||||
}
|
||||
}
|
||||
|
42
src/main/java/writer2latex/latex/i18n/UnicodeRow.java
Normal file
42
src/main/java/writer2latex/latex/i18n/UnicodeRow.java
Normal file
|
@ -0,0 +1,42 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* UnicodeRow.java
|
||||
*
|
||||
* Copyright: 2002-2007 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* UnicodeStringParser.java
|
||||
*
|
||||
* Copyright: 2002-2007 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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; }
|
||||
|
||||
}
|
168
src/main/java/writer2latex/latex/i18n/UnicodeTable.java
Normal file
168
src/main/java/writer2latex/latex/i18n/UnicodeTable.java
Normal file
|
@ -0,0 +1,168 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* UnicodeTable.java
|
||||
*
|
||||
* Copyright: 2002-2010 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2010-05-11)
|
||||
*
|
||||
*/
|
||||
|
||||
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, char cProtect){
|
||||
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].cProtect=cProtect;
|
||||
}
|
||||
|
||||
// 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 ligature protect character for text character
|
||||
public char getProtectChar(char c) {
|
||||
UnicodeCharacter entry = getEntry(c);
|
||||
if (entry==null) return '\u0000';
|
||||
return entry.cProtect;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
172
src/main/java/writer2latex/latex/i18n/UnicodeTableHandler.java
Normal file
172
src/main/java/writer2latex/latex/i18n/UnicodeTableHandler.java
Normal file
|
@ -0,0 +1,172 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* UnicodeTableHandler.java
|
||||
*
|
||||
* Copyright: 2002-2010 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2010-05-11)
|
||||
*
|
||||
*/
|
||||
|
||||
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
|
||||
*/
|
||||
public class UnicodeTableHandler extends DefaultHandler{
|
||||
private Hashtable<String,UnicodeTable> 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;
|
||||
|
||||
/** Create a new <code>UnicodeTableHandler</code>
|
||||
*
|
||||
* @param tableSet the <code>Hashtable</code> to fill with tables read from the file
|
||||
* @param sSymbolSets string containing table names to read (separated by |)
|
||||
*/
|
||||
public UnicodeTableHandler(Hashtable<String,UnicodeTable> 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(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.getProtectChar(eqc));
|
||||
}
|
||||
}
|
||||
else {
|
||||
String sType=attributes.getValue("char-type");
|
||||
String sMath=attributes.getValue("math");
|
||||
String sText=attributes.getValue("text");
|
||||
String sProtect=attributes.getValue("protect");
|
||||
char cProtect = sProtect!=null && sProtect.length()>0 ? sProtect.charAt(0) : '\u0000';
|
||||
if (sType!=null) table.addCharType(c,sType);
|
||||
if (sMath!=null) table.addMathChar(c,sMath);
|
||||
if (sText!=null) table.addTextChar(c,sText,nFontencs,cProtect);
|
||||
}
|
||||
}
|
||||
}
|
||||
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,'\u0000');
|
||||
}
|
||||
}
|
||||
}
|
||||
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,'\u0000');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
231
src/main/java/writer2latex/latex/i18n/XeTeXI18n.java
Normal file
231
src/main/java/writer2latex/latex/i18n/XeTeXI18n.java
Normal file
|
@ -0,0 +1,231 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* XeTeXI18n.java
|
||||
*
|
||||
* Copyright: 2002-2015 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2015-06-30)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex.i18n;
|
||||
|
||||
import java.text.Bidi;
|
||||
|
||||
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 {
|
||||
|
||||
private Polyglossia polyglossia;
|
||||
private boolean bLTR;
|
||||
private boolean bUseXepersian;
|
||||
private String sLTRCommand=null;
|
||||
private String sRTLCommand=null;
|
||||
|
||||
/** 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);
|
||||
|
||||
// CTL support: Currently two CTL languages are supported
|
||||
// - hebrew (he_IL) using polyglossia.sty
|
||||
// - farsi (fa_IR) using xepersian.sty
|
||||
// TODO: Add a use_xepersian option, using polyglossia if false
|
||||
// For these languages currently only monolingual documents are supported
|
||||
// For LTR languages, multilingual documents are supported using polyglossia
|
||||
polyglossia = new Polyglossia();
|
||||
bLTR = !"fa".equals(sDefaultCTLLanguage) && !"he".equals(sDefaultCTLLanguage);
|
||||
if (bLTR) {
|
||||
polyglossia.applyLanguage(sDefaultLanguage, sDefaultCountry);
|
||||
}
|
||||
else {
|
||||
polyglossia.applyLanguage(sDefaultCTLLanguage, sDefaultCTLCountry);
|
||||
}
|
||||
// For farsi, we load xepersian.sty
|
||||
bUseXepersian = "fa".equals(sDefaultCTLLanguage);
|
||||
if (bUseXepersian) {
|
||||
sLTRCommand = "\\lr";
|
||||
sRTLCommand = "\\rl";
|
||||
}
|
||||
}
|
||||
|
||||
/** 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{amsmath,amssymb,amsfonts}").nl()
|
||||
.append("\\usepackage{fontspec}").nl()
|
||||
.append("\\usepackage{xunicode}").nl()
|
||||
.append("\\usepackage{xltxtra}").nl();
|
||||
// xepersian.sty and polyglossia (or rather bidi) should be loaded as the last package
|
||||
// We put it them the declarations part to achieve this
|
||||
if (!bUseXepersian) {
|
||||
String[] polyglossiaDeclarations = polyglossia.getDeclarations();
|
||||
for (String s: polyglossiaDeclarations) {
|
||||
decl.append(s).nl();
|
||||
}
|
||||
if (!bLTR) { // Use a default font set for hebrew
|
||||
decl.append("\\setmainfont[Script=Hebrew]{Frank Ruehl CLM}").nl();
|
||||
decl.append("\\setsansfont[Script=Hebrew]{Nachlieli CLM}").nl();
|
||||
decl.append("\\setmonofont[Script=Hebrew]{Miriam Mono CLM}").nl();
|
||||
}
|
||||
}
|
||||
else {
|
||||
decl.append("\\usepackage{xepersian}").nl();
|
||||
// Set the default font to the default CTL font defined in the document
|
||||
StyleWithProperties defaultStyle = ofr.getDefaultParStyle();
|
||||
if (defaultStyle!=null) {
|
||||
String sDefaultCTLFont = defaultStyle.getProperty(XMLString.STYLE_FONT_NAME_COMPLEX);
|
||||
if (sDefaultCTLFont!=null) {
|
||||
decl.append("\\settextfont{").append(sDefaultCTLFont).append("}").nl();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Apply a language
|
||||
* @param style the OOo style to read attributes from
|
||||
* @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 (bLTR && !bAlwaysUseDefaultLang && style!=null) {
|
||||
// TODO: Support CTL and CJK
|
||||
String sISOLang = style.getProperty(XMLString.FO_LANGUAGE,bInherit);
|
||||
String sISOCountry = style.getProperty(XMLString.FO_COUNTRY, bInherit);
|
||||
if (sISOLang!=null) {
|
||||
String[] sCommand = polyglossia.applyLanguage(sISOLang, sISOCountry);
|
||||
if (bDecl) {
|
||||
ba.add(sCommand[1],sCommand[2]);
|
||||
}
|
||||
else {
|
||||
ba.add(sCommand[0]+"{","}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 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){
|
||||
StringBuilder buf = new StringBuilder();
|
||||
int nLen = s.length();
|
||||
char c;
|
||||
if (bMathMode) {
|
||||
// No string replace or writing direction in math mode
|
||||
for (int i=0; i<nLen; i++) {
|
||||
convert(s.charAt(i),buf);
|
||||
}
|
||||
}
|
||||
else if (!bUseXepersian) {
|
||||
int i = 0;
|
||||
while (i<nLen) {
|
||||
ReplacementTrieNode node = stringReplace.get(s,i,nLen);
|
||||
if (node!=null) {
|
||||
buf.append(node.getLaTeXCode());
|
||||
i += node.getInputLength();
|
||||
}
|
||||
else {
|
||||
c = s.charAt(i++);
|
||||
convert (c,buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO: Add support for string replace
|
||||
Bidi bidi = new Bidi(s,Bidi.DIRECTION_RIGHT_TO_LEFT);
|
||||
int nCurrentLevel = bidi.getBaseLevel();
|
||||
int nNestingLevel = 0;
|
||||
for (int i=0; i<nLen; i++) {
|
||||
int nLevel = bidi.getLevelAt(i);
|
||||
if (nLevel>nCurrentLevel) {
|
||||
if (nLevel%2==0) { // even is LTR
|
||||
buf.append(sLTRCommand).append("{");
|
||||
}
|
||||
else { // odd is RTL
|
||||
buf.append(sRTLCommand).append("{");
|
||||
}
|
||||
nCurrentLevel=nLevel;
|
||||
nNestingLevel++;
|
||||
}
|
||||
else if (nLevel<nCurrentLevel) {
|
||||
buf.append("}");
|
||||
nCurrentLevel=nLevel;
|
||||
nNestingLevel--;
|
||||
}
|
||||
convert(s.charAt(i),buf);
|
||||
}
|
||||
while (nNestingLevel>0) {
|
||||
buf.append("}");
|
||||
nNestingLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private void convert(char c, StringBuilder buf) {
|
||||
switch (c) {
|
||||
case '#' : buf.append("\\#"); break; // Parameter
|
||||
case '$' : buf.append("\\$"); break; // Math shift
|
||||
case '%' : buf.append("\\%"); break; // Comment
|
||||
case '&' : buf.append("\\&"); break; // Alignment tab
|
||||
case '\\' : buf.append("\\textbackslash{}"); break; // Escape
|
||||
case '^' : buf.append("\\^{}"); break; // Superscript
|
||||
case '_' : buf.append("\\_"); break; // Subscript
|
||||
case '{' : buf.append("\\{"); break; // Begin group
|
||||
case '}' : buf.append("\\}"); break; // End group
|
||||
case '~' : buf.append("\\textasciitilde{}"); break; // Active (non-breaking space)
|
||||
case '\u00A0' : buf.append('~'); break; // Make non-breaking spaces visible
|
||||
default: buf.append(c);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
4021
src/main/java/writer2latex/latex/i18n/symbols.xml
Normal file
4021
src/main/java/writer2latex/latex/i18n/symbols.xml
Normal file
File diff suppressed because it is too large
Load diff
96
src/main/java/writer2latex/latex/util/BeforeAfter.java
Normal file
96
src/main/java/writer2latex/latex/util/BeforeAfter.java
Normal file
|
@ -0,0 +1,96 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* BeforeAfter.java
|
||||
*
|
||||
* Copyright: 2002-2011 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2011-03-29)
|
||||
*
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/** <p>Add data to the <code>BeforeAfter</code></p>
|
||||
* <p>The new data will be be added "outside", thus for example</p>
|
||||
* <ul><li><code>enclose("\textsf{","}");</code>
|
||||
* <li><code>enclose("\textit{","}");</code></ul>
|
||||
* <p>will create the pair <code>\textit{\textsf{</code>, <code>}}</code></p>
|
||||
*
|
||||
* @param sBefore1 LaTeX code to put before
|
||||
* @param sAfter1 LaTeX code to put after
|
||||
*/
|
||||
public void enclose(String sBefore1, String sAfter1) {
|
||||
sBefore=sBefore1+sBefore; sAfter+=sAfter1;
|
||||
}
|
||||
|
||||
/** <p>Add the content of another <code>BeforeAfter</code> to this <code>BeforeAfter</code></p>
|
||||
* <p>The new data will be be added "inside"</p>
|
||||
*
|
||||
* @param ba the code to add
|
||||
*/
|
||||
public void add(BeforeAfter ba) {
|
||||
add(ba.getBefore(), ba.getAfter());
|
||||
}
|
||||
|
||||
/** 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; }
|
||||
|
||||
}
|
325
src/main/java/writer2latex/latex/util/Context.java
Normal file
325
src/main/java/writer2latex/latex/util/Context.java
Normal file
|
@ -0,0 +1,325 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* Context.java
|
||||
*
|
||||
* Copyright: 2002-2011 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2011-01-24)
|
||||
*
|
||||
*/
|
||||
|
||||
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 list that continues numbering
|
||||
private boolean bInContinuedList = false;
|
||||
|
||||
// within a section command
|
||||
private boolean bInSection = false;
|
||||
|
||||
// within a caption
|
||||
private boolean bInCaption = false;
|
||||
|
||||
// within a Zotero/JabRef citation
|
||||
private boolean bInZoteroJabRefText = false;
|
||||
|
||||
// within a floating figure (figure environment)
|
||||
private boolean bInFigureFloat = false;
|
||||
|
||||
// within a floating table (table environment)
|
||||
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 setInContinuedList(boolean bInContinuedList) { this.bInContinuedList=bInContinuedList; }
|
||||
|
||||
public boolean isInContinuedList() { return this.bInContinuedList; }
|
||||
|
||||
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 setInZoteroJabRefText(boolean bInZoteroJabRefText) { this.bInZoteroJabRefText = bInZoteroJabRefText; }
|
||||
|
||||
public boolean isInZoteroJabRefText() { return bInZoteroJabRefText; }
|
||||
|
||||
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.setInContinuedList(bInContinuedList);
|
||||
newContext.setInSection(bInSection);
|
||||
newContext.setInCaption(bInCaption);
|
||||
newContext.setInZoteroJabRefText(bInZoteroJabRefText);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
68
src/main/java/writer2latex/latex/util/HeadingMap.java
Normal file
68
src/main/java/writer2latex/latex/util/HeadingMap.java
Normal file
|
@ -0,0 +1,68 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* HeadingMap.java
|
||||
*
|
||||
* Copyright: 2002-2006 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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]; }
|
||||
}
|
11
src/main/java/writer2latex/latex/util/Package.html
Normal file
11
src/main/java/writer2latex/latex/util/Package.html
Normal 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>
|
116
src/main/java/writer2latex/latex/util/StyleMap.java
Normal file
116
src/main/java/writer2latex/latex/util/StyleMap.java
Normal file
|
@ -0,0 +1,116 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* StyleMap.java
|
||||
*
|
||||
* Copyright: 2002-2011 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2011-03-30)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex.util;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Enumeration;
|
||||
|
||||
public class StyleMap {
|
||||
private Hashtable<String, StyleMapItem> items = new Hashtable<String, StyleMapItem>();
|
||||
|
||||
public void put(String sName, String sBefore, String sAfter, String sNext, boolean bLineBreak, int nBreakAfter, boolean bVerbatim) {
|
||||
StyleMapItem item = new StyleMapItem();
|
||||
item.sBefore = sBefore;
|
||||
item.sAfter = sAfter;
|
||||
item.sNext = ";"+sNext+";";
|
||||
item.bLineBreak = bLineBreak;
|
||||
item.nBreakAfter = nBreakAfter;
|
||||
item.bVerbatim = bVerbatim;
|
||||
items.put(sName,item);
|
||||
}
|
||||
|
||||
public void put(String sName, String sBefore, String sAfter, boolean bLineBreak, int nBreakAfter, boolean bVerbatim) {
|
||||
StyleMapItem item = new StyleMapItem();
|
||||
item.sBefore = sBefore;
|
||||
item.sAfter = sAfter;
|
||||
item.sNext = ";;";
|
||||
item.bLineBreak = bLineBreak;
|
||||
item.nBreakAfter = nBreakAfter;
|
||||
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.nBreakAfter = StyleMapItem.PAR;
|
||||
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.nBreakAfter = StyleMapItem.PAR;
|
||||
item.bVerbatim = false;
|
||||
items.put(sName,item);
|
||||
}
|
||||
|
||||
public boolean contains(String sName) {
|
||||
return sName!=null && items.containsKey(sName);
|
||||
}
|
||||
|
||||
public String getBefore(String sName) {
|
||||
return items.get(sName).sBefore;
|
||||
}
|
||||
|
||||
public String getAfter(String sName) {
|
||||
return items.get(sName).sAfter;
|
||||
}
|
||||
|
||||
public String getNext(String sName) {
|
||||
String sNext = items.get(sName).sNext;
|
||||
return sNext.substring(1,sNext.length()-1);
|
||||
}
|
||||
|
||||
public boolean isNext(String sName, String sNext) {
|
||||
String sNext1 = items.get(sName).sNext;
|
||||
return sNext1.indexOf(";"+sNext+";")>-1;
|
||||
}
|
||||
|
||||
public boolean getLineBreak(String sName) {
|
||||
return contains(sName) && items.get(sName).bLineBreak;
|
||||
}
|
||||
|
||||
public int getBreakAfter(String sName) {
|
||||
return contains(sName) ? items.get(sName).nBreakAfter : StyleMapItem.PAR;
|
||||
}
|
||||
|
||||
public boolean getVerbatim(String sName) {
|
||||
return contains(sName) && items.get(sName).bVerbatim;
|
||||
}
|
||||
|
||||
public Enumeration<String> getNames() {
|
||||
return items.keys();
|
||||
}
|
||||
|
||||
}
|
40
src/main/java/writer2latex/latex/util/StyleMapItem.java
Normal file
40
src/main/java/writer2latex/latex/util/StyleMapItem.java
Normal file
|
@ -0,0 +1,40 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* StyleMapItem.java
|
||||
*
|
||||
* Copyright: 2002-2011 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.2 (2011-03-30)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.latex.util;
|
||||
|
||||
// A struct to hold data about a style map
|
||||
public class StyleMapItem {
|
||||
public static final int NONE = 0;
|
||||
public static final int LINE = 1;
|
||||
public static final int PAR = 2;
|
||||
|
||||
String sBefore;
|
||||
String sAfter;
|
||||
String sNext;
|
||||
int nBreakAfter;
|
||||
boolean bLineBreak;
|
||||
boolean bVerbatim;
|
||||
}
|
131
src/main/java/writer2latex/office/BibMark.java
Normal file
131
src/main/java/writer2latex/office/BibMark.java
Normal file
|
@ -0,0 +1,131 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* BibMark.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.6 (2014-11-24)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.office;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
import writer2latex.util.Misc;
|
||||
|
||||
/** This class represents a single bibliography-mark in an ODF document
|
||||
*/
|
||||
public final class BibMark {
|
||||
|
||||
/** Entry types in an ODF bibliography marks. These are more or less modeled on BibTeX with
|
||||
* the following exceptions: organizations is organization in BibTeX, report_type is report in BibTeX,
|
||||
* the BibTeX fields crossref and key are missing in ODF, the ODF fields
|
||||
* url, custom1, custom2, custom3, custom4, custom5, isbn are not standard in BibTeX
|
||||
*
|
||||
*/
|
||||
public enum EntryType { address, annote, author, booktitle, chapter,
|
||||
edition, editor, howpublished, institution, journal, month,
|
||||
note, number, organizations, pages, publisher, school, series,
|
||||
title, report_type, volume, year,
|
||||
url, custom1, custom2, custom3, custom4, custom5, isbn
|
||||
}
|
||||
|
||||
// The data of the bibliography mark
|
||||
private String sIdentifier;
|
||||
private String sEntryType;
|
||||
private Map<EntryType,String> fields = new HashMap<EntryType,String>();
|
||||
|
||||
/** Create a new BibMark from scratch.
|
||||
* @param sIdentifier the unique identifier for this BibMark
|
||||
* @param sEntryType the type of entry such as book or article
|
||||
*/
|
||||
public BibMark(String sIdentifier, String sEntryType) {
|
||||
this.sIdentifier = sIdentifier;
|
||||
this.sEntryType = sEntryType;
|
||||
}
|
||||
|
||||
/** Create a new <code>BibMark</code> from a text:bibliography-mark node.
|
||||
*/
|
||||
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.put(EntryType.address, Misc.getAttribute(node,XMLString.TEXT_ADDRESS));
|
||||
fields.put(EntryType.annote, Misc.getAttribute(node,XMLString.TEXT_ANNOTE));
|
||||
fields.put(EntryType.author, Misc.getAttribute(node,XMLString.TEXT_AUTHOR));
|
||||
fields.put(EntryType.booktitle, Misc.getAttribute(node,XMLString.TEXT_BOOKTITLE));
|
||||
fields.put(EntryType.chapter, Misc.getAttribute(node,XMLString.TEXT_CHAPTER));
|
||||
fields.put(EntryType.edition, Misc.getAttribute(node,XMLString.TEXT_EDITION));
|
||||
fields.put(EntryType.editor, Misc.getAttribute(node,XMLString.TEXT_EDITOR));
|
||||
fields.put(EntryType.howpublished, Misc.getAttribute(node,XMLString.TEXT_HOWPUBLISHED));
|
||||
fields.put(EntryType.institution, Misc.getAttribute(node,XMLString.TEXT_INSTITUTION));
|
||||
fields.put(EntryType.journal, Misc.getAttribute(node,XMLString.TEXT_JOURNAL));
|
||||
fields.put(EntryType.month, Misc.getAttribute(node,XMLString.TEXT_MONTH));
|
||||
fields.put(EntryType.note, Misc.getAttribute(node,XMLString.TEXT_NOTE));
|
||||
fields.put(EntryType.number, Misc.getAttribute(node,XMLString.TEXT_NUMBER));
|
||||
fields.put(EntryType.organizations, Misc.getAttribute(node,XMLString.TEXT_ORGANIZATIONS));
|
||||
fields.put(EntryType.pages, Misc.getAttribute(node,XMLString.TEXT_PAGES));
|
||||
fields.put(EntryType.publisher, Misc.getAttribute(node,XMLString.TEXT_PUBLISHER));
|
||||
fields.put(EntryType.school, Misc.getAttribute(node,XMLString.TEXT_SCHOOL));
|
||||
fields.put(EntryType.series, Misc.getAttribute(node,XMLString.TEXT_SERIES));
|
||||
fields.put(EntryType.title, Misc.getAttribute(node,XMLString.TEXT_TITLE));
|
||||
fields.put(EntryType.report_type, Misc.getAttribute(node,XMLString.TEXT_REPORT_TYPE));
|
||||
fields.put(EntryType.volume, Misc.getAttribute(node,XMLString.TEXT_VOLUME));
|
||||
fields.put(EntryType.year, Misc.getAttribute(node,XMLString.TEXT_YEAR));
|
||||
fields.put(EntryType.url, Misc.getAttribute(node,XMLString.TEXT_URL));
|
||||
fields.put(EntryType.custom1, Misc.getAttribute(node,XMLString.TEXT_CUSTOM1));
|
||||
fields.put(EntryType.custom2, Misc.getAttribute(node,XMLString.TEXT_CUSTOM2));
|
||||
fields.put(EntryType.custom3, Misc.getAttribute(node,XMLString.TEXT_CUSTOM3));
|
||||
fields.put(EntryType.custom4, Misc.getAttribute(node,XMLString.TEXT_CUSTOM4));
|
||||
fields.put(EntryType.custom5, Misc.getAttribute(node,XMLString.TEXT_CUSTOM5));
|
||||
fields.put(EntryType.isbn, Misc.getAttribute(node,XMLString.TEXT_ISBN));
|
||||
}
|
||||
|
||||
/** Get the identifier.
|
||||
*
|
||||
* @return the unique identifier of this <code>BibMark</code>
|
||||
*/
|
||||
public String getIdentifier() { return sIdentifier; }
|
||||
|
||||
/** Get the entry type.
|
||||
*
|
||||
* @return the entry type of this <code>BibMark</code>
|
||||
*/
|
||||
public String getEntryType() { return sEntryType; }
|
||||
|
||||
/** Set a specific field.
|
||||
*
|
||||
* @param entryType the type of field to set
|
||||
* @param sValue the new value of the field
|
||||
*/
|
||||
public void setField(EntryType entryType,String sValue) { fields.put(entryType, sValue); }
|
||||
|
||||
/** Return a specific field.
|
||||
*
|
||||
* @param entryType the type of the field to get
|
||||
* @return the value of the field, or null if the field is not set
|
||||
*/
|
||||
public String getField(EntryType entryType) {
|
||||
return fields.containsKey(entryType) ? fields.get(entryType) : null;
|
||||
}
|
||||
}
|
39
src/main/java/writer2latex/office/CellView.java
Normal file
39
src/main/java/writer2latex/office/CellView.java
Normal file
|
@ -0,0 +1,39 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* CellView.java
|
||||
*
|
||||
* Copyright: 2002-2008 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.0 (2008-09-07)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.office;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* This class represent a cell in a table view
|
||||
*/
|
||||
public class CellView {
|
||||
public Element cell = null;
|
||||
public int nRowSpan = 1;
|
||||
public int nColSpan = 1;
|
||||
public int nOriginalRow = -1;
|
||||
public int nOriginalCol = -1;
|
||||
}
|
150
src/main/java/writer2latex/office/ControlReader.java
Normal file
150
src/main/java/writer2latex/office/ControlReader.java
Normal file
|
@ -0,0 +1,150 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* ControlReader.java
|
||||
*
|
||||
* Copyright: 2002-2008 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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<Node> items = new Vector<Node>(); // 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
65
src/main/java/writer2latex/office/EmbeddedBinaryObject.java
Normal file
65
src/main/java/writer2latex/office/EmbeddedBinaryObject.java
Normal file
|
@ -0,0 +1,65 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* EmbeddedBinaryObject.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2012-03-28)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.office;
|
||||
|
||||
import writer2latex.util.SimpleZipReader;
|
||||
|
||||
/**
|
||||
* This class represents an embedded object with a binary representation in an ODF package document
|
||||
*/
|
||||
public class EmbeddedBinaryObject extends EmbeddedObject {
|
||||
|
||||
/** The object's binary representation. */
|
||||
private byte[] blob = null;
|
||||
|
||||
/**
|
||||
* Package private constructor for use when reading an object from a
|
||||
* package ODF file
|
||||
*
|
||||
* @param sName The name of the object.
|
||||
* @param sType The MIME-type of the object.
|
||||
* @param doc The document containing the object.
|
||||
* @param source A <code>SimpleZipReader</code> containing the object
|
||||
*/
|
||||
protected EmbeddedBinaryObject(String sName, String sType, OfficeDocument doc, SimpleZipReader source) {
|
||||
super(sName,sType,doc);
|
||||
blob = source.getEntry(sName);
|
||||
}
|
||||
|
||||
/** Get the binary data for this object
|
||||
*
|
||||
* @return A <code>byte</code> array containing the object's data.
|
||||
*/
|
||||
public byte[] getBinaryData() {
|
||||
return blob;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
blob = null;
|
||||
}
|
||||
|
||||
}
|
71
src/main/java/writer2latex/office/EmbeddedObject.java
Normal file
71
src/main/java/writer2latex/office/EmbeddedObject.java
Normal file
|
@ -0,0 +1,71 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* EmbeddedObject.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2014-08-27)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.office;
|
||||
|
||||
/** This class represents and embedded object within an ODF package document
|
||||
*/
|
||||
public abstract class EmbeddedObject {
|
||||
private OfficeDocument doc;
|
||||
private String sName;
|
||||
private String sType;
|
||||
|
||||
/** Construct a new embedded object
|
||||
*
|
||||
* @param sName The name of the object.
|
||||
* @param sType The MIME-type of the object.
|
||||
* @param doc The document to which the object belongs.
|
||||
*/
|
||||
protected EmbeddedObject(String sName, String sType, OfficeDocument doc) {
|
||||
this.sName = sName;
|
||||
this.sType = sType;
|
||||
this.doc = doc;
|
||||
}
|
||||
|
||||
/** Get the name of the embedded object represented by this instance.
|
||||
* The name refers to the manifest.xml file
|
||||
*
|
||||
* @return The name of the object.
|
||||
*/
|
||||
public final String getName() {
|
||||
return sName;
|
||||
}
|
||||
|
||||
/** Get the MIME type of the embedded object represented by this instance.
|
||||
* The MIME type refers to the manifest.xml file
|
||||
*/
|
||||
public final String getType() {
|
||||
return sType;
|
||||
}
|
||||
|
||||
/** Dispose this <code>EmbeddedObject</code>. This implies that the content is nullified and the object
|
||||
* is removed from the collection in the <code>OfficeDocument</code>.
|
||||
*
|
||||
*/
|
||||
public void dispose() {
|
||||
doc.removeEmbeddedObject(sName);
|
||||
}
|
||||
|
||||
}
|
111
src/main/java/writer2latex/office/EmbeddedXMLObject.java
Normal file
111
src/main/java/writer2latex/office/EmbeddedXMLObject.java
Normal file
|
@ -0,0 +1,111 @@
|
|||
/************************************************************************
|
||||
*
|
||||
* EmbeddedXMLObject.java
|
||||
*
|
||||
* Copyright: 2002-2014 by Henrik Just
|
||||
*
|
||||
* This file is part of Writer2LaTeX.
|
||||
*
|
||||
* Writer2LaTeX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Writer2LaTeX 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Writer2LaTeX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Version 1.4 (2014-08-28)
|
||||
*
|
||||
*/
|
||||
|
||||
package writer2latex.office;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import writer2latex.util.SimpleZipReader;
|
||||
|
||||
/** This class represents those embedded objects in an ODF document that have an XML representation:
|
||||
* Formulas, charts, spreadsheets, text, drawings and presentations.
|
||||
* These object types are stored using a combination of content, settings and styles XML files.
|
||||
* The settings are application specific and ignored.
|
||||
*/
|
||||
public class EmbeddedXMLObject extends EmbeddedObject {
|
||||
|
||||
// Byte entries for the XML streams of this object
|
||||
private byte[] contentBytes = null;
|
||||
private byte[] stylesBytes = null;
|
||||
|
||||
// DOM trees representing the XML parts of this object
|
||||
protected Document contentDOM = null;
|
||||
protected Document stylesDOM = null;
|
||||
|
||||
/** Read an object from an ODF package document
|
||||
*
|
||||
* @param sName The name of the object.
|
||||
* @param sType The MIME-type of the object.
|
||||
* @param source A ZIP reader providing the contents of the package
|
||||
*/
|
||||
protected EmbeddedXMLObject(String sName, String sType, OfficeDocument doc, SimpleZipReader source) {
|
||||
super(sName, sType, doc);
|
||||
// Read the bytes, but defer parsing until required (at that point, the bytes are nullified)
|
||||
contentBytes = source.getEntry(sName+"/"+OfficeDocument.CONTENTXML);
|
||||
stylesBytes = source.getEntry(sName+"/"+OfficeDocument.STYLESXML);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content data for this embedded object.
|
||||
*
|
||||
* @return DOM representation of "content.xml"
|
||||
*
|
||||
* @throws SAXException If any parser error occurs
|
||||
* @throws IOException If any IO error occurs
|
||||
*/
|
||||
public Document getContentDOM() throws SAXException, IOException {
|
||||
if (contentDOM==null) {
|
||||
contentDOM=getDOM(contentBytes);
|
||||
contentBytes=null;
|
||||
}
|
||||
return contentDOM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the style data for this embedded object.
|
||||
*
|
||||
* @return DOM representation of "styles.xml"
|
||||
*
|
||||
* @throws SAXException If any parser error occurs
|
||||
* @throws IOException If any IO error occurs
|
||||
*/
|
||||
public Document getStylesDOM() throws SAXException, IOException {
|
||||
if (stylesDOM==null) {
|
||||
stylesDOM = getDOM(stylesBytes);
|
||||
stylesBytes=null;
|
||||
}
|
||||
return stylesDOM;
|
||||
}
|
||||
|
||||
private Document getDOM(byte[] data) throws SAXException, IOException {
|
||||
if (data!=null) {
|
||||
return OfficeDocument.parse(data);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
contentBytes = null;
|
||||
stylesBytes = null;
|
||||
contentDOM = null;
|
||||
stylesDOM = null;
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue