diff --git a/nop-commons/src/main/java/io/nop/commons/bytes/ByteString.java b/nop-commons/src/main/java/io/nop/commons/bytes/ByteString.java index a3604b5ac..781621a1b 100644 --- a/nop-commons/src/main/java/io/nop/commons/bytes/ByteString.java +++ b/nop-commons/src/main/java/io/nop/commons/bytes/ByteString.java @@ -85,7 +85,7 @@ public static ByteString from(Object value, Function er } public boolean isEmpty() { - return bytes.length <= 0; + return bytes.length == 0; } public int size() { diff --git a/nop-core/src/main/java/io/nop/core/lang/xml/XNode.java b/nop-core/src/main/java/io/nop/core/lang/xml/XNode.java index 152a500b1..541278dd3 100644 --- a/nop-core/src/main/java/io/nop/core/lang/xml/XNode.java +++ b/nop-core/src/main/java/io/nop/core/lang/xml/XNode.java @@ -43,10 +43,10 @@ import io.nop.core.model.tree.TreeVisitResult; import io.nop.core.model.tree.TreeVisitors; import io.nop.core.resource.IResource; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import jakarta.annotation.Nonnull; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; @@ -393,6 +393,10 @@ public void content(SourceLocation loc, Object value) { this.content = content == null || content.isEmpty() ? NULL_VALUE : content; } + public int contentAsInt(int defaultValue) { + return ConvertHelper.toPrimitiveInt(getContentValue(), defaultValue, err -> new NopException(err).param(ARG_NODE, this)); + } + public boolean isCDataText() { return content.isCDataText(); } diff --git a/nop-core/src/main/java/io/nop/core/resource/impl/ByteArrayResource.java b/nop-core/src/main/java/io/nop/core/resource/impl/ByteArrayResource.java index d5e3d5e21..411a9271a 100644 --- a/nop-core/src/main/java/io/nop/core/resource/impl/ByteArrayResource.java +++ b/nop-core/src/main/java/io/nop/core/resource/impl/ByteArrayResource.java @@ -38,6 +38,10 @@ public ByteArrayResource(String path, byte[] data, long lastModified) { this.lastModified = lastModified; } + public byte[] getData(){ + return data; + } + @Override protected Object internalObj() { return data; diff --git a/nop-excel/src/main/java/io/nop/excel/model/ExcelClientAnchor.java b/nop-excel/src/main/java/io/nop/excel/model/ExcelClientAnchor.java index c9b07f14d..5385d30c1 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/ExcelClientAnchor.java +++ b/nop-excel/src/main/java/io/nop/excel/model/ExcelClientAnchor.java @@ -7,10 +7,30 @@ */ package io.nop.excel.model; +import io.nop.core.model.table.CellPosition; import io.nop.excel.model._gen._ExcelClientAnchor; public class ExcelClientAnchor extends _ExcelClientAnchor { public ExcelClientAnchor() { } + + public ExcelClientAnchor copy(){ + ExcelClientAnchor ret = new ExcelClientAnchor(); + ret.setRow1(getRow1()); + ret.setCol1(getCol1()); + + ret.setRowDelta(getRowDelta()); + ret.setColDelta(getColDelta()); + ret.setDx1(getDx1()); + ret.setDx2(getDx2()); + ret.setDy1(getDy1()); + ret.setDy2(getDy2()); + ret.setType(getType()); + return ret; + } + + public CellPosition getStartPosition() { + return CellPosition.of(getRow1(), getCol1()); + } } diff --git a/nop-excel/src/main/java/io/nop/excel/model/ExcelImage.java b/nop-excel/src/main/java/io/nop/excel/model/ExcelImage.java index 2e906f5b8..02429f58b 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/ExcelImage.java +++ b/nop-excel/src/main/java/io/nop/excel/model/ExcelImage.java @@ -10,7 +10,20 @@ import io.nop.excel.model._gen._ExcelImage; public class ExcelImage extends _ExcelImage { + /** + * 嵌入图片的id + */ + private String embedId; + public ExcelImage() { } + + public String getEmbedId() { + return embedId; + } + + public void setEmbedId(String embedId) { + this.embedId = embedId; + } } diff --git a/nop-excel/src/main/java/io/nop/excel/model/IExcelSheet.java b/nop-excel/src/main/java/io/nop/excel/model/IExcelSheet.java index edf16899d..6585a7298 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/IExcelSheet.java +++ b/nop-excel/src/main/java/io/nop/excel/model/IExcelSheet.java @@ -9,6 +9,8 @@ import io.nop.core.model.table.ITableView; +import java.util.List; + public interface IExcelSheet { String getName(); @@ -23,4 +25,6 @@ public interface IExcelSheet { Double getDefaultColumnWidth(); ITableView getTable(); + + List getImages(); } diff --git a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelAnnotation.java b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelAnnotation.java index 0400b0f3b..e9e19cb4d 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelAnnotation.java +++ b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelAnnotation.java @@ -7,7 +7,7 @@ // tell cpd to start ignoring code - CPD-OFF /** - * generate from [152:18:0:0]/nop/schema/excel/workbook.xdef

+ * generate from [156:18:0:0]/nop/schema/excel/workbook.xdef

* 行区间的类型标注。例如标记表头,表旁,表尾等 */ @SuppressWarnings({"PMD.UselessOverridingMethod","PMD.UnusedLocalVariable", diff --git a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelClientAnchor.java b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelClientAnchor.java index fd842c28f..fc9e82215 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelClientAnchor.java +++ b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelClientAnchor.java @@ -7,7 +7,7 @@ // tell cpd to start ignoring code - CPD-OFF /** - * generate from [126:22:0:0]/nop/schema/excel/workbook.xdef

+ * generate from [130:22:0:0]/nop/schema/excel/workbook.xdef

* */ @SuppressWarnings({"PMD.UselessOverridingMethod","PMD.UnusedLocalVariable", diff --git a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelConditionalStyle.java b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelConditionalStyle.java index 9e1c27390..e8045b455 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelConditionalStyle.java +++ b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelConditionalStyle.java @@ -7,7 +7,7 @@ // tell cpd to start ignoring code - CPD-OFF /** - * generate from [142:18:0:0]/nop/schema/excel/workbook.xdef

+ * generate from [146:18:0:0]/nop/schema/excel/workbook.xdef

* 当条件满足时,将对指定区间单元格的样式进行增量修改 */ @SuppressWarnings({"PMD.UselessOverridingMethod","PMD.UnusedLocalVariable", diff --git a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelHeaderFooter.java b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelHeaderFooter.java index 3ca4849c8..a12b7b665 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelHeaderFooter.java +++ b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelHeaderFooter.java @@ -7,7 +7,7 @@ // tell cpd to start ignoring code - CPD-OFF /** - * generate from [165:18:0:0]/nop/schema/excel/workbook.xdef

+ * generate from [169:18:0:0]/nop/schema/excel/workbook.xdef

* */ @SuppressWarnings({"PMD.UselessOverridingMethod","PMD.UnusedLocalVariable", diff --git a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelImage.java b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelImage.java index 6fb943de9..b5526df1a 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelImage.java +++ b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelImage.java @@ -7,7 +7,7 @@ // tell cpd to start ignoring code - CPD-OFF /** - * generate from [125:18:0:0]/nop/schema/excel/workbook.xdef

+ * generate from [128:18:0:0]/nop/schema/excel/workbook.xdef

* */ @SuppressWarnings({"PMD.UselessOverridingMethod","PMD.UnusedLocalVariable", @@ -21,17 +21,10 @@ public abstract class _ExcelImage extends io.nop.core.resource.component.Abstrac */ private io.nop.excel.model.ExcelClientAnchor _anchor ; - /** - * - * xml name: autoSize - * - */ - private boolean _autoSize = false; - /** * * xml name: data - * + * data为对应图片数据 */ private io.nop.commons.bytes.ByteString _data ; @@ -51,10 +44,10 @@ public abstract class _ExcelImage extends io.nop.core.resource.component.Abstrac /** * - * xml name: id + * xml name: imgType * */ - private java.lang.String _id ; + private java.lang.String _imgType ; /** * @@ -65,52 +58,61 @@ public abstract class _ExcelImage extends io.nop.core.resource.component.Abstrac /** * - * xml name: testExpr + * xml name: name * */ - private io.nop.core.lang.eval.IEvalPredicate _testExpr ; + private java.lang.String _name ; /** * - * xml name: title + * xml name: noChangeAspect * */ - private java.lang.String _title ; + private boolean _noChangeAspect = false; /** - * - * xml name: anchor * + * xml name: print + * 控制图片是否被打印。套打对应的背景图片不需要被打印 */ + private boolean _print = true; - public io.nop.excel.model.ExcelClientAnchor getAnchor(){ - return _anchor; - } - + /** + * + * xml name: ref + * + */ + private java.lang.String _ref ; - public void setAnchor(io.nop.excel.model.ExcelClientAnchor value){ - checkAllowChange(); - - this._anchor = value; - - } - + /** + * + * xml name: rotateDegree + * + */ + private double _rotateDegree = 0.0; + + /** + * + * xml name: testExpr + * + */ + private io.nop.core.lang.eval.IEvalPredicate _testExpr ; /** * - * xml name: autoSize + * xml name: anchor * */ - public boolean isAutoSize(){ - return _autoSize; + public io.nop.excel.model.ExcelClientAnchor getAnchor(){ + return _anchor; } - public void setAutoSize(boolean value){ + public void setAnchor(io.nop.excel.model.ExcelClientAnchor value){ checkAllowChange(); - this._autoSize = value; + this._anchor = value; } @@ -118,7 +120,7 @@ public void setAutoSize(boolean value){ /** * * xml name: data - * + * data为对应图片数据 */ public io.nop.commons.bytes.ByteString getData(){ @@ -174,19 +176,19 @@ public void setDescription(java.lang.String value){ /** * - * xml name: id + * xml name: imgType * */ - public java.lang.String getId(){ - return _id; + public java.lang.String getImgType(){ + return _imgType; } - public void setId(java.lang.String value){ + public void setImgType(java.lang.String value){ checkAllowChange(); - this._id = value; + this._imgType = value; } @@ -212,38 +214,114 @@ public void setLinkUrl(java.lang.String value){ /** * - * xml name: testExpr + * xml name: name * */ - public io.nop.core.lang.eval.IEvalPredicate getTestExpr(){ - return _testExpr; + public java.lang.String getName(){ + return _name; } - public void setTestExpr(io.nop.core.lang.eval.IEvalPredicate value){ + public void setName(java.lang.String value){ checkAllowChange(); - this._testExpr = value; + this._name = value; } /** * - * xml name: title + * xml name: noChangeAspect * */ - public java.lang.String getTitle(){ - return _title; + public boolean isNoChangeAspect(){ + return _noChangeAspect; } - public void setTitle(java.lang.String value){ + public void setNoChangeAspect(boolean value){ checkAllowChange(); - this._title = value; + this._noChangeAspect = value; + + } + + + /** + * + * xml name: print + * 控制图片是否被打印。套打对应的背景图片不需要被打印 + */ + + public boolean isPrint(){ + return _print; + } + + + public void setPrint(boolean value){ + checkAllowChange(); + + this._print = value; + + } + + + /** + * + * xml name: ref + * + */ + + public java.lang.String getRef(){ + return _ref; + } + + + public void setRef(java.lang.String value){ + checkAllowChange(); + + this._ref = value; + + } + + + /** + * + * xml name: rotateDegree + * + */ + + public double getRotateDegree(){ + return _rotateDegree; + } + + + public void setRotateDegree(double value){ + checkAllowChange(); + + this._rotateDegree = value; + + } + + + /** + * + * xml name: testExpr + * + */ + + public io.nop.core.lang.eval.IEvalPredicate getTestExpr(){ + return _testExpr; + } + + + public void setTestExpr(io.nop.core.lang.eval.IEvalPredicate value){ + checkAllowChange(); + + this._testExpr = value; } @@ -264,14 +342,17 @@ protected void outputJson(IJsonHandler out){ super.outputJson(out); out.put("anchor",this.getAnchor()); - out.put("autoSize",this.isAutoSize()); out.put("data",this.getData()); out.put("dataExpr",this.getDataExpr()); out.put("description",this.getDescription()); - out.put("id",this.getId()); + out.put("imgType",this.getImgType()); out.put("linkUrl",this.getLinkUrl()); + out.put("name",this.getName()); + out.put("noChangeAspect",this.isNoChangeAspect()); + out.put("print",this.isPrint()); + out.put("ref",this.getRef()); + out.put("rotateDegree",this.getRotateDegree()); out.put("testExpr",this.getTestExpr()); - out.put("title",this.getTitle()); } } // resume CPD analysis - CPD-ON diff --git a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPageBreaks.java b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPageBreaks.java index 0cc7c3a08..d2f0408ea 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPageBreaks.java +++ b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPageBreaks.java @@ -7,7 +7,7 @@ // tell cpd to start ignoring code - CPD-OFF /** - * generate from [185:14:0:0]/nop/schema/excel/workbook.xdef

+ * generate from [189:14:0:0]/nop/schema/excel/workbook.xdef

* */ @SuppressWarnings({"PMD.UselessOverridingMethod","PMD.UnusedLocalVariable", diff --git a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPageMargins.java b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPageMargins.java index a6a82a8e4..72c483067 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPageMargins.java +++ b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPageMargins.java @@ -7,7 +7,7 @@ // tell cpd to start ignoring code - CPD-OFF /** - * generate from [178:14:0:0]/nop/schema/excel/workbook.xdef

+ * generate from [182:14:0:0]/nop/schema/excel/workbook.xdef

* */ @SuppressWarnings({"PMD.UselessOverridingMethod","PMD.UnusedLocalVariable", diff --git a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPageSetup.java b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPageSetup.java index 1a73cf293..4a527cfbd 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPageSetup.java +++ b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPageSetup.java @@ -7,7 +7,7 @@ // tell cpd to start ignoring code - CPD-OFF /** - * generate from [161:14:0:0]/nop/schema/excel/workbook.xdef

+ * generate from [165:14:0:0]/nop/schema/excel/workbook.xdef

* */ @SuppressWarnings({"PMD.UselessOverridingMethod","PMD.UnusedLocalVariable", diff --git a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPrint.java b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPrint.java index 822729c78..edabae863 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPrint.java +++ b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelPrint.java @@ -7,7 +7,7 @@ // tell cpd to start ignoring code - CPD-OFF /** - * generate from [181:14:0:0]/nop/schema/excel/workbook.xdef

+ * generate from [185:14:0:0]/nop/schema/excel/workbook.xdef

* */ @SuppressWarnings({"PMD.UselessOverridingMethod","PMD.UnusedLocalVariable", diff --git a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelSheet.java b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelSheet.java index ec52c5d81..4a0662445 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelSheet.java +++ b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelSheet.java @@ -247,7 +247,7 @@ public java.util.List getImages(){ public void setImages(java.util.List value){ checkAllowChange(); - this._images = KeyedList.fromList(value, io.nop.excel.model.ExcelImage::getId); + this._images = KeyedList.fromList(value, io.nop.excel.model.ExcelImage::getName); } @@ -264,7 +264,7 @@ public void addImage(io.nop.excel.model.ExcelImage item) { checkAllowChange(); java.util.List list = this.getImages(); if (list == null || list.isEmpty()) { - list = new KeyedList<>(io.nop.excel.model.ExcelImage::getId); + list = new KeyedList<>(io.nop.excel.model.ExcelImage::getName); setImages(list); } list.add(item); diff --git a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelSheetOptions.java b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelSheetOptions.java index b6d6a8535..8383e19db 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelSheetOptions.java +++ b/nop-excel/src/main/java/io/nop/excel/model/_gen/_ExcelSheetOptions.java @@ -7,7 +7,7 @@ // tell cpd to start ignoring code - CPD-OFF /** - * generate from [155:14:0:0]/nop/schema/excel/workbook.xdef

+ * generate from [159:14:0:0]/nop/schema/excel/workbook.xdef

* */ @SuppressWarnings({"PMD.UselessOverridingMethod","PMD.UnusedLocalVariable", diff --git a/nop-excel/src/main/java/io/nop/excel/model/_gen/_XptSheetModel.java b/nop-excel/src/main/java/io/nop/excel/model/_gen/_XptSheetModel.java index 10356aefc..3c092fe04 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/_gen/_XptSheetModel.java +++ b/nop-excel/src/main/java/io/nop/excel/model/_gen/_XptSheetModel.java @@ -7,7 +7,7 @@ // tell cpd to start ignoring code - CPD-OFF /** - * generate from [195:14:0:0]/nop/schema/excel/workbook.xdef

+ * generate from [199:14:0:0]/nop/schema/excel/workbook.xdef

* */ @SuppressWarnings({"PMD.UselessOverridingMethod","PMD.UnusedLocalVariable", diff --git a/nop-excel/src/main/java/io/nop/excel/model/_gen/_XptWorkbookModel.java b/nop-excel/src/main/java/io/nop/excel/model/_gen/_XptWorkbookModel.java index d670b477d..311b90517 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/_gen/_XptWorkbookModel.java +++ b/nop-excel/src/main/java/io/nop/excel/model/_gen/_XptWorkbookModel.java @@ -7,7 +7,7 @@ // tell cpd to start ignoring code - CPD-OFF /** - * generate from [214:6:0:0]/nop/schema/excel/workbook.xdef

+ * generate from [218:6:0:0]/nop/schema/excel/workbook.xdef

* */ @SuppressWarnings({"PMD.UselessOverridingMethod","PMD.UnusedLocalVariable", diff --git a/nop-excel/src/main/java/io/nop/excel/model/_gen/_XptXplModel.java b/nop-excel/src/main/java/io/nop/excel/model/_gen/_XptXplModel.java index 6c8d0a562..3e8ea8ee6 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/_gen/_XptXplModel.java +++ b/nop-excel/src/main/java/io/nop/excel/model/_gen/_XptXplModel.java @@ -7,7 +7,7 @@ // tell cpd to start ignoring code - CPD-OFF /** - * generate from [222:14:0:0]/nop/schema/excel/workbook.xdef

+ * generate from [226:14:0:0]/nop/schema/excel/workbook.xdef

* */ @SuppressWarnings({"PMD.UselessOverridingMethod","PMD.UnusedLocalVariable", diff --git a/nop-excel/src/main/java/io/nop/excel/model/constants/ExcelAnchorType.java b/nop-excel/src/main/java/io/nop/excel/model/constants/ExcelAnchorType.java index 1527a8392..379038a86 100644 --- a/nop-excel/src/main/java/io/nop/excel/model/constants/ExcelAnchorType.java +++ b/nop-excel/src/main/java/io/nop/excel/model/constants/ExcelAnchorType.java @@ -9,22 +9,25 @@ public enum ExcelAnchorType { /** + * 表示图像或形状的编辑行为将基于两个单元格的范围进行调整和移动。这意味着当调整单元格大小或移动单元格时,图像或形状将相应地调整和移动。 * Move and Resize With Anchor Cells (0) *

* Specifies that the current drawing shall move and resize to maintain its row and column anchors (i.e. the object * is anchored to the actual from and to row and column) *

*/ - MOVE_AND_RESIZE(0), + twoCell(0), /** + * 表示图像或形状的编辑行为将基于一个单元格的范围进行调整和移动。这意味着当调整单元格大小时,图像或形状将自动调整大小,但不会随着单元格的移动而移动 + * * Don't Move but do Resize With Anchor Cells (1) *

* Specifies that the current drawing shall not move with its row and column, but should be resized. This option is * not normally used, but is included for completeness. *

*/ - DONT_MOVE_DO_RESIZE(1), + oneCell(1), /** * Move With Cells but Do Not Resize (2) @@ -37,9 +40,11 @@ public enum ExcelAnchorType { * to anchors as needed to maintain this same absolute size. *

*/ - MOVE_DONT_RESIZE(2), + absolute(2), /** + * 表示图像或形状的编辑行为将保持绝对位置和大小不变。无论单元格如何调整或移动,图像或形状都将保持在原始的位置和大小 + * * Do Not Move or Resize With Underlying Rows/Columns (3) *

* Specifies that the current start and end positions shall be maintained with respect to the distances from the @@ -50,7 +55,7 @@ public enum ExcelAnchorType { * this same absolute position. *

*/ - DONT_MOVE_AND_RESIZE(3); + anchor(3); public final short value; diff --git a/nop-ooxml/nop-ooxml-common/src/main/java/io/nop/ooxml/common/impl/ResourceOfficePackagePart.java b/nop-ooxml/nop-ooxml-common/src/main/java/io/nop/ooxml/common/impl/ResourceOfficePackagePart.java index 96ee50c93..c665e73c0 100644 --- a/nop-ooxml/nop-ooxml-common/src/main/java/io/nop/ooxml/common/impl/ResourceOfficePackagePart.java +++ b/nop-ooxml/nop-ooxml-common/src/main/java/io/nop/ooxml/common/impl/ResourceOfficePackagePart.java @@ -36,7 +36,8 @@ public IOfficePackagePart loadInMemory() { if (resource instanceof ByteArrayResource) return this; - if (resource.getName().endsWith(".xml")) { + String name = resource.getName(); + if (name.endsWith(".xml") || name.endsWith(".rels")) { XNode node = loadXml(); return new XmlOfficePackagePart(path, node); } @@ -55,6 +56,14 @@ public void generateToStream(OutputStream os, IEvalContext context) throws IOExc resource.writeToStream(os); } + @Override + public byte[] generateBytes(IEvalContext context) { + if (resource instanceof ByteArrayResource) + return ((ByteArrayResource) resource).getData(); + + return IOfficePackagePart.super.generateBytes(context); + } + @Override public String getPath() { return path; diff --git a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/model/ExcelOfficePackage.java b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/model/ExcelOfficePackage.java index aed7cb13a..f18324ddf 100644 --- a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/model/ExcelOfficePackage.java +++ b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/model/ExcelOfficePackage.java @@ -11,6 +11,7 @@ import io.nop.excel.model.ExcelStyle; import io.nop.ooxml.common.IOfficePackagePart; import io.nop.ooxml.common.OfficePackage; +import io.nop.ooxml.xlsx.XSSFRelation; import io.nop.ooxml.xlsx.parse.StylesPartParser; import java.util.List; @@ -61,4 +62,11 @@ public ThemesPart getTheme1() { addFile(themes); return themes; } + + public CommentsPart getCommentsTable(IOfficePackagePart sheetPart) { + IOfficePackagePart commentsPart = getRelPartByType(sheetPart, XSSFRelation.SHEET_COMMENTS.getRelation()); + if (commentsPart == null) + return null; + return CommentsPart.parse(commentsPart); + } } \ No newline at end of file diff --git a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/model/drawing/DrawingBuilder.java b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/model/drawing/DrawingBuilder.java new file mode 100644 index 000000000..f24c10ed2 --- /dev/null +++ b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/model/drawing/DrawingBuilder.java @@ -0,0 +1,85 @@ +package io.nop.ooxml.xlsx.model.drawing; + +import io.nop.core.lang.xml.XNode; +import io.nop.excel.model.ExcelClientAnchor; +import io.nop.excel.model.ExcelImage; + +import java.util.List; + +public class DrawingBuilder { + public XNode build(List images) { + XNode node = XNode.makeDocNode("xdr:wsDr"); + node.setAttr("xmlns:xdr", "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"); + node.setAttr("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"); + + for (int i = 0, n = images.size(); i < n; i++) { + XNode anchor = buildAnchor(images.get(0), i); + node.appendChild(anchor); + } + return node; + } + + public XNode buildAnchor(ExcelImage image, int index) { + XNode anchor = buildAnchor0(image.getAnchor()); + + XNode pic = anchor.addChild("xdr:pic"); + XNode nvPicPr = pic.addChild("xdr:nvPicPr"); + XNode cNvPr = nvPicPr.addChild("xdr:cNvPr"); + cNvPr.setAttr("id", index); + cNvPr.setAttr("name", image.getName()); + cNvPr.setAttr("descr", image.getDescription()); + + XNode cNvPicPr = nvPicPr.addChild("xdr:cNvPicPr"); + cNvPicPr.addChild("a:picLocks").setAttr("noChangeAspect", image.isNoChangeAspect() ? 1 : 0); + + int rot = (int) (image.getRotateDegree() * 60000); + XNode blipFill = pic.addChild("xdr:blipFill"); + if (rot > 0) { + blipFill.setAttr("rotWithShape", 1); + } + XNode blip = blipFill.addChild("a:blip"); + blip.setAttr("r:embed", image.getEmbedId()); + blipFill.addChild("a:stretch"); + + XNode spPr = pic.addChild("xdr:spPr"); + XNode xfrm = spPr.addChild("a:xfrm"); + if (rot > 0) { + xfrm.setAttr("rot", rot); + } + XNode off = xfrm.addChild("a:off"); + off.setAttr("x", 0); + off.setAttr("y", 0); + XNode ext = xfrm.addChild("a:ext"); + ext.setAttr("cx", 0); + ext.setAttr("cy", 0); + + XNode prstGeom = spPr.addChild("a:prstGeom"); + prstGeom.setAttr("prst", "rect"); + prstGeom.addChild("a:avLst"); + + XNode clientData = anchor.addChild("xdr:clientData"); + clientData.setAttr("fPrintsWithSheet", image.isPrint() ? 0 : 1); + return anchor; + } + + private XNode buildAnchor0(ExcelClientAnchor anchor) { + XNode node = XNode.make("xdr:twoCellAnchor"); + node.setAttr("editAs", anchor.getType()); + + XNode from = XNode.make("xdr:from"); + from.addChild("xdr:col").content(anchor.getCol1()); + from.addChild("xdr:colOff").content(anchor.getDx1()); + from.addChild("xdr:row").content(anchor.getRow1()); + from.addChild("xdr:rowOff").content(anchor.getDy1()); + + node.appendChild(from); + + XNode to = XNode.make("xdr:to"); + to.addChild("xdr:col").content(anchor.getCol2()); + to.addChild("xdr:colOff").content(anchor.getDx2()); + to.addChild("xdr:row").content(anchor.getRow2()); + to.addChild("xdr:rowOff").content(anchor.getDy2()); + node.appendChild(to); + return node; + } +} \ No newline at end of file diff --git a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/model/drawing/DrawingParser.java b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/model/drawing/DrawingParser.java new file mode 100644 index 000000000..c14099458 --- /dev/null +++ b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/model/drawing/DrawingParser.java @@ -0,0 +1,122 @@ +package io.nop.ooxml.xlsx.model.drawing; + +import io.nop.commons.util.StringHelper; +import io.nop.core.lang.xml.XNode; +import io.nop.excel.model.ExcelClientAnchor; +import io.nop.excel.model.ExcelImage; +import io.nop.excel.model.constants.ExcelAnchorType; +import io.nop.excel.util.UnitsHelper; + +import java.util.ArrayList; +import java.util.List; + +import static io.nop.xlang.xdsl.XDslParseHelper.parseAttrEnumValue; + +/** + * - 是一个命名空间声明,指定了XML片段中使用的命名空间。 + * - 是一个单元格锚定的元素,表示图片在电子表格中的位置。 editAs 属性设置为"oneCell",表示将整个图片作为一个单元格处理。 + * - 标签表示起始单元格的位置,其中 表示列索引, 表示行索引。 + * - 标签表示结束单元格的位置,其中 表示列索引, 表示行索引。 + * - 包含有关图片的信息。 + * - 包含有关图片的非可视化属性。 + * - 指定图片的非可视化属性,如id和名称。 + * - 包含有关图片的非可视化图像属性,如图像锁定。 + * - 指定图片的填充属性。 + * - 指定图片的图像数据,使用 r:embed 属性指定了图片的关联ID。 + * - 指定图片的拉伸属性。 + * - 指定图片的形状属性,如位置和尺寸。 + * - 指定图片的转换属性,如偏移量和扩展。 + * - 指定图片的预设几何属性,如形状类型。 + * - 包含有关图片的客户端数据。 + */ +public class DrawingParser { + public List parseDrawing(XNode node) { + List ret = new ArrayList<>(node.getChildCount()); + + for (XNode child : node.getChildren()) { + if (child.getTagName().equals("xdr:twoCellAnchor")) { + ret.add(parseAnchor(child)); + } + } + return ret; + } + + public ExcelImage parseAnchor(XNode anchorNode) { + XNode clientData = anchorNode.childByTag("xdr:clientData"); + XNode pic = anchorNode.childByTag("xdr:pic"); + XNode picPr = pic.childByTag("xdr:nvPicPr"); + XNode cNvPr = picPr != null ? picPr.childByTag("xdr:cNvPr") : null; + XNode cNvPicPr = picPr != null ? picPr.childByTag("xdr:cNvPicPr") : null; + XNode picLocks = cNvPicPr != null ? cNvPicPr.childByTag("a:picLocks") : null; + + XNode blipFill = pic.childByTag("xdr:blipFill"); + XNode blip = blipFill != null ? blipFill.childByTag("a:blip") : null; + + XNode spPr = pic.childByTag("spPr"); + XNode xfrm = spPr != null ? spPr.childByTag("a:xfrm") : null; + + ExcelImage image = new ExcelImage(); + ExcelClientAnchor anchor = parseAnchor0(anchorNode); + image.setAnchor(anchor); + + if (cNvPr != null) { + String name = cNvPr.attrText("name"); + String desc = cNvPr.attrText("descr"); + image.setDescription(desc); + image.setName(name); + } + + if (image.getName() == null) + image.setName(StringHelper.generateUUID()); + + if (blip != null) { + String embedId = blip.attrText("r:embed"); + image.setEmbedId(embedId); + } + + if (xfrm != null) { + Integer rot = xfrm.attrInt("rot"); + if (rot != null) { + image.setRotateDegree(rot / 60000.0); + } + } + + if (picLocks != null) { + image.setNoChangeAspect(picLocks.attrInt("noChangeAspect", 1) == 1); + } + + if (clientData != null) { + image.setPrint(clientData.attrInt("fPrintsWithSheet", 1) == 1); + } + return image; + } + + ExcelClientAnchor parseAnchor0(XNode anchor) { + ExcelAnchorType anchorType = parseAttrEnumValue(anchor, "editAs", ExcelAnchorType.class); + + XNode from = anchor.childByTag("xdr:from"); + int col1 = from.childByTag("xdr:col").contentAsInt(0); + int row1 = from.childByTag("xdr:row").contentAsInt(0); + int colOff1 = from.childByTag("xdr:colOff").contentAsInt(0); + int rowOff1 = from.childByTag("xdr:rowOff").contentAsInt(0); + + XNode to = anchor.childByTag("xdr:to"); + int col2 = to.childByTag("xdr:col").contentAsInt(0); + int row2 = to.childByTag("xdr:row").contentAsInt(0); + int colOff2 = to.childByTag("xdr:colOff").contentAsInt(0); + int rowOff2 = to.childByTag("xdr:rowOff").contentAsInt(0); + + ExcelClientAnchor ret = new ExcelClientAnchor(); + ret.setType(anchorType); + ret.setCol1(col1); + ret.setRow1(row1); + ret.setColDelta(col2 - col1); + ret.setRowDelta(row2 - row1); + ret.setDx1(UnitsHelper.emuToPoints(colOff1)); + ret.setDy1(UnitsHelper.emuToPoints(rowOff1)); + ret.setDx2(UnitsHelper.emuToPoints(colOff2)); + ret.setDy2(UnitsHelper.emuToPoints(rowOff2)); + + return ret; + } +} diff --git a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/output/ExcelSheetWriter.java b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/output/ExcelSheetWriter.java index bda417ba6..319ded499 100644 --- a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/output/ExcelSheetWriter.java +++ b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/output/ExcelSheetWriter.java @@ -70,6 +70,8 @@ public void generateXml(IXNodeHandler out, IEvalContext context) { genPageMargins(out, sheet); + if (sheet.getImages() != null && !sheet.getImages().isEmpty()) + out.simpleNode(null, "drawing", attrs("r:id", "rId1")); out.endNode("worksheet"); out.endDoc(); } diff --git a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/output/ExcelTemplate.java b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/output/ExcelTemplate.java index 45c7b27e1..3abcdb67e 100644 --- a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/output/ExcelTemplate.java +++ b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/output/ExcelTemplate.java @@ -7,15 +7,18 @@ */ package io.nop.ooxml.xlsx.output; -import io.nop.commons.mutable.MutableInt; +import io.nop.commons.bytes.ByteString; import io.nop.core.context.IEvalContext; +import io.nop.core.lang.xml.XNode; import io.nop.core.resource.IResource; import io.nop.core.resource.impl.FileResource; +import io.nop.excel.model.ExcelImage; import io.nop.excel.model.ExcelSheet; import io.nop.excel.model.ExcelWorkbook; import io.nop.excel.model.IExcelSheet; import io.nop.ooxml.common.IOfficePackagePart; import io.nop.ooxml.common.OfficeConstants; +import io.nop.ooxml.common.impl.XmlOfficePackagePart; import io.nop.ooxml.common.model.ContentTypesPart; import io.nop.ooxml.common.model.OfficeRelsPart; import io.nop.ooxml.common.output.AbstractOfficeTemplate; @@ -23,8 +26,11 @@ import io.nop.ooxml.xlsx.model.ExcelOfficePackage; import io.nop.ooxml.xlsx.model.StylesPart; import io.nop.ooxml.xlsx.model.WorkbookPart; +import io.nop.ooxml.xlsx.model.drawing.DrawingBuilder; import java.io.File; +import java.util.HashMap; +import java.util.Map; import static io.nop.ooxml.common.model.PackagingURIHelper.createPartName; @@ -51,6 +57,14 @@ public ExcelTemplate(ExcelWorkbook workbook) { this(workbook, null); } + static class GenState { + int nextSheetIndex; + Map images = new HashMap<>(); + int nextImageIndex; + + int nextDrawingIndex; + } + @Override public void generateToDir(File dir, IEvalContext context) { ExcelOfficePackage pkg = this.pkg.copy(); @@ -59,17 +73,17 @@ public void generateToDir(File dir, IEvalContext context) { pkg.getWorkbook().clearSheets(); - MutableInt index = new MutableInt(); + GenState genState = new GenState(); if (sheetGenerator != null) { sheetGenerator.generate(context, sheet -> { - generateSheet(pkg, dir, index.get(), sheet, context); - index.incrementAndGet(); + int index = genState.nextSheetIndex++; + generateSheet(pkg, dir, index, sheet, context, genState); }); } else if (workbook != null) { for (ExcelSheet sheet : workbook.getSheets()) { - generateSheet(pkg, dir, index.get(), sheet, context); - index.incrementAndGet(); + int index = genState.nextSheetIndex++; + generateSheet(pkg, dir, index, sheet, context, genState); } } @@ -79,7 +93,8 @@ public void generateToDir(File dir, IEvalContext context) { pkg.generateToDir(dir, context.getEvalScope()); } - private void generateSheet(ExcelOfficePackage pkg, File dir, int index, IExcelSheet sheet, IEvalContext context) { + private void generateSheet(ExcelOfficePackage pkg, File dir, int index, IExcelSheet sheet, + IEvalContext context, GenState genState) { ContentTypesPart contentTypes = pkg.getContentTypes(); int sheetId = index + 1; String sheetPath = "/xl/worksheets/sheet" + sheetId + ".xml"; @@ -98,6 +113,8 @@ private void generateSheet(ExcelOfficePackage pkg, File dir, int index, IExcelSh new ExcelSheetWriter(sheet, index == 0, this.workbook).indent(isIndent()).generateToResource(resource, context); IOfficePackagePart sheetPart = pkg.addFile(sheetPath, resource); + generateDrawings(sheet, sheetPart, genState); + IResource commentResource = new FileResource(new File(dir, commentPath)); new ExcelCommentsWriter(sheet).indent(isIndent()).generateToResource(commentResource, context); pkg.addFile(commentPath, commentResource); @@ -107,4 +124,35 @@ private void generateSheet(ExcelOfficePackage pkg, File dir, int index, IExcelSh sheetRels.removeRelationshipByType(XSSFRelation.SHEET_COMMENTS.getRelation()); sheetRels.addRelationship(XSSFRelation.SHEET_COMMENTS.getRelation(), relCommentsPath, null); } + + private void generateDrawings(IExcelSheet sheet, IOfficePackagePart sheetPart, GenState genState) { + if (sheet.getImages() == null) + return; + + + OfficeRelsPart relPart = pkg.makeRelsForPart(sheetPart); + + for (ExcelImage image : sheet.getImages()) { + if (image.getData() == null) + continue; + String path = addImageData(image.getData(), image.getImgType(), genState); + relPart.addImage(path); + } + + XNode node = new DrawingBuilder().build(sheet.getImages()); + + int drawingIndex = genState.nextDrawingIndex++; + XmlOfficePackagePart part = new XmlOfficePackagePart("xl/drawings/drawing" + (drawingIndex + 1) + ".xml", node); + pkg.addFile(part); + } + + private String addImageData(ByteString data, String imgType, GenState genState) { + String path = genState.images.get(data); + if (path == null) { + int index = genState.nextImageIndex++; + path = "/xl/media/image" + (index + 1) + "." + imgType; + genState.images.put(data, path); + } + return path; + } } \ No newline at end of file diff --git a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/AbstractXlsxParser.java b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/AbstractXlsxParser.java index a0f6613ab..612b4010a 100644 --- a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/AbstractXlsxParser.java +++ b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/AbstractXlsxParser.java @@ -47,21 +47,22 @@ private ExcelWorkbook parseFromPkg(ExcelOfficePackage pkg) { workbookPart = pkg.getWorkbook(); ExcelWorkbook wk = new ExcelWorkbook(); + wk.setLocation(pkg.getLocation()); wk.setStyles(pkg.getStyles().getStyles()); for (XSSFSheetRef sheetRef : workbookPart.getSheets()) { ExcelSheet sheet = parseSheet(wk, sheetRef, workbookPart); wk.addSheet(sheet); } + + endParseWorkbook(wk); return wk; } - protected abstract ExcelSheet parseSheet(ExcelWorkbook workbook, XSSFSheetRef sheetRef, WorkbookPart workbookFile); + protected void endParseWorkbook(ExcelWorkbook wk) { - protected CommentsPart getCommentsTable(IOfficePackagePart sheetPart) { - IOfficePackagePart commentsPart = pkg.getRelPartByType(sheetPart, XSSFRelation.SHEET_COMMENTS.getRelation()); - if (commentsPart == null) - return null; - return CommentsPart.parse(commentsPart); } + + protected abstract ExcelSheet parseSheet(ExcelWorkbook workbook, XSSFSheetRef sheetRef, WorkbookPart workbookFile); + } diff --git a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/ExcelWorkbookParser.java b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/ExcelWorkbookParser.java index 94964f0c4..acbdc43b6 100644 --- a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/ExcelWorkbookParser.java +++ b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/ExcelWorkbookParser.java @@ -9,6 +9,9 @@ import io.nop.api.core.exceptions.NopException; import io.nop.api.core.util.SourceLocation; +import io.nop.commons.bytes.ByteString; +import io.nop.commons.util.StringHelper; +import io.nop.core.lang.eval.DisabledEvalScope; import io.nop.core.model.table.CellPosition; import io.nop.core.model.table.CellRange; import io.nop.core.model.table.ICell; @@ -16,6 +19,7 @@ import io.nop.excel.ExcelConstants; import io.nop.excel.model.ExcelCell; import io.nop.excel.model.ExcelColumnConfig; +import io.nop.excel.model.ExcelImage; import io.nop.excel.model.ExcelPageMargins; import io.nop.excel.model.ExcelSheet; import io.nop.excel.model.ExcelStyle; @@ -26,6 +30,7 @@ import io.nop.ooxml.xlsx.model.CommentsPart; import io.nop.ooxml.xlsx.model.WorkbookPart; import io.nop.ooxml.xlsx.model.XSSFSheetRef; +import io.nop.ooxml.xlsx.model.drawing.DrawingParser; import java.util.List; @@ -34,13 +39,12 @@ import static io.nop.ooxml.xlsx.XlsxErrors.ERR_XLSX_NULL_REL_PART; public class ExcelWorkbookParser extends AbstractXlsxParser { - @Override protected ExcelSheet parseSheet(ExcelWorkbook workbook, XSSFSheetRef sheetRef, WorkbookPart workbookFile) { IOfficePackagePart sheetPart = pkg.getRelPart(workbookFile, sheetRef.getRelId()); if (sheetPart == null) throw new NopException(ERR_XLSX_NULL_REL_PART).param(ARG_TYPE, "sheet").param(ARG_REL_ID, sheetRef.getRelId()); - CommentsPart comments = getCommentsTable(sheetPart); + CommentsPart comments = pkg.getCommentsTable(sheetPart); SimpleSheetContentsHandler contentsHandler = new SimpleSheetContentsHandler(workbook, sheetRef.getName()); @@ -65,6 +69,24 @@ protected ExcelSheet parseSheet(ExcelWorkbook workbook, XSSFSheetRef sheetRef, W }); } + if (contentsHandler.getDrawingId() != null) { + IOfficePackagePart drawing = pkg.getRelPart(sheetPart, contentsHandler.getDrawingId()); + if (drawing != null) { + List images = new DrawingParser().parseDrawing(drawing.loadXml()); + for (ExcelImage image : images) { + if (image.getEmbedId() == null) + continue; + + IOfficePackagePart imagePart = pkg.getRelPart(drawing, image.getEmbedId()); + if (imagePart == null) + continue; + image.setImgType(StringHelper.fileExt(imagePart.getPath())); + image.setData(ByteString.of(imagePart.generateBytes(DisabledEvalScope.INSTANCE))); + } + sheet.setImages(images); + } + } + return sheet; } @@ -74,6 +96,8 @@ static class SimpleSheetContentsHandler implements SheetContentsHandler { private final ExcelSheet sheet = new ExcelSheet(); private ExcelTable table = sheet.getTable(); + private String drawingId; + public SimpleSheetContentsHandler(ExcelWorkbook workbook, String sheetName) { this.workbook = workbook; this.sheet.setName(sheetName); @@ -83,6 +107,13 @@ public ExcelSheet getSheet() { return sheet; } + public String getDrawingId() { + return drawingId; + } + + public void drawing(String id) { + this.drawingId = StringHelper.emptyAsNull(id); + } @Override public void startSheet(String sheetName) { diff --git a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/SheetContentsHandler.java b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/SheetContentsHandler.java index 4c737b080..92fbd7bee 100644 --- a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/SheetContentsHandler.java +++ b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/SheetContentsHandler.java @@ -61,6 +61,8 @@ public interface SheetContentsHandler { void mergeCell(CellRange range); + void drawing(String id); + /** * A header or footer has been encountered */ diff --git a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/SheetNodeHandler.java b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/SheetNodeHandler.java index 451d9801e..9baa8e94f 100644 --- a/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/SheetNodeHandler.java +++ b/nop-ooxml/nop-ooxml-xlsx/src/main/java/io/nop/ooxml/xlsx/parse/SheetNodeHandler.java @@ -30,14 +30,11 @@ Licensed to the Apache Software Foundation (ASF) under one or more import io.nop.excel.model.ExcelColumnConfig; import io.nop.excel.model.ExcelPageMargins; import io.nop.excel.util.UnitsHelper; -import io.nop.ooxml.xlsx.model.CommentsPart; import io.nop.ooxml.xlsx.model.SharedStringsPart; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Queue; public class SheetNodeHandler extends XNodeHandlerAdapter { @@ -83,7 +80,7 @@ enum xssfDataType { private final StringBuilder formula = new StringBuilder(64); private final StringBuilder headerFooter = new StringBuilder(64); - private Queue commentCellRefs; +// private Queue commentCellRefs; private List cols; @@ -92,11 +89,11 @@ public SheetNodeHandler(SharedStringsPart sharedStringsTable, SheetContentsHandl this.output = output; } - private void init(CommentsPart commentsTable) { - if (commentsTable != null) { - commentCellRefs = new LinkedList<>(commentsTable.getCellAddresses()); - } - } +// private void init(CommentsPart commentsTable) { +// if (commentsTable != null) { +// commentCellRefs = new LinkedList<>(commentsTable.getCellAddresses()); +// } +// } private boolean isTextTag(String name) { if ("v".equals(name)) { @@ -260,6 +257,10 @@ else if ("str".equals(cellType)) } else if ("sheetFormatPr".equals(localName)) { Double defaultRowHeight = getAttrDouble(attrs, "defaultRowHeight", null); output.sheetFormat(defaultRowHeight); + } else if ("drawing".equals(localName)) { + String id = getAttr(attrs, "r:id"); + if (id != null) + output.drawing(id); } } diff --git a/nop-report/nop-report-core/src/main/java/io/nop/report/core/XptConstants.java b/nop-report/nop-report-core/src/main/java/io/nop/report/core/XptConstants.java index f52bea6a3..f296edf7a 100644 --- a/nop-report/nop-report-core/src/main/java/io/nop/report/core/XptConstants.java +++ b/nop-report/nop-report-core/src/main/java/io/nop/report/core/XptConstants.java @@ -18,6 +18,8 @@ public interface XptConstants { String VAR_XPT_RT = "xptRt"; + String VAR_IMAGE = "image"; + String VAR_ENTITY = "entity"; String VAR_SHEET_TPL = "sheetTpl"; String VAR_WORKBOOK_TPL = "workbookTpl"; @@ -44,6 +46,8 @@ public interface XptConstants { String XDEF_NODE_EXCEL_CELL = "ExcelCell"; + String XDEF_NODE_EXCEL_IMAGE = "ExcelImage"; + String STD_DOMAIN_REPORT_EXPR = "report-expr"; String MODEL_TYPE_XPT = "xpt"; diff --git a/nop-report/nop-report-core/src/main/java/io/nop/report/core/XptErrors.java b/nop-report/nop-report-core/src/main/java/io/nop/report/core/XptErrors.java index 99f794f81..a42ed58a3 100644 --- a/nop-report/nop-report-core/src/main/java/io/nop/report/core/XptErrors.java +++ b/nop-report/nop-report-core/src/main/java/io/nop/report/core/XptErrors.java @@ -73,6 +73,10 @@ public interface XptErrors { define("nop.err.xpt.undefined-cell-model-prop", "未定义的单元格模型的属性[{propName}]", ARG_PROP_NAME); + ErrorCode ERR_XPT_UNDEFINED_IMAGE_MODEL_PROP = + define("nop.err.xpt.undefined-image-model-prop", + "未定义的图片模型的属性[{propName}]", ARG_PROP_NAME); + ErrorCode ERR_XPT_INVALID_DS_NAME = define("nop.err.xpt.invalid-ds-name", "非法的数据源名称", ARG_DS_NAME); @@ -96,4 +100,9 @@ public interface XptErrors { ErrorCode ERR_XPT_UNKNOWN_REPORT_MODEL = define("nop.err.xpt.unknown-report-model", "未知的报表模型:{reportName}", ARG_REPORT_NAME); + + ErrorCode ERR_XPT_INVALID_IMAGE_DATA = + define("nop.err.xpt.invalid-image-data", + "图片数据不是字节数组或者IResource对象"); + } diff --git a/nop-report/nop-report-core/src/main/java/io/nop/report/core/build/ExcelToXptModelTransformer.java b/nop-report/nop-report-core/src/main/java/io/nop/report/core/build/ExcelToXptModelTransformer.java index 991b5db0a..e36053180 100644 --- a/nop-report/nop-report-core/src/main/java/io/nop/report/core/build/ExcelToXptModelTransformer.java +++ b/nop-report/nop-report-core/src/main/java/io/nop/report/core/build/ExcelToXptModelTransformer.java @@ -18,6 +18,7 @@ import io.nop.excel.imp.model.ImportModel; import io.nop.excel.imp.model.ImportSheetModel; import io.nop.excel.model.ExcelCell; +import io.nop.excel.model.ExcelImage; import io.nop.excel.model.ExcelSheet; import io.nop.excel.model.ExcelWorkbook; import io.nop.excel.model.XptCellModel; @@ -38,6 +39,7 @@ import static io.nop.report.core.XptErrors.ARG_PROP_NAME; import static io.nop.report.core.XptErrors.ERR_XPT_UNDEFINED_CELL_MODEL_PROP; +import static io.nop.report.core.XptErrors.ERR_XPT_UNDEFINED_IMAGE_MODEL_PROP; /** * 将Excel模型转换为Xpt报表模型 @@ -48,6 +50,7 @@ public void transform(ExcelWorkbook workbook) { ImportModel importModel = ImportModelHelper.getImportModel(XptConstants.XPT_IMP_MODEL_PATH); IXDefinition xptXDef = SchemaLoader.loadXDefinition(XptConstants.XDSL_SCHEMA_WORKBOOK); IXDefNode cellModelNode = xptXDef.getXdefDefine(XptConstants.XDEF_NODE_EXCEL_CELL).getChild(XptConstants.PROP_MODEL); + IXDefNode imageNode = xptXDef.getXdefDefine(XptConstants.XDEF_NODE_EXCEL_IMAGE); XptConfigParseHelper.parseWorkbookModel(workbook, importModel); @@ -68,6 +71,7 @@ public void transform(ExcelWorkbook workbook) { sheet.setModel(sheetModel); } parseCellModel(sheet, cellModelNode, transformer); + parseImageModel(sheet, imageNode, transformer); } } @@ -140,4 +144,43 @@ private Object addSource(ValueWithLocation vl, Object value) { private T importModel(ImportSheetModel impModel, ExcelSheet sheet, IEvalScope scope, Class clazz) { return ImportModelHelper.parseSheet(impModel, sheet, scope, clazz); } + + private void parseImageModel(ExcelSheet sheet, IXDefNode defNode, DslXNodeToJsonTransformer transformer) { + if (sheet.getImages() == null) + return; + + for (ExcelImage image : sheet.getImages()) { + String desc = image.getDescription(); + if (desc == null) + continue; + + int pos = desc.indexOf("----"); + if (pos < 0) + break; + + for (; pos < desc.length(); pos++) { + if (desc.charAt(pos) != '-') + break; + } + + String str = desc.substring(pos); + Map config = + MultiLineConfigParser.INSTANCE.parseConfig(sheet.getLocation(), str); + + for (Map.Entry entry : config.entrySet()) { + String varName = entry.getKey(); + ValueWithLocation vl = entry.getValue(); + if (varName.equals("testExpr") || varName.equals("dataExpr")) { + IXDefNode child = defNode.getChild(varName); + Object value = transformer.parseValue(vl, varName, child.getXdefValue()); + value = addSource(vl, value); + BeanTool.setProperty(image, varName, value); + } else { + throw new NopException(ERR_XPT_UNDEFINED_IMAGE_MODEL_PROP) + .source(vl) + .param(ARG_PROP_NAME, entry.getKey()); + } + } + } + } } \ No newline at end of file diff --git a/nop-report/nop-report-core/src/main/java/io/nop/report/core/engine/IXptRuntime.java b/nop-report/nop-report-core/src/main/java/io/nop/report/core/engine/IXptRuntime.java index dc7e2d296..6fcb2421d 100644 --- a/nop-report/nop-report-core/src/main/java/io/nop/report/core/engine/IXptRuntime.java +++ b/nop-report/nop-report-core/src/main/java/io/nop/report/core/engine/IXptRuntime.java @@ -9,6 +9,7 @@ import io.nop.core.context.IEvalContext; import io.nop.core.lang.eval.IEvalScope; +import io.nop.excel.model.ExcelImage; import io.nop.excel.model.ExcelWorkbook; import io.nop.report.core.XptConstants; import io.nop.report.core.dataset.DynamicReportDataSet; @@ -31,6 +32,10 @@ static IXptRuntime fromScope(IEvalScope scope){ void setRow(ExpandedRow row); + ExcelImage getImage(); + + void setImage(ExcelImage image); + ExpandedTable getTable(); ExpandedSheet getSheet(); diff --git a/nop-report/nop-report-core/src/main/java/io/nop/report/core/engine/ReportSheetGenerator.java b/nop-report/nop-report-core/src/main/java/io/nop/report/core/engine/ReportSheetGenerator.java index a3531aee3..a793016d5 100644 --- a/nop-report/nop-report-core/src/main/java/io/nop/report/core/engine/ReportSheetGenerator.java +++ b/nop-report/nop-report-core/src/main/java/io/nop/report/core/engine/ReportSheetGenerator.java @@ -8,23 +8,34 @@ package io.nop.report.core.engine; import io.nop.api.core.convert.ConvertHelper; +import io.nop.api.core.exceptions.NopException; import io.nop.api.core.util.Guard; +import io.nop.api.core.util.ProcessResult; +import io.nop.commons.bytes.ByteString; +import io.nop.commons.mutable.MutableInt; import io.nop.commons.util.CollectionHelper; import io.nop.commons.util.StringHelper; import io.nop.core.context.IEvalContext; import io.nop.core.lang.eval.IEvalAction; import io.nop.core.lang.eval.IEvalPredicate; import io.nop.core.lang.eval.IEvalScope; +import io.nop.core.model.table.CellPosition; +import io.nop.core.resource.IResource; +import io.nop.core.resource.ResourceHelper; +import io.nop.excel.model.ExcelClientAnchor; +import io.nop.excel.model.ExcelImage; import io.nop.excel.model.ExcelSheet; import io.nop.excel.model.ExcelWorkbook; import io.nop.excel.model.IExcelSheet; import io.nop.excel.model.ILoopModel; +import io.nop.excel.model.XptCellModel; import io.nop.excel.model.XptRowModel; import io.nop.excel.model.XptSheetModel; import io.nop.excel.model.XptWorkbookModel; import io.nop.ooxml.xlsx.output.IExcelSheetGenerator; import io.nop.report.core.XptConstants; import io.nop.report.core.engine.expand.TableExpander; +import io.nop.report.core.model.ExpandedCell; import io.nop.report.core.model.ExpandedCol; import io.nop.report.core.model.ExpandedRow; import io.nop.report.core.model.ExpandedSheet; @@ -32,11 +43,16 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.function.Consumer; +import static io.nop.core.CoreErrors.ARG_CELL; +import static io.nop.report.core.XptErrors.ERR_XPT_INVALID_IMAGE_DATA; + public class ReportSheetGenerator implements IExcelSheetGenerator { public static final Logger LOG = LoggerFactory.getLogger(ReportSheetGenerator.class); @@ -125,6 +141,8 @@ private void generateSheet(ExcelSheet sheet, IXptRuntime xptRt, Consumer images, IXptRuntime xptRt) { + if (images == null || images.isEmpty()) + return; + + Map> map = new HashMap<>(); + for (ExcelImage image : images) { + CellPosition pos = image.getAnchor().getStartPosition(); + map.computeIfAbsent(pos, k -> new ArrayList<>(1)).add(image); + } + + List genImages = new ArrayList<>(); + MutableInt index = new MutableInt(0); + sheet.getTable().forEachCell((cell, rowIndex, colIndex) -> { + ExpandedCell ec = (ExpandedCell) cell.getRealCell(); + if (ec != null) { + XptCellModel cm = ec.getModel(); + if (cm != null) { + List list = map.get(cm.getCellPosition()); + if (list != null) { + for (ExcelImage image : list) { + image = genImage(ec, image, xptRt, index); + if (image != null) { + index.incrementAndGet(); + } + } + } + } + } + return ProcessResult.CONTINUE; + }); + sheet.setImages(genImages); + } + + private ExcelImage genImage(ExpandedCell cell, + ExcelImage model, IXptRuntime xptRt, MutableInt index) { + xptRt.setCell(cell); + xptRt.setRow(cell.getRow()); + + if (model.getTestExpr() != null) { + if (!model.getTestExpr().passConditions(xptRt)) + return null; + } + + ExcelImage ret = newExcelImage(cell, model, index); + xptRt.setImage(ret); + + if (model.getDataExpr() != null) { + Object data = model.getDataExpr().invoke(xptRt); + if (data == null) + return null; + + if (data instanceof ExcelImage) + return (ExcelImage) data; + + if (data instanceof IResource) { + IResource resource = (IResource) data; + byte[] bytes = ResourceHelper.readBytes(resource); + ret.setData(ByteString.of(bytes)); + String fileExt = StringHelper.fileExt(resource.getPath()); + if (!StringHelper.isEmpty(fileExt)) { + ret.setImgType(fileExt); + } + } else if (data instanceof ByteString) { + ret.setData((ByteString) data); + } else if (data instanceof byte[]) { + ret.setData(ByteString.of((byte[]) data)); + } else { + throw new NopException(ERR_XPT_INVALID_IMAGE_DATA) + .param(ARG_CELL, cell); + } + } + + return ret; + } + + private static ExcelImage newExcelImage(ExpandedCell cell, ExcelImage model, MutableInt index) { + ExcelImage ret = new ExcelImage(); + ret.setName(model.getName() + '-' + index); + ret.setDescription(model.getDescription()); + ret.setImgType(model.getImgType()); + ret.setRotateDegree(model.getRotateDegree()); + ret.setNoChangeAspect(model.isNoChangeAspect()); + + ExcelClientAnchor anchor = model.getAnchor(); + ExcelClientAnchor retAnchor = anchor.copy(); + retAnchor.setRow1(cell.getRowIndex()); + retAnchor.setCol1(cell.getColIndex()); + + ret.setData(model.getData()); + return ret; + } } \ No newline at end of file diff --git a/nop-report/nop-report-core/src/main/java/io/nop/report/core/engine/XptRuntime.java b/nop-report/nop-report-core/src/main/java/io/nop/report/core/engine/XptRuntime.java index e4c283533..c07c2b5b7 100644 --- a/nop-report/nop-report-core/src/main/java/io/nop/report/core/engine/XptRuntime.java +++ b/nop-report/nop-report-core/src/main/java/io/nop/report/core/engine/XptRuntime.java @@ -15,6 +15,7 @@ import io.nop.core.lang.eval.IEvalScope; import io.nop.core.lang.utils.Underscore; import io.nop.excel.format.ExcelFormatHelper; +import io.nop.excel.model.ExcelImage; import io.nop.excel.model.ExcelStyle; import io.nop.excel.model.ExcelWorkbook; import io.nop.excel.model.XptCellModel; @@ -49,6 +50,8 @@ public class XptRuntime implements IXptRuntime, IVariableScope { private ExpandedRow row; private ExcelWorkbook workbook; + private ExcelImage image; + public XptRuntime(IEvalScope scope) { this.scope = scope.newChildScope(); scope.setLocalValue(null, XptConstants.VAR_XPT_RT, this); @@ -78,6 +81,14 @@ public void setCell(ExpandedCell cell) { this.cell = cell; } + public ExcelImage getImage() { + return image; + } + + public void setImage(ExcelImage image) { + this.image = image; + } + @Override public ExpandedTable getTable() { return table; @@ -109,6 +120,9 @@ public Object getValue(String name) { return sheet; if (XptConstants.VAR_ROW.equals(name)) return row; + + if (XptConstants.VAR_IMAGE.equals(name)) + return image; // 这里只判断扩展属性名,因此对于不识别的属性直接返回null return null; } diff --git a/nop-report/nop-report-core/src/main/java/io/nop/report/core/model/ExpandedSheet.java b/nop-report/nop-report-core/src/main/java/io/nop/report/core/model/ExpandedSheet.java index 8af4f193f..cb0e7f8d5 100644 --- a/nop-report/nop-report-core/src/main/java/io/nop/report/core/model/ExpandedSheet.java +++ b/nop-report/nop-report-core/src/main/java/io/nop/report/core/model/ExpandedSheet.java @@ -7,6 +7,7 @@ */ package io.nop.report.core.model; +import io.nop.excel.model.ExcelImage; import io.nop.excel.model.ExcelPageBreaks; import io.nop.excel.model.ExcelPageMargins; import io.nop.excel.model.ExcelPageSetup; @@ -14,6 +15,8 @@ import io.nop.excel.model.IExcelSheet; import io.nop.excel.model.XptSheetModel; +import java.util.List; + public class ExpandedSheet implements IExcelSheet { private XptSheetModel model; private final ExpandedTable table; @@ -24,6 +27,8 @@ public class ExpandedSheet implements IExcelSheet { private Double defaultRowHeight; private Double defaultColumnWidth; + private List images; + public ExpandedSheet(XptSheetModel model, ExpandedTable table) { this.model = model; this.table = table; @@ -39,6 +44,15 @@ public ExpandedSheet(ExcelSheet sheet) { setDefaultColumnWidth(sheet.getDefaultColumnWidth()); } + @Override + public List getImages() { + return images; + } + + public void setImages(List images) { + this.images = images; + } + public XptSheetModel getModel() { return model; } diff --git a/nop-xdefs/src/main/resources/_vfs/nop/schema/excel/workbook.xdef b/nop-xdefs/src/main/resources/_vfs/nop/schema/excel/workbook.xdef index 0219545a1..f5cb081a4 100644 --- a/nop-xdefs/src/main/resources/_vfs/nop/schema/excel/workbook.xdef +++ b/nop-xdefs/src/main/resources/_vfs/nop/schema/excel/workbook.xdef @@ -121,15 +121,20 @@ ooxml的文档参考 http://officeopenxml.com/SSstyles.php - - + + + + - <description xdef:value="string"/> - <linkUrl xdef:value="string"/> + <linkUrl xdef:value="string" /> + <linkExpr xdef:value="report-expr"/> <testExpr xdef:value="xpl-predicate"/> <dataExpr xdef:value="xpl"/> </image>