SVG support + merge spans + bugfixes

git-svn-id: svn://svn.code.sf.net/p/writer2latex/code/trunk@170 f0f2a975-2e09-46c8-9428-3b39399b9f3c
This commit is contained in:
henrikjust 2014-09-06 19:19:17 +00:00
parent a336023983
commit a0b76b3729
18 changed files with 212 additions and 130 deletions

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2014-09-03)
* Version 1.4 (2014-09-06)
*
*/
@ -33,7 +33,7 @@ public class ConverterFactory {
// Version information
private static final String VERSION = "1.3.2";
private static final String DATE = "2014-09-03";
private static final String DATE = "2014-09-06";
/** 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 (2014-09-03)
* Version 1.4 (2014-09-05)
*
*/
@ -30,7 +30,6 @@ import java.io.OutputStream;
import java.io.IOException;
import writer2latex.api.OutputFile;
import writer2latex.util.Misc;
/** This class is used to represent a binary graphics document to be included in the converter result.
@ -40,37 +39,55 @@ import writer2latex.util.Misc;
public class BinaryGraphicsDocument implements OutputFile {
private String sFileName;
private String sFileExtension;
private String sMimeType;
private boolean bIsLinked = false;
private boolean bIsAcceptedFormat = false;
private boolean bAcceptedFormat;
private boolean bRecycled = false;
// Data for an embedded image
private byte[] blob = null;
private int nOff;
private int nLen;
private int nOff = 0;
private int nLen = 0;
// Data for a linked image
private String sURL = null;
/**Constructs a new graphics document.
* This new document does not contain any data. Document data must
* be added using the appropriate methods.
* 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 sName The name of the <code>GraphicsDocument</code>.
* @param sFileExtension the file extension
* @param sFileName The name or URL of the <code>GraphicsDocument</code>.
* @param sMimeType the MIME type of the document
*/
public BinaryGraphicsDocument(String sName, String sFileExtension, String sMimeType) {
this.sFileExtension = sFileExtension;
public BinaryGraphicsDocument(String sFileName, String sMimeType) {
this.sFileName = sFileName;
this.sMimeType = sMimeType;
sFileName = Misc.trimDocumentName(sName, sFileExtension);
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);
@ -87,30 +104,7 @@ public class BinaryGraphicsDocument implements OutputFile {
this.blob = data;
this.nOff = nOff;
this.nLen = nLen;
this.bIsAcceptedFormat = bIsAcceptedFormat;
this.bIsLinked = false;
this.sURL = null;
}
/** Set the URL of a linked image
*
* @param sURL the URL
*/
public void setURL(String sURL) {
this.blob = null;
this.nOff = 0;
this.nLen = 0;
this.bIsAcceptedFormat = false; // or rather don't know
this.bIsLinked = true;
this.sURL = sURL;
}
/** Get the URL of a linked image
*
* @return the URL or null if this is an embedded image
*/
public String getURL() {
return sURL;
this.bAcceptedFormat = bIsAcceptedFormat;
}
/** Does this <code>BinaryGraphicsDocument</code> represent a linked image?
@ -118,7 +112,7 @@ public class BinaryGraphicsDocument implements OutputFile {
* @return true if so
*/
public boolean isLinked() {
return bIsLinked;
return blob==null && !bRecycled;
}
/** Is this image in an acceptable format for the converter?
@ -126,9 +120,13 @@ public class BinaryGraphicsDocument implements OutputFile {
* @return true if so (always returns false for linked images)
*/
public boolean isAcceptedFormat() {
return bIsAcceptedFormat;
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;
}
@ -148,20 +146,12 @@ public class BinaryGraphicsDocument implements OutputFile {
}
}
/** Get the file extension
*
* @return the file extension
*/
public String getFileExtension() {
return sFileExtension;
}
/** Get the document with file extension.</p>
/** Get the document name or URL</p>
*
* @return The document with file extension.
* @return The document name or URL
*/
public String getFileName() {
return sFileName + sFileExtension;
return sFileName;
}
/** Get the MIME type of the document.

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2014-09-03)
* Version 1.4 (2014-09-05)
*
*/
@ -28,6 +28,7 @@ package writer2latex.base;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.HashSet;
import org.w3c.dom.Element;
@ -69,6 +70,10 @@ public final class ImageConverter {
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
*
@ -159,17 +164,48 @@ public final class ImageConverter {
* 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()) { // 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()) {
bgd = altBgd;
}
}
}
}
if (bgd==null || bgd.isLinked() || bgd.isRecycled()) {
// The file name was not used
nImageCount--;
}
else if (node.hasAttribute(XMLString.XLINK_HREF)) {
// This is an embedded image we meet for the first time.
// Recycle it on behalf of the original image node.
String sHref = node.getAttribute(XMLString.XLINK_HREF);
recycledImages.put(sHref, new BinaryGraphicsDocument(bgd));
}
return bgd;
}
private BinaryGraphicsDocument getImage(Element node, String sName) {
assert(XMLString.DRAW_IMAGE.equals(node.getTagName()));
// Image data
String sExt = null;
String sMIME = null;
String sExt = null;
byte[] blob = 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); }
@ -192,9 +228,9 @@ public final class ImageConverter {
else {
// This is a linked image
// TODO: Add option to download image from the URL?
String sFileName = ofr.fixRelativeLink(sHref);
BinaryGraphicsDocument bgd
= new BinaryGraphicsDocument(Misc.getFileName(sHref),Misc.getFileExtension(sHref),null);
bgd.setURL(ofr.fixRelativeLink(sHref));
= new BinaryGraphicsDocument(sFileName,null);
return bgd;
}
}
@ -225,21 +261,22 @@ public final class ImageConverter {
}
}
// We have an embedded image. Assign a name (without extension)
String sName = sSubDirName+sBaseFileName+formatter.format(++nImageCount);
// 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(sName,MIMETypes.EPS_EXT,MIMETypes.EPS);
= 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...
@ -264,10 +301,12 @@ public final class ImageConverter {
sExt = MIMETypes.getFileExtension(sMIME);
}
}
// Create the result
if (isAcceptedFormat(sMIME) || bAcceptOtherFormats) {
BinaryGraphicsDocument bgd = new BinaryGraphicsDocument(sName,sExt,sMIME);
String sFileName = sName+sExt;
BinaryGraphicsDocument bgd = new BinaryGraphicsDocument(sFileName,sMIME);
bgd.setData(blob,isAcceptedFormat(sMIME));
return bgd;
}
@ -276,4 +315,12 @@ public final class ImageConverter {
}
}
private Element getAlternativeImage(Element node) {
Node sibling = node.getNextSibling();
if (sibling!=null && Misc.isElement(sibling, XMLString.DRAW_IMAGE)) {
return (Element) sibling;
}
return null;
}
}

View file

@ -16,11 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2001-2010 by Henrik Just
* Copyright: 2001-2014 by Henrik Just
*
* All Rights Reserved.
*
* version 1.2 (2010-12-15)
* version 1.4 (2014-09-06)
*
*/
@ -41,12 +41,22 @@ public final class EPUBConverter extends Xhtml11Converter {
// Constructor
public EPUBConverter() {
super();
System.out.println("Creating epub converter");
}
@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,getXhtmlConfig()));
epubResult.setMetaData(xhtmlResult.getMetaData());

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2014-09-03)
* Version 1.4 (2014-09-05)
*
*/
@ -297,7 +297,7 @@ public class DrawConverter extends ConverterHelper {
BinaryGraphicsDocument bgd = palette.getImageCv().getImage(node);
if (bgd!=null) {
if (!bgd.isLinked()) { // embedded image
palette.addDocument(bgd);
if (!bgd.isRecycled()) { palette.addDocument(bgd); }
sFileName = bgd.getFileName();
String sMIME = bgd.getMIMEType();
bCommentOut = !(
@ -311,8 +311,8 @@ public class DrawConverter extends ConverterHelper {
(config.getBackend()==LaTeXConfig.DVIPS && MIMETypes.EPS.equals(sMIME)));
}
else { // linked image
sFileName = bgd.getURL();
String sExt = bgd.getFileExtension().toLowerCase();
sFileName = bgd.getFileName();
String sExt = Misc.getFileExtension(sFileName).toLowerCase();
// Accept only relative filenames and supported filetypes:
bCommentOut = sFileName.indexOf(":")>-1 || !(
config.getBackend()==LaTeXConfig.UNSPECIFIED ||

View file

@ -16,11 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2012 by Henrik Just
* Copyright: 2002-2014 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.2 (2012-03-05)
* Version 1.4 (2014-09-06)
*
*/
@ -94,7 +94,7 @@ public class ListStyleConverter extends StyleConverter {
if (config.formatting()==LaTeXConfig.CONVERT_BASIC ||
(config.formatting()>=LaTeXConfig.CONVERT_MOST && oc.isInTable())) {
if (oc.getListLevel()==1) {
if (!styleNames.containsName(getDisplayName(oc.getListStyleName()))) {
if (!listStyleLevelNames.containsKey(oc.getListStyleName())) {
createListStyleLabels(oc.getListStyleName());
}
ba.add("\\liststyle"+styleNames.getExportName(getDisplayName(oc.getListStyleName()))+"\n","");

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2014-08-13)
* Version 1.4 (2014-09-05)
*
*/
@ -69,6 +69,7 @@ public final class MIMETypes extends writer2latex.api.MIMETypes {
public static final String BIBTEX_EXT = ".bib";
public static final String XHTML_EXT = ".html";
public static final String XHTML_MATHML_EXT = ".xhtml";
public static final String HTML5_EXT = ".html";
public static final String PNG_EXT = ".png";
public static final String JPEG_EXT = ".jpg"; // this is the default in graphicx.sty
public static final String GIF_EXT = ".gif";
@ -90,8 +91,8 @@ public final class MIMETypes extends writer2latex.api.MIMETypes {
}
private static final boolean isSVG(byte[] blob) {
// Look for <svg within the first 250 bytes
int m = Math.min(blob.length, 250);
// Look for <svg within the first 500 bytes
int m = Math.min(blob.length, 500);
int n = SVG_SIG.length;
for (int j=0; j<m-n; j++) {
boolean bFound = true;
@ -141,6 +142,7 @@ public final class MIMETypes extends writer2latex.api.MIMETypes {
if (BIBTEX.equals(sMIME)) { return BIBTEX_EXT; }
if (XHTML.equals(sMIME)) { return XHTML_EXT; }
if (XHTML_MATHML.equals(sMIME)) { return XHTML_MATHML_EXT; }
if (HTML5.equals(sMIME)) { return XHTML_EXT; }
return "";
}

View file

@ -16,9 +16,9 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2012 by Henrik Just
* Copyright: 2002-2014 by Henrik Just
*
* Version 1.4 (2012-04-12)
* Version 1.4 (2014-09-05)
*
* All Rights Reserved.
*/
@ -134,11 +134,15 @@ public class TableReader {
if (bHasRelWidth) {
sRelColWidth[nCol] = (100.0F*nRelColWidth[nCol]/nColSum)+"%";
}
else {
else if (sTableWidth!=null){
// Calculate the relative column width from the absolute column widths
// This may not add up to exactly 100%, but we will live with that
sRelColWidth[nCol] = Misc.divide(sColWidth[nCol], sTableWidth, true);
}
else {
// The table has not width, distribute the columns evenly
sRelColWidth[nCol] = Double.toString(100.0/nCols)+"%";
}
}
// Now determine the actual number of rows and columns

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2014-08-27)
* Version 1.4 (2014-09-05)
*
*/
@ -262,7 +262,7 @@ public class Converter extends ConverterBase {
imageConverter.addAcceptedFormat(MIMETypes.JPEG);
imageConverter.addAcceptedFormat(MIMETypes.GIF);
if (nType==XhtmlDocument.HTML5 && config.useSVG()) { // HTML supports (inline) SVG as well
if (nType==XhtmlDocument.HTML5) { // HTML supports SVG as well
imageConverter.setDefaultVectorFormat(MIMETypes.SVG);
}

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2014-09-03)
* Version 1.4 (2014-09-05)
*
*/
@ -128,7 +128,7 @@ public class DrawConverter extends ConverterHelper {
nImageSize = config.imageSize();
sImageSplit = config.imageSplit();
bCoverImage = config.coverImage();
bUseSVG = config.useSVG();
bUseSVG = config.inlineSVG();
}
///////////////////////////////////////////////////////////////////////
@ -477,7 +477,7 @@ public class DrawConverter extends ConverterHelper {
}
else { // linked image
if (!converter.isOPS()) { // Cannot have linked images in EPUB, ignore the image
sFileName = bgd.getURL();
sFileName = bgd.getFileName();
}
}
}
@ -509,7 +509,7 @@ public class DrawConverter extends ConverterHelper {
}
else {
// In all other cases, create an img element
if (bgd!=null) { converter.addDocument(bgd); }
if (bgd!=null && !bgd.isLinked() && !bgd.isRecycled()) { converter.addDocument(bgd); }
Element image = converter.createElement("img");
String sName = Misc.getAttribute(getFrame(onode),XMLString.DRAW_NAME);
converter.addTarget(image,sName+"|graphic");

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2014-08-27)
* Version 1.4 (2014-09-06)
*
*/
@ -77,6 +77,8 @@ final class IndexData {
Element hnode; // a div node where the index should be added
}
/** This class handles text content
*/
public class TextConverter extends ConverterHelper {
// Data used to handle splitting over several files
@ -1511,11 +1513,14 @@ public class TextConverter extends ConverterHelper {
while (child!=null) {
if (child.getNodeType()==Node.ELEMENT_NODE) {
Element elm = (Element) child;
String sTag = elm.getTagName();
if (OfficeReader.isDrawElement(elm)) {
elm = getDrawCv().getRealDrawElement(elm);
String sAnchor = elm.getAttribute(XMLString.TEXT_ANCHOR_TYPE);
if (Misc.isElement(elm, XMLString.DRAW_FRAME)) {
elm = Misc.getFirstChildElement(elm);
}
if (elm!=null) {
String sAnchor = elm.getAttribute(XMLString.TEXT_ANCHOR_TYPE);
String sTag = elm.getTagName();
// Convert only floating frames; text-boxes must always float
if (!"as-char".equals(sAnchor)) {
getDrawCv().handleDrawElement(elm,(Element)hnodeBlock,
@ -2132,10 +2137,23 @@ public class TextConverter extends ConverterHelper {
getTextSc().applyStyle(sStyleName,info);
Element newNode = node;
if (info.hasAttributes() || !"span".equals(info.sTagName)) {
// We need to create a new element
// We (probably) need to create a new element
newNode = converter.createElement(info.sTagName);
node.appendChild(newNode);
applyStyle(info,newNode);
// But we may want to merge it with the previous element
Node prev = node.getLastChild();
if (prev!=null && Misc.isElement(prev, info.sTagName)) {
// The previous node is of the same type, compare attributes
Element prevNode = (Element) prev;
if (newNode.getAttribute("class").equals(prevNode.getAttribute("class")) &&
newNode.getAttribute("style").equals(prevNode.getAttribute("style")) &&
newNode.getAttribute("xml:lang").equals(prevNode.getAttribute("xml:lang")) &&
newNode.getAttribute("dir").equals(prevNode.getAttribute("dir"))) {
// Attribute style mapped elements are *not* merged, we will live with that
return applyAttributes(prevNode,ofr.getTextStyle(sStyleName));
}
}
node.appendChild(newNode);
}
return applyAttributes(newNode,ofr.getTextStyle(sStyleName));
}
@ -2149,5 +2167,3 @@ public class TextConverter extends ConverterHelper {
}

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2014-08-13)
* Version 1.4 (2014-09-05)
*
*/
@ -143,7 +143,7 @@ public class XhtmlConfig extends writer2latex.base.ConfigBase {
private static final int SPLIT_AFTER = 40;
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 INLINE_SVG = 43;
private static final int USE_MATHJAX = 44;
private static final int CALC_SPLIT = 45;
private static final int DISPLAY_HIDDEN_SHEETS = 46;
@ -274,7 +274,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[INLINE_SVG] = new BooleanOption("inline_svg","true");
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");
@ -401,7 +401,7 @@ public class XhtmlConfig extends writer2latex.base.ConfigBase {
public int splitAfter() { return ((IntegerOption) options[SPLIT_AFTER]).getValue(); }
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 inlineSVG() { return ((BooleanOption) options[INLINE_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(); }