/******************************************************************************* * Copyright (c) 2009, Adobe Systems Incorporated * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * · Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * · Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * · Neither the name of Adobe Systems Incorporated nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ package com.adobe.dp.office.conv; import java.io.IOException; import java.io.PrintWriter; import java.io.StringReader; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.Stack; import java.util.StringTokenizer; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import com.adobe.dp.css.CSSLength; import com.adobe.dp.css.CSSNumber; import com.adobe.dp.css.CSSParser; import com.adobe.dp.css.InlineRule; import com.adobe.dp.epub.io.BufferedDataSource; import com.adobe.dp.epub.io.ContainerSource; import com.adobe.dp.epub.io.DataSource; import com.adobe.dp.epub.io.StringDataSource; import com.adobe.dp.epub.opf.OPSResource; import com.adobe.dp.epub.opf.Publication; import com.adobe.dp.epub.opf.Resource; import com.adobe.dp.epub.opf.StyleResource; import com.adobe.dp.epub.ops.Element; import com.adobe.dp.epub.ops.HyperlinkElement; import com.adobe.dp.epub.ops.ImageElement; import com.adobe.dp.epub.ops.OPSDocument; import com.adobe.dp.epub.ops.SVGElement; import com.adobe.dp.epub.ops.SVGImageElement; import com.adobe.dp.epub.ops.XRef; import com.adobe.dp.office.drawing.PictureData; import com.adobe.dp.office.metafile.EMFParser; import com.adobe.dp.office.metafile.WMFParser; import com.adobe.dp.office.vml.VMLGroupElement; import com.adobe.dp.office.vml.VMLPathConverter; import com.adobe.dp.office.word.BRElement; import com.adobe.dp.office.word.BodyElement; import com.adobe.dp.office.word.ContainerElement; import com.adobe.dp.office.word.DrawingElement; import com.adobe.dp.office.word.FootnoteElement; import com.adobe.dp.office.word.FootnoteReferenceElement; import com.adobe.dp.office.word.LastRenderedPageBreakElement; import com.adobe.dp.office.word.NumberingLabel; import com.adobe.dp.office.word.ParagraphElement; import com.adobe.dp.office.word.ParagraphProperties; import com.adobe.dp.office.word.PictElement; import com.adobe.dp.office.word.RunElement; import com.adobe.dp.office.word.RunProperties; import com.adobe.dp.office.word.SmartTagElement; import com.adobe.dp.office.word.Style; import com.adobe.dp.office.word.TXBXContentElement; import com.adobe.dp.office.word.TabElement; import com.adobe.dp.office.word.TableCellElement; import com.adobe.dp.office.word.TableCellProperties; import com.adobe.dp.office.word.TableElement; import com.adobe.dp.office.word.TableProperties; import com.adobe.dp.office.word.TableRowElement; import com.adobe.dp.office.word.TextElement; import com.adobe.dp.office.word.WordDocument; class WordMLConverter { private StyleConverter styleConverter; private HashSet listElements = new HashSet(); private Publication epub; private OPSDocument chapter; private OPSResource resource; private WordDocument doc; private boolean hadSpace = false; private Hashtable footnoteMap; private Hashtable convResources = new Hashtable(); private ContainerSource wordResources; private Hashtable metadataNS = new Hashtable(); private static final String mediaFolder = "OPS/media/"; private boolean hasEpubMetadata; boolean includeWordMetadata = true; boolean chapterNameWasSet; boolean chapterSplitAllowed; private String nextPageName; boolean useWordPageBreaks; StringBuffer styleAcc = new StringBuffer(); StringBuffer injectAcc = new StringBuffer(); PrintWriter log; String nextResourceName; String nextResourceMediaType; Stack nesting = new Stack(); int lastNumId = -1; WordMLConverter(WordDocument doc, Publication epub, StyleConverter styleConverter, PrintWriter log) { this.log = log; this.doc = doc; this.epub = epub; this.styleConverter = styleConverter; this.metadataNS.put("DC", Publication.dcns); this.chapterSplitAllowed = true; } WordMLConverter(WordMLConverter parent, OPSResource chapterResource) { this.log = parent.log; this.resource = chapterResource; this.chapter = chapterResource.getDocument(); this.doc = parent.doc; this.epub = parent.epub; this.styleConverter = new StyleConverter(true); this.chapterSplitAllowed = false; } WordMLConverter(WordMLConverter parent, StyleConverter styleConverter) { this.log = parent.log; this.chapter = parent.chapter; this.doc = parent.doc; this.epub = parent.epub; this.styleConverter = styleConverter; this.chapterSplitAllowed = false; } static class NestingItem { NestingItem(Element opsElement, int listLevel) { this.opsElement = opsElement; this.listLevel = listLevel; } Element opsElement; int listLevel; CSSLength topMargin; } class WMFResourceWriter implements ResourceWriter { public StreamAndName createResource(String base, String suffix, boolean doNotCompress) throws IOException { String name = mediaFolder + "wmf-" + base + suffix; name = epub.makeUniqueResourceName(name); BufferedDataSource dataSource = new BufferedDataSource(); epub.createBitmapImageResource(name, "image/png", dataSource); return new StreamAndName(name.substring(mediaFolder.length()), dataSource.getOutputStream()); } } class XMLInjector extends DefaultHandler { Stack nesting = new Stack(); XMLInjector() { nesting.push(getCurrentOPSContainer()); } Element parent() { return (Element) nesting.lastElement(); } public void characters(char[] chars, int index, int len) throws SAXException { parent().add(new String(chars, index, len)); } public void endElement(String uri, String localName, String qName) throws SAXException { nesting.pop(); } public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException { Element e = null; if (uri == null || uri.equals("") || uri.equals(OPSDocument.xhtmlns)) { if (localName.equals("th") || localName.equals("td")) { String align = attrs.getValue("align"); String colspanStr = attrs.getValue("colspan"); int colspan = 1; if (colspanStr != null) { try { colspan = Integer.parseInt(colspanStr); } catch (Exception ex) { ex.printStackTrace(log); } } String rowspanStr = attrs.getValue("rowspan"); int rowspan = 1; if (rowspanStr != null) { try { rowspan = Integer.parseInt(rowspanStr); } catch (Exception ex) { ex.printStackTrace(log); } } e = chapter.createTableCellElement(localName, align, colspan, rowspan); } else if (localName.equals("img")) { ImageElement imageElement = chapter.createImageElement(localName); String src = attrs.getValue("src"); if (src != null) { Resource imageResource = epub.getResourceByName("OPS/" + src); imageElement.setImageResource(imageResource); } e = imageElement; } else { e = chapter.createElement(localName); } } else if (uri.equals(OPSDocument.svgns)) { SVGElement svg; if (localName.equals("image")) { SVGImageElement svgImage = chapter.createSVGImageElement("image"); String href = attrs.getValue(OPSDocument.xlinkns, "href"); if (href != null) { Resource imageResource = epub.getResourceByName("OPS/" + href); svgImage.setImageResource(imageResource); } svg = svgImage; } else svg = chapter.createSVGElement(localName); int count = attrs.getLength(); for (int i = 0; i < count; i++) { String attrNS = attrs.getURI(i); String attrName = attrs.getLocalName(i); if (attrNS.equals("")) { if (attrName.equals("id") || attrName.equals("style") || attrName.equals("class")) continue; } if (localName.equals("image") && attrNS.equals(OPSDocument.xlinkns) && attrName.equals("href")) continue; String attrValue = attrs.getValue(i); svg.setAttribute(attrNS, attrName, attrValue); } e = svg; } if (e == null) { // unknown container e = chapter.createElement("span"); } String id = attrs.getValue("id"); if (id != null) e.setId("id"); String className = attrs.getValue("class"); if (className != null) e.setClassName(className); String style = attrs.getValue("style"); if (style != null) { CSSParser parser = new CSSParser(); InlineRule parsedStyle = parser.readInlineStyle(style); e.setStyle(parsedStyle); } parent().add(e); nesting.push(e); } public InputSource resolveEntity(String publicId, String systemId) throws SAXException { throw new SAXException("External entities not allowed"); } } Element getCurrentOPSContainer() { return ((NestingItem) nesting.peek()).opsElement; } NestingItem getOPSContainer(ParagraphElement wordElement, boolean forceNew) { ParagraphElement wp = (ParagraphElement) wordElement; ParagraphProperties pp = wp.getParagraphProperties(); int level = -1; int numId = -1; NumberingLabel label = pp.getNumberingLabel(); if (label != null) { level = label.getLevel(); numId = label.getNumId(); } NestingItem item = (NestingItem) nesting.peek(); if (numId < 0 || (lastNumId >= 0 && lastNumId != numId)) { // end all lists while (true) { if (item.listLevel < 0) break; nesting.pop(); item = (NestingItem) nesting.peek(); } } if (numId <= 0 || !listElements.contains(wordElement)) { // possibly create nesting structure for non-list paragraphs boolean createDiv = true; if (item.opsElement.getElementName().equals("div")) { if (forceNew) { nesting.pop(); item = (NestingItem) nesting.peek(); } else { createDiv = false; } } if (createDiv) { Element e = chapter.createElement("div"); item.opsElement.add(e); NestingItem div = new NestingItem(e, -1); nesting.push(div); item = div; } } else { // create nesting structure for list numId if (item.opsElement.getElementName().equals("div")) { nesting.pop(); item = (NestingItem) nesting.peek(); } lastNumId = numId; while (level > item.listLevel) { if (item.opsElement.getElementName().equals("ul")) { Element e = chapter.createElement("li"); item.opsElement.add(e); e.setClassName("nested"); NestingItem li = new NestingItem(e, item.listLevel); nesting.push(li); item = li; } Element e = chapter.createElement("ul"); item.opsElement.add(e); NestingItem ul = new NestingItem(e, item.listLevel + 1); nesting.push(ul); item = ul; } while (level < item.listLevel || item.opsElement.getElementName().equals("li")) { nesting.pop(); item = (NestingItem) nesting.peek(); } } return item; } int pushOPSContainer(Element p) { int size = nesting.size(); nesting.push(new NestingItem(p, -1)); return size; } void restoreOPSContainer(int size) { nesting.setSize(size); } void useWordPageBreaks() { useWordPageBreaks = true; } void setFootnoteMap(Hashtable footnoteMap) { this.footnoteMap = footnoteMap; } Publication getPublication() { return epub; } private Resource getImageResource(PictureData data, String nameOverride, String mediaTypeOverride) { String src = data.getSrc(); if (src == null) return null; String epubName = (String) convResources.get(src); if (epubName == null || nameOverride != null) { DataSource dataSource = wordResources.getDataSource(src); int index = src.lastIndexOf('/'); String shortName = src.substring(index + 1); String mediaType; if (mediaTypeOverride != null) mediaType = mediaTypeOverride; else { mediaType = doc.getResourceMediaType(src); if (mediaType.equals("image/x-wmf")) { WMFResourceWriter dw = new WMFResourceWriter(); GDISVGSurface svg = new GDISVGSurface(dw); try { WMFParser parser = new WMFParser(dataSource.getInputStream(), svg); parser.readAll(); } catch (IOException e) { e.printStackTrace(log); return null; } dataSource = new StringDataSource(svg.getSVG()); mediaType = "image/svg+xml"; shortName = shortName + ".svg"; } else if (mediaType.equals("application/octet-stream") && src.endsWith(".emf")) { // don't support EMF yet if (false) { WMFResourceWriter dw = new WMFResourceWriter(); GDISVGSurface svg = new GDISVGSurface(dw); try { EMFParser parser = new EMFParser(dataSource.getInputStream(), svg); parser.readAll(); } catch (IOException e) { // e.printStackTrace(); e.printStackTrace(log); return null; } dataSource = new StringDataSource(svg.getSVG()); mediaType = "image/svg+xml"; shortName = shortName + ".svg"; } } } if (nameOverride != null) epubName = "OPS/" + nameOverride; else epubName = mediaFolder + shortName; if (dataSource == null) return null; epub.createBitmapImageResource(epubName, mediaType, dataSource); if (nameOverride != null) convResources.put(src, epubName); } return epub.getResourceByName(epubName); } private void resetSpaceProcessing() { hadSpace = false; } private void treatAsSpace() { hadSpace = true; } private String processSpaces(String s) { int len = s.length(); StringBuffer result = null; int last = 0; for (int i = 0; i < len; i++) { char c = s.charAt(i); if (c == ' ') { if (hadSpace) { if (result == null) result = new StringBuffer(); result.append(s.substring(last, i)); result.append('\u00A0'); // nbsp last = i + 1; } else { hadSpace = true; } } else { hadSpace = false; } } if (result != null) { result.append(s.substring(last)); return result.toString(); } return s; } void setImageWidth(Element img, String baseClassName, double widthPt, float emScale) { InlineRule rule = new InlineRule(); if (styleConverter.usingPX()) { rule.set("width", new CSSLength(widthPt, "px")); } else { double defaultFontSize = styleConverter.defaultFontSize; double widthEM = widthPt / (emScale * defaultFontSize / 2); rule.set("width", new CSSLength(widthEM, "em")); } rule.set("max-width", new CSSLength(100, "%")); img.setStyle(rule); img.setClassName(baseClassName); } void parseAndInjectXML(StringBuffer xml) { try { XMLInjector injector = new XMLInjector(); SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(true); SAXParser parser = factory.newSAXParser(); XMLReader reader = parser.getXMLReader(); reader.setContentHandler(injector); reader.setEntityResolver(injector); InputSource source = new InputSource(new StringReader(xml.toString())); source.setSystemId(""); reader.parse(source); } catch (Exception e) { // e.printStackTrace(); e.printStackTrace(log); } } String getMagicStyleName(ParagraphProperties pp) { if (pp == null) return null; Style ps = pp.getParagraphStyle(); if (ps == null) return null; String name = ps.getName(); if (name == null) return null; String ln = name.toLowerCase(); if (ln.startsWith("*epub")) return ln.substring(5); return null; } String getMagicStyleName(RunProperties rp) { if (rp == null) return null; Style rs = rp.getRunStyle(); if (rs == null) return null; String name = rs.getName(); if (name == null) return null; String ln = name.toLowerCase(); if (ln.startsWith("*epub")) return ln.substring(5); return null; } boolean processEPUBCommand(String command, int depth) { if (command.startsWith(".")) { // log.println("Command: " + command); int index = command.indexOf(' '); String cmd; String param; if (index > 0) { cmd = command.substring(1, index); param = command.substring(index + 1).trim(); } else { cmd = command.substring(1); param = ""; } if (cmd.equals("chapter")) { String newChapterName = "OPS/" + param; if (chapterNameWasSet) { if (!chapterSplitAllowed || depth > 1) log.println("chapter split not allowed here"); else { resource = epub.createOPSResource(newChapterName); return false; } } else { epub.renameResource(resource, newChapterName); chapterNameWasSet = true; } } else if (cmd.equals("columns")) { InlineRule rule = new InlineRule(); rule.set("oeb-column-number", new CSSNumber(Integer.parseInt(param))); resource.getDocument().getBody().setStyle(rule); } else if (cmd.equals("pageMap")) { if (param.length() == 0 || param.toLowerCase().startsWith("t") || param.equals("1")) epub.usePageMap(); } else if (cmd.equals("translit")) { if (param.length() == 0 || param.toLowerCase().startsWith("t") || param.equals("1")) epub.setTranslit(true); else epub.setTranslit(false); } else if (cmd.equals("fontMangling")) { if (param.toLowerCase().equals("adobe")) epub.useAdobeFontMangling(); else epub.useIDPFFontMangling(); } else if (cmd.equals("resource")) { StringTokenizer tok = new StringTokenizer(param); nextResourceName = tok.nextToken(); if (tok.hasMoreTokens()) nextResourceMediaType = tok.nextToken(); else nextResourceMediaType = null; } else if (cmd.equals("page")) { Element parent = getCurrentOPSContainer(); if (!parent.content().hasNext()) { XRef xref = createOPSContainerXRefIfPossible(); if (xref != null) { epub.getTOC().addPage(param, xref); } else { epub.getTOC().addPage(param, parent.getSelfRef()); } } else { nextPageName = param; } } } return true; } XRef createOPSContainerXRefIfPossible() { int depth = nesting.size() - 1; Element lastGood = null; while (depth >= 0) { NestingItem item = (NestingItem) nesting.elementAt(depth--); Iterator content = item.opsElement.content(); if (content.hasNext() && (content.next() != null && content.hasNext())) break; lastGood = item.opsElement; } if (lastGood != null) return lastGood.getSelfRef(); return null; } void processEPUBMetadata(String command) { if (command.startsWith(".")) { StringTokenizer tok = new StringTokenizer(command); int count = tok.countTokens(); String cmd = tok.nextToken(); if (cmd.equals(".namespace")) { if (count == 3) { String prefix = tok.nextToken(); String ns = tok.nextToken(); metadataNS.put(prefix, ns); } } else if (cmd.equals(".includeWordMetadata")) { includeWordMetadata = true; } } else { int index = command.indexOf(' '); if (index > 0) { String name = command.substring(0, index); String value = command.substring(index + 1); index = name.indexOf('.'); if (index > 0) { String prefix = name.substring(0, index); String ns = (String) metadataNS.get(prefix); if (ns != null) { name = name.substring(index + 1); epub.addMetadata(ns, name, value); return; } } epub.addMetadata(null, name, value); } } } void flushMagic() { if (injectAcc.length() > 0) { parseAndInjectXML(injectAcc); injectAcc.delete(0, injectAcc.length()); } if (styleAcc.length() > 0) { try { CSSParser parser = new CSSParser(); StyleResource global = (StyleResource) epub.getResourceByName("OPS/global.css"); parser.readStylesheet(new StringReader(styleAcc.toString()), global.getStylesheet().getCSS()); } catch (Exception e) { // e.printStackTrace(); e.printStackTrace(log); } styleAcc.delete(0, styleAcc.length()); } } boolean possiblyAddResource(com.adobe.dp.office.word.Element we) { if (nextResourceName == null) return false; Iterator it = we.content(); while (it.hasNext()) { Object child = it.next(); if (child instanceof DrawingElement) { DrawingElement wd = (DrawingElement) child; PictureData picture = wd.getPictureData(); if (picture != null) { try { getImageResource(picture, nextResourceName, nextResourceMediaType); nextResourceName = null; nextResourceMediaType = null; return true; } catch (Exception e) { e.printStackTrace(log); } } } else if (child instanceof com.adobe.dp.office.word.Element) { com.adobe.dp.office.word.Element ce = (com.adobe.dp.office.word.Element) child; if (possiblyAddResource(ce)) return true; } } return false; } ParagraphProperties getNonMagicParagraphProperties(com.adobe.dp.office.word.Element we) { if (we instanceof ParagraphElement) { ParagraphProperties pp = ((ParagraphElement) we).getParagraphProperties(); if (getMagicStyleName(pp) == null) return pp; } return null; } boolean getNeigborCode(ParagraphProperties p1, ParagraphProperties p2) { if (p1 == null) return p2 == null; if (p2 == null) return false; return p1.sameStyle(p2); } boolean appendConvertedElement(com.adobe.dp.office.word.Element we, com.adobe.dp.office.word.Element prev, com.adobe.dp.office.word.Element next, float emScale, int depth, InlineRule tableCellProps) { Element conv = null; boolean addToParent = true; boolean resetSpaceProcessing = false; boolean ensureContent = false; if (we instanceof ParagraphElement) { ParagraphElement wp = (ParagraphElement) we; ParagraphProperties pp = wp.getParagraphProperties(); String className = null; String elementName = null; String epubStyle = getMagicStyleName(pp); ensureContent = true; if (epubStyle != null) { if (epubStyle.startsWith("*command")) { if (possiblyAddResource(wp)) return true; return processEPUBCommand(we.getTextContent(), depth); } if (epubStyle.startsWith("*style")) { styleAcc.append(we.getTextContent() + "\n"); return true; } if (epubStyle.startsWith("*inject")) { injectAcc.append(we.getTextContent() + "\n"); return true; } if (epubStyle.equals("*metadata")) { if (!hasEpubMetadata) { includeWordMetadata = false; hasEpubMetadata = true; } processEPUBMetadata(we.getTextContent()); return true; } flushMagic(); if (epubStyle.startsWith("-")) { int index = epubStyle.indexOf('.'); if (index < 0) { elementName = epubStyle.substring(1); epubStyle = ""; } else { elementName = epubStyle.substring(1, index); epubStyle = epubStyle.substring(index); } } else elementName = "p"; if (epubStyle.startsWith(".")) className = epubStyle.substring(1); if (conv == null && elementName != null) { conv = chapter.createElement(elementName); if (className != null) conv.setClassName(className); resetSpaceProcessing = true; } else { treatAsSpace(); } } else { flushMagic(); ParagraphProperties prevpp = getNonMagicParagraphProperties(prev); ParagraphProperties nextpp = getNonMagicParagraphProperties(next); boolean nc1 = getNeigborCode(prevpp, pp); boolean nc2 = getNeigborCode(pp, nextpp); StylingResult result = styleConverter.styleElement(pp, listElements.contains(we), emScale, nc1, nc2); NestingItem containerItem = getOPSContainer(wp, !nc1); if (!nc1 && result.containerRule != null) containerItem.topMargin = (CSSLength) result.containerRule.get("margin-top"); // don't finalize container until last element of the same style if (!nc2) { if (containerItem.topMargin != null) { if (result.containerRule == null) result.containerRule = new InlineRule(); result.containerRule.set("margin-top", containerItem.topMargin); } if (containerItem.opsElement != null) { containerItem.opsElement.setClassName(result.containerClassName); containerItem.opsElement.setDesiredCascadeResult(result.containerRule); } } elementName = result.elementName; if (elementName == null) elementName = "p"; className = result.elementClassName; conv = chapter.createElement(elementName); if (className != null) { conv.setClassName(className); conv.setDesiredCascadeResult(result.elementRule); } resetSpaceProcessing = true; NumberingLabel label = pp.getNumberingLabel(); if (label != null) { if (!styleConverter.convertLabelToProperty(label, null)) { // add label as a first child StylingResult labelRes = styleConverter.getLabelRule(result.elementRule, label, emScale * emScaleMultiplier(conv)); Element labelElement = chapter.createElement("span"); labelElement.setClassName(labelRes.elementClassName); labelElement.setDesiredCascadeResult(labelRes.elementRule); labelElement.add(label.getText() + " "); conv.add(labelElement); } } } } else { flushMagic(); if (we instanceof RunElement) { RunElement wr = (RunElement) we; RunProperties rp = wr.getRunProperties(); String epubStyle = getMagicStyleName(rp); String className = null; String elementName; if (epubStyle != null) { if (epubStyle.startsWith("*command")) { return processEPUBCommand(we.getTextContent(), depth); } if (epubStyle.startsWith("-")) { int index = epubStyle.indexOf('.'); if (index < 0) { elementName = epubStyle.substring(1); epubStyle = ""; } else { elementName = epubStyle.substring(1, index); epubStyle = epubStyle.substring(index); } } else elementName = "span"; if (epubStyle.startsWith(".")) className = epubStyle.substring(1); if (elementName == null) elementName = "span"; if (elementName != null) { conv = chapter.createElement(elementName); if (className != null) conv.setClassName(className); } } else { StylingResult result = styleConverter.styleElement(rp, false, emScale, false, false); elementName = result.elementName; className = result.elementClassName; if (elementName == null && className != null) elementName = "span"; if (elementName != null) { conv = chapter.createElement(elementName); if (className != null) conv.setClassName(className); } conv.setDesiredCascadeResult(result.elementRule); } } else if (we instanceof com.adobe.dp.office.word.HyperlinkElement) { com.adobe.dp.office.word.HyperlinkElement wa = (com.adobe.dp.office.word.HyperlinkElement) we; HyperlinkElement a = chapter.createHyperlinkElement("a"); String href = wa.getHRef(); if (href != null) a.setExternalHRef(href); conv = a; } else if (we instanceof FootnoteReferenceElement) { FootnoteReferenceElement wf = (FootnoteReferenceElement) we; String fid = wf.getID(); if (fid != null) { XRef xref = (XRef) footnoteMap.get(fid); if (xref != null) { HyperlinkElement a = chapter.createHyperlinkElement("a"); a.setClassName("footnote-ref"); a.setXRef(xref); a.add("[" + fid + "]"); conv = a; } } resetSpaceProcessing = true; } else if (we instanceof FootnoteElement) { FootnoteElement wf = (FootnoteElement) we; String fid = wf.getID(); if (fid != null) { conv = chapter.createElement("div"); footnoteMap.put(fid, conv.getSelfRef()); conv.setClassName("footnote"); Element ft = chapter.createElement("h6"); ft.setClassName("footnote-title"); conv.add(ft); ft.add(fid); } resetSpaceProcessing = true; } else if (we instanceof LastRenderedPageBreakElement) { if (useWordPageBreaks) { XRef xref = createOPSContainerXRefIfPossible(); if (xref != null) { conv = chapter.createElement("span"); xref = conv.getSelfRef(); } epub.getTOC().addPage(null, xref); } } else if (we instanceof TableElement) { TableElement wt = (TableElement) we; TableProperties tp = wt.getTableProperties(); conv = chapter.createElement("table"); StylingResult result = styleConverter.convertTableStylingRule(tp, emScale); conv.setDesiredCascadeResult(result.elementRule); conv.setClassName("table"); tableCellProps = result.tableCellRule; resetSpaceProcessing = true; } else if (we instanceof TableRowElement) { conv = chapter.createElement("tr"); resetSpaceProcessing = true; } else if (we instanceof TableCellElement) { TableCellElement wtc = (TableCellElement) we; TableCellProperties tcp = wtc.getTableCellProperties(); StylingResult result = styleConverter.convertTableCellStylingRule(tcp, emScale, tableCellProps); com.adobe.dp.epub.ops.TableCellElement tce = chapter.createTableCellElement("td", null, result.cols != null ? result.cols.intValue() : 1, 1); conv = tce; conv.setDesiredCascadeResult(result.elementRule); conv.setClassName("tc"); resetSpaceProcessing = true; } else if (we instanceof TextElement) { Element parent = getCurrentOPSContainer(); parent.add(processSpaces(((TextElement) we).getText())); return true; } else if (we instanceof TabElement) { Element parent = getCurrentOPSContainer(); parent.add(processSpaces("\t")); return true; } else if (we instanceof BRElement) { conv = chapter.createElement("br"); resetSpaceProcessing = true; } else if (we instanceof DrawingElement) { DrawingElement wd = (DrawingElement) we; PictureData picture = wd.getPictureData(); if (picture != null) { try { Resource imageResource = getImageResource(picture, null, null); if (imageResource != null) { ImageElement img = chapter.createImageElement("img"); img.setImageResource(imageResource); conv = img; if (picture.getWidth() > 0 && picture.getHeight() > 0) { double widthPt = picture.getWidth(); setImageWidth(img, "img", widthPt, emScale * emScaleMultiplier(conv)); } } } catch (Exception e) { // e.printStackTrace(); e.printStackTrace(log); } } resetSpaceProcessing = true; } else if (we instanceof PictElement) { Iterator it = we.content(); VMLGroupElement group = null; while (it.hasNext()) { Object child = it.next(); if (child instanceof VMLGroupElement) { group = (VMLGroupElement) child; break; } } if (group != null) { Hashtable style = group.getStyle(); if (style != null) { String widthStr = (String) style.get("width"); if (widthStr != null) { float widthPt = VMLPathConverter.readCSSLength(widthStr, 0); boolean embed = false; VMLConverter vmlConv = new VMLConverter(this, embed); Element parent = getCurrentOPSContainer(); if (embed) { SVGElement svg = chapter.createSVGElement("svg"); vmlConv.convertVML(resource, svg, group); parent.add(svg); setImageWidth(svg, "svg", widthPt, emScale); } else { String name = epub.makeUniqueResourceName(mediaFolder + "vml-embed.svg"); OPSResource svgResource = epub.createOPSResource(name, "image/svg+xml"); OPSDocument svgDoc = svgResource.getDocument(); SVGElement svg = (SVGElement) svgDoc.getBody(); vmlConv.convertVML(svgResource, svg, group); ImageElement img = chapter.createImageElement("img"); parent.add(img); img.setImageResource(svgResource); setImageWidth(img, "img", widthPt, emScale); } } } } return true; } else if (we instanceof TXBXContentElement) { conv = chapter.createElement("body"); conv.setClassName("embed"); resetSpaceProcessing = true; } else if (we instanceof SmartTagElement) { // pure container } else { // unknown element return true; } } if (conv != null) { if (nextPageName != null) { epub.getTOC().addPage(nextPageName, conv.getSelfRef()); nextPageName = null; } if (addToParent) { Element parent = getCurrentOPSContainer(); parent.add(conv); } emScale *= emScaleMultiplier(conv); } if (resetSpaceProcessing) resetSpaceProcessing(); int cdepth = 0; if (conv != null) cdepth = pushOPSContainer(conv); addChildren(we, null, emScale, depth + 1, tableCellProps); if (conv != null) restoreOPSContainer(cdepth); if (ensureContent && conv != null && !conv.content().hasNext()) conv.add("\u00A0"); // nbsp if (resetSpaceProcessing) resetSpaceProcessing(); return true; } private float emScaleMultiplier(Element e) { Object fontSize = e.getCascadedProperty("font-size"); if (fontSize != null && fontSize instanceof CSSLength) { CSSLength fs = (CSSLength) fontSize; if (fs.getUnit().equals("em")) { double scale = fs.getValue(); if (scale > 0) return (float) scale; } } return 1; } private com.adobe.dp.office.word.Element addChildren(com.adobe.dp.office.word.Element we, com.adobe.dp.office.word.Element skipToChild, float emScale, int depth, InlineRule tableCellProps) { Iterator children = we.content(); if (skipToChild != null) { while (children.hasNext()) { if (children.next() == skipToChild) break; } } com.adobe.dp.office.word.Element prev = null; com.adobe.dp.office.word.Element curr = children.hasNext() ? (com.adobe.dp.office.word.Element) children.next() : null; com.adobe.dp.office.word.Element next = null; while (curr != null) { if (children.hasNext()) next = (com.adobe.dp.office.word.Element) children.next(); else next = null; if (!appendConvertedElement(curr, prev, next, emScale, depth, tableCellProps)) { flushMagic(); return curr; } prev = curr; curr = next; } flushMagic(); return null; } void findLists(ContainerElement ce) { Iterator it = ce.content(); while (it.hasNext()) { Object n = it.next(); if (n instanceof ParagraphElement) { ParagraphElement pe = (ParagraphElement) n; ParagraphProperties pp = pe.getParagraphProperties(); if (pp != null) { Style style = pp.getParagraphStyle(); if (style != null && !style.getStyleId().startsWith("Heading") && !style.getStyleId().startsWith("heading")) { if (pp.getNumberingLabel() != null) listElements.add(pe); } } } else if (n instanceof ContainerElement) { findLists((ContainerElement) n); } } } void convert(BodyElement wbody, OPSResource ops, boolean addToSpine) { StyleResource global = (StyleResource) epub.getResourceByName("OPS/global.css"); resource = ops; com.adobe.dp.office.word.Element child = null; do { chapter = resource.getDocument(); chapter.addStyleResource(global); if (addToSpine) epub.addToSpine(resource); Element body = chapter.getBody(); body.setClassName("primary"); int depth = pushOPSContainer(body); child = addChildren(wbody, child, 1, 1, null); restoreOPSContainer(depth); } while (child != null); } void setWordResources(ContainerSource source) { wordResources = source; } }