w2x: Recognize display equations + add MathJax support

git-svn-id: svn://svn.code.sf.net/p/writer2latex/code/trunk@148 f0f2a975-2e09-46c8-9428-3b39399b9f3c
This commit is contained in:
henrikjust 2012-04-08 09:42:52 +00:00
parent ac7f03f44f
commit e330b3dfab
15 changed files with 254 additions and 40 deletions

View file

@ -8,8 +8,8 @@
<project name="w2l" default="help" basedir=".">
<!-- set this property to the location of your SO/OOo installation -->
<property name="OFFICE_CLASSES" location="/usr/share/java/openoffice" />
<property name="URE_CLASSES" location="/usr/share/java/openoffice" />
<property name="OFFICE_CLASSES" location="/usr/share/libreoffice/program/classes" />
<property name="URE_CLASSES" location="/usr/share/java" />
<description>writer2latex - build file</description>

View file

@ -2,7 +2,15 @@ Changelog for Writer2LaTeX version 1.2 -> 1.4
---------- version 1.3.1 alpha ----------
[w2x] New boolean option use_svg (default false): If export format is HTML5 vector graphics are exported as inline SVG, if possible
[w2x] Display equations are now recognized by the same means as in the LaTeX export: A single equation in a paragraph with no
text content except whitespace and an optional sequence number in brackets is considered a display equation. In that case
it is exported with display="block"
[w2x] New boolean option use_mathjax (default false): If set to true and export format is HTML5, documents will load the MathJax
JavaScript library for rendering of formulas (otherwise the document will rely on native MathML support in the browser)
[w2x] New boolean option use_svg (default false): If set to true and export format is HTML5, vector graphics are exported as
inline SVG, if possible
[w2x] Added support for HTML5 as export type (the ConverterFactory understands the pseudo-MIME type text/html5).
The converter creates polyglot HTML5 documents, i.e. documents will be conforming to HTML5 as well as XML standards.

Binary file not shown.

View file

@ -93,8 +93,7 @@ XTypeProvider {
xComponentContext = xComponentContext1;
xMSF = null;
}
// Utility method:
String getFileName(String origName) {
@ -125,7 +124,6 @@ XTypeProvider {
public boolean exporter(com.sun.star.beans.PropertyValue[] aSourceData,
java.lang.String[] msUserData) throws com.sun.star.uno.RuntimeException{
sURL=null;
filterData = null;

View file

@ -125,7 +125,7 @@ public class GraphicConverterImpl2 implements GraphicConverter {
PropertyValue[] fileProps = new PropertyValue[3];
fileProps[0] = new PropertyValue();
fileProps[0].Name = "FilterName";
fileProps[0].Value = (String) importFilter.get(sSourceMime);
fileProps[0].Value = importFilter.get(sSourceMime);
fileProps[1] = new PropertyValue();
fileProps[1].Name = "InputStream";
fileProps[1].Value = new ByteArrayToXInputStreamAdapter(source);
@ -135,11 +135,12 @@ public class GraphicConverterImpl2 implements GraphicConverter {
XComponent xDocument = xComponentLoader.loadComponentFromURL(
"private:stream", "_blank", 0, fileProps);
// Get the first draw page as xDrawPage
XDrawPagesSupplier xDrawPagesSupplier = (XDrawPagesSupplier)
UnoRuntime.queryInterface(XDrawPagesSupplier.class, xDocument);
XDrawPages xDrawPages = xDrawPagesSupplier.getDrawPages();
Object drawPage = xDrawPages.getByIndex(0);
XDrawPage xDrawPage = (XDrawPage) UnoRuntime.queryInterface(
XDrawPage.class, drawPage);
@ -164,16 +165,16 @@ public class GraphicConverterImpl2 implements GraphicConverter {
xPageProps.setPropertyValue("BorderBottom", new Integer(0));
xPageProps.setPropertyValue("BorderLeft", new Integer(0));
xPageProps.setPropertyValue("BorderRight", new Integer(0));
// Export the draw document (xDocument)
refreshDocument(xDocument);
XOutputStreamToByteArrayAdapter outputStream = new XOutputStreamToByteArrayAdapter();
PropertyValue[] exportProps = new PropertyValue[3];
exportProps[0] = new PropertyValue();
exportProps[0].Name = "FilterName";
exportProps[0].Value = (String) exportFilter.get(sTargetMime);
exportProps[0].Value = exportFilter.get(sTargetMime);
exportProps[1] = new PropertyValue();
exportProps[1].Name = "OutputStream";
exportProps[1].Value = outputStream;
@ -189,7 +190,7 @@ public class GraphicConverterImpl2 implements GraphicConverter {
byte[] result = outputStream.getBuffer();
xDocument.dispose();
if (MIMETypes.EPS.equals(sTargetMime)) {
return epsCleaner.cleanEps(result);
}

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2012-04-03)
* Version 1.4 (2012-04-07)
*
*/
@ -33,7 +33,7 @@ public class ConverterFactory {
// Version information
private static final String VERSION = "1.3.1";
private static final String DATE = "2012-04-03";
private static final String DATE = "2012-04-07";
/** Return the Writer2LaTeX version in the form
* (major version).(minor version).(patch level)<br/>

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2012-03-23)
* Version 1.4 (2012-04-07)
*
*/
@ -31,15 +31,24 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.IOException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import writer2latex.api.GraphicConverter;
import writer2latex.api.Converter;
import writer2latex.api.ConverterResult;
import writer2latex.api.OutputFile;
import writer2latex.office.EmbeddedObject;
import writer2latex.office.EmbeddedXMLObject;
import writer2latex.office.ImageLoader;
import writer2latex.office.MIMETypes;
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>
*/
@ -57,6 +66,10 @@ public abstract class ConverterBase implements Converter {
// The output file(s)
protected String sTargetFileName;
protected ConverterResultImpl converterResult;
// Result of latest parsing of a display equation
private Element theEquation = null;
private Element theSequence = null;
// Constructor
public ConverterBase() {
@ -137,6 +150,139 @@ public abstract class ConverterBase implements Converter {
public EmbeddedObject getEmbeddedObject(String sHref) {
return odDoc.getEmbeddedObject(sHref);
}
/** Get the equation found by the last invocation of <code>parseDisplayEquation</code>
*
* @return the equation or null if no equation was found
*/
public Element getEquation() {
return theEquation;
}
/** Get the sequence number found by the last invocation of <code>parseDisplayEquation</code>
*
* @return the sequence number or null if no sequence number was found
*/
public Element getSequence() {
return theSequence;
}
/** 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
*/
public boolean parseDisplayEquation(Node node) {
theEquation = null;
theSequence = null;
return doParseDisplayEquation(node);
}
private boolean doParseDisplayEquation(Node node) {
Node child = node.getFirstChild();
while (child!=null) {
Node equation = getFormula(child);
if (equation!=null) {
if (theEquation==null) {
theEquation = (Element) equation;
}
else { // two or more equations -> not a display
return false;
}
}
else if (Misc.isElement(child)) {
String sName = child.getNodeName();
if (XMLString.TEXT_SEQUENCE.equals(sName)) {
if (theSequence==null) {
theSequence = (Element) child;
}
else { // two sequence numbers -> not a display
return false;
}
}
else if (XMLString.TEXT_SPAN.equals(sName)) {
if (!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;
}
// TODO: Extend OfficeReader to handle frames
private Node getFormula(Node node) {
if (Misc.isElement(node,XMLString.DRAW_FRAME)) {
node=Misc.getFirstChildElement(node);
}
String sHref = Misc.getAttribute(node,XMLString.XLINK_HREF);
if (sHref!=null) { // Embedded object in package or linked object
if (ofr.isInPackage(sHref)) { // Embedded object in package
if (sHref.startsWith("#")) { sHref=sHref.substring(1); }
if (sHref.startsWith("./")) { sHref=sHref.substring(2); }
EmbeddedObject object = 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;
}

View file

@ -26,6 +26,8 @@
package writer2latex.latex;
// TODO: Use parseDisplayEquation of ConverterBase
//import java.util.Hashtable;
import org.w3c.dom.Document;

View file

@ -172,7 +172,7 @@ public final class ImageLoader {
// Try vector format first
newBlob = gcv.convert(blob, sMIME, sTargetMIME=sDefaultVectorFormat);
}
else if (gcv.supportsConversion(sMIME,sDefaultFormat,false,false)) {
if (newBlob==null && gcv.supportsConversion(sMIME,sDefaultFormat,false,false)) {
// Then try bitmap format
newBlob = gcv.convert(blob,sMIME,sTargetMIME=sDefaultFormat);
}

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2012-04-03)
* Version 1.4 (2012-04-07)
*
*/
@ -349,6 +349,23 @@ public class Converter extends ConverterBase {
}
}
// Load MathJax
// TODO: Should we support different configurations of MathJax?
if (nType==XhtmlDocument.HTML5 && config.useMathJax()) {
for (int i=0; i<=nOutFileIndex; i++) {
if (outFiles.get(i).hasMath()) {
XhtmlDocument doc = outFiles.get(i);
Element head = doc.getHeadNode();
if (head!=null) {
Element script = doc.getContentDOM().createElement("script");
head.appendChild(script);
script.setAttribute("type", "text/javascript");
script.setAttribute("src", "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=MML_HTMLorMML");
}
}
}
}
// Create headers & footers (if nodes are available)
if (ofr.isSpreadsheet()) {
for (int i=0; i<=nOutFileIndex; i++) {
@ -486,6 +503,7 @@ public class Converter extends ConverterBase {
converterResult.addDocument(cssDoc);
}
}
}
private void addNavigationLink(Document dom, Node node, String s, int nIndex) {
@ -600,7 +618,7 @@ public class Converter extends ConverterBase {
public boolean outFileHasContent() {
return htmlDoc.getContentNode().hasChildNodes();
}
// Use another document. TODO: This is very ugly; clean it up!!!
public void changeOutFile(int nIndex) {
nOutFileIndex = nIndex;

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2012-04-03)
* Version 1.4 (2012-04-07)
*
*/
@ -399,7 +399,7 @@ public class DrawConverter extends ConverterHelper {
}
}
else { // flat xml format
Node formula = Misc.getChildByTagName(onode,XMLString.MATH); // Since OOo 3.2
Element formula = Misc.getChildByTagName(onode,XMLString.MATH); // Since OOo 3.2
if (formula==null) {
formula = Misc.getChildByTagName(onode,XMLString.MATH_MATH);
}

View file

@ -16,11 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2010 by Henrik Just
* Copyright: 2002-2012 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.2 (2010-03-15)
* Version 1.4 (2012-04-07)
*
*/
@ -70,8 +70,11 @@ public class MathConverter extends ConverterHelper {
* @param onode the math node
* @param hnode the xhtml node to which content should be added
*/
public void convert(Node image, Node onode, Node hnode) {
public void convert(Node image, Element onode, Node hnode) {
if (bSupportMathML) {
if (converter.getTextCv().isDisplayEquation()) {
onode.setAttribute("display", "block");
}
convertAsMathML(onode,hnode);
}
else {

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.2 (2012-03-07)
* Version 1.4 (2012-04-07)
*
*/
@ -140,6 +140,9 @@ public class TextConverter extends ConverterHelper {
// (labels for footnotes and endnotes)
// We put it here and insert it in the first paragraph/heading to come:
private Node asapNode = null;
// Are we within a display equation?
private boolean bDisplayEquation = false;
// When generating toc, a few things should be done differently
private boolean bInToc = false;
@ -357,6 +360,10 @@ public class TextConverter extends ConverterHelper {
p.appendChild(inline);
return inline;
}
public boolean isDisplayEquation() {
return bDisplayEquation;
}
////////////////////////////////////////////////////////////////////////
// BLOCK TEXT (returns current html node at end of block)
@ -813,7 +820,9 @@ public class TextConverter extends ConverterHelper {
insertListLabel(currentListStyle, nCurrentListLevel, "ItemNumber", null, sCurrentListLabel, par);
}
sCurrentListLabel = null;
bDisplayEquation=converter.parseDisplayEquation(onode);
traverseInlineText(onode,par);
bDisplayEquation=false;
}
else {
// An empty paragraph (this includes paragraphs that only contains

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2012-04-03)
* Version 1.4 (2012-04-07)
*
*/
@ -41,7 +41,7 @@ import writer2latex.util.Misc;
public class XhtmlConfig extends writer2latex.base.ConfigBase {
// Implement configuration methods
protected int getOptionCount() { return 57; }
protected int getOptionCount() { return 58; }
protected String getDefaultConfigPath() { return "/writer2latex/xhtml/config/"; }
// Override setOption: To be backwards compatible, we must accept options
@ -138,19 +138,20 @@ public class XhtmlConfig extends writer2latex.base.ConfigBase {
private static final int IMAGE_SPLIT = 41;
private static final int COVER_IMAGE = 42;
private static final int USE_SVG = 43;
private static final int CALC_SPLIT = 44;
private static final int DISPLAY_HIDDEN_SHEETS = 45;
private static final int DISPLAY_HIDDEN_ROWS_COLS = 46;
private static final int DISPLAY_FILTERED_ROWS_COLS = 47;
private static final int APPLY_PRINT_RANGES = 48;
private static final int USE_TITLE_AS_HEADING = 49;
private static final int USE_SHEET_NAMES_AS_HEADINGS = 50;
private static final int XSLT_PATH = 51;
private static final int SAVE_IMAGES_IN_SUBDIR = 52;
private static final int UPLINK = 53;
private static final int DIRECTORY_ICON = 54;
private static final int DOCUMENT_ICON = 55;
private static final int ZEN_HACK = 56; // temporary hack for ePub Zen Garden styles
private static final int USE_MATHJAX = 44;
private static final int CALC_SPLIT = 45;
private static final int DISPLAY_HIDDEN_SHEETS = 46;
private static final int DISPLAY_HIDDEN_ROWS_COLS = 47;
private static final int DISPLAY_FILTERED_ROWS_COLS = 48;
private static final int APPLY_PRINT_RANGES = 49;
private static final int USE_TITLE_AS_HEADING = 50;
private static final int USE_SHEET_NAMES_AS_HEADINGS = 51;
private static final int XSLT_PATH = 52;
private static final int SAVE_IMAGES_IN_SUBDIR = 53;
private static final int UPLINK = 54;
private static final int DIRECTORY_ICON = 55;
private static final int DOCUMENT_ICON = 56;
private static final int ZEN_HACK = 57; // temporary hack for ePub Zen Garden styles
protected ComplexOption xheading = addComplexOption("heading-map");
protected ComplexOption xpar = addComplexOption("paragraph-map");
@ -262,6 +263,7 @@ public class XhtmlConfig extends writer2latex.base.ConfigBase {
options[IMAGE_SPLIT] = new Option("image_split","none");
options[COVER_IMAGE] = new BooleanOption("cover_image","false");
options[USE_SVG] = new BooleanOption("use_svg","false");
options[USE_MATHJAX] = new BooleanOption("use_mathjax","false");
options[CALC_SPLIT] = new BooleanOption("calc_split","false");
options[DISPLAY_HIDDEN_SHEETS] = new BooleanOption("display_hidden_sheets", "false");
options[DISPLAY_HIDDEN_ROWS_COLS] = new BooleanOption("display_hidden_rows_cols","false");
@ -389,6 +391,7 @@ public class XhtmlConfig extends writer2latex.base.ConfigBase {
public String imageSplit() { return options[IMAGE_SPLIT].getString(); }
public boolean coverImage() { return ((BooleanOption) options[COVER_IMAGE]).getValue(); }
public boolean useSVG() { return ((BooleanOption) options[USE_SVG]).getValue(); }
public boolean useMathJax() { return ((BooleanOption) options[USE_MATHJAX]).getValue(); }
public boolean xhtmlCalcSplit() { return ((BooleanOption) options[CALC_SPLIT]).getValue(); }
public boolean xhtmlDisplayHiddenSheets() { return ((BooleanOption) options[DISPLAY_HIDDEN_SHEETS]).getValue(); }
public boolean displayHiddenRowsCols() { return ((BooleanOption) options[DISPLAY_HIDDEN_ROWS_COLS]).getValue(); }

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2012-04-01)
* Version 1.4 (2012-04-07)
*
*/
@ -44,6 +44,7 @@ import javax.xml.parsers.DocumentBuilder;
//import javax.xml.parsers.ParserConfigurationException;
import writer2latex.api.MIMETypes;
import writer2latex.office.XMLString;
import writer2latex.xmerge.DOMDocument;
import java.io.InputStream;
@ -351,6 +352,31 @@ public class XhtmlDocument extends DOMDocument {
super.setContentDOM(doc);
collectNodes();
}
/** Does this document contain any math nodes?
*
* @return true if so
*/
public boolean hasMath() {
return hasMath(getContentDOM().getDocumentElement());
}
private boolean hasMath(Element node) {
// Check this element
if (node.getTagName().equals(XMLString.MATH)) {
return true;
}
// Check children
Node child = node.getFirstChild();
while (child!=null) {
if (child.getNodeType()==Node.ELEMENT_NODE && hasMath((Element)child)) {
return true;
}
child = child.getNextSibling();
}
// Found nothing
return false;
}
public void read(InputStream is) throws IOException {
super.read(is);