From 37f30baa7ab68fe4eb98a3ccb8301f052b6bc8ee Mon Sep 17 00:00:00 2001 From: Thomas Gutmann Date: Mon, 4 Nov 2024 20:58:16 +0100 Subject: [PATCH] Optimization of docx emitter output (#1957) --- .../engine/emitter/docx/DocxEmitterImpl.java | 16 +- .../emitter/docx/writer/BasicComponent.java | 156 +++++-- .../emitter/docx/writer/DocxWriter.java | 16 +- .../emitter/wpml/AbstractEmitterImpl.java | 55 ++- .../engine/emitter/wpml/DocEmitter.java | 60 ++- .../engine/emitter/wpml/EmitterServices.java | 418 ++++++++++++++++++ .../engine/emitter/wpml/IWordWriter.java | 12 +- .../report/engine/emitter/wpml/WordUtil.java | 28 +- .../engine/emitter/wpml/writer/DocWriter.java | 4 + 9 files changed, 710 insertions(+), 55 deletions(-) create mode 100644 engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/EmitterServices.java diff --git a/engine/org.eclipse.birt.report.engine.emitter.docx/src/org/eclipse/birt/report/engine/emitter/docx/DocxEmitterImpl.java b/engine/org.eclipse.birt.report.engine.emitter.docx/src/org/eclipse/birt/report/engine/emitter/docx/DocxEmitterImpl.java index e24bac48110..3a900a5dcc0 100644 --- a/engine/org.eclipse.birt.report.engine.emitter.docx/src/org/eclipse/birt/report/engine/emitter/docx/DocxEmitterImpl.java +++ b/engine/org.eclipse.birt.report.engine.emitter.docx/src/org/eclipse/birt/report/engine/emitter/docx/DocxEmitterImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013 Actuate Corporation. + * Copyright (c) 2013, 2024 Actuate Corporation and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -71,7 +71,8 @@ public void initialize(IEmitterServices service) throws EngineException { this.embedHtml = (Boolean) value; } - wordWriter = new DocxWriter(out, tempFileDir, getCompressionMode(service).getValue(), getWordVersion()); + wordWriter = new DocxWriter(out, tempFileDir, getCompressionMode(service).getValue(), getWordVersion(), + wrappedTableHeaderFooter); } private CompressionMode getCompressionMode(IEmitterServices service) { @@ -102,14 +103,21 @@ public void startForeign(IForeignContent foreign) throws BirtException { wordWriter.insertHiddenParagraph(); context.setIsAfterTable(false); } - if (embedHtml) { + if (embedHtml && !wrappedTableForMarginPadding) { + writeBookmark(foreign); + writeToc(foreign); + wordWriter.writeForeign(foreign, false, combineMarginPadding); + adjustInline(); + context.setIsAfterTable(false); + context.addContainer(true); + } else if (embedHtml) { writeBookmark(foreign); int width = WordUtil.convertTo(foreign.getWidth(), context.getCurrentWidth(), reportDpi); width = Math.min(width, context.getCurrentWidth()); wordWriter.startTable(foreign.getComputedStyle(), width, true); wordWriter.startTableRow(-1); wordWriter.startTableCell(width, foreign.getComputedStyle(), null, null); - // TODO:need text paser for foreign raw value + // TODO:need text parser for foreign raw value wordWriter.writeForeign(foreign, true); if (isInSpannedCell(foreign)) { // insert empty line after embed html diff --git a/engine/org.eclipse.birt.report.engine.emitter.docx/src/org/eclipse/birt/report/engine/emitter/docx/writer/BasicComponent.java b/engine/org.eclipse.birt.report.engine.emitter.docx/src/org/eclipse/birt/report/engine/emitter/docx/writer/BasicComponent.java index 58d023a81b6..274150c50f3 100644 --- a/engine/org.eclipse.birt.report.engine.emitter.docx/src/org/eclipse/birt/report/engine/emitter/docx/writer/BasicComponent.java +++ b/engine/org.eclipse.birt.report.engine.emitter.docx/src/org/eclipse/birt/report/engine/emitter/docx/writer/BasicComponent.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013 Actuate Corporation. + * Copyright (c) 2013, 2024 Actuate Corporation and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -36,6 +36,7 @@ import org.eclipse.birt.report.engine.content.IStyle; import org.eclipse.birt.report.engine.css.engine.StyleConstants; import org.eclipse.birt.report.engine.css.engine.value.css.CSSConstants; +import org.eclipse.birt.report.engine.css.engine.value.css.CSSValueConstants; import org.eclipse.birt.report.engine.emitter.EmitterUtil; import org.eclipse.birt.report.engine.emitter.HTMLTags; import org.eclipse.birt.report.engine.emitter.HTMLWriter; @@ -46,7 +47,6 @@ import org.eclipse.birt.report.engine.emitter.wpml.writer.AbstractWordXmlWriter; import org.eclipse.birt.report.engine.executor.css.HTMLProcessor; import org.eclipse.birt.report.engine.ir.DimensionType; -import org.eclipse.birt.report.engine.ir.EngineIRConstants; import org.eclipse.birt.report.engine.layout.pdf.util.PropertyUtil; import org.eclipse.birt.report.engine.ooxml.IPart; import org.eclipse.birt.report.engine.ooxml.ImageManager; @@ -59,6 +59,7 @@ import org.eclipse.birt.report.engine.util.FileUtil; import org.eclipse.birt.report.model.api.IResourceLocator; import org.eclipse.birt.report.model.api.ReportDesignHandle; +import org.eclipse.birt.report.model.api.elements.DesignChoiceConstants; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -105,6 +106,10 @@ public abstract class BasicComponent extends AbstractWordXmlWriter { protected boolean wrappedTable = true; + protected boolean combineMarginPadding = true; + + protected boolean wrappedTableHeaderFooter = true; + protected BasicComponent(IPart part) throws IOException { this.part = part; this.imageManager = (ImageManager) part.getPackage().getExtensionData(); @@ -316,8 +321,9 @@ protected void writeBookmark(String bm) { bookmarkId++; } - protected void writeForeign(IForeignContent foreignContent, boolean wrappedTable) { + protected void writeForeign(IForeignContent foreignContent, boolean wrappedTable, boolean combineMarginPadding) { this.wrappedTable = wrappedTable; + this.combineMarginPadding = combineMarginPadding; writeForeign(foreignContent); } @@ -469,13 +475,13 @@ private int getElementType(DimensionType x, DimensionType y, DimensionType width display = style.getDisplay(); } - if (EngineIRConstants.DISPLAY_NONE.equalsIgnoreCase(display)) { + if (DesignChoiceConstants.DISPLAY_NONE.equalsIgnoreCase(display)) { type |= DISPLAY_NONE; } if (x != null || y != null) { return type | DISPLAY_BLOCK; - } else if (EngineIRConstants.DISPLAY_INLINE.equalsIgnoreCase(display)) { + } else if (DesignChoiceConstants.DISPLAY_INLINE.equalsIgnoreCase(display)) { type |= DISPLAY_INLINE; if (width != null || height != null) { type |= DISPLAY_INLINE_BLOCK; @@ -499,6 +505,7 @@ private String getTagByType(int display, int mask) { return tag; } + @SuppressWarnings("unused") private void buildForeignStyles(IForeignContent foreignContent, StringBuffer foreignStyles, int display) { IStyle style = foreignContent.getComputedStyle(); foreignStyles.setLength(0); @@ -590,43 +597,104 @@ private void buildMargins(StringBuffer styleBuffer, IStyle style) { leftMargin = style.getMarginLeft(); } - if (null != topMargin && null != rightMargin && null != bottomMargin && null != leftMargin) { - if (rightMargin.equals(leftMargin)) { - if (topMargin.equals(bottomMargin)) { - if (topMargin.equals(rightMargin)) { - // The four margins have the same value - buildProperty(styleBuffer, HTMLTags.ATTR_MARGIN, topMargin); + // MHT-files for DOCX needs for each margin-attribute an own style tag to be + // displayed correctly + boolean marginStyleMultipleAttr = !wrappedTable; + if (marginStyleMultipleAttr) { + if (null != topMargin) { + if (combineMarginPadding) { + int marginTopPt = WordUtil.convertToPt(topMargin); + String topPadding = style.getPaddingTop(); + if (topPadding != null) { + marginTopPt += WordUtil.convertToPt(topPadding); + topMargin = marginTopPt + "pt"; + } + } + addPropName(styleBuffer, HTMLTags.ATTR_MARGIN_TOP); + addPropValue(styleBuffer, topMargin); + styleBuffer.append(';'); + } + + if (null != rightMargin) { + if (combineMarginPadding) { + int marginRightPt = WordUtil.convertToPt(rightMargin); + String rightPadding = style.getPaddingRight(); + if (rightPadding != null) { + marginRightPt += WordUtil.convertToPt(rightPadding); + rightMargin = marginRightPt + "pt"; + } + } + addPropName(styleBuffer, HTMLTags.ATTR_MARGIN_RIGHT); + addPropValue(styleBuffer, rightMargin); + styleBuffer.append(';'); + } + + if (null != bottomMargin) { + if (combineMarginPadding) { + int marginBottomPt = WordUtil.convertToPt(bottomMargin); + String bottomPadding = style.getPaddingBottom(); + if (bottomPadding != null) { + marginBottomPt += WordUtil.convertToPt(bottomPadding); + bottomMargin = marginBottomPt + "pt"; + } + } + addPropName(styleBuffer, HTMLTags.ATTR_MARGIN_BOTTOM); + addPropValue(styleBuffer, bottomMargin); + styleBuffer.append(';'); + } + + if (null != leftMargin) { + if (combineMarginPadding) { + int marginLeftPt = WordUtil.convertToPt(leftMargin); + String leftPadding = style.getPaddingLeft(); + if (leftPadding != null) { + marginLeftPt += WordUtil.convertToPt(leftPadding); + leftMargin = marginLeftPt + "pt"; + } + } + addPropName(styleBuffer, HTMLTags.ATTR_MARGIN_LEFT); + addPropValue(styleBuffer, leftMargin); + styleBuffer.append(';'); + } + } else { + if (null != topMargin && null != rightMargin && null != bottomMargin && null != leftMargin) { + if (rightMargin.equals(leftMargin)) { + if (topMargin.equals(bottomMargin)) { + if (topMargin.equals(rightMargin)) { + // The four margins have the same value + buildProperty(styleBuffer, HTMLTags.ATTR_MARGIN, topMargin); + } else { + // The top & bottom margins have the same value. The + // right & left margins have the same value. + addPropName(styleBuffer, HTMLTags.ATTR_MARGIN); + addPropValue(styleBuffer, topMargin); + addPropValue(styleBuffer, rightMargin); + styleBuffer.append(';'); + } } else { - // The top & bottom margins have the same value. The - // right & left margins have the same value. + // only the right & left margins have the same value. addPropName(styleBuffer, HTMLTags.ATTR_MARGIN); addPropValue(styleBuffer, topMargin); addPropValue(styleBuffer, rightMargin); + addPropValue(styleBuffer, bottomMargin); styleBuffer.append(';'); } } else { - // only the right & left margins have the same value. + // four margins have different values. addPropName(styleBuffer, HTMLTags.ATTR_MARGIN); addPropValue(styleBuffer, topMargin); addPropValue(styleBuffer, rightMargin); addPropValue(styleBuffer, bottomMargin); + addPropValue(styleBuffer, leftMargin); styleBuffer.append(';'); } } else { - // four margins have different values. - addPropName(styleBuffer, HTMLTags.ATTR_MARGIN); - addPropValue(styleBuffer, topMargin); - addPropValue(styleBuffer, rightMargin); - addPropValue(styleBuffer, bottomMargin); - addPropValue(styleBuffer, leftMargin); - styleBuffer.append(';'); + // At least one margin has null value. + buildProperty(styleBuffer, HTMLTags.ATTR_MARGIN_TOP, topMargin); + buildProperty(styleBuffer, HTMLTags.ATTR_MARGIN_RIGHT, rightMargin); + buildProperty(styleBuffer, HTMLTags.ATTR_MARGIN_BOTTOM, bottomMargin); + buildProperty(styleBuffer, HTMLTags.ATTR_MARGIN_LEFT, leftMargin); } - } else { - // At least one margin has null value. - buildProperty(styleBuffer, HTMLTags.ATTR_MARGIN_TOP, topMargin); - buildProperty(styleBuffer, HTMLTags.ATTR_MARGIN_RIGHT, rightMargin); - buildProperty(styleBuffer, HTMLTags.ATTR_MARGIN_BOTTOM, bottomMargin); - buildProperty(styleBuffer, HTMLTags.ATTR_MARGIN_LEFT, leftMargin); } } @@ -696,20 +764,20 @@ private void buildVisual(StringBuffer styleBuffer, IStyle style) { } private void buildTextDecoration(StringBuffer styleBuffer, IStyle style) { - CSSValue linethrough = style.getProperty(IStyle.STYLE_TEXT_LINETHROUGH); - CSSValue underline = style.getProperty(IStyle.STYLE_TEXT_UNDERLINE); - CSSValue overline = style.getProperty(IStyle.STYLE_TEXT_OVERLINE); + CSSValue linethrough = style.getProperty(StyleConstants.STYLE_TEXT_LINETHROUGH); + CSSValue underline = style.getProperty(StyleConstants.STYLE_TEXT_UNDERLINE); + CSSValue overline = style.getProperty(StyleConstants.STYLE_TEXT_OVERLINE); - if (linethrough == IStyle.LINE_THROUGH_VALUE || underline == IStyle.UNDERLINE_VALUE - || overline == IStyle.OVERLINE_VALUE) { + if (linethrough == CSSValueConstants.LINE_THROUGH_VALUE || underline == CSSValueConstants.UNDERLINE_VALUE + || overline == CSSValueConstants.OVERLINE_VALUE) { styleBuffer.append(" text-decoration:"); //$NON-NLS-1$ - if (IStyle.LINE_THROUGH_VALUE == linethrough) { + if (CSSValueConstants.LINE_THROUGH_VALUE == linethrough) { addPropValue(styleBuffer, "line-through"); } - if (IStyle.UNDERLINE_VALUE == underline) { + if (CSSValueConstants.UNDERLINE_VALUE == underline) { addPropValue(styleBuffer, "underline"); } - if (IStyle.OVERLINE_VALUE == overline) { + if (CSSValueConstants.OVERLINE_VALUE == overline) { addPropValue(styleBuffer, "overline"); } styleBuffer.append(';'); @@ -915,9 +983,8 @@ public String validHtmlText(String foreignText) { Matcher matcher = pattern.matcher(foreignText); if (matcher.matches()) { return foreignText; - } else { - return "" + foreignText + ""; } + return "" + foreignText + ""; } protected String getRelationshipId() { @@ -1078,7 +1145,7 @@ private Node convertFontTagToSpanTag(Node nodeFont, HashMap cssSty /** * Get the corrected font size to solve the MS Word (DOCX) / MHT font size issue - * MHT font size 12pt will be changed to at MS Word side to font size 10pt + * MHT font size 12pt will be changed at MS Word side to font size 10pt * * @param nodeTag html tag to validate the font size * @param cssStyles CSS style around the tag @@ -1094,4 +1161,17 @@ private void getCorrectFontSize(Node nodeTag, HashMap cssStyles) { } } } + + protected void startHeaderFooterContainer(int headerHeight, int headerWidth, boolean writeColumns) { + if (wrappedTableHeaderFooter) { + super.startHeaderFooterContainer(headerHeight, headerWidth, writeColumns); + } + } + + @Override + protected void endHeaderFooterContainer() { + if (wrappedTableHeaderFooter) { + super.endHeaderFooterContainer(); + } + } } diff --git a/engine/org.eclipse.birt.report.engine.emitter.docx/src/org/eclipse/birt/report/engine/emitter/docx/writer/DocxWriter.java b/engine/org.eclipse.birt.report.engine.emitter.docx/src/org/eclipse/birt/report/engine/emitter/docx/writer/DocxWriter.java index 43e998dba12..5efd7ee497f 100644 --- a/engine/org.eclipse.birt.report.engine.emitter.docx/src/org/eclipse/birt/report/engine/emitter/docx/writer/DocxWriter.java +++ b/engine/org.eclipse.birt.report.engine.emitter.docx/src/org/eclipse/birt/report/engine/emitter/docx/writer/DocxWriter.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013 Actuate Corporation. + * Copyright (c) 2013, 2024 Actuate Corporation and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -56,6 +56,8 @@ public class DocxWriter implements IWordWriter { private String documentLanguage = "en"; + private boolean wrappedTableHeaderFooter = false; + /** * Constructor * @@ -64,10 +66,12 @@ public class DocxWriter implements IWordWriter { * @param compressionMode compression mode * @param wordVersion word version */ - public DocxWriter(OutputStream out, String tempFileDir, int compressionMode, int wordVersion) { + public DocxWriter(OutputStream out, String tempFileDir, int compressionMode, int wordVersion, + boolean wrappedTableHeaderFooter) { pkg = Package.createInstance(out, tempFileDir, compressionMode); pkg.setExtensionData(new ImageManager()); this.wordVersion = wordVersion; + this.wrappedTableHeaderFooter = wrappedTableHeaderFooter; } @Override @@ -133,6 +137,7 @@ private void initializeDocumentPart(String backgroundColor, String backgroundIma rtl, wordVersion, this.getDocumentLanguage()); document.start(); currentComponent = document; + currentComponent.wrappedTableHeaderFooter = this.wrappedTableHeaderFooter; } @Override @@ -313,7 +318,12 @@ public void writeForeign(IForeignContent foreignContent) { @Override public void writeForeign(IForeignContent foreignContent, boolean embedHTML) { - currentComponent.writeForeign(foreignContent, embedHTML); + currentComponent.writeForeign(foreignContent, embedHTML, true); + } + + @Override + public void writeForeign(IForeignContent foreignContent, boolean embedHTML, boolean combineMarginPadding) { + currentComponent.writeForeign(foreignContent, embedHTML, combineMarginPadding); } @Override diff --git a/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/AbstractEmitterImpl.java b/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/AbstractEmitterImpl.java index ff23a5aef4e..742df6dd21f 100644 --- a/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/AbstractEmitterImpl.java +++ b/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/AbstractEmitterImpl.java @@ -24,6 +24,7 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Set; import java.util.Stack; import java.util.logging.Level; @@ -236,8 +237,20 @@ public enum TextFlag { private boolean fixedLayout; + private ULocale locale = null; + protected int reportDpi; + protected boolean combineMarginPadding = true; + + protected boolean wrappedTableForMarginPadding = false; + + protected boolean wrappedTableHeaderFooter = false; + + protected boolean addEmptyParagraphToForAllCells = false; + + protected boolean addEmptyParagraphToListCell=false; + /** * The original DOCX emitters generated output for word version 2010. Newer * version of Microsoft Word render these files incorrectly the compatibility @@ -265,8 +278,6 @@ public enum TextFlag { private static final String URL_PROTOCOL_URL_ENCODED_SPACE = "%20"; - private ULocale locale = null; - /** * Initialize of the service * @@ -324,6 +335,28 @@ public void start(IReportContent report) { } fixedLayout = IHTMLRenderOption.LAYOUT_PREFERENCE_FIXED.equals(layoutPreference); } + + // foreign text: wrap HTML-text with table to simulate margin & padding + if (EmitterServices.booleanOption(null, report, DocEmitter.WORD_MARGIN_PADDING_WRAPPED_TABLE, false)) { + wrappedTableForMarginPadding = true; + } + // foreign text: use for indent calculation margin & padding + if (!wrappedTableForMarginPadding && EmitterServices.booleanOption(null, report, DocEmitter.WORD_MARGIN_PADDING_COMBINE, false)) { + combineMarginPadding = true; + } + // header & footer: wrap header and footer with table + if (EmitterServices.booleanOption(null, report, DocEmitter.WORD_HEADER_FOOTER_WRAPPED_TABLE, false)) { + wrappedTableHeaderFooter = true; + } + // list: add empty paragraph to list table cell + if (EmitterServices.booleanOption(null, report, DocEmitter.WORD_ADD_EMPTY_PARAGRAPH_FOR_ALL_CELLS, false)) { + addEmptyParagraphToForAllCells = true; + } + + // list: add empty paragraph to list table cell + if (EmitterServices.booleanOption(null, report, DocEmitter.WORD_ADD_EMPTY_PARAGRAPH_FOR_LIST_CELL, false)) { + addEmptyParagraphToListCell = true; + } } /** @@ -775,6 +808,7 @@ private DiagonalLineInfo createDiagonalLineInfo(ICellContent cell, double cellWi return diagonalLineInfo; } + @SuppressWarnings("unused") private void drawDiagonalLine(ICellContent cell, double cellWidth) { if (cellWidth == 0) { return; @@ -899,7 +933,15 @@ private void writeTableToc() { public void endCell(ICellContent cell) { adjustInline(); context.removeWidth(); - wordWriter.endTableCell(context.needEmptyP()); + if (addEmptyParagraphToForAllCells) { + wordWriter.endTableCell(context.needEmptyP()); + } else { + boolean needEmptyPara = !cell.hasChildren(); + if (needEmptyPara) { + needEmptyPara = context.needEmptyP(); + } + wordWriter.endTableCell(needEmptyPara, true); + } context.endCell(); } @@ -927,7 +969,12 @@ public void endGroup(IGroupContent group) { */ public void endList(IListContent list) { adjustInline(); - wordWriter.endTableCell(context.needEmptyP()); + // main handle of the usage of empty list cell paragraph + boolean needEmptyP = addEmptyParagraphToListCell; + if (needEmptyP) { + needEmptyP = context.needEmptyP(); + } + wordWriter.endTableCell(needEmptyP); context.endCell(); wordWriter.endTableRow(); if (!styles.isEmpty()) { diff --git a/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/DocEmitter.java b/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/DocEmitter.java index a14e06df55c..fac4bceb5ec 100644 --- a/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/DocEmitter.java +++ b/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/DocEmitter.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006 Inetsoft Technology Corp. + * Copyright (c) 2006, 2024 Inetsoft Technology Corp and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -42,12 +42,59 @@ import org.eclipse.birt.report.engine.internal.content.wrap.TableContentWrapper; import org.eclipse.birt.report.engine.presentation.ContentEmitterVisitor; +/** + * Representation of standard doc emitter + * + * @since 3.3 + * + */ public class DocEmitter extends ContentEmitterAdapter { - private static Logger logger = Logger.getLogger(DocEmitter.class.getName()); - + /** property: DocxEmitter maximum column count */ public final static int MAX_COLUMN = 63; + /** + * property: Word emitter, use wrapped styling table to get margin and padding + * (standard up to BIRT 4.17) + * + * @since 4.18 + */ + public static final String WORD_MARGIN_PADDING_WRAPPED_TABLE = "WordEmitter.WrappedTableForMarginPadding"; + + /** + * property: Word emitter, use a standard grid like master structure for header + * and footer area independent of the content (standard up to BIRT 4.17) + * + * @since 4.18 + */ + public static final String WORD_HEADER_FOOTER_WRAPPED_TABLE = "WordEmitter.WrappedTableHeaderFooter"; + + /** + * property: Word emitter, use combined calculation of margin and padding + * + * @since 4.18 + */ + public static final String WORD_MARGIN_PADDING_COMBINE = "WordEmitter.CombineMarginPadding"; + + /** + * property: Word emitter, add empty paragraph for all cells independent of the + * cell content (standard up to BIRT 4.17) + * + * @since 4.18 + */ + public static final String WORD_ADD_EMPTY_PARAGRAPH_FOR_ALL_CELLS = "WordEmitter.AddEmptyParagraphForAllCells"; + + /** + * property: Word emitter, add empty paragraph for the list table cell + * independent of the cell content (standard up to BIRT 4.17) + * + * @since 4.18 + */ + public static final String WORD_ADD_EMPTY_PARAGRAPH_FOR_LIST_CELL = "WordEmitter.AddEmptyParagraphForListCell"; + + + private static Logger logger = Logger.getLogger(DocEmitter.class.getName()); + protected AbstractEmitterImpl emitterImplement = null; protected ContentEmitterVisitor contentVisitor; @@ -140,6 +187,11 @@ public void endGroup(IGroupContent group) { emitterImplement.endGroup(group); } + /** + * Compute accounted page properties + * + * @param page page content + */ public void accountPageProp(IPageContent page) { emitterImplement.computePageProperties(page); } @@ -329,7 +381,7 @@ public void startTable(ITableContent table) { } private ITableContent getPartTable(ITableContent table) { - List columns = table.getColumns(); + List columns = table.getColumns(); columns = columns.subList(0, MAX_COLUMN); ITableContent content = new TableContentWrapper(table, columns); return content; diff --git a/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/EmitterServices.java b/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/EmitterServices.java new file mode 100644 index 00000000000..f0df13bfafd --- /dev/null +++ b/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/EmitterServices.java @@ -0,0 +1,418 @@ +/************************************************************************************* + * Copyright (c) 2024 Thomas Gutmann. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Thomas Gutmann - Initial implementation. + ************************************************************************************/ + +package org.eclipse.birt.report.engine.emitter.wpml; + +import java.util.Map; + +import org.eclipse.birt.report.engine.api.ITaskOption; +import org.eclipse.birt.report.engine.content.IContent; +import org.eclipse.birt.report.engine.content.IElement; +import org.eclipse.birt.report.engine.content.IReportContent; +import org.eclipse.birt.report.engine.ir.Expression; +import org.eclipse.birt.report.engine.ir.ReportElementDesign; +import org.eclipse.birt.report.engine.ir.ReportItemDesign; + +/** + * Emitter service to handle the emitter configuration options + * + * @since 4.18 + * + */ +public class EmitterServices { + + /** + * Convert an Object to a boolean, with quite a few options about the class of + * the Object. + * + * @param options The task options to extract the value from. + * @param birtContent The leaf node to look for UserProperties + * @param name The name of the value to extract from options. + * @param defaultValue Value to return if value is null. + * @return true if value in some way represents a boolean TRUE value. + */ + public static boolean booleanOption(ITaskOption options, IContent birtContent, String name, boolean defaultValue) { + boolean result = defaultValue; + Object value = null; + + IElement currentElement = birtContent; + + while ((currentElement != null) && (value == null)) { + if (currentElement instanceof IContent) { + Object designObject = ((IContent) currentElement).getGenerateBy(); + if (designObject instanceof ReportElementDesign) { + Map userProperties = ((ReportElementDesign) designObject).getUserProperties(); + if (userProperties != null) { + Expression expression = userProperties.get(name); + if (expression instanceof Expression.Constant) { + Expression.Constant constant = (Expression.Constant) expression; + value = constant.getValue(); + } + } + } + } + if (value == null) { + currentElement = currentElement.getParent(); + } + } + if ((value == null) && (birtContent != null)) { + Map userProperties = birtContent.getReportContent().getDesign().getUserProperties(); + if (userProperties != null) { + Expression expression = userProperties.get(name); + if (expression instanceof Expression.Constant) { + Expression.Constant constant = (Expression.Constant) expression; + value = constant.getValue(); + } + } + } + + if ((value == null) && (options != null)) { + value = options.getOption(name); + } + + if (birtContent != null && birtContent.getReportContent() != null && value == null) { + value = getReportDesignConfiguration(birtContent.getReportContent(), name); + } + + if (value != null) { + result = booleanOption(value, defaultValue); + } + + return result; + } + + /** + * Convert an Object to a boolean, with quite a few options about the class of + * the Object. + * + * @param options The task options to extract the value from. + * @param reportContent The report node to look for UserProperties + * @param name The name of the value to extract from options. + * @param defaultValue Value to return if value is null. + * @return true if value in some way represents a boolean TRUE value. + */ + public static boolean booleanOption(ITaskOption options, IReportContent reportContent, String name, + boolean defaultValue) { + boolean result = defaultValue; + Object value = null; + + if (reportContent != null) { + Map userProperties = reportContent.getDesign().getUserProperties(); + if (userProperties != null) { + Expression expression = userProperties.get(name); + if (expression instanceof Expression.Constant) { + Expression.Constant constant = (Expression.Constant) expression; + value = constant.getValue(); + } + } + } + + if ((value == null) && (options != null)) { + value = options.getOption(name); + } + + if (reportContent != null && value == null) { + value = getReportDesignConfiguration(reportContent, name); + } + + if (value != null) { + result = booleanOption(value, defaultValue); + } + + return result; + } + + /** + * Search for an emitter option and return it as a string + * + * @param options The task options to extract the value from. + * @param birtContent The leaf node to look for UserProperties + * @param name The name of the value to extract from options. + * @param defaultValue Value to return if value is null. + * @return a string, or the defaultValue + */ + public static String stringOption(ITaskOption options, IContent birtContent, String name, String defaultValue) { + String result = defaultValue; + Object value = null; + + IElement currentElement = birtContent; + + while ((currentElement != null) && (value == null)) { + if (currentElement instanceof IContent) { + Object designObject = ((IContent) currentElement).getGenerateBy(); + if (designObject instanceof ReportElementDesign) { + Map userProperties = ((ReportElementDesign) designObject).getUserProperties(); + if (userProperties != null) { + Expression expression = userProperties.get(name); + if (expression instanceof Expression.Constant) { + Expression.Constant constant = (Expression.Constant) expression; + value = constant.getValue(); + } + } + } + } + if (value == null) { + currentElement = currentElement.getParent(); + } + } + if ((value == null) && (birtContent != null)) { + Map userProperties = birtContent.getReportContent().getDesign().getUserProperties(); + if (userProperties != null) { + Expression expression = userProperties.get(name); + if (expression instanceof Expression.Constant) { + Expression.Constant constant = (Expression.Constant) expression; + value = constant.getValue(); + } + } + } + + if ((value == null) && (options != null)) { + value = options.getOption(name); + } + + if (birtContent != null && birtContent.getReportContent() != null && value == null) { + value = getReportDesignConfiguration(birtContent.getReportContent(), name); + } + + if (value != null) { + result = value.toString(); + } + + return result; + } + + /** + * Search for an emitter option and return it as a string + * + * @param options The task options to extract the value from. + * @param reportContent The report + * @param name The name of the value to extract from options. + * @param defaultValue Value to return if value is null. + * @return a string, or the defaultValue + */ + public static String stringOption(ITaskOption options, IReportContent reportContent, String name, + String defaultValue) { + String result = defaultValue; + Object value = null; + + if (reportContent != null) { + Map userProperties = reportContent.getDesign().getUserProperties(); + if (userProperties != null) { + Expression expression = userProperties.get(name); + if (expression instanceof Expression.Constant) { + Expression.Constant constant = (Expression.Constant) expression; + value = constant.getValue(); + } + } + } + + if ((value == null) && (options != null)) { + value = options.getOption(name); + } + + if (reportContent != null && value == null) { + value = getReportDesignConfiguration(reportContent, name); + } + + if (value != null) { + result = value.toString(); + } + + return result; + } + + /** + * Search for an emitter option and return it as an integer + * + * @param options The task options to extract the value from. + * @param birtContent The leaf node to look for UserProperties + * @param name The name of the value to extract from options. + * @param defaultValue Value to return if value is null. + * @return an integer, or the defaultValue + */ + public static int integerOption(ITaskOption options, IContent birtContent, String name, int defaultValue) { + int result = defaultValue; + Object value = null; + + IElement currentElement = birtContent; + + while ((currentElement != null) && (value == null)) { + if (currentElement instanceof IContent) { + Object designObject = ((IContent) currentElement).getGenerateBy(); + if (designObject instanceof ReportElementDesign) { + Map userProperties = ((ReportElementDesign) designObject).getUserProperties(); + if (userProperties != null) { + Expression expression = userProperties.get(name); + if (expression instanceof Expression.Constant) { + Expression.Constant constant = (Expression.Constant) expression; + value = constant.getValue(); + } + } + } + } + if (value == null) { + currentElement = currentElement.getParent(); + } + } + if ((value == null) && (birtContent != null)) { + Map userProperties = birtContent.getReportContent().getDesign().getUserProperties(); + if (userProperties != null) { + Expression expression = userProperties.get(name); + if (expression instanceof Expression.Constant) { + Expression.Constant constant = (Expression.Constant) expression; + value = constant.getValue(); + } + } + } + + if ((value == null) && (options != null)) { + value = options.getOption(name); + } + + if (birtContent != null && birtContent.getReportContent() != null && value == null) { + value = getReportDesignConfiguration(birtContent.getReportContent(), name); + } + + if (value instanceof Number) { + result = ((Number) value).intValue(); + } else if (value != null) { + try { + result = Integer.parseInt(value.toString()); + } catch (Exception ex) { + } + } + + return result; + } + + /** + * Convert an Object to a boolean, with quite a few options about the class of + * the Object. + * + * @param value A value that can be of any type. + * @param defaultValue Value to return if value is null. + * @return true if value in some way represents a boolean TRUE value. + */ + public static boolean booleanOption(Object value, boolean defaultValue) { + if (value != null) { + if (value instanceof Boolean) { + return ((Boolean) value).booleanValue(); + } + if (value instanceof Number) { + return ((Number) value).doubleValue() != 0.0; + } + if (value != null) { + return Boolean.parseBoolean(value.toString()); + } + } + return defaultValue; + } + + /** + * Get the boolean value of a user property + * + * @param propertyName user property name + * @param defaultValue default of the user property + * @return the user property result of boolean + */ + public static boolean booleanOption(String propertyName, boolean defaultValue) { + return booleanOption(null, propertyName, defaultValue); + } + + /** + * Get the boolean value of a user property + * + * @param birtContent element of the report + * @param propertyName user property name + * @param defaultValue default of the user property + * @return the user property result of boolean + */ + public static boolean booleanOption(IContent birtContent, String propertyName, boolean defaultValue) { + Object value = getUserProperty(birtContent, propertyName); + return booleanOption(value, defaultValue); + } + + /** + * Get the string value of a user property + * + * @param birtContent element of the report + * @param propertyName user property name + * @param defaultValue default of the user property + * @return the user property result as string + */ + public static String stringOption(IContent birtContent, String propertyName, boolean defaultValue) { + String stringValue = null; + Object value = getUserProperty(birtContent, propertyName); + if (value != null) { + stringValue = value.toString(); + } + return stringValue; + } + + /** + * Get the object of the user property + * + * @param birtContent report element, e.g. a IListContent + * @param propName property name + * @return user property object independent of the type + */ + protected static Object getUserProperty(IContent birtContent, String propName) { + Object value = null; + + Map userprops = birtContent.getUserProperties(); + if (userprops != null) { + value = userprops.get(propName); + } + if (value == null) { + ReportItemDesign designElem = (ReportItemDesign) birtContent.getGenerateBy(); + if (designElem != null) { + Map designUserprops = designElem.getUserProperties(); + if (designUserprops != null) { + Expression expression = designUserprops.get(propName); + if (expression instanceof Expression.Constant) { + Expression.Constant constant = (Expression.Constant) expression; + value = constant.getValue(); + } + } + } + } + if (value == null) { + Map designUserprops = birtContent.getReportContent().getDesign().getUserProperties(); + if (designUserprops != null) { + Expression expression = designUserprops.get(propName); + if (expression instanceof Expression.Constant) { + Expression.Constant constant = (Expression.Constant) expression; + value = constant.getValue(); + } + } + } + return value; + } + + /* + * Read the configuration from the report design if no user property is set + */ + private static Object getReportDesignConfiguration(IReportContent reportContent, String name) { + Object value = null; + + if (name.equalsIgnoreCase(DocEmitter.WORD_MARGIN_PADDING_WRAPPED_TABLE)) { + value = reportContent.getDesign().getReportDesign().getExcelAutoFilter(); + + } else if (name.equalsIgnoreCase(DocEmitter.WORD_MARGIN_PADDING_COMBINE)) { + value = reportContent.getDesign().getReportDesign().getExcelDisableGrouping(); + + } + return value; + } + +} diff --git a/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/IWordWriter.java b/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/IWordWriter.java index 3b5a1a59b66..11437e98bf3 100644 --- a/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/IWordWriter.java +++ b/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/IWordWriter.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008 Actuate Corporation. + * Copyright (c) 2008, 2024 Actuate Corporation and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -340,6 +340,16 @@ void drawImage(byte[] data, double height, double width, HyperlinkInfo hyper, IS */ void writeForeign(IForeignContent foreignContent, boolean wrappedTable); + /** + * Write foreign + * + * @param foreignContent foreign content + * @param wrappedTable foreign text is wrapped with table + * @param combineMarginPadding foreign text handling of margin and + * padding + */ + void writeForeign(IForeignContent foreignContent, boolean wrappedTable, boolean combineMarginPadding); + /** * Write content * diff --git a/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/WordUtil.java b/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/WordUtil.java index 47484c1b049..ee7a7c06afe 100644 --- a/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/WordUtil.java +++ b/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/WordUtil.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006 Inetsoft Technology Corp. + * Copyright (c) 2006, 2024 Inetsoft Technology Corp and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -399,4 +399,30 @@ public static boolean isField(IContent content) { } return false; } + + /** + * Converting method to calculate the pt value of given unit + * + * @param size size value + * @return size of pt + */ + public static int convertToPt(String size) { + try { + int s = Integer.parseInt(size.substring(0, size.length() - 2)); + if (size.endsWith("in")) { + return s * 72; + } else if (size.endsWith("cm")) { + return (int) (s / 2.54 * 72); + } else if (size.endsWith("mm")) { + return (int) (s * 10 / 2.54 * 72); + } else if (size.endsWith("pc")) { + return s; + } else { + return s; + } + } catch (Exception e) { + return 0; + } + } + } diff --git a/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/writer/DocWriter.java b/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/writer/DocWriter.java index d45a4d573a8..7595df000a7 100644 --- a/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/writer/DocWriter.java +++ b/engine/org.eclipse.birt.report.engine.emitter.wpml/src/org/eclipse/birt/report/engine/emitter/wpml/writer/DocWriter.java @@ -556,6 +556,10 @@ public void writeForeign(IForeignContent foreignContent) { public void writeForeign(IForeignContent foreignContent, boolean wrappedTable) { } + @Override + public void writeForeign(IForeignContent foreignContent, boolean wrappedTable, boolean combineMarginPadding) { + } + @Override public void writePageBorders(IStyle style, int topMargin, int bottomMargin, int leftMargin, int rightMargin) { // TODO Auto-generated method stub