diff --git a/src/main/java/org/olat/core/util/openxml/HTMLToOpenXMLHandler.java b/src/main/java/org/olat/core/util/openxml/HTMLToOpenXMLHandler.java index 8b43222e01a5ba2afb5b809537e00946d12edcb3..03ed08d5c9eb18e14d12ec95cbaa324fa2c7c65a 100644 --- a/src/main/java/org/olat/core/util/openxml/HTMLToOpenXMLHandler.java +++ b/src/main/java/org/olat/core/util/openxml/HTMLToOpenXMLHandler.java @@ -26,7 +26,10 @@ import java.util.Deque; import java.util.List; import org.olat.core.util.StringHelper; +import org.olat.core.util.openxml.OpenXMLDocument.Border; +import org.olat.core.util.openxml.OpenXMLDocument.Indent; import org.olat.core.util.openxml.OpenXMLDocument.ListParagraph; +import org.olat.core.util.openxml.OpenXMLDocument.PredefinedStyle; import org.olat.core.util.openxml.OpenXMLDocument.Spacing; import org.olat.core.util.openxml.OpenXMLDocument.Style; import org.w3c.dom.Element; @@ -44,6 +47,8 @@ import org.xml.sax.helpers.DefaultHandler; * */ public class HTMLToOpenXMLHandler extends DefaultHandler { + + private static final Border QUOTE_BORDER = new Border(400, 24, "EEEEEE"); private boolean latex = false; private StringBuilder textBuffer; @@ -81,12 +86,12 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { flushText(); addContent(currentParagraph); } - if(startSpacing == null) { - currentParagraph = factory.createParagraphEl(); - } else { - currentParagraph = factory.createParagraphEl(startSpacing); - startSpacing = null;//consumed - } + + Indent indent = getCurrentIndent(); + Border leftBorder = getCurrentLeftBorder(); + PredefinedStyle predefinedStyle = getCurrentPredefinedStyle(); + currentParagraph = factory.createParagraphEl(indent, leftBorder, startSpacing, predefinedStyle); + startSpacing = null;//consumed } return currentParagraph; } @@ -97,8 +102,10 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { flushText(); addContent(currentParagraph); } - - currentParagraph = factory.createParagraphEl(spacing); + Indent indent = getCurrentIndent(); + Border leftBorder = getCurrentLeftBorder(); + PredefinedStyle predefinedStyle = getCurrentPredefinedStyle(); + currentParagraph = factory.createParagraphEl(indent, leftBorder, spacing, predefinedStyle); return currentParagraph; } @@ -166,7 +173,11 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { private Element getCurrentRun() { Element paragraphEl; if(currentParagraph == null) { - paragraphEl = currentParagraph = factory.createParagraphEl(); + Indent indent = getCurrentIndent(); + Border leftBorder = getCurrentLeftBorder(); + PredefinedStyle predefinedStyle = getCurrentPredefinedStyle(); + paragraphEl = currentParagraph = factory.createParagraphEl(indent, leftBorder, startSpacing, predefinedStyle); + startSpacing = null; } else { paragraphEl = currentParagraph; } @@ -174,7 +185,9 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { if(lastChild != null && "w:r".equals(lastChild.getNodeName())) { return (Element)lastChild; } - return (Element)paragraphEl.appendChild(factory.createRunEl(null)); + + PredefinedStyle runStyle = getCurrentPredefinedStyle(); + return (Element)paragraphEl.appendChild(factory.createRunEl(null, runStyle)); } private Style[] setTextPreferences(String cssStyles) { @@ -218,7 +231,8 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { } if(runPrefs == null) { - run = paragraphEl.appendChild(factory.createRunEl(null)); + PredefinedStyle style = getCurrentPredefinedStyle(); + run = paragraphEl.appendChild(factory.createRunEl(null, style)); runPrefs = run.appendChild(factory.createRunPrefsEl()); } @@ -233,6 +247,55 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { return styleStack.getLast().getStyles(); } + public Indent getCurrentIndent() { + if(styleStack.isEmpty()) return null; + + int indent = 0; + for(StyleStatus style:styleStack) { + if(style.isQuote()) { + indent++; + } + } + + int emuIndent = 0; + if(indent > 0) { + emuIndent = 700; + } + if(indent > 1) { + emuIndent += (indent - 1) * 100; + } + return emuIndent == 0 ? null : new Indent(emuIndent); + } + + public Border getCurrentLeftBorder() { + if(styleStack.isEmpty()) return null; + + int indent = 0; + for(StyleStatus style:styleStack) { + if(style.isQuote()) { + indent++; + } + } + + String val; + switch(indent) { + case 1: val = "single"; break; + case 2: val = "double"; break; + default: val = "triple"; + } + return indent == 0 ? null : new Border(QUOTE_BORDER, val); + } + + public PredefinedStyle getCurrentPredefinedStyle() { + if(styleStack.isEmpty()) return null; + + boolean quote = false; + for(StyleStatus style:styleStack) { + quote |= style.isQuote(); + } + return quote ? PredefinedStyle.quote : null; + } + public Style[] popStyle(String tag) { StyleStatus status = styleStack.pollLast(); if(status != null && status.getTag().equals(tag)) { @@ -244,7 +307,8 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { private void setImage(String path) { Element imgEl = factory.createImageEl(path); if(imgEl != null) { - Element runEl = factory.createRunEl(Collections.singletonList(imgEl)); + PredefinedStyle style = getCurrentPredefinedStyle(); + Element runEl = factory.createRunEl(Collections.singletonList(imgEl), style); Element paragrapheEl = getCurrentParagraph(false); paragrapheEl.appendChild(runEl); } @@ -293,19 +357,17 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { currentListParagraph = factory.createListParagraph(); } else if("li".equals(tag)) { getCurrentListParagraph(true); + } else if("blockquote".equals(tag)) { + Style[] styles = setTextPreferences(Style.italic); + styleStack.add(new StyleStatus(tag, true, styles)); + appendParagraph(new Spacing(90, 0)); } else if("div".equals(tag)) { String cl = attributes.getValue("class"); if(StringHelper.containsNonWhitespace(cl)) { if(cl.contains("o_quote_author")) { - appendParagraph(new Spacing(180, 0)); Style[] styles = setTextPreferences(Style.italic); - styleStack.add(new StyleStatus(tag, styles)); - } else if(cl.contains("o_quote_wrapper")) { - // - } else if(cl.contains("o_quote")) { + styleStack.add(new StyleStatus(tag, true, styles)); appendParagraph(new Spacing(120, 0)); - Style[] styles = setTextPreferences(Style.italic); - styleStack.add(new StyleStatus(tag, styles)); } else { styleStack.add(new StyleStatus(tag, new Style[0])); } @@ -362,6 +424,8 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { currentListParagraph = null; } else if("li".equals(tag)) { //do nothing + } else if("blockquote".equals(tag)) { + popStyle(tag); } else if("div".equals(tag)) { popStyle(tag); } @@ -380,9 +444,15 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { private static class StyleStatus { private final String tag; private final Style[] styles; + private final boolean quote; public StyleStatus(String tag, Style[] styles) { + this(tag, false, styles); + } + + public StyleStatus(String tag, boolean quote, Style[] styles) { this.tag = tag; + this.quote = quote; this.styles = styles; } @@ -390,6 +460,10 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { return tag; } + public boolean isQuote() { + return quote; + } + public Style[] getStyles() { return styles; } diff --git a/src/main/java/org/olat/core/util/openxml/OpenXMLDocument.java b/src/main/java/org/olat/core/util/openxml/OpenXMLDocument.java index a9e5c04e49abc4821559e9e78ac694f15715a721..45cd6bded18da81f8c381cfc2141733eca24ce8f 100644 --- a/src/main/java/org/olat/core/util/openxml/OpenXMLDocument.java +++ b/src/main/java/org/olat/core/util/openxml/OpenXMLDocument.java @@ -164,25 +164,25 @@ public class OpenXMLDocument { } public void appendTitle(String text) { - appendHeading(text, Heading.title, null); + appendHeading(text, PredefinedStyle.title, null); } public void appendHeading1(String text, String additionalText) { - appendHeading(text, Heading.heading1, additionalText); + appendHeading(text, PredefinedStyle.heading1, additionalText); } public void appendHeading2(String text, String additionalText) { - appendHeading(text, Heading.heading2, additionalText); + appendHeading(text, PredefinedStyle.heading2, additionalText); } - private void appendHeading(String text, Heading style, String additionalText) { + private void appendHeading(String text, PredefinedStyle style, String additionalText) { if(!StringHelper.containsNonWhitespace(text)) return; Element textEl = createTextEl(text); List<Element> runsEl = new ArrayList<Element>(2); Element runEl = createRunEl(Collections.singletonList(textEl)); runsEl.add(runEl); - Element styleEl = createParagraphStyle(style.styleId(), style.runStyleId()); + Element styleEl = createParagraphStyle(style); if(StringHelper.containsNonWhitespace(additionalText)) { //add an "insecable" blank between the title and the additional text Element blankRunEl = document.createElement("w:r"); @@ -212,9 +212,9 @@ public class OpenXMLDocument { public void appendSubtitle(String text) { Element textEl = createTextEl(text); List<Element> runsEl = new ArrayList<Element>(2); - Element runEl = createRunEl(Collections.singletonList(textEl), Heading.subSubtleEmphasis.runStyleId()); + Element runEl = createRunEl(Collections.singletonList(textEl), PredefinedStyle.subSubtleEmphasis); runsEl.add(runEl); - Element styleEl = createParagraphStyle(Heading.subSubtleEmphasis.styleId(), Heading.subSubtleEmphasis.runStyleId()); + Element styleEl = createParagraphStyle(PredefinedStyle.subSubtleEmphasis); Element paragraphEl = createParagraphEl(styleEl, runsEl); getCursor().appendChild(paragraphEl); } @@ -422,14 +422,17 @@ public class OpenXMLDocument { </w:rPr> </w:pPr> */ - public Element createParagraphStyle(String styleId, String runStyleId) { + public Element createParagraphStyle(PredefinedStyle styleId) { Element paragraphEl = document.createElement("w:pPr"); - Element styleEl = (Element)paragraphEl.appendChild(document.createElement("w:pStyle")); - styleEl.setAttribute("w:val", styleId); - if(StringHelper.containsNonWhitespace(runStyleId)) { + if(styleId != null && styleId.paragraphStyleId() != null) { + Element styleEl = (Element)paragraphEl.appendChild(document.createElement("w:pStyle")); + styleEl.setAttribute("w:val", styleId.paragraphStyleId()); + } + + if(styleId != null && styleId.runStyleId() != null) { Element runPrefsEl = (Element)paragraphEl.appendChild(document.createElement("w:rPr")); Element rStyleEl = (Element)runPrefsEl.appendChild(document.createElement("w:rStyle")); - rStyleEl.setAttribute("w:val", runStyleId); + rStyleEl.setAttribute("w:val", styleId.runStyleId()); } return paragraphEl; } @@ -462,14 +465,41 @@ public class OpenXMLDocument { <w:spacing w:before="120" w:after="120" w:beforeAutospacing="0" w:afterAutospacing="0"/> </w:pPr> */ - public Element createParagraphEl(Spacing spacing) { + public Element createParagraphEl(Indent indent, Border leftBorder, Spacing spacing, PredefinedStyle predefinedStyle) { Element paragraphEl = document.createElement("w:p"); Element paragraphPrefsEl = (Element)paragraphEl.appendChild(document.createElement("w:pPr")); - Element spacingEl = (Element)paragraphPrefsEl.appendChild(document.createElement("w:spacing")); - spacingEl.setAttribute("w:before", Integer.toString(spacing.getBefore())); - spacingEl.setAttribute("w:after", Integer.toString(spacing.getAfter())); - spacingEl.setAttribute("w:beforeAutospacing", "0"); - spacingEl.setAttribute("w:afterAutospacing", "0"); + if(indent != null) { + //<w:ind w:left="1440" w:right="1440" w:hanging="1080" /> + Element indEl = (Element)paragraphPrefsEl.appendChild(document.createElement("w:ind")); + if(indent.getLeft() > 0) { + indEl.setAttribute("w:left", Integer.toString(indent.getLeft())); + } + } + + if(predefinedStyle != null && predefinedStyle.paragraphStyleId() != null) { + Element styleEl = (Element)paragraphPrefsEl.appendChild(document.createElement("w:pStyle")); + styleEl.setAttribute("w:val", predefinedStyle.paragraphStyleId()); + } + + if(leftBorder != null) { + //<w:pBdr> + // <w:left w:val="single" w:sz="24" w:space="4" w:color="B97034" w:themeColor="accent6" w:themeShade="BF" /> + + Element borderEl = (Element)paragraphPrefsEl.appendChild(document.createElement("w:pBdr")); + Element leftEl = (Element)borderEl.appendChild(document.createElement("w:left")); + leftEl.setAttribute("w:val", leftBorder.getVal()); + leftEl.setAttribute("w:sz", Integer.toString(leftBorder.getSize())); + leftEl.setAttribute("w:space", Integer.toString(leftBorder.getSpace())); + leftEl.setAttribute("w:color", leftBorder.getColor()); + } + + if(spacing != null) { + Element spacingEl = (Element)paragraphPrefsEl.appendChild(document.createElement("w:spacing")); + spacingEl.setAttribute("w:before", Integer.toString(spacing.getBefore())); + spacingEl.setAttribute("w:after", Integer.toString(spacing.getAfter())); + spacingEl.setAttribute("w:beforeAutospacing", "0"); + spacingEl.setAttribute("w:afterAutospacing", "0"); + } return paragraphEl; } @@ -477,12 +507,12 @@ public class OpenXMLDocument { return createRunEl(textEls, null); } - public Element createRunEl(Collection<? extends Node> textEls, String runStyleId) { + public Element createRunEl(Collection<? extends Node> textEls, PredefinedStyle style) { Element runEl = document.createElement("w:r"); - if(StringHelper.containsNonWhitespace(runStyleId)) { + if(style != null && style.runStyleId() != null) { Element runPrefsEl = (Element)runEl.appendChild(document.createElement("w:rPr")); Element rStyleEl = (Element)runPrefsEl.appendChild(document.createElement("w:rStyle")); - rStyleEl.setAttribute("w:val", runStyleId); + rStyleEl.setAttribute("w:val", style.runStyleId()); } if(textEls != null && textEls.size() > 0) { for(Node textEl:textEls) { @@ -1154,23 +1184,24 @@ public class OpenXMLDocument { } } - public enum Heading { + public enum PredefinedStyle { title("ooTitle", null), heading1("ooHeading1", null), heading2("ooHeading2", null), subTitle("ooUntertitel", "ooUntertitelZeichen"), - subSubtleEmphasis("ooSubtleEmphasis", "ooSubtleEmphasisZeichen"); + subSubtleEmphasis("ooSubtleEmphasis", "ooSubtleEmphasisZeichen"), + quote("ooQuote", "ooQuoteZeichen"); - private final String styleId; + private final String paragraphStyleId; private final String runStyleId; - private Heading(String styleId, String runStyleId) { - this.styleId = styleId; + private PredefinedStyle(String paragraphStyleId, String runStyleId) { + this.paragraphStyleId = paragraphStyleId; this.runStyleId = runStyleId; } - public String styleId() { - return styleId; + public String paragraphStyleId() { + return paragraphStyleId; } public String runStyleId() { @@ -1219,6 +1250,68 @@ public class OpenXMLDocument { } } + public static class Border { + private final int space; + private final int size; + private final String val; + private final String color; + + public Border(int space, int size, String color) { + this.space = space; + this.size = size; + this.color = color; + val = "single"; + } + + public Border(Border border, String val) { + this.space = border.space; + this.size = border.size; + this.color = border.color; + this.val = val; + } + + public int getSpace() { + return space; + } + + public int getSize() { + return size; + } + + public String getColor() { + return color; + } + + public String getVal() { + return val; + } + + public boolean same(Border border) { + return color.equals(border.color) && size == border.size && space == border.space; + } + + public Border cloneAndStack(Border border) { + String stackedVal = border.val; + switch(border.val) { + case "single": stackedVal = "double"; break; + case "double": stackedVal = "triple"; break; + } + return new Border(this, stackedVal); + } + } + + public static class Indent { + private final int left; + + public Indent(int left) { + this.left = left; + } + + public int getLeft() { + return left; + } + } + public static class ListParagraph { private final int abstractNumId; diff --git a/src/main/java/org/olat/core/util/openxml/_resources/styles.xml b/src/main/java/org/olat/core/util/openxml/_resources/styles.xml index 9b35421624b9b037996694782ea524e05e6b4852..f4aa0dcc2d3097e80c2c6368c252f81a7c508a12 100644 --- a/src/main/java/org/olat/core/util/openxml/_resources/styles.xml +++ b/src/main/java/org/olat/core/util/openxml/_resources/styles.xml @@ -517,4 +517,40 @@ <w:szCs w:val="20" /> </w:rPr> </w:style> + <w:style w:type="paragraph" w:styleId="ooQuote"> + <w:name w:val="OO Quote" /> + <w:basedOn w:val="Standard" /> + <w:next w:val="Standard" /> + <w:link w:val="ooQuoteZeichen" /> + <w:uiPriority w:val="11" /> + <w:qFormat /> + <w:rsid w:val="00384E21" /> + <w:pPr> + <w:numPr> + <w:ilvl w:val="1" /> + </w:numPr> + </w:pPr> + <w:rPr> + <w:rFonts w:asciiTheme="majorHAnsi" w:eastAsiaTheme="majorEastAsia" w:hAnsiTheme="majorHAnsi" w:cstheme="majorBidi" /> + <w:i /> + <w:iCs /> + <w:color w:val="555555" w:themeColor="quote1" /> + <w:sz w:val="20" /> + <w:szCs w:val="20" /> + </w:rPr> + </w:style> + <w:style w:type="character" w:styleId="ooQuoteZeichen"> + <w:name w:val="OO Quote" /> + <w:basedOn w:val="Absatzstandardschriftart" /> + <w:uiPriority w:val="19" /> + <w:qFormat /> + <w:rsid w:val="00384E22" /> + <w:rPr> + <w:i /> + <w:iCs /> + <w:color w:val="555555" w:themeColor="quote1" /> + <w:sz w:val="20" /> + <w:szCs w:val="20" /> + </w:rPr> + </w:style> </w:styles> \ No newline at end of file