diff --git a/EasyExcel/pom.xml b/EasyExcel/pom.xml
index 6a52c4b08..d8230d98b 100644
--- a/EasyExcel/pom.xml
+++ b/EasyExcel/pom.xml
@@ -6,7 +6,7 @@
com.netease.lowcode.extensions
EasyExcel
- 2.3.1
+ 2.4.1
8
@@ -23,13 +23,13 @@
nasl-metadata-collector
com.netease.lowcode
- 0.8.0
+ 0.14.1
com.alibaba
easyexcel
- 3.3.2
+ 3.3.3
@@ -115,10 +115,10 @@
com.netease.lowcode
nasl-metadata-maven-plugin
- 1.4.3
+ 1.7.1
false
-
+ true
diff --git a/EasyExcel/src/main/java/com/netease/lowcode/extensions/EasyExcelTools.java b/EasyExcel/src/main/java/com/netease/lowcode/extensions/EasyExcelTools.java
index 37b2d378c..f60302bc7 100644
--- a/EasyExcel/src/main/java/com/netease/lowcode/extensions/EasyExcelTools.java
+++ b/EasyExcel/src/main/java/com/netease/lowcode/extensions/EasyExcelTools.java
@@ -156,16 +156,19 @@ public static InputStream openUrlStream(String url) {
* @return
*/
private static String getFileUrl(String url) {
- int index = url.indexOf("/upload/");
- HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
- int port = request.getLocalPort();
- if (port == -1) {
- return url;
- }
- if (index >= 0) {
- url = "http://localhost:" + port + "/upload/" + url.substring(index + "/upload/".length());
+ try {
+ int index = url.indexOf("/upload/");
+ HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+ int port = request.getLocalPort();
+ if (port == -1) {
+ return url;
+ }
+ if (index >= 0) {
+ url = "http://localhost:" + port + "/upload/" + url.substring(index + "/upload/".length());
+ }
+ } catch (Exception e) {
+ log.error("url转本地地址异常", e);
}
-
return url;
}
diff --git a/itextpdf-tool/README.md b/itextpdf-tool/README.md
new file mode 100644
index 000000000..143861436
--- /dev/null
+++ b/itextpdf-tool/README.md
@@ -0,0 +1,370 @@
+# 依赖库名称
+
+封装ITextPdf工具,提供通用生成pdf文件的功能。
+
+## 逻辑说明:
+
+### createDocument 创建文档
+
+返回的文档key须保存,后续用来生成文档/压缩包。
+
+```
+ /**
+ * 创建pdf文档
+ *
+ * @param fileName 文件名称(带扩展名)
+ * @return 文档key
+ * @throws IOException
+ * @throws DocumentException
+ */
+ @NaslLogic
+ public String createDocument(String fileName)
+```
+
+### closeDocument 关闭文档
+
+关闭文档后,在服务器本地生成文件,返回文件本地路径。该本地路径跟文件连接器无关。
+
+```
+/**
+ * 关闭文档
+ *
+ * @param documentKey
+ * @return 文件本地路径
+ */
+ @NaslLogic
+ public String closeDocument(String documentKey) {
+
+```
+
+### closeDocumentGetFileURl 关闭文档并且获取文件地址
+
+关闭文档后,在服务器本地生成文件,并且把文件通过默认文件连接器配置上传至文件连接器。最终返回文件连接器中该文件的地址。
+
+```
+ /**
+ * 关闭文档并且获取文件地址
+ *
+ * @param documentKey
+ * @return 文件访问信息
+ */
+ @NaslLogic
+ public UploadResponseDTO closeDocumentGetFileURl(String documentKey)
+
+```
+
+### addDocumentElement 组装元素
+
+组装元素,返回组装结果。
+
+```
+ /**
+ * 组装元素
+ *
+ * @param addedElementKey 子元素key
+ * @param addElementKey 父元素key
+ * @return
+ * @throws DocumentException
+ */
+ @NaslLogic
+ public Boolean addDocumentElement(String addedElementKey, String addElementKey) {
+
+```
+
+### buildParagraph 构造段落
+
+构造段落,返回段落key。
+
+```
+ /**
+ * 构造段落
+ *
+ * @param paragraphStr 段落文本。可空,空时创建空段落
+ * @param iTextParagraphStructure 段落配置
+ * @param iTextFontStructure 段落文本格式。paragraphStr为空时可空
+ * @return 段落key
+ * @throws DocumentException
+ * @throws IOException
+ */
+ @NaslLogic
+ public String buildParagraph(String paragraphStr, ITextParagraphStructure iTextParagraphStructure, ITextFontStructure iTextFontStructure)
+```
+
+### buildTable 构造表格
+
+构造表格,返回表格key。
+
+```
+ /**
+ * 构造表格
+ *
+ * @param iTextTableStructure 表格配置
+ * @return
+ * @throws DocumentException
+ */
+ @NaslLogic
+ public String buildTable(ITextTableStructure iTextTableStructure)
+```
+
+### buildCell 构造单元格
+
+构造单元格,返回单元格key。
+
+```
+/**
+ * @param cellStr 单元格文本。可空,空时创建空段落
+ * @param iTextCellStructure 单元格配置
+ * @param iTextFontStructure 单元格文本格式。paragraphStr为空时可空
+ * @return
+ * @throws DocumentException
+ * @throws IOException
+ */
+ @NaslLogic
+ public String buildCell(String cellStr, ITextCellStructure iTextCellStructure, ITextFontStructure iTextFontStructure) {
+
+```
+
+### buildChunk 构造Chunk
+
+构造Chunk,返回Chunk key。
+
+```
+ /**
+ * 构造chunk
+ *
+ * @param chunkStr 文本。可空,空时创建空段落
+ * @param iTextChunkStructure 配置
+ * @param iTextFontStructure 文本格式
+ * @return
+ */
+ @NaslLogic
+ public String buildChunk(String chunkStr, ITextChunkStructure iTextChunkStructure, ITextFontStructure iTextFontStructure) {
+
+```
+
+### buildPhrase 构造短语
+
+构造短语,返回短语key。
+
+```
+ /**
+ * 构造短语
+ * @param phraseStr 文本。可空,空时创建空段落
+ * @param iTextFontStructure 文本格式
+ * @param iTextPhraseStructure 短语配置
+ * @return
+ */
+ @NaslLogic
+ public String buildPhrase(String phraseStr, ITextFontStructure iTextFontStructure, ITextPhraseStructure iTextPhraseStructure) {
+
+```
+
+### buildListItem 构造列表项
+
+构造列表项,返回列表项key。
+
+```
+ /**
+ * 构造列表
+ * @param listItemStr 列表项文本。可空,空时创建空列表
+ * @param iTextFontStructure 列表项文本格式
+ * @param iTextListStructure 列表配置
+ * @return
+ */
+ @NaslLogic
+ public String buildListItem(String listItemStr, ITextFontStructure iTextFontStructure, ITextListStructure iTextListStructure) {
+
+```
+
+### buildImage 构造图片
+
+构造图片,返回图片key。
+
+```
+ /**
+ * 构造图片元素
+ * @param imageUrl 图片url
+ * @param iTextImageStructure 图片配置
+ * @return
+ */
+ @NaslLogic
+ public String buildImage(String imageUrl, ITextImageStructure iTextImageStructure) {
+
+```
+
+### buildImageBase64 使用base64字符串构造图片
+
+构造图片,返回图片key。
+
+```
+/**
+
+* 构造图片元素
+*
+* @param base64ImageString 图片base64字符串
+* @param iTextImageStructure 图片配置
+* @return
+ */
+ @NaslLogic
+ public String buildImageBase64(String base64ImageString, ITextImageStructure iTextImageStructure) {
+```
+
+### addCanvasImage 新增图片canvas
+
+新增图片canvas,置于顶层。可用于插入盖章等全文档随意位置的图片元素。
+
+```
+/**
+ * 新增图片canvas,置于顶层
+ * @param documentKey
+ * @param imageKey
+ * @return
+ */
+ @NaslLogic
+ public Boolean addCanvasImage(String documentKey, String imageKey)
+```
+### mergePdf 合并pdf
+```
+ /**
+ * 合并pdf
+ *
+ * @param inputFiles 原文件地址集合
+ * @param targetFileName 目标文件名
+ * @param isDeleteOriginLocalFile 是否删除本地原文件
+ * @return 目标文件本地路径
+ */
+ @NaslLogic
+ public String mergePdf(List inputFiles, String targetFileName, Boolean isDeleteOriginLocalFile)
+```
+###
+``` mergePdfToNosFileURl 合并pdf,并上传到nos
+ /**
+ * 合并pdf,并上传到nos
+ *
+ * @param inputFiles 原文件地址集合
+ * @param targetFileName 目标文件名,文件名称不可重复
+ * @param isDeleteOriginLocalFile 是否删除本地原文件
+ * @return nos文件访问信息
+ */
+ @NaslLogic
+ public UploadResponseDTO mergePdfToNosFileURl(List inputFiles, String targetFileName, Boolean isDeleteOriginLocalFile)
+```
+
+## 结构说明
+
+### 字体格式
+
+| 字段名 | 类型 | 描述/可选值 |
+|:------------:|:-------:|:----------------------------------------------------------------------------------------
+| fontName | String | 字体名称,默认STSong-Light
+| fontEncoding | String | 字体编码,默认UniGB-UCS2-H
+| embedded | Boolean | 是否嵌入字体:true表示将字体文件嵌入文档(确保跨设备显示一致),false表示不嵌入
+| forceRead | Boolean | 强制读取字体:true表示忽略系统缓存强制重新加载字体文件(解决字体更新问题)
+| size | Double | 字体大小(单位:磅/pt)
+| style | String | 字体样式:NORMAL/BOLD/ITALIC/UNDERLINE/STRIKETHRU/BOLDITALIC/UNDEFINED/DEFAULTSIZE
+| color | String | 颜色:WHITE/LIGHT_GRAY/GRAY/DARK_GRAY/BLACK/RED/PINK/ORANGE/YELLOW/GREEN/MAGENTA/CYAN/BLUE
+
+### 单元格配置
+
+| 字段名 | 类型 | 描述/可选值 |
+|:-------------------------|:----------|:--------------------------------------------------------------------------------------------------------------------------------------------------|
+| border | String | 全局边框类型:UNDEFINED/TOP/BOTTOM/LEFT/RIGHT/NO_BORDER/BOX |
+| borderWidth | Double | 单元格边框宽度 |
+| padding | Double | 单元格内边距 |
+| borderColor | String | 边框颜色:WHITE/LIGHT_GRAY/GRAY/DARK_GRAY/BLACK/RED/PINK/ORANGE/YELLOW/GREEN/MAGENTA/CYAN/BLUE |
+| paddingLeft | Double | 左边距 |
+| paddingRight | Double | 右边距 |
+| paddingTop | Double | 上边距 |
+| paddingBottom | Double | 下边距 |
+| horizontalAlignmentText | String | 水平对齐方式:ALIGN_UNDEFINED/ALIGN_LEFT/ALIGN_CENTER/ALIGN_RIGHT/ALIGN_JUSTIFIED/ALIGN_TOP/ALIGN_MIDDLE/ALIGN_BOTTOM/ALIGN_BASELINE/ALIGN_JUSTIFIED_ALL
+| verticalAlignmentText | String | 垂直对齐方式:ALIGN_UNDEFINED/ALIGN_LEFT/ALIGN_CENTER/ALIGN_RIGHT/ALIGN_JUSTIFIED/ALIGN_TOP/ALIGN_MIDDLE/ALIGN_BOTTOM/ALIGN_BASELINE/ALIGN_JUSTIFIED_ALL
+| fixedHeight | Double | 固定高度
+| minimumHeight | Double | 最小高度
+| fixedLeading | Double | 行间距(固定值)
+| multipliedLeading | Double | 行间距(倍数)
+| backgroundColor | String | 背景颜色:WHITE/LIGHT_GRAY/GRAY/DARK_GRAY/BLACK/RED/PINK/ORANGE/YELLOW/GREEN/MAGENTA/CYAN/BLUE
+| colSpan | Integer | 横向合并列数
+| rowSpan | Integer | 纵向合并行数
+
+### Chunk配置
+
+| 字段名 | 类型 | 描述/可选值 |
+|:-----------------|:--------|:---------------------------------------------------------------------------------------------------|
+| characterSpacing | Double | 设置字符间距
+| anchor | String | 添加超链接(可以是URL或文档内锚点)
+| wordSpacing | Double | 设置单词间距(仅对空格字符有效)
+| backgroundColor | String | 设置背景色(颜色值或枚举:WHITE/LIGHT_GRAY/GRAY/DARK_GRAY/BLACK/RED/PINK/ORANGE/YELLOW/GREEN/MAGENTA/CYAN/BLUE)
+
+### 图片配置
+
+| 字段名 | 类型 | 描述/可选值 |
+|:---------------------|:----------|:------------------------------------------------------------------------------------------------------------------------------------------------|
+| scaleToFitHeight | Double | 按比例缩放图片到目标高度(保持宽高比)
+| scaleToFitWidth | Double | 按比例缩放图片到目标宽度(保持宽高比)
+| scalePercent | Double | 缩放图片的百分比(100表示原尺寸)
+| scaleAbsoluteWidth | Double | 固定宽度,高度按比例调整
+| scaleAbsoluteHeight | Double | 固定高度,宽度按比例调整
+| alignText | String | 对齐方式:ALIGN_UNDEFINED/ALIGN_LEFT/ALIGN_CENTER/ALIGN_RIGHT/ALIGN_JUSTIFIED/ALIGN_TOP/ALIGN_MIDDLE/ALIGN_BOTTOM/ALIGN_BASELINE/ALIGN_JUSTIFIED_ALL
+| absoluteX | Double | 设置绝对X坐标(相对于页面左下角)
+| absoluteY | Double | 设置绝对Y坐标(相对于页面左下角)
+| indentationLeft | Double | 左缩进(需在非绝对定位模式下使用)
+| border | String | 全局边框:UNDEFINED/TOP/BOTTOM/LEFT/RIGHT/NO_BORDER/BOX
+| borderWidth | Double | 边框宽度
+| borderColor | String | 边框颜色:WHITE/LIGHT_GRAY/GRAY/DARK_GRAY/BLACK/RED/PINK/ORANGE/YELLOW/GREEN/MAGENTA/CYAN/BLUE
+| spacingBefore | Double | 图片上方的空白间距
+| spacingAfter | Double | 图片下方的空白间距
+| transparency | List | 透明度(ARGB值),示例:[200, 255, 255, 255](A=200, R=255, G=255, B=255)
+| anchor | String | 设置超链接(点击跳转URL)
+| compressionLevel | Integer | JPEG压缩质量(0-9,0=最低压缩/质量最差,9=最高压缩/质量最好)
+| dpiX | Integer | 水平DPI(影响打印质量)
+| dpiY | Integer | 垂直DPI(影响打印质量)
+
+### 列表配置
+
+| 字段名 | 类型 | 描述/可选值 |
+|:-------------------|:---------|:-------------------------------------------------------------------------------------------------------------------------------------------------|
+| alignText | String | 对齐方式:ALIGN_UNDEFINED/ALIGN_LEFT/ALIGN_CENTER/ALIGN_RIGHT/ALIGN_JUSTIFIED/ALIGN_TOP/ALIGN_MIDDLE/ALIGN_BOTTOM/ALIGN_BASELINE/ALIGN_JUSTIFIED_ALL
+| indentationLeft | Double | 左缩进(单位:像素)
+| indentationRight | Double | 右缩进(单位:像素)
+| firstLineIndent | Double | 首行缩进(单位:像素)
+| keepTogether | Boolean | 是否禁止分页时拆分段落(true表示整段必须在同一页)
+| spacingBefore | Double | 段前间距(单位:像素)
+| spacingAfter | Double | 段后间距(单位:像素)
+| paddingTop | Double | 上边距(单位:像素)
+| fixedLeading | Double | 行间距(固定值,单位:像素)
+| multipliedLeading | Double | 行间距(倍数,如1.5表示1.5倍行距)
+
+### 段落配置
+
+| 字段名 | 类型 | 描述/可选值 |
+|:------------------|:----------|:-------------------------------------------------------------------------------------------------------------------------------------------------|
+| alignText | String | 对齐方式:ALIGN_UNDEFINED/ALIGN_LEFT/ALIGN_CENTER/ALIGN_RIGHT/ALIGN_JUSTIFIED/ALIGN_TOP/ALIGN_MIDDLE/ALIGN_BOTTOM/ALIGN_BASELINE/ALIGN_JUSTIFIED_ALL
+| indentationLeft | Double | 左缩进(单位:像素)
+| indentationRight | Double | 右缩进(单位:像素)
+| firstLineIndent | Double | 首行缩进(单位:像素)
+| spacingBefore | Double | 段前间距(单位:像素)
+| spacingAfter | Double | 段后间距(单位:像素)
+| keepTogether | Boolean | 是否禁止分页时拆分段落(true表示整段必须在同一页)
+| fixedLeading | Double | 行间距(固定值,单位:像素)
+| multipliedLeading | Double | 行间距(倍数,如 1.5 表示1.5倍行距)
+
+### Phrase短语配置
+
+| 字段名 | 类型 | 描述/可选值 |
+|:-------------------|:--------|:-------------------------------|
+| fixedLeading | Double | 行间距(固定值,单位:像素/磅)
+| multipliedLeading | Double | 行间距(倍数,基于字体大小计算,如1.5表示1.5倍行距)
+
+### 表格配置
+
+| 字段名 | 类型 | 描述/可选值 |
+|:------------------|:----------|:--------------------------------------------------------------------------------------------------------------------------------------------------|
+| alignText | String | 文本对齐方式:ALIGN_UNDEFINED/ALIGN_LEFT/ALIGN_CENTER/ALIGN_RIGHT/ALIGN_JUSTIFIED/ALIGN_TOP/ALIGN_MIDDLE/ALIGN_BOTTOM/ALIGN_BASELINE/ALIGN_JUSTIFIED_ALL
+| indentationLeft | Double | 左缩进(单位:像素)
+| indentationRight | Double | 右缩进(单位:像素)
+| firstLineIndent | Double | 首行缩进(单位:像素)
+| spacingBefore | Double | 段前间距(单位:像素)
+| spacingAfter | Double | 段后间距(单位:像素)
+| keepTogether | Boolean | 段落分页控制:true-禁止拆分段落(整段必须在同一页)false-允许拆分段落
+| fixedLeading | Double | 固定行高(单位:像素)
+| multipliedLeading | Double | 相对行高(基于字体大小的倍数,如1.5=1.5倍行距)
diff --git a/itextpdf-tool/jar/nasl-metadata-collector-0.12.0/nasl-metadata-collector-0.12.0/install.bat b/itextpdf-tool/jar/nasl-metadata-collector-0.12.0/nasl-metadata-collector-0.12.0/install.bat
new file mode 100644
index 000000000..f1647dff0
--- /dev/null
+++ b/itextpdf-tool/jar/nasl-metadata-collector-0.12.0/nasl-metadata-collector-0.12.0/install.bat
@@ -0,0 +1 @@
+mvn install:install-file -Dfile="nasl-metadata-collector-0.12.0.jar" -DgroupId="com.netease.lowcode" -DartifactId="nasl-metadata-collector" -Dversion="0.12.0" -Dpackaging="jar" -DgeneratePom=true
\ No newline at end of file
diff --git a/itextpdf-tool/jar/nasl-metadata-collector-0.12.0/nasl-metadata-collector-0.12.0/install.sh b/itextpdf-tool/jar/nasl-metadata-collector-0.12.0/nasl-metadata-collector-0.12.0/install.sh
new file mode 100644
index 000000000..f1647dff0
--- /dev/null
+++ b/itextpdf-tool/jar/nasl-metadata-collector-0.12.0/nasl-metadata-collector-0.12.0/install.sh
@@ -0,0 +1 @@
+mvn install:install-file -Dfile="nasl-metadata-collector-0.12.0.jar" -DgroupId="com.netease.lowcode" -DartifactId="nasl-metadata-collector" -Dversion="0.12.0" -Dpackaging="jar" -DgeneratePom=true
\ No newline at end of file
diff --git a/itextpdf-tool/jar/nasl-metadata-collector-0.12.0/nasl-metadata-collector-0.12.0/nasl-metadata-collector-0.12.0.jar b/itextpdf-tool/jar/nasl-metadata-collector-0.12.0/nasl-metadata-collector-0.12.0/nasl-metadata-collector-0.12.0.jar
new file mode 100644
index 000000000..675e25054
Binary files /dev/null and b/itextpdf-tool/jar/nasl-metadata-collector-0.12.0/nasl-metadata-collector-0.12.0/nasl-metadata-collector-0.12.0.jar differ
diff --git a/itextpdf-tool/jar/nasl-metadata-maven-plugin-1.5.1/install.bat b/itextpdf-tool/jar/nasl-metadata-maven-plugin-1.5.1/install.bat
new file mode 100644
index 000000000..900bd9c25
--- /dev/null
+++ b/itextpdf-tool/jar/nasl-metadata-maven-plugin-1.5.1/install.bat
@@ -0,0 +1 @@
+mvn install:install-file -Dfile="nasl-metadata-maven-plugin-1.5.1.jar" -DpomFile="nasl-metadata-maven-plugin-1.5.1.pom"
\ No newline at end of file
diff --git a/itextpdf-tool/jar/nasl-metadata-maven-plugin-1.5.1/install.sh b/itextpdf-tool/jar/nasl-metadata-maven-plugin-1.5.1/install.sh
new file mode 100644
index 000000000..900bd9c25
--- /dev/null
+++ b/itextpdf-tool/jar/nasl-metadata-maven-plugin-1.5.1/install.sh
@@ -0,0 +1 @@
+mvn install:install-file -Dfile="nasl-metadata-maven-plugin-1.5.1.jar" -DpomFile="nasl-metadata-maven-plugin-1.5.1.pom"
\ No newline at end of file
diff --git a/itextpdf-tool/jar/nasl-metadata-maven-plugin-1.5.1/nasl-metadata-maven-plugin-1.5.1.jar b/itextpdf-tool/jar/nasl-metadata-maven-plugin-1.5.1/nasl-metadata-maven-plugin-1.5.1.jar
new file mode 100644
index 000000000..d584d03c3
Binary files /dev/null and b/itextpdf-tool/jar/nasl-metadata-maven-plugin-1.5.1/nasl-metadata-maven-plugin-1.5.1.jar differ
diff --git a/itextpdf-tool/jar/nasl-metadata-maven-plugin-1.5.1/nasl-metadata-maven-plugin-1.5.1.pom b/itextpdf-tool/jar/nasl-metadata-maven-plugin-1.5.1/nasl-metadata-maven-plugin-1.5.1.pom
new file mode 100644
index 000000000..be041b53f
--- /dev/null
+++ b/itextpdf-tool/jar/nasl-metadata-maven-plugin-1.5.1/nasl-metadata-maven-plugin-1.5.1.pom
@@ -0,0 +1,223 @@
+
+ 4.0.0
+
+ com.netease.lowcode
+ nasl-metadata-maven-plugin
+ 1.5.1
+ maven-plugin
+
+ Nasl Metadata Maven Plugin
+
+ UTF-8
+ 3.2.5
+ 9.4.54.v20240208
+ 3.3.0
+ 1.0.2.v20150114
+ 8
+ 1.7.36
+ 4.9.2
+
+
+
+
+
+ org.apache.maven
+ maven-artifact
+ ${mavenVersion}
+ provided
+
+
+ org.apache.maven
+ maven-plugin-api
+ ${mavenVersion}
+ provided
+
+
+ org.apache.maven
+ maven-model
+ ${mavenVersion}
+ provided
+
+
+ org.apache.maven
+ maven-core
+ ${mavenVersion}
+ provided
+
+
+ org.apache.maven
+ maven-repository-metadata
+ ${mavenVersion}
+ provided
+
+
+ org.apache.maven
+ maven-settings
+ ${mavenVersion}
+ provided
+
+
+ org.apache.maven
+ maven-aether-provider
+ ${mavenVersion}
+ provided
+
+
+
+
+ org.apache.maven.doxia
+ doxia-sink-api
+ 1.12.0
+
+
+ org.codehaus.plexus
+ plexus-container-default
+
+
+
+
+ org.apache.maven.reporting
+ maven-reporting-api
+ 3.1.1
+
+
+ org.apache.maven.reporting
+ maven-reporting-impl
+ 3.2.0
+
+
+ org.codehaus.plexus
+ plexus-container-default
+
+
+
+
+
+
+ org.codehaus.plexus
+ plexus-archiver
+ ${plexus-archiver.version}
+
+
+ org.codehaus.plexus
+ plexus-utils
+ 4.0.0
+
+
+ org.codehaus.plexus
+ plexus-xml
+ 3.0.0
+
+
+ org.codehaus.plexus
+ plexus-io
+ 3.4.2
+
+
+ org.codehaus.plexus
+ plexus-i18n
+ 1.0-beta-10
+
+
+ org.codehaus.plexus
+ plexus-component-api
+
+
+
+
+
+
+ org.apache.maven.shared
+ maven-dependency-analyzer
+ 1.13.2
+
+
+ org.apache.maven.shared
+ maven-dependency-tree
+ 3.2.1
+
+
+ org.apache.maven.shared
+ maven-common-artifact-filters
+ 3.3.2
+
+
+ org.apache.maven.shared
+ maven-artifact-transfer
+ 0.13.1
+
+
+ org.apache.maven.shared
+ maven-shared-utils
+ 3.4.2
+
+
+
+
+ org.apache.maven.plugin-tools
+ maven-plugin-annotations
+ provided
+ 3.11.0
+
+
+ javax.inject
+ javax.inject
+ 1
+
+
+
+ org.eclipse.aether
+ aether-api
+ ${resolverVersion}
+ provided
+
+
+ org.eclipse.aether
+ aether-util
+ ${resolverVersion}
+ provided
+
+
+ org.sonatype.plexus
+ plexus-build-api
+ 0.0.7
+ compile
+
+
+
+
+ commons-io
+ commons-io
+ 2.16.1
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.15.3
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-plugin-plugin
+ 3.5
+
+ nasl-metadata-maven-plugin
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 8
+ 8
+
+
+
+
+
diff --git a/itextpdf-tool/pom.xml b/itextpdf-tool/pom.xml
new file mode 100644
index 000000000..f79958800
--- /dev/null
+++ b/itextpdf-tool/pom.xml
@@ -0,0 +1,94 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.2.9.RELEASE
+
+
+
+ com.netease.lowcode
+ itextpdf-tool
+ 1.2.0
+
+
+ 8
+ 8
+ UTF-8
+ 3.11
+
+
+
+
+ nasl-metadata-collector
+ com.netease.lowcode
+ 0.12.0
+
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
+ org.slf4j
+ slf4j-api
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ provided
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ com.itextpdf
+ itextpdf
+ 5.5.13.3
+
+
+ com.itextpdf
+ itext-asian
+ 5.2.0
+
+
+ com.netease.cloud
+ codewave-file-connector-library
+ 1.0.3
+ provided
+
+
+
+
+
+ com.netease.lowcode
+ nasl-metadata-maven-plugin
+ 1.5.1
+
+ false
+ true
+
+
+
+
+ archive
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/ItextpdfToolBasicSpringEnvironmentConfiguration.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/ItextpdfToolBasicSpringEnvironmentConfiguration.java
new file mode 100644
index 000000000..f3d487b8a
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/ItextpdfToolBasicSpringEnvironmentConfiguration.java
@@ -0,0 +1,12 @@
+package com.netease.lowcode.lib;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 加入spring环境配置(在spring.factories中指定)
+ */
+@Configuration
+@ComponentScan(basePackageClasses = LibraryAutoScan.class)
+public class ItextpdfToolBasicSpringEnvironmentConfiguration {
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/LibraryAutoScan.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/LibraryAutoScan.java
new file mode 100644
index 000000000..b573d47af
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/LibraryAutoScan.java
@@ -0,0 +1,8 @@
+package com.netease.lowcode.lib;
+
+/**
+ * 依赖库自动扫描类
+ * @author system
+ */
+public class LibraryAutoScan {
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/api/ItextPdfApi.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/api/ItextPdfApi.java
new file mode 100644
index 000000000..3fdf14829
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/api/ItextPdfApi.java
@@ -0,0 +1,662 @@
+package com.netease.lowcode.lib.api;
+
+
+import com.itextpdf.text.*;
+import com.itextpdf.text.pdf.*;
+import com.netease.lowcode.core.annotation.NaslLogic;
+import com.netease.lowcode.lib.file.FileConnectorUtils;
+import com.netease.lowcode.lib.file.UploadResponseDTO;
+import com.netease.lowcode.lib.itextpdf.BaseFontBuilder;
+import com.netease.lowcode.lib.itextpdf.PdfBuilderUtil;
+import com.netease.lowcode.lib.itextpdf.PdfDocumentBuilder;
+import com.netease.lowcode.lib.structure.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import javax.annotation.Resource;
+import java.io.*;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Component
+public class ItextPdfApi {
+
+ //参数使用LCAP_EXTENSION_LOGGER后日志会显示在平台日志功能中
+ private static final Logger log = LoggerFactory.getLogger("LCAP_EXTENSION_LOGGER");
+
+ private String tempDirectory = "./itextpdf";
+ private Map pdfDocumentBuilderMap = new HashMap<>();
+ private Map elementMap = new HashMap<>();
+ @Resource
+ private FileConnectorUtils fileConnectorUtils;
+
+ /**
+ * 合并pdf,并上传到nos
+ *
+ * @param inputFiles 原文件地址集合
+ * @param targetFileName 目标文件名,文件名称不可重复
+ * @param isDeleteOriginLocalFile 是否删除本地原文件
+ * @return nos文件访问信息
+ */
+ @NaslLogic
+ public UploadResponseDTO mergePdfToNosFileURl(List inputFiles, String targetFileName, Boolean isDeleteOriginLocalFile) {
+ String localFilePath = mergePdf(inputFiles, targetFileName, isDeleteOriginLocalFile);
+ String[] path = localFilePath.split(File.separator);
+ String fileName = path[path.length - 1];
+ try {
+ FileInputStream fis = new FileInputStream(localFilePath);
+ UploadResponseDTO res = fileConnectorUtils.fileUploadV2(fis, fileName, new HashMap<>());
+ return res;
+ } catch (Exception e) {
+ log.error("close document error", e);
+ throw new IllegalArgumentException("close document error");
+ } finally {
+ try {
+ //删除本地文件
+ Files.delete(Paths.get(localFilePath));
+ String directoryPath = localFilePath.replace(fileName, "");
+ Files.delete(Paths.get(directoryPath));
+ } catch (IOException e) {
+ log.error("删除失败", e);
+ }
+ }
+ }
+
+ /**
+ * 获取inputFiles的pdf文件,并下载到本地
+ *
+ * @param inputFiles 原文件地址集合
+ * @return inputFiles的pdf文件
+ */
+ private List getInputFilesLocal(List inputFiles) throws IOException {
+ //inputFiles去重
+ inputFiles = inputFiles.stream().distinct().collect(Collectors.toList());
+ List inputFilesFinal = new ArrayList<>();
+ String key = "document-" + UUID.randomUUID();
+ //创建目录
+ String tempDirectoryFinal = tempDirectory + File.separator + key + File.separator;
+ File file = new File(tempDirectoryFinal);
+ file.mkdirs();
+ for (String input : inputFiles) {
+ String targetFileName = "tomergepdf-" + UUID.randomUUID() + ".pdf";
+ String targetFileNamePath = tempDirectoryFinal + targetFileName;
+ if (input.startsWith("http://") || input.startsWith("https://")) {
+ InputStream inputStream = fileConnectorUtils.fileDownloadV2(input);
+ // 使用try-with-resources确保流关闭
+ try (OutputStream outputStream = new FileOutputStream(targetFileNamePath)) {
+ byte[] buffer = new byte[1024];
+ int bytesRead;
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, bytesRead);
+ }
+ }
+ inputFilesFinal.add(targetFileNamePath);
+ } else {
+ inputFilesFinal.add(input);
+ }
+ }
+ return inputFilesFinal;
+ }
+
+ /**
+ * 合并pdf
+ *
+ * @param inputFiles 原文件地址集合
+ * @param targetFileName 目标文件名
+ * @param isDeleteOriginLocalFile 是否删除本地原文件
+ * @return 目标文件本地路径
+ */
+ @NaslLogic
+ public String mergePdf(List inputFiles, String targetFileName, Boolean isDeleteOriginLocalFile) {
+ String key = "merge-document-" + UUID.randomUUID();
+ //创建目录
+ String tempDirectoryFinal = tempDirectory + File.separator + key + File.separator;
+ String targetFileNamePath = tempDirectoryFinal + targetFileName;
+ File file = new File(tempDirectoryFinal);
+ file.mkdirs();
+ Document document = new Document();
+ PdfCopy copy;
+ List readers = new ArrayList<>();
+ List inputFilesFinal = new ArrayList<>();
+ try {
+ inputFilesFinal = getInputFilesLocal(inputFiles);
+ copy = new PdfSmartCopy(document, Files.newOutputStream(Paths.get(targetFileNamePath)));
+ document.open();
+ for (String input : inputFilesFinal) {
+ PdfReader reader = new PdfReader(new RandomAccessFileOrArray(input), null);
+ readers.add(reader);
+ copy.addDocument(reader);
+ }
+ return targetFileNamePath;
+ } catch (Exception e) {
+ log.error("合并失败: ", e);
+ throw new IllegalArgumentException("合并失败");
+ } finally {
+ try {
+ Set directorys = new HashSet<>();
+ if (isDeleteOriginLocalFile) {
+ //删除本地合并前原文件
+ for (String input : inputFilesFinal) {
+ Files.delete(Paths.get(input));
+ String directory = input.replace(input.substring(input.lastIndexOf(File.separator)), "");
+ directorys.add(directory);
+ }
+ for (String directory : directorys) {
+ Files.delete(Paths.get(directory));
+ }
+ }
+ if (document.isOpen()) {
+ document.close();
+ }
+ for (PdfReader r : readers) {
+ if (r != null) {
+ r.close();
+ }
+ }
+ } catch (Exception e) {
+ log.error(" ", e);
+ }
+ }
+ }
+
+ /**
+ * 创建pdf文档
+ *
+ * @param fileName 文件名称(带扩展名)
+ * @return 文档key
+ * @throws IOException
+ * @throws DocumentException
+ */
+ @NaslLogic
+ public String createDocument(String fileName) {
+ try {
+ String key = "document-" + UUID.randomUUID();
+ //创建目录
+ String tempDirectoryFinal = tempDirectory + File.separator + key + File.separator;
+ File file = new File(tempDirectoryFinal);
+ file.mkdirs();
+ //将pdf档写入响应输出流
+ Document document = new Document();
+ PdfWriter pdfWriter = PdfWriter.getInstance(document, Files.newOutputStream(Paths.get(tempDirectoryFinal + fileName)));
+ document.open();
+ //信息放入内存
+ PdfDocumentBuilder pdfDocumentBuilder = new PdfDocumentBuilder(document, tempDirectoryFinal, fileName, pdfWriter);
+ pdfDocumentBuilderMap.put(key, pdfDocumentBuilder);
+ return key;
+ } catch (Exception e) {
+ log.error("create document error", e);
+ throw new IllegalArgumentException("create document error");
+ }
+ }
+
+ /**
+ * 关闭文档
+ *
+ * @param documentKey
+ * @return 文件本地路径
+ */
+ @NaslLogic
+ public String closeDocument(String documentKey) {
+ try {
+ PdfDocumentBuilder pdfDocumentBuilder = pdfDocumentBuilderMap.get(documentKey);
+ if (pdfDocumentBuilder == null) {
+ return null;
+ }
+ pdfDocumentBuilder.getDocument().close();
+ String path = pdfDocumentBuilder.getTempDirectoryFinal() + pdfDocumentBuilder.getFileName();
+ pdfDocumentBuilderMap.remove(documentKey);
+ return path;
+ } catch (Exception e) {
+ log.error("close document error", e);
+ throw new IllegalArgumentException("close document error");
+ }
+ }
+
+ /**
+ * 关闭文档并且获取文件地址
+ *
+ * @param documentKey
+ * @return 文件访问信息
+ */
+ @NaslLogic
+ public UploadResponseDTO closeDocumentGetFileURl(String documentKey) {
+ PdfDocumentBuilder pdfDocumentBuilder = pdfDocumentBuilderMap.get(documentKey);
+ if (pdfDocumentBuilder == null) {
+ return null;
+ }
+ try {
+ pdfDocumentBuilder.getDocument().close();
+ FileInputStream fis = new FileInputStream(pdfDocumentBuilder.getTempDirectoryFinal() + File.separator + pdfDocumentBuilder.getFileName());
+ UploadResponseDTO res = fileConnectorUtils.fileUploadV2(fis, pdfDocumentBuilder.getFileName(), new HashMap<>());
+ pdfDocumentBuilderMap.remove(documentKey);
+ return res;
+ } catch (Exception e) {
+ log.error("close document error", e);
+ throw new IllegalArgumentException("close document error");
+ } finally {
+ try {
+ Files.delete(Paths.get(pdfDocumentBuilder.getTempDirectoryFinal() + File.separator + pdfDocumentBuilder.getFileName()));
+ Files.delete(Paths.get(pdfDocumentBuilder.getTempDirectoryFinal()));
+ } catch (IOException e) {
+ log.error("删除失败", e);
+ }
+ }
+ }
+
+ /**
+ * 构造段落
+ *
+ * @param paragraphStr 段落文本。可空,空时创建空段落
+ * @param iTextParagraphStructure 段落配置
+ * @param iTextFontStructure 段落文本格式。paragraphStr为空时可空
+ * @return 段落key
+ * @throws DocumentException
+ * @throws IOException
+ */
+ @NaslLogic
+ public String buildParagraph(String paragraphStr, ITextParagraphStructure iTextParagraphStructure, ITextFontStructure iTextFontStructure) {
+ try {
+ Paragraph paragraph = new Paragraph();
+ if (!StringUtils.isEmpty(paragraphStr)) {
+ Font font = BaseFontBuilder.setFont(iTextFontStructure);
+ paragraph = new Paragraph(paragraphStr, font);
+ }
+ PdfBuilderUtil.fillParagraph(paragraph, iTextParagraphStructure);
+ String paragraphKey = "paragraph-" + UUID.randomUUID();
+ elementMap.put(paragraphKey, paragraph);
+ return paragraphKey;
+ } catch (Exception e) {
+ log.error("build paragraph error", e);
+ throw new IllegalArgumentException("build paragraph error");
+ }
+ }
+
+ /**
+ * 构造表格
+ *
+ * @param iTextTableStructure 表格配置
+ * @return
+ * @throws DocumentException
+ */
+ @NaslLogic
+ public String buildTable(ITextTableStructure iTextTableStructure) {
+ try {
+ PdfPTable table = new PdfPTable(iTextTableStructure.getNumColumns());
+ PdfBuilderUtil.fillTable(table, iTextTableStructure);
+ String tableKey = "table-" + UUID.randomUUID();
+ elementMap.put(tableKey, table);
+ return tableKey;
+ } catch (Exception e) {
+ log.error("build table error", e);
+ throw new IllegalArgumentException("build table error");
+ }
+ }
+
+ /**
+ * @param cellStr 单元格文本。可空,空时创建空段落
+ * @param iTextCellStructure 单元格配置
+ * @param iTextFontStructure 单元格文本格式。paragraphStr为空时可空
+ * @return
+ * @throws DocumentException
+ * @throws IOException
+ */
+ @NaslLogic
+ public String buildCell(String cellStr, ITextCellStructure iTextCellStructure, ITextFontStructure iTextFontStructure) {
+ try {
+ PdfPCell cell = new PdfPCell();
+ if (!StringUtils.isEmpty(cellStr)) {
+ Font font = BaseFontBuilder.setFont(iTextFontStructure);
+ cell = new PdfPCell(new Paragraph(cellStr, font));
+ }
+ PdfBuilderUtil.fillCell(cell, iTextCellStructure);
+ String cellKey = "cell-" + UUID.randomUUID();
+ elementMap.put(cellKey, cell);
+ return cellKey;
+ } catch (Exception e) {
+ log.error("build cell error", e);
+ throw new IllegalArgumentException("build cell error");
+ }
+ }
+
+ /**
+ * 构造chunk
+ *
+ * @param chunkStr 文本。可空,空时创建空段落
+ * @param iTextChunkStructure 配置
+ * @param iTextFontStructure 文本格式
+ * @return
+ */
+ @NaslLogic
+ public String buildChunk(String chunkStr, ITextChunkStructure iTextChunkStructure, ITextFontStructure iTextFontStructure) {
+ try {
+ Chunk chunk;
+ if (iTextFontStructure == null) {
+ chunk = new Chunk(chunkStr);
+ } else {
+ Font font = BaseFontBuilder.setFont(iTextFontStructure);
+ chunk = new Chunk(chunkStr, font);
+ PdfBuilderUtil.fillChunk(chunk, iTextChunkStructure);
+ }
+ String chunkKey = "chunk-" + UUID.randomUUID();
+ elementMap.put(chunkKey, chunk);
+ return chunkKey;
+ } catch (Exception e) {
+ log.error("build chunk error", e);
+ throw new IllegalArgumentException("build chunk error");
+ }
+ }
+
+ /**
+ * 构造短语
+ *
+ * @param phraseStr 文本。可空,空时创建空段落
+ * @param iTextFontStructure 文本格式
+ * @param iTextPhraseStructure 短语配置
+ * @return
+ */
+ @NaslLogic
+ public String buildPhrase(String phraseStr, ITextFontStructure iTextFontStructure, ITextPhraseStructure iTextPhraseStructure) {
+ try {
+ Phrase phrase = new Phrase();
+ if (!StringUtils.isEmpty(phraseStr)) {
+ phrase = new Phrase(phraseStr, BaseFontBuilder.setFont(iTextFontStructure));
+ }
+ PdfBuilderUtil.fillPhrase(phrase, iTextPhraseStructure);
+ String phraseKey = "phrase-" + UUID.randomUUID();
+ elementMap.put(phraseKey, phrase);
+ return phraseKey;
+ } catch (Exception e) {
+ log.error("build phrase error", e);
+ throw new IllegalArgumentException("build phrase error");
+ }
+ }
+
+ /**
+ * 新增图片canvas,置于顶层
+ *
+ * @param documentKey
+ * @param imageKey
+ * @return
+ */
+ @NaslLogic
+ public Boolean addCanvasImage(String documentKey, String imageKey) {
+ try {
+ PdfDocumentBuilder pdfDocumentBuilder = pdfDocumentBuilderMap.get(documentKey);
+ if (pdfDocumentBuilder == null) {
+ throw new IllegalArgumentException("documentKey is not exist");
+ }
+ Image image = (Image) elementMap.get(imageKey);
+ if (image == null) {
+ throw new IllegalArgumentException("imageKey is not exist");
+ }
+ PdfWriter pdfWriter = pdfDocumentBuilder.getPdfWriter();
+ PdfContentByte canvas = pdfWriter.getDirectContent();
+ canvas.addImage(image);
+ return true;
+ } catch (Exception e) {
+ log.error("add canvasImage error", e);
+ throw new IllegalArgumentException("add canvasImage error");
+ }
+ }
+
+ /**
+ * 构造列表
+ *
+ * @param listItemStr 列表项文本。可空,空时创建空列表
+ * @param iTextFontStructure 列表项文本格式
+ * @param iTextListStructure 列表配置
+ * @return
+ */
+ @NaslLogic
+ public String buildListItem(String listItemStr, ITextFontStructure iTextFontStructure, ITextListStructure iTextListStructure) {
+ try {
+ ListItem listItem = new ListItem();
+ if (!StringUtils.isEmpty(listItemStr)) {
+ listItem = new ListItem(listItemStr, BaseFontBuilder.setFont(iTextFontStructure));
+ }
+ PdfBuilderUtil.filListItem(listItem, iTextListStructure);
+ String listItemKey = "listItem-" + UUID.randomUUID();
+ elementMap.put(listItemKey, listItem);
+ return listItemKey;
+ } catch (Exception e) {
+ log.error("build listItem error", e);
+ throw new IllegalArgumentException("build listItem error");
+ }
+ }
+
+ /**
+ * 构造图片元素
+ *
+ * @param imageUrl 图片url
+ * @param iTextImageStructure 图片配置
+ * @return
+ */
+ @NaslLogic
+ public String buildImage(String imageUrl, ITextImageStructure iTextImageStructure) {
+ try {
+ Image image = Image.getInstance(new URL(imageUrl));
+ PdfBuilderUtil.fillImage(image, iTextImageStructure);
+ String imageKey = "image-" + UUID.randomUUID();
+ elementMap.put(imageKey, image);
+ return imageKey;
+ } catch (Exception e) {
+ log.error("build image error", e);
+ throw new IllegalArgumentException("build image error");
+ }
+ }
+
+ /**
+ * 使用base64字符串构造图片
+ *
+ * @param base64ImageString 图片base64字符串
+ * @param iTextImageStructure 图片配置
+ * @return
+ */
+ @NaslLogic
+ public String buildImageBase64(String base64ImageString, ITextImageStructure iTextImageStructure) {
+ try {
+ byte[] imageBytes = Base64.getDecoder().decode(base64ImageString);
+ Image image = Image.getInstance(imageBytes);
+ PdfBuilderUtil.fillImage(image, iTextImageStructure);
+ String imageKey = "image-" + UUID.randomUUID();
+ elementMap.put(imageKey, image);
+ return imageKey;
+ } catch (Exception e) {
+ log.error("build image error", e);
+ throw new IllegalArgumentException("build image error");
+ }
+ }
+
+ /**
+ * 组装元素
+ *
+ * @param addedElementKey 子元素key
+ * @param addElementKey 父元素key
+ * @return
+ * @throws DocumentException
+ */
+ @NaslLogic
+ public Boolean addDocumentElement(String addedElementKey, String addElementKey) {
+ boolean flag = true;
+ try {
+ PdfDocumentBuilder pdfDocumentBuilder = pdfDocumentBuilderMap.get(addElementKey);
+ Object elementSon = elementMap.get(addedElementKey);
+ if (pdfDocumentBuilder != null) {
+ if (elementSon instanceof Paragraph) {
+ Paragraph paragraph = (Paragraph) elementSon;
+ Document document = pdfDocumentBuilder.getDocument();
+ document.add(paragraph);
+ pdfDocumentBuilder.setDocument(document);
+ } else if (elementSon instanceof Phrase) {
+ Phrase phrase = (Phrase) elementSon;
+ Document document = pdfDocumentBuilder.getDocument();
+ document.add(phrase);
+ pdfDocumentBuilder.setDocument(document);
+ } else if (elementSon instanceof PdfPTable) {
+ PdfPTable pdfPTable = (PdfPTable) elementSon;
+ Document document = pdfDocumentBuilder.getDocument();
+ document.add(pdfPTable);
+ pdfDocumentBuilder.setDocument(document);
+ } else if (elementSon instanceof Image) {
+ Image image = (Image) elementSon;
+ Document document = pdfDocumentBuilder.getDocument();
+ document.add(image);
+ pdfDocumentBuilder.setDocument(document);
+ } else if (elementSon instanceof ListItem) {
+ ListItem listItem = (ListItem) elementSon;
+ Document document = pdfDocumentBuilder.getDocument();
+ document.add(listItem);
+ pdfDocumentBuilder.setDocument(document);
+ } else if (elementSon instanceof Chunk) {
+ Chunk chunk = (Chunk) elementSon;
+ Document document = pdfDocumentBuilder.getDocument();
+ document.add(chunk);
+ pdfDocumentBuilder.setDocument(document);
+ } else {
+ log.error("addedElementKey获取的elementSon类型错误");
+ flag = false;
+ }
+ } else {
+ Object elementParent = elementMap.get(addElementKey);
+ if (elementParent instanceof Paragraph) {
+ Paragraph paragraph = (Paragraph) elementParent;
+ if (elementSon instanceof Paragraph) {
+ Paragraph paragraphSon = (Paragraph) elementSon;
+ paragraph.add(paragraphSon);
+ elementMap.put(addElementKey, paragraph);
+ } else if (elementSon instanceof Phrase) {
+ Phrase phrase = (Phrase) elementSon;
+ paragraph.add(phrase);
+ elementMap.put(addElementKey, paragraph);
+ } else if (elementSon instanceof PdfPTable) {
+ PdfPTable pdfPTable = (PdfPTable) elementSon;
+ paragraph.add(pdfPTable);
+ elementMap.put(addElementKey, paragraph);
+ } else if (elementSon instanceof Image) {
+ Image image = (Image) elementSon;
+ paragraph.add(image);
+ elementMap.put(addElementKey, paragraph);
+ } else if (elementSon instanceof ListItem) {
+ ListItem listItem = (ListItem) elementSon;
+ paragraph.add(listItem);
+ elementMap.put(addElementKey, paragraph);
+ } else if (elementSon instanceof Chunk) {
+ Chunk chunk = (Chunk) elementSon;
+ paragraph.add(chunk);
+ elementMap.put(addElementKey, paragraph);
+ } else {
+ flag = false;
+ log.error("addedElementKey获取的elementSon类型错误");
+ }
+ } else if (elementParent instanceof PdfPTable) {
+ PdfPTable pdfPTable = (PdfPTable) elementParent;
+ if (elementSon instanceof Paragraph) {
+ Paragraph paragraphSon = (Paragraph) elementSon;
+ pdfPTable.addCell(paragraphSon);
+ elementMap.put(addElementKey, pdfPTable);
+ } else if (elementSon instanceof Phrase) {
+ Phrase phrase = (Phrase) elementSon;
+ pdfPTable.addCell(phrase);
+ elementMap.put(addElementKey, pdfPTable);
+ } else if (elementSon instanceof PdfPTable) {
+ PdfPTable pdfPTableSon = (PdfPTable) elementSon;
+ pdfPTable.addCell(pdfPTableSon);
+ elementMap.put(addElementKey, pdfPTable);
+ } else if (elementSon instanceof Image) {
+ Image image = (Image) elementSon;
+ pdfPTable.addCell(image);
+ elementMap.put(addElementKey, pdfPTable);
+ } else if (elementSon instanceof ListItem) {
+ ListItem listItem = (ListItem) elementSon;
+ pdfPTable.addCell(listItem);
+ elementMap.put(addElementKey, pdfPTable);
+ } else if (elementSon instanceof PdfPCell) {
+ PdfPCell cell = (PdfPCell) elementSon;
+ pdfPTable.addCell(cell);
+ elementMap.put(addElementKey, pdfPTable);
+ } else {
+ flag = false;
+ log.error("addedElementKey获取的elementSon类型错误");
+ }
+ } else if (elementParent instanceof PdfPCell) {
+ PdfPCell pdfPCell = (PdfPCell) elementParent;
+ if (elementSon instanceof Paragraph) {
+ Paragraph paragraphSon = (Paragraph) elementSon;
+ pdfPCell.addElement(paragraphSon);
+ elementMap.put(addElementKey, pdfPCell);
+ } else if (elementSon instanceof Phrase) {
+ Phrase phrase = (Phrase) elementSon;
+ pdfPCell.addElement(phrase);
+ elementMap.put(addElementKey, pdfPCell);
+ } else if (elementSon instanceof PdfPTable) {
+ PdfPTable pdfPTable = (PdfPTable) elementSon;
+ pdfPCell.addElement(pdfPTable);
+ elementMap.put(addElementKey, pdfPCell);
+ } else if (elementSon instanceof Image) {
+ Image image = (Image) elementSon;
+ pdfPCell.addElement(image);
+ elementMap.put(addElementKey, pdfPCell);
+ } else if (elementSon instanceof ListItem) {
+ ListItem listItem = (ListItem) elementSon;
+ pdfPCell.addElement(listItem);
+ elementMap.put(addElementKey, pdfPCell);
+ } else if (elementSon instanceof Chunk) {
+ Chunk chunk = (Chunk) elementSon;
+ pdfPCell.addElement(chunk);
+ elementMap.put(addElementKey, pdfPCell);
+ } else {
+ flag = false;
+ log.error("addedElementKey获取的elementSon类型错误");
+ }
+ } else if (elementParent instanceof ListItem) {
+ ListItem listItem = (ListItem) elementParent;
+ if (elementSon instanceof Paragraph) {
+ Paragraph paragraphSon = (Paragraph) elementSon;
+ listItem.add(paragraphSon);
+ elementMap.put(addElementKey, listItem);
+ } else if (elementSon instanceof Phrase) {
+ Phrase phrase = (Phrase) elementSon;
+ listItem.add(phrase);
+ elementMap.put(addElementKey, listItem);
+ } else if (elementSon instanceof PdfPTable) {
+ PdfPTable pdfPTable = (PdfPTable) elementSon;
+ listItem.add(pdfPTable);
+ elementMap.put(addElementKey, listItem);
+ } else if (elementSon instanceof Image) {
+ Image image = (Image) elementSon;
+ listItem.add(image);
+ elementMap.put(addElementKey, listItem);
+ } else if (elementSon instanceof ListItem) {
+ ListItem listItemSon = (ListItem) elementSon;
+ listItem.add(listItemSon);
+ elementMap.put(addElementKey, listItem);
+ } else if (elementSon instanceof Chunk) {
+ Chunk chunk = (Chunk) elementSon;
+ listItem.add(chunk);
+ elementMap.put(addElementKey, listItem);
+ } else {
+ flag = false;
+ log.error("addedElementKey获取的elementSon类型错误");
+ }
+ } else {
+ flag = false;
+ log.error("addElementKey获取的elementParent类型错误");
+ }
+ }
+ } catch (Exception e) {
+ log.error("build addElement error", e);
+ throw new IllegalArgumentException("build addElement error");
+ }
+ if (flag) {
+ elementMap.remove(addedElementKey);
+ }
+ return flag;
+ }
+
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/enums/BaseColorEnum.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/enums/BaseColorEnum.java
new file mode 100644
index 000000000..f45beabaa
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/enums/BaseColorEnum.java
@@ -0,0 +1,60 @@
+package com.netease.lowcode.lib.enums;
+
+import com.itextpdf.text.BaseColor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public enum BaseColorEnum {
+ WHITE("WHITE", BaseColor.WHITE),
+ LIGHT_GRAY("LIGHT_GRAY", BaseColor.LIGHT_GRAY),
+ GRAY("GRAY", BaseColor.GRAY),
+ DARK_GRAY("DARK_GRAY", BaseColor.DARK_GRAY),
+ BLACK("BLACK", BaseColor.BLACK),
+ RED("RED", BaseColor.RED),
+ PINK("PINK", BaseColor.PINK),
+ ORANGE("ORANGE", BaseColor.ORANGE),
+ YELLOW("YELLOW", BaseColor.YELLOW),
+ GREEN("GREEN", BaseColor.GREEN),
+ MAGENTA("MAGENTA", BaseColor.MAGENTA),
+ CYAN("CYAN", BaseColor.CYAN),
+ BLUE("BLUE", BaseColor.BLUE),
+ ;
+ private static final Logger log = LoggerFactory.getLogger("LCAP_EXTENSION_LOGGER");
+
+ private String color;
+ private BaseColor baseColor;
+
+ BaseColorEnum(String color, BaseColor baseColor) {
+ this.color = color;
+ this.baseColor = baseColor;
+ }
+
+ /**
+ * 根据color获取BaseColor
+ */
+ public static BaseColor getBaseColor(String color) {
+ for (BaseColorEnum value : BaseColorEnum.values()) {
+ if (value.color.equals(color)) {
+ return value.baseColor;
+ }
+ }
+ log.error("未定义的颜色color:{}", color);
+ return BaseColor.BLACK;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public void setColor(String color) {
+ this.color = color;
+ }
+
+ public BaseColor getBaseColor() {
+ return baseColor;
+ }
+
+ public void setBaseColor(BaseColor baseColor) {
+ this.baseColor = baseColor;
+ }
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/enums/ElementAlignEnum.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/enums/ElementAlignEnum.java
new file mode 100644
index 000000000..d6eb48ad9
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/enums/ElementAlignEnum.java
@@ -0,0 +1,39 @@
+package com.netease.lowcode.lib.enums;
+
+import com.itextpdf.text.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public enum ElementAlignEnum {
+ ALIGN_UNDEFINED("ALIGN_UNDEFINED", Element.ALIGN_UNDEFINED),
+ ALIGN_LEFT("ALIGN_LEFT", Element.ALIGN_LEFT),
+ ALIGN_CENTER("ALIGN_CENTER", Element.ALIGN_CENTER),
+ ALIGN_RIGHT("ALIGN_RIGHT", Element.ALIGN_RIGHT),
+ ALIGN_JUSTIFIED("ALIGN_JUSTIFIED", Element.ALIGN_JUSTIFIED),
+ ALIGN_TOP("ALIGN_TOP", Element.ALIGN_TOP),
+ ALIGN_MIDDLE("ALIGN_MIDDLE", Element.ALIGN_MIDDLE),
+ ALIGN_BOTTOM("ALIGN_BOTTOM", Element.ALIGN_BOTTOM),
+ ALIGN_BASELINE("ALIGN_BASELINE", Element.ALIGN_BASELINE),
+ ALIGN_JUSTIFIED_ALL("ALIGN_JUSTIFIED_ALL", Element.ALIGN_JUSTIFIED_ALL),
+ ;
+ private static final Logger log = LoggerFactory.getLogger("LCAP_EXTENSION_LOGGER");
+
+ private String alignText;
+ private Integer alignElement;
+
+ ElementAlignEnum(String alignText, Integer alignElement) {
+ this.alignText = alignText;
+ this.alignElement = alignElement;
+ }
+
+ public static Integer getAlignElement(String alignText) {
+ for (ElementAlignEnum elementAlignEnum : ElementAlignEnum.values()) {
+ if (elementAlignEnum.alignText.equals(alignText)) {
+ return elementAlignEnum.alignElement;
+ }
+ }
+ log.error("未定义的样式alignText:{}", alignText);
+ return Element.ALIGN_UNDEFINED;
+ }
+
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/enums/FontStyleEnum.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/enums/FontStyleEnum.java
new file mode 100644
index 000000000..87045f8a7
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/enums/FontStyleEnum.java
@@ -0,0 +1,56 @@
+package com.netease.lowcode.lib.enums;
+
+import com.itextpdf.text.Font;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public enum FontStyleEnum {
+ NORMAL("NORMAL", Font.NORMAL),
+ BOLD("BOLD", Font.BOLD),
+ ITALIC("ITALIC", Font.ITALIC),
+ UNDERLINE("UNDERLINE", Font.UNDERLINE),
+ STRIKETHRU("STRIKETHRU", Font.STRIKETHRU),
+ BOLDITALIC("BOLDITALIC", Font.BOLDITALIC),
+ UNDEFINED("UNDEFINED", Font.UNDEFINED),
+ DEFAULTSIZE("DEFAULTSIZE", Font.DEFAULTSIZE),
+ ;
+ private static final Logger log = LoggerFactory.getLogger("LCAP_EXTENSION_LOGGER");
+ private String style;
+ private Integer fontStyle;
+
+ FontStyleEnum(String style, Integer fontStyle) {
+ this.style = style;
+ this.fontStyle = fontStyle;
+ }
+
+ /**
+ * 根据style查询fontStyle
+ *
+ * @param style
+ */
+ public static Integer getFontStyle(String style) {
+ for (FontStyleEnum fontStyleEnum : FontStyleEnum.values()) {
+ if (fontStyleEnum.getStyle().equals(style)) {
+ return fontStyleEnum.getFontStyle();
+ }
+ }
+ log.error("未定义的字体style:{}", style);
+ return Font.NORMAL;
+ }
+
+ public String getStyle() {
+ return style;
+ }
+
+ public void setStyle(String style) {
+ this.style = style;
+ }
+
+ public Integer getFontStyle() {
+ return fontStyle;
+ }
+
+ public void setFontStyle(Integer fontStyle) {
+ this.fontStyle = fontStyle;
+ }
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/enums/RectangleEnum.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/enums/RectangleEnum.java
new file mode 100644
index 000000000..12d0f79eb
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/enums/RectangleEnum.java
@@ -0,0 +1,54 @@
+package com.netease.lowcode.lib.enums;
+
+import com.itextpdf.text.Rectangle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public enum RectangleEnum {
+ UNDEFINED("UNDEFINED", Rectangle.UNDEFINED),
+ TOP("TOP", Rectangle.TOP),
+ BOTTOM("BOTTOM", Rectangle.BOTTOM),
+ LEFT("LEFT", Rectangle.LEFT),
+ RIGHT("RIGHT", Rectangle.RIGHT),
+ NO_BORDER("NO_BORDER", Rectangle.NO_BORDER),
+ BOX("BOX", Rectangle.BOX);;
+ private static final Logger log = LoggerFactory.getLogger("LCAP_EXTENSION_LOGGER");
+ private String rectangleText;
+ private Integer rectangle;
+
+ RectangleEnum(String rectangleText, Integer rectangle) {
+ this.rectangleText = rectangleText;
+ this.rectangle = rectangle;
+ }
+
+ /**
+ * 根据rectangleText获取rectangle
+ *
+ * @return rectangle
+ */
+ public static Integer getRectangle(String rectangleText) {
+ for (RectangleEnum rectangleEnum : RectangleEnum.values()) {
+ if (rectangleEnum.getRectangleText().equals(rectangleText)) {
+ return rectangleEnum.getRectangle();
+ }
+ }
+ log.error("未定义的布局rectangleText:{}", rectangleText);
+ return Rectangle.UNDEFINED;
+ }
+
+ public String getRectangleText() {
+ return rectangleText;
+ }
+
+ public void setRectangleText(String rectangleText) {
+ this.rectangleText = rectangleText;
+ }
+
+ public Integer getRectangle() {
+ return rectangle;
+ }
+
+ public void setRectangle(Integer rectangle) {
+ this.rectangle = rectangle;
+ }
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/file/Base64Util.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/file/Base64Util.java
new file mode 100644
index 000000000..0a304eff6
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/file/Base64Util.java
@@ -0,0 +1,27 @@
+package com.netease.lowcode.lib.file;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.util.Base64;
+
+public class Base64Util {
+ private final static Logger log = LoggerFactory.getLogger("LCAP_EXTENSION_LOGGER");
+
+ public static String convertImageToBase64String(InputStream is) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int len;
+ while ((len = is.read(buffer)) != -1) {
+ baos.write(buffer, 0, len);
+ }
+ byte[] imageBytes = baos.toByteArray();
+ return Base64.getEncoder().encodeToString(imageBytes);
+ } catch (IOException e) {
+ log.error("convertImageToBase64String error", e);
+ return null;
+ }
+ }
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/file/FileConnectorUtils.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/file/FileConnectorUtils.java
new file mode 100644
index 000000000..a635171e1
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/file/FileConnectorUtils.java
@@ -0,0 +1,89 @@
+package com.netease.lowcode.lib.file;
+
+import com.netease.cloud.codewave.file.connector.AbstractFileConnector;
+import com.netease.cloud.codewave.file.connector.CodeWaveFileConstants;
+import com.netease.cloud.codewave.file.connector.FileConnectionManager;
+import com.netease.cloud.codewave.file.connector.FileDownloadResult;
+import com.netease.cloud.codewave.file.connector.utils.CodeWaveFileUrl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+@Component("itextpdfFileConnectorUtils")
+public class FileConnectorUtils {
+ public static final String UPLOAD_API_PATH_PREFIX = "/upload";
+ private final static Logger log = LoggerFactory.getLogger("LCAP_EXTENSION_LOGGER");
+
+ /**
+ * 文件上传-311及之后版本
+ *
+ * @param fis 文件输入流,不能为空
+ * @param fileName 文件名称,包含文件后缀,不能为空
+ * @return success true时上传成功,false时上传失败
+ */
+ public UploadResponseDTO fileUploadV2(InputStream fis, String fileName, Map payloads) {
+ UploadResponseDTO responseDTO = new UploadResponseDTO();
+ // 获取文件连接器
+ AbstractFileConnector defaultConnector = FileConnectionManager.getDefaultFileConnector();
+ CodeWaveFileUrl fileUrl = new CodeWaveFileUrl(fileName);
+ //指定默认文件连接器的唯一标志。用在url拼接上。
+ fileUrl.addQueryString(CodeWaveFileConstants.FILE_CONNECTION, defaultConnector.fileStorageCode());
+ //调用制品配置的文件连接器的上传方法
+ CodeWaveFileUrl result = defaultConnector.upload(fis, fileUrl, payloads);
+ String filePath = ((result.toUrl().startsWith("/")) ? result.toUrl() : "/" + result.toUrl());
+ //处理文件url
+ responseDTO.setFilePath("/upload" + filePath);
+ ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+ if (Objects.nonNull(requestAttributes)) {
+ HttpServletRequest request = requestAttributes.getRequest();
+ responseDTO.setResult(request.getScheme() + "://" + request.getServerName() +
+ (80 == request.getServerPort() ? "" : ":" + request.getServerPort()) +
+ responseDTO.getFilePath());
+ } else {
+ responseDTO.setResult(responseDTO.getFilePath());
+ }
+ responseDTO.setSuccess(true);
+ responseDTO.setCode(200);
+ return responseDTO;
+ }
+
+ /**
+ * 文件下载-311及之后版本
+ *
+ * @param fileUrl 符合文件连接器规范的文件url
+ * @return base64String
+ */
+ public InputStream fileDownloadV2(String fileUrl) {
+ try {
+ String path = dealPath(fileUrl);
+ // 获取文件连接器
+ AbstractFileConnector defaultConnector = FileConnectionManager.getDefaultFileConnector();
+ //将fileUrl构造成CodeWaveFileUrl
+ CodeWaveFileUrl fileUrlCodeWaveFileUrl = CodeWaveFileUrl.fromUri(path);
+ //调用制品配置的文件连接器的下载方法
+ FileDownloadResult result = defaultConnector.download(fileUrlCodeWaveFileUrl, new HashMap<>());
+ return result.getInputStream();
+ } catch (Exception e) {
+ log.error("uploadBase64File error", e);
+ }
+ return null;
+ }
+
+ public String dealPath(String path) {
+ path = path.replaceAll("/{2,}", "/");
+ path = path.startsWith("/download") ? path.substring("/download".length()) :
+ path.substring(path.indexOf(UPLOAD_API_PATH_PREFIX) + UPLOAD_API_PATH_PREFIX.length());
+ if (path.startsWith("/")) {
+ path = path.substring(1);
+ }
+ return path;
+ }
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/file/UploadResponseDTO.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/file/UploadResponseDTO.java
new file mode 100644
index 000000000..7f28486a8
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/file/UploadResponseDTO.java
@@ -0,0 +1,84 @@
+package com.netease.lowcode.lib.file;
+
+import com.netease.lowcode.core.annotation.NaslStructure;
+
+import java.io.Serializable;
+
+@NaslStructure
+public class UploadResponseDTO implements Serializable {
+ public Integer code;
+ public String msg;
+ public String result;
+ public String filePath;
+ public Boolean success;
+ public String trace;
+
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public String getResult() {
+ return result;
+ }
+
+ public void setResult(String result) {
+ this.result = result;
+ }
+
+ public String getFilePath() {
+ return filePath;
+ }
+
+ public void setFilePath(String filePath) {
+ this.filePath = filePath;
+ }
+
+ public Boolean getSuccess() {
+ return success;
+ }
+
+ public void setSuccess(Boolean success) {
+ this.success = success;
+ }
+
+ public String getTrace() {
+ return trace;
+ }
+
+ public void setTrace(String trace) {
+ this.trace = trace;
+ }
+
+ public static UploadResponseDTO FAIL(String msg) {
+ return FAIL(msg, null);
+ }
+ public static UploadResponseDTO FAIL(String msg, String trace) {
+ UploadResponseDTO fail = new UploadResponseDTO();
+ fail.setSuccess(false);
+ fail.setMsg(msg);
+ fail.setTrace(trace);
+ fail.setCode(500);
+ return fail;
+ }
+ public static UploadResponseDTO OK(String path, String url) {
+ UploadResponseDTO callSuccess=new UploadResponseDTO();
+ callSuccess.setFilePath(path);
+ callSuccess.setSuccess(true);
+ callSuccess.setResult(url);
+ callSuccess.setCode(200);
+ return callSuccess;
+ }
+
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/itextpdf/BaseFontBuilder.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/itextpdf/BaseFontBuilder.java
new file mode 100644
index 000000000..7ac350db5
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/itextpdf/BaseFontBuilder.java
@@ -0,0 +1,35 @@
+package com.netease.lowcode.lib.itextpdf;
+
+import com.itextpdf.text.DocumentException;
+import com.itextpdf.text.Font;
+import com.itextpdf.text.pdf.BaseFont;
+import com.netease.lowcode.lib.enums.BaseColorEnum;
+import com.netease.lowcode.lib.enums.FontStyleEnum;
+import com.netease.lowcode.lib.structure.ITextFontStructure;
+import org.springframework.util.StringUtils;
+
+import java.io.IOException;
+
+public class BaseFontBuilder {
+ public static Font setFont(ITextFontStructure iTextFontStructure) throws DocumentException, IOException {
+ if (StringUtils.isEmpty(iTextFontStructure.getFontEncoding())) {
+ iTextFontStructure.setFontEncoding("UniGB-UCS2-H");
+ }
+ if (iTextFontStructure.getEmbedded() == null) {
+ iTextFontStructure.setEmbedded(false);
+ }
+ if (iTextFontStructure.getForceRead() == null) {
+ iTextFontStructure.setForceRead(false);
+ }
+ if (iTextFontStructure.getSize() == null) {
+ iTextFontStructure.setSize(-1.0d);
+ }
+ if (StringUtils.isEmpty(iTextFontStructure.getFontName())) {
+ iTextFontStructure.setFontName("STSong-Light");
+ }
+ BaseFont baseFont = BaseFont.createFont(iTextFontStructure.getFontName(), iTextFontStructure.getFontEncoding(), iTextFontStructure.getEmbedded(), iTextFontStructure.getForceRead());
+ Integer style = FontStyleEnum.getFontStyle(iTextFontStructure.getStyle());
+ return new Font(baseFont, Float.parseFloat(iTextFontStructure.getSize() + ""), style, BaseColorEnum.getBaseColor(iTextFontStructure.getColor()));
+ }
+
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/itextpdf/PdfBuilderUtil.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/itextpdf/PdfBuilderUtil.java
new file mode 100644
index 000000000..d857060c9
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/itextpdf/PdfBuilderUtil.java
@@ -0,0 +1,275 @@
+package com.netease.lowcode.lib.itextpdf;
+
+import com.itextpdf.text.*;
+import com.itextpdf.text.pdf.PdfPCell;
+import com.itextpdf.text.pdf.PdfPTable;
+import com.netease.lowcode.lib.enums.BaseColorEnum;
+import com.netease.lowcode.lib.enums.ElementAlignEnum;
+import com.netease.lowcode.lib.enums.RectangleEnum;
+import com.netease.lowcode.lib.structure.*;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
+public class PdfBuilderUtil {
+
+ public static void fillParagraph(Paragraph paragraph, ITextParagraphStructure iTextParagraphStructure) {
+ if (iTextParagraphStructure == null) {
+ return;
+ }
+ if (iTextParagraphStructure.getAlignText() != null) {
+ paragraph.setAlignment(ElementAlignEnum.getAlignElement(iTextParagraphStructure.getAlignText()));
+ }
+ if (iTextParagraphStructure.getIndentationLeft() != null) {
+ paragraph.setIndentationLeft(Float.parseFloat(iTextParagraphStructure.getIndentationLeft() + ""));
+ }
+ if (iTextParagraphStructure.getIndentationRight() != null) {
+ paragraph.setIndentationRight(Float.parseFloat(iTextParagraphStructure.getIndentationRight() + ""));
+ }
+ if (iTextParagraphStructure.getFirstLineIndent() != null) {
+ paragraph.setFirstLineIndent(Float.parseFloat(iTextParagraphStructure.getFirstLineIndent() + ""));
+ }
+ if (iTextParagraphStructure.getSpacingBefore() != null) {
+ paragraph.setSpacingBefore(Float.parseFloat(iTextParagraphStructure.getSpacingBefore() + ""));
+ }
+ if (iTextParagraphStructure.getSpacingAfter() != null) {
+ paragraph.setSpacingAfter(Float.parseFloat(iTextParagraphStructure.getSpacingAfter() + ""));
+ }
+ if (iTextParagraphStructure.getFixedLeading() != null) {
+ paragraph.setLeading(Float.parseFloat(iTextParagraphStructure.getFixedLeading() + ""));
+ }
+ if (iTextParagraphStructure.getMultipliedLeading() != null) {
+ paragraph.setMultipliedLeading(Float.parseFloat(iTextParagraphStructure.getMultipliedLeading() + ""));
+ }
+ if (iTextParagraphStructure.getKeepTogether() != null) {
+ paragraph.setKeepTogether(iTextParagraphStructure.getKeepTogether());
+ }
+ }
+
+ public static void fillTable(PdfPTable table, ITextTableStructure iTextTableStructure) throws DocumentException {
+ if (iTextTableStructure == null) {
+ return;
+ }
+ if (iTextTableStructure.getWidthPercentage() != null) {
+ table.setWidthPercentage(Float.parseFloat(iTextTableStructure.getWidthPercentage() + ""));
+ }
+ if (iTextTableStructure.getTotalWidth() != null) {
+ table.setTotalWidth(Float.parseFloat(iTextTableStructure.getTotalWidth() + ""));
+ }
+ if (iTextTableStructure.getLockedWidth() != null) {
+ table.setLockedWidth(iTextTableStructure.getLockedWidth());
+ }
+ if (iTextTableStructure.getHorizontalAlignmentText() != null) {
+ table.setHorizontalAlignment(ElementAlignEnum.getAlignElement(iTextTableStructure.getHorizontalAlignmentText()));
+ }
+ if (iTextTableStructure.getSpacingBefore() != null) {
+ table.setSpacingBefore(Float.parseFloat(iTextTableStructure.getSpacingBefore() + ""));
+ }
+ if (iTextTableStructure.getSpacingAfter() != null) {
+ table.setSpacingAfter(Float.parseFloat(iTextTableStructure.getSpacingAfter() + ""));
+ }
+ if (iTextTableStructure.getWidths() != null && !iTextTableStructure.getWidths().isEmpty()) {
+ float[] widths = new float[iTextTableStructure.getWidths().size()];
+ for (int i = 0; i < iTextTableStructure.getWidths().size(); i++) {
+ widths[i] = Float.parseFloat(iTextTableStructure.getWidths().get(i) + "");
+ }
+ table.setWidths(widths);
+ }
+ if (iTextTableStructure.getHeaderRows() != null) {
+ table.setHeaderRows(iTextTableStructure.getHeaderRows());
+ }
+ if (iTextTableStructure.getFooterRows() != null) {
+ table.setFooterRows(iTextTableStructure.getFooterRows());
+ }
+ if (iTextTableStructure.getSplitLate() != null) {
+ table.setSplitLate(iTextTableStructure.getSplitLate());
+ }
+ if (iTextTableStructure.getSkipFirstHeader() != null) {
+ table.setSkipFirstHeader(iTextTableStructure.getSkipFirstHeader());
+ }
+ }
+
+ public static void fillCell(PdfPCell cell, ITextCellStructure iTextCellStructure) {
+ if (iTextCellStructure == null) {
+ return;
+ }
+ if (iTextCellStructure.getBorder() != null) {
+ cell.setBorder(RectangleEnum.getRectangle(iTextCellStructure.getBorder()));
+ }
+ if (iTextCellStructure.getBorderWidth() != null) {
+ cell.setBorderWidth(Float.parseFloat(iTextCellStructure.getBorderWidth() + ""));
+ }
+ if (iTextCellStructure.getPadding() != null) {
+ cell.setPadding(Float.parseFloat(iTextCellStructure.getPadding() + ""));
+ }
+ if (iTextCellStructure.getBorderColor() != null) {
+ cell.setBorderColor(BaseColorEnum.getBaseColor(iTextCellStructure.getBorderColor()));
+ }
+ if (iTextCellStructure.getPaddingLeft() != null) {
+ cell.setPaddingLeft(Float.parseFloat(iTextCellStructure.getPaddingLeft() + ""));
+ }
+ if (iTextCellStructure.getPaddingRight() != null) {
+ cell.setPaddingRight(Float.parseFloat(iTextCellStructure.getPaddingRight() + ""));
+ }
+ if (iTextCellStructure.getPaddingTop() != null) {
+ cell.setPaddingTop(Float.parseFloat(iTextCellStructure.getPaddingTop() + ""));
+ }
+ if (iTextCellStructure.getPaddingBottom() != null) {
+ cell.setPaddingBottom(Float.parseFloat(iTextCellStructure.getPaddingBottom() + ""));
+ }
+ if (iTextCellStructure.getHorizontalAlignmentText() != null) {
+ cell.setHorizontalAlignment(ElementAlignEnum.getAlignElement(iTextCellStructure.getHorizontalAlignmentText()));
+ }
+ if (iTextCellStructure.getVerticalAlignmentText() != null) {
+ cell.setVerticalAlignment(ElementAlignEnum.getAlignElement(iTextCellStructure.getVerticalAlignmentText()));
+ }
+ if (iTextCellStructure.getFixedHeight() != null) {
+ cell.setFixedHeight(Float.parseFloat(iTextCellStructure.getFixedHeight() + ""));
+ }
+ if (iTextCellStructure.getMinimumHeight() != null) {
+ cell.setMinimumHeight(Float.parseFloat(iTextCellStructure.getMinimumHeight() + ""));
+ }
+ if (iTextCellStructure.getFixedLeading() == null) {
+ iTextCellStructure.setFixedLeading(0d);
+ }
+ if (iTextCellStructure.getMultipliedLeading() == null) {
+ iTextCellStructure.setMultipliedLeading(0d);
+ }
+ cell.setLeading(Float.parseFloat(iTextCellStructure.getFixedLeading() + ""), Float.parseFloat(iTextCellStructure.getMultipliedLeading() + ""));
+
+ if (iTextCellStructure.getBackgroundColor() != null) {
+ cell.setBackgroundColor(BaseColorEnum.getBaseColor(iTextCellStructure.getBackgroundColor()));
+ }
+ if (iTextCellStructure.getColSpan() != null) {
+ cell.setColspan(iTextCellStructure.getColSpan());
+ }
+ if (iTextCellStructure.getRowSpan() != null) {
+ cell.setRowspan(iTextCellStructure.getRowSpan());
+ }
+ }
+
+
+ public static void fillChunk(Chunk chunk, ITextChunkStructure iTextChunkStructure) {
+ if (iTextChunkStructure == null) {
+ return;
+ }
+ if (iTextChunkStructure.getCharacterSpacing() != null) {
+ chunk.setCharacterSpacing(Float.parseFloat(iTextChunkStructure.getCharacterSpacing() + ""));
+ }
+ if (iTextChunkStructure.getWordSpacing() != null) {
+ chunk.setWordSpacing(Float.parseFloat(iTextChunkStructure.getWordSpacing() + ""));
+ }
+ if (iTextChunkStructure.getAnchor() != null) {
+ chunk.setAnchor(iTextChunkStructure.getAnchor());
+ }
+ if (iTextChunkStructure.getBackgroundColor() != null) {
+ chunk.setBackground(BaseColorEnum.getBaseColor(iTextChunkStructure.getBackgroundColor()));
+ }
+ }
+
+ public static void fillPhrase(Phrase phrase, ITextPhraseStructure iTextPhraseStructure) {
+ if (iTextPhraseStructure == null) {
+ return;
+ }
+ if (iTextPhraseStructure.getFixedLeading() != null) {
+ phrase.setLeading(Float.parseFloat(iTextPhraseStructure.getFixedLeading() + ""));
+ }
+ if (iTextPhraseStructure.getMultipliedLeading() != null) {
+ phrase.setMultipliedLeading(Float.parseFloat(iTextPhraseStructure.getMultipliedLeading() + ""));
+ }
+ }
+
+ public static void filListItem(ListItem listItem, ITextListStructure iTextListStructure) {
+ if (iTextListStructure == null) {
+ return;
+ }
+ if (iTextListStructure.getAlignText() != null) {
+ listItem.setAlignment(ElementAlignEnum.getAlignElement(iTextListStructure.getAlignText()));
+ }
+ if (iTextListStructure.getIndentationLeft() != null) {
+ listItem.setIndentationLeft(Float.parseFloat(iTextListStructure.getIndentationLeft() + ""));
+ }
+ if (iTextListStructure.getIndentationRight() != null) {
+ listItem.setIndentationRight(Float.parseFloat(iTextListStructure.getIndentationRight() + ""));
+ }
+ if (iTextListStructure.getFirstLineIndent() != null) {
+ listItem.setFirstLineIndent(Float.parseFloat(iTextListStructure.getFirstLineIndent() + ""));
+ }
+ if (iTextListStructure.getKeepTogether() != null) {
+ listItem.setKeepTogether(iTextListStructure.getKeepTogether());
+ }
+ if (iTextListStructure.getSpacingBefore() != null) {
+ listItem.setSpacingBefore(Float.parseFloat(iTextListStructure.getSpacingBefore() + ""));
+ }
+ if (iTextListStructure.getSpacingAfter() != null) {
+ listItem.setSpacingAfter(Float.parseFloat(iTextListStructure.getSpacingAfter() + ""));
+ }
+ if (iTextListStructure.getPaddingTop() != null) {
+ listItem.setPaddingTop(Float.parseFloat(iTextListStructure.getPaddingTop() + ""));
+ }
+ if (iTextListStructure.getFixedLeading() != null) {
+ listItem.setLeading(Float.parseFloat(iTextListStructure.getFixedLeading() + ""));
+ }
+ if (iTextListStructure.getMultipliedLeading() != null) {
+ listItem.setMultipliedLeading(Float.parseFloat(iTextListStructure.getMultipliedLeading() + ""));
+ }
+ }
+
+ public static void fillImage(Image image, ITextImageStructure iTextImageStructure) throws MalformedURLException {
+ if (iTextImageStructure == null) {
+ return;
+ }
+ if (iTextImageStructure.getScaleToFitHeight() != null && iTextImageStructure.getScaleToFitWidth() != null) {
+ image.scaleToFit(Float.parseFloat(iTextImageStructure.getScaleToFitWidth() + ""), Float.parseFloat(iTextImageStructure.getScaleToFitHeight() + ""));
+ }
+
+ if (iTextImageStructure.getScaleAbsoluteWidth() != null && iTextImageStructure.getScaleAbsoluteHeight() == null) {
+ image.scaleAbsolute(Float.parseFloat(iTextImageStructure.getScaleAbsoluteWidth() + ""), Float.parseFloat(iTextImageStructure.getScaleAbsoluteHeight() + ""));
+ }
+ if (iTextImageStructure.getScalePercent() != null) {
+ image.scalePercent(Float.parseFloat(iTextImageStructure.getScalePercent() + ""));
+ }
+ if (iTextImageStructure.getAlignText() != null) {
+ image.setAlignment(ElementAlignEnum.getAlignElement(iTextImageStructure.getAlignText()));
+ }
+ if (iTextImageStructure.getAbsoluteX() != null && iTextImageStructure.getAbsoluteY() != null) {
+ image.setAbsolutePosition(Float.parseFloat(iTextImageStructure.getAbsoluteX() + ""), Float.parseFloat(iTextImageStructure.getAbsoluteY() + ""));
+ }
+ if (iTextImageStructure.getIndentationLeft() != null) {
+ image.setIndentationLeft(Float.parseFloat(iTextImageStructure.getIndentationLeft() + ""));
+ }
+ if (iTextImageStructure.getBorder() != null) {
+ image.setBorder(RectangleEnum.getRectangle(iTextImageStructure.getBorder()));
+ }
+ if (iTextImageStructure.getBorderWidth() != null) {
+ image.setBorderWidth(Float.parseFloat(iTextImageStructure.getBorderWidth() + ""));
+ }
+ if (iTextImageStructure.getBorderColor() != null) {
+ image.setBorderColor(BaseColorEnum.getBaseColor(iTextImageStructure.getBorderColor()));
+ }
+ if (iTextImageStructure.getSpacingBefore() != null) {
+ image.setSpacingBefore(Float.parseFloat(iTextImageStructure.getSpacingBefore() + ""));
+ }
+ if (iTextImageStructure.getSpacingAfter() != null) {
+ image.setSpacingAfter(Float.parseFloat(iTextImageStructure.getSpacingAfter() + ""));
+ }
+ if (iTextImageStructure.getTransparency() != null && !iTextImageStructure.getTransparency().isEmpty()) {
+ List transparencyList = iTextImageStructure.getTransparency();
+ int[] transparency = new int[transparencyList.size()];
+ for (int i = 0; i < transparencyList.size(); i++) {
+ transparency[i] = transparencyList.get(i);
+ }
+ image.setTransparency(transparency);
+ }
+ if (iTextImageStructure.getAnchor() != null) {
+ image.setUrl(new URL(iTextImageStructure.getAnchor()));
+ }
+ if (iTextImageStructure.getCompressionLevel() != null) {
+ image.setCompressionLevel(iTextImageStructure.getCompressionLevel());
+ }
+ if (iTextImageStructure.getDpiX() != null && iTextImageStructure.getDpiY() != null) {
+ image.setDpi(iTextImageStructure.getDpiX(), iTextImageStructure.getDpiY());
+ }
+ }
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/itextpdf/PdfDocumentBuilder.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/itextpdf/PdfDocumentBuilder.java
new file mode 100644
index 000000000..ccaa8ec97
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/itextpdf/PdfDocumentBuilder.java
@@ -0,0 +1,52 @@
+package com.netease.lowcode.lib.itextpdf;
+
+import com.itextpdf.text.Document;
+import com.itextpdf.text.pdf.PdfWriter;
+
+public class PdfDocumentBuilder {
+
+
+ private Document document;
+ private String tempDirectoryFinal;
+ private String fileName;
+ private PdfWriter pdfWriter;
+
+ public PdfDocumentBuilder(Document document, String tempDirectoryFinal, String fileName, PdfWriter pdfWriter) {
+ this.document = document;
+ this.tempDirectoryFinal = tempDirectoryFinal;
+ this.fileName = fileName;
+ this.pdfWriter = pdfWriter;
+ }
+
+ public PdfWriter getPdfWriter() {
+ return pdfWriter;
+ }
+
+ public void setPdfWriter(PdfWriter pdfWriter) {
+ this.pdfWriter = pdfWriter;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ public String getTempDirectoryFinal() {
+ return tempDirectoryFinal;
+ }
+
+ public void setTempDirectoryFinal(String tempDirectoryFinal) {
+ this.tempDirectoryFinal = tempDirectoryFinal;
+ }
+
+ public Document getDocument() {
+ return document;
+ }
+
+ public void setDocument(Document document) {
+ this.document = document;
+ }
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextCellStructure.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextCellStructure.java
new file mode 100644
index 000000000..a6031fa25
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextCellStructure.java
@@ -0,0 +1,212 @@
+package com.netease.lowcode.lib.structure;
+
+import com.netease.lowcode.core.annotation.NaslStructure;
+
+@NaslStructure
+public class ITextCellStructure {
+ /**
+ * 全局边框(UNDEFINED/TOP/BOTTOM/LEFT/RIGHT/NO_BORDER/BOX)
+ */
+ public String border;
+ /**
+ * 单元格边框宽度
+ */
+ public Double borderWidth;
+ /**
+ * 单元格内边距
+ */
+ public Double padding;
+ /**
+ * 边框颜色,枚举:WHITE/LIGHT_GRAY/GRAY/DARK_GRAY/BLACK/RED/PINK/ORANGE/YELLOW/GREEN/MAGENTA/CYAN/BLUE
+ */
+ public String borderColor;
+ /**
+ * 左边距
+ */
+ public Double paddingLeft;
+ /**
+ * 右边距
+ */
+ public Double paddingRight;
+ /**
+ * 上边距
+ */
+ public Double paddingTop;
+ /**
+ * 下边距
+ */
+ public Double paddingBottom;
+ /**
+ * 水平对齐方式(ALIGN_UNDEFINED/ALIGN_LEFT/ALIGN_CENTER/ALIGN_RIGHT/ALIGN_JUSTIFIED/ALIGN_TOP/ALIGN_MIDDLE/ALIGN_BOTTOM/ALIGN_BASELINE/ALIGN_JUSTIFIED_ALL)
+ */
+ public String horizontalAlignmentText;
+ /**
+ * 垂直对齐方式(ALIGN_UNDEFINED/ALIGN_LEFT/ALIGN_CENTER/ALIGN_RIGHT/ALIGN_JUSTIFIED/ALIGN_TOP/ALIGN_MIDDLE/ALIGN_BOTTOM/ALIGN_BASELINE/ALIGN_JUSTIFIED_ALL)
+ */
+ public String verticalAlignmentText;
+ /**
+ * 固定高度
+ */
+ public Double fixedHeight;
+ /**
+ * 最小高度
+ */
+ public Double minimumHeight;
+ /**
+ * 行间距(固定值)
+ */
+ public Double fixedLeading;
+ /**
+ * 行间距(倍数)
+ */
+ public Double multipliedLeading;
+ /**
+ * 背景颜色,枚举:WHITE/LIGHT_GRAY/GRAY/DARK_GRAY/BLACK/RED/PINK/ORANGE/YELLOW/GREEN/MAGENTA/CYAN/BLUE
+ */
+ public String backgroundColor;
+ /**
+ * 横向合并列数
+ */
+ public Integer colSpan;
+ /**
+ * 纵向合并行数
+ */
+ public Integer rowSpan;
+
+ public String getVerticalAlignmentText() {
+ return verticalAlignmentText;
+ }
+
+ public void setVerticalAlignmentText(String verticalAlignmentText) {
+ this.verticalAlignmentText = verticalAlignmentText;
+ }
+
+ public String getHorizontalAlignmentText() {
+ return horizontalAlignmentText;
+ }
+
+ public void setHorizontalAlignmentText(String horizontalAlignmentText) {
+ this.horizontalAlignmentText = horizontalAlignmentText;
+ }
+
+
+ public Double getFixedHeight() {
+ return fixedHeight;
+ }
+
+ public void setFixedHeight(Double fixedHeight) {
+ this.fixedHeight = fixedHeight;
+ }
+
+ public Double getMinimumHeight() {
+ return minimumHeight;
+ }
+
+ public void setMinimumHeight(Double minimumHeight) {
+ this.minimumHeight = minimumHeight;
+ }
+
+ public Double getFixedLeading() {
+ return fixedLeading;
+ }
+
+ public void setFixedLeading(Double fixedLeading) {
+ this.fixedLeading = fixedLeading;
+ }
+
+ public Double getMultipliedLeading() {
+ return multipliedLeading;
+ }
+
+ public void setMultipliedLeading(Double multipliedLeading) {
+ this.multipliedLeading = multipliedLeading;
+ }
+
+ public String getBackgroundColor() {
+ return backgroundColor;
+ }
+
+ public void setBackgroundColor(String backgroundColor) {
+ this.backgroundColor = backgroundColor;
+ }
+
+ public Integer getColSpan() {
+ return colSpan;
+ }
+
+ public void setColSpan(Integer colSpan) {
+ this.colSpan = colSpan;
+ }
+
+ public Integer getRowSpan() {
+ return rowSpan;
+ }
+
+ public void setRowSpan(Integer rowSpan) {
+ this.rowSpan = rowSpan;
+ }
+
+ public String getBorder() {
+ return border;
+ }
+
+ public void setBorder(String border) {
+ this.border = border;
+ }
+
+ public Double getBorderWidth() {
+ return borderWidth;
+ }
+
+ public void setBorderWidth(Double borderWidth) {
+ this.borderWidth = borderWidth;
+ }
+
+ public Double getPadding() {
+ return padding;
+ }
+
+ public void setPadding(Double padding) {
+ this.padding = padding;
+ }
+
+ public String getBorderColor() {
+ return borderColor;
+ }
+
+ public void setBorderColor(String borderColor) {
+ this.borderColor = borderColor;
+ }
+
+ public Double getPaddingLeft() {
+ return paddingLeft;
+ }
+
+ public void setPaddingLeft(Double paddingLeft) {
+ this.paddingLeft = paddingLeft;
+ }
+
+ public Double getPaddingRight() {
+ return paddingRight;
+ }
+
+ public void setPaddingRight(Double paddingRight) {
+ this.paddingRight = paddingRight;
+ }
+
+ public Double getPaddingTop() {
+ return paddingTop;
+ }
+
+ public void setPaddingTop(Double paddingTop) {
+ this.paddingTop = paddingTop;
+ }
+
+ public Double getPaddingBottom() {
+ return paddingBottom;
+ }
+
+ public void setPaddingBottom(Double paddingBottom) {
+ this.paddingBottom = paddingBottom;
+ }
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextChunkStructure.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextChunkStructure.java
new file mode 100644
index 000000000..7a8e4e860
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextChunkStructure.java
@@ -0,0 +1,55 @@
+package com.netease.lowcode.lib.structure;
+
+import com.netease.lowcode.core.annotation.NaslStructure;
+
+@NaslStructure
+public class ITextChunkStructure {
+ /**
+ * 设置字符间距
+ */
+ public Double characterSpacing;
+ /**
+ * 添加超链接
+ */
+ public String anchor;
+ /**
+ * 设置单词间距(仅对空格有效)
+ */
+ public Double wordSpacing;
+ /**
+ * 设置背景色
+ */
+ public String backgroundColor;
+
+ public Double getCharacterSpacing() {
+ return characterSpacing;
+ }
+
+ public void setCharacterSpacing(Double characterSpacing) {
+ this.characterSpacing = characterSpacing;
+ }
+
+ public String getAnchor() {
+ return anchor;
+ }
+
+ public void setAnchor(String anchor) {
+ this.anchor = anchor;
+ }
+
+ public Double getWordSpacing() {
+ return wordSpacing;
+ }
+
+ public void setWordSpacing(Double wordSpacing) {
+ this.wordSpacing = wordSpacing;
+ }
+
+ public String getBackgroundColor() {
+ return backgroundColor;
+ }
+
+ public void setBackgroundColor(String backgroundColor) {
+ this.backgroundColor = backgroundColor;
+ }
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextFontStructure.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextFontStructure.java
new file mode 100644
index 000000000..f17df02dd
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextFontStructure.java
@@ -0,0 +1,98 @@
+package com.netease.lowcode.lib.structure;
+
+import com.itextpdf.text.DocumentException;
+import com.itextpdf.text.pdf.BaseFont;
+import com.netease.lowcode.core.annotation.NaslStructure;
+import org.springframework.util.StringUtils;
+
+import java.io.IOException;
+
+@NaslStructure
+public class ITextFontStructure {
+ /**
+ * 字体名称,默认STSong-Light
+ */
+ public String fontName;
+ /**
+ * 字体编码,默认UniGB-UCS2-H
+ */
+ public String fontEncoding;
+ /**
+ * 是否嵌入字体:true表示将字体文件嵌入文档(确保跨设备显示一致),false表示不嵌入
+ */
+ public Boolean embedded;
+ /**
+ * 强制读取字体:true表示忽略系统缓存强制重新加载字体文件(解决字体更新问题)
+ */
+ public Boolean forceRead;
+ /**
+ * 字体大小(单位:磅/pt),例如 12.0 表示12磅字号
+ */
+ public Double size;
+ /**
+ * 样式,枚举:NORMAL/BOLD/ITALIC/UNDERLINE/STRIKETHRU/BOLDITALIC/UNDEFINED/DEFAULTSIZE
+ */
+ public String style;
+ /**
+ * 颜色,枚举:WHITE/LIGHT_GRAY/GRAY/DARK_GRAY/BLACK/RED/PINK/ORANGE/YELLOW/GREEN/MAGENTA/CYAN/BLUE
+ */
+ public String color;
+
+ public String getFontName() {
+ return fontName;
+ }
+
+ public void setFontName(String fontName) {
+ this.fontName = fontName;
+ }
+
+ public String getFontEncoding() {
+ return fontEncoding;
+ }
+
+ public void setFontEncoding(String fontEncoding) {
+ this.fontEncoding = fontEncoding;
+ }
+
+ public Boolean getEmbedded() {
+ return embedded;
+ }
+
+ public void setEmbedded(Boolean embedded) {
+ this.embedded = embedded;
+ }
+
+ public Boolean getForceRead() {
+ return forceRead;
+ }
+
+ public void setForceRead(Boolean forceRead) {
+ this.forceRead = forceRead;
+ }
+
+
+ public Double getSize() {
+ return size;
+ }
+
+ public void setSize(Double size) {
+ this.size = size;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public void setColor(String color) {
+ this.color = color;
+ }
+
+ public String getStyle() {
+ return style;
+ }
+
+ public void setStyle(String style) {
+ this.style = style;
+ }
+
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextImageStructure.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextImageStructure.java
new file mode 100644
index 000000000..86588d318
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextImageStructure.java
@@ -0,0 +1,239 @@
+package com.netease.lowcode.lib.structure;
+
+import com.netease.lowcode.core.annotation.NaslStructure;
+
+import java.util.List;
+
+@NaslStructure
+public class ITextImageStructure {
+ /**
+ * 按比例缩放图片到目标尺寸(保持宽高比)
+ */
+ public Double scaleToFitHeight;
+ /**
+ * 按比例缩放图片到目标尺寸(保持宽高比)
+ */
+ public Double scaleToFitWidth;
+ /**
+ * 缩放图片的百分比
+ */
+ public Double scalePercent;
+ /**
+ * 固定宽度,高度按比例调整。
+ */
+ public Double scaleAbsoluteWidth;
+ /**
+ * 固定高度,宽度按比例调整。
+ */
+ public Double scaleAbsoluteHeight;
+ /**
+ * 对齐方式
+ * ALIGN_UNDEFINED/ALIGN_LEFT/ALIGN_CENTER/ALIGN_RIGHT/ALIGN_JUSTIFIED/ALIGN_TOP/ALIGN_MIDDLE/ALIGN_BOTTOM/ALIGN_BASELINE/ALIGN_JUSTIFIED_ALL
+ */
+ public String alignText;
+ /**
+ * 设置绝对坐标(相对于页面左下角)
+ */
+ public Double absoluteX;
+ /**
+ * 设置绝对坐标(相对于页面左下角)
+ */
+ public Double absoluteY;
+ /**
+ * 左缩进(需在非绝对定位模式下使用)
+ */
+ public Double indentationLeft;
+ /**
+ * 全局边框(UNDEFINED/TOP/BOTTOM/LEFT/RIGHT/NO_BORDER/BOX)
+ */
+ public String border;
+ /**
+ * 边框宽度
+ */
+ public Double borderWidth;
+
+ /**
+ * 边框颜色,枚举:WHITE/LIGHT_GRAY/GRAY/DARK_GRAY/BLACK/RED/PINK/ORANGE/YELLOW/GREEN/MAGENTA/CYAN/BLUE
+ */
+ public String borderColor;
+ /**
+ * 图片上方的空白间距
+ */
+ public Double spacingBefore;
+ /**
+ * 图片下方的空白间距
+ */
+ public Double spacingAfter;
+ /**
+ * 置透明度(ARGB值),示例:{200, 255, 255, 255}
+ */
+ public List transparency;
+ /**
+ * 设置超链接(点击跳转URL)
+ */
+ public String anchor;
+ /**
+ * 设置JPEG压缩质量(0-9,0=最低压缩)
+ */
+ public Integer compressionLevel;
+ /**
+ * 设置DPI(影响打印质量)
+ */
+ public Integer dpiX;
+ /**
+ * 设置DPI(影响打印质量)
+ */
+ public Integer dpiY;
+
+ public Double getScaleToFitHeight() {
+ return scaleToFitHeight;
+ }
+
+ public void setScaleToFitHeight(Double scaleToFitHeight) {
+ this.scaleToFitHeight = scaleToFitHeight;
+ }
+
+ public Double getScaleToFitWidth() {
+ return scaleToFitWidth;
+ }
+
+ public void setScaleToFitWidth(Double scaleToFitWidth) {
+ this.scaleToFitWidth = scaleToFitWidth;
+ }
+
+ public Double getScalePercent() {
+ return scalePercent;
+ }
+
+ public void setScalePercent(Double scalePercent) {
+ this.scalePercent = scalePercent;
+ }
+
+ public Double getScaleAbsoluteWidth() {
+ return scaleAbsoluteWidth;
+ }
+
+ public void setScaleAbsoluteWidth(Double scaleAbsoluteWidth) {
+ this.scaleAbsoluteWidth = scaleAbsoluteWidth;
+ }
+
+ public Double getScaleAbsoluteHeight() {
+ return scaleAbsoluteHeight;
+ }
+
+ public void setScaleAbsoluteHeight(Double scaleAbsoluteHeight) {
+ this.scaleAbsoluteHeight = scaleAbsoluteHeight;
+ }
+
+ public String getAlignText() {
+ return alignText;
+ }
+
+ public void setAlignText(String alignText) {
+ this.alignText = alignText;
+ }
+
+ public Double getAbsoluteX() {
+ return absoluteX;
+ }
+
+ public void setAbsoluteX(Double absoluteX) {
+ this.absoluteX = absoluteX;
+ }
+
+ public Double getAbsoluteY() {
+ return absoluteY;
+ }
+
+ public void setAbsoluteY(Double absoluteY) {
+ this.absoluteY = absoluteY;
+ }
+
+ public Double getIndentationLeft() {
+ return indentationLeft;
+ }
+
+ public void setIndentationLeft(Double indentationLeft) {
+ this.indentationLeft = indentationLeft;
+ }
+
+ public String getBorder() {
+ return border;
+ }
+
+ public void setBorder(String border) {
+ this.border = border;
+ }
+
+ public Double getBorderWidth() {
+ return borderWidth;
+ }
+
+ public void setBorderWidth(Double borderWidth) {
+ this.borderWidth = borderWidth;
+ }
+
+ public String getBorderColor() {
+ return borderColor;
+ }
+
+ public void setBorderColor(String borderColor) {
+ this.borderColor = borderColor;
+ }
+
+ public Double getSpacingBefore() {
+ return spacingBefore;
+ }
+
+ public void setSpacingBefore(Double spacingBefore) {
+ this.spacingBefore = spacingBefore;
+ }
+
+ public Double getSpacingAfter() {
+ return spacingAfter;
+ }
+
+ public void setSpacingAfter(Double spacingAfter) {
+ this.spacingAfter = spacingAfter;
+ }
+
+ public List getTransparency() {
+ return transparency;
+ }
+
+ public void setTransparency(List transparency) {
+ this.transparency = transparency;
+ }
+
+ public String getAnchor() {
+ return anchor;
+ }
+
+ public void setAnchor(String anchor) {
+ this.anchor = anchor;
+ }
+
+ public Integer getCompressionLevel() {
+ return compressionLevel;
+ }
+
+ public void setCompressionLevel(Integer compressionLevel) {
+ this.compressionLevel = compressionLevel;
+ }
+
+ public Integer getDpiX() {
+ return dpiX;
+ }
+
+ public void setDpiX(Integer dpiX) {
+ this.dpiX = dpiX;
+ }
+
+ public Integer getDpiY() {
+ return dpiY;
+ }
+
+ public void setDpiY(Integer dpiY) {
+ this.dpiY = dpiY;
+ }
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextListStructure.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextListStructure.java
new file mode 100644
index 000000000..242bf68b1
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextListStructure.java
@@ -0,0 +1,136 @@
+package com.netease.lowcode.lib.structure;
+
+import com.netease.lowcode.core.annotation.NaslStructure;
+
+@NaslStructure
+public class ITextListStructure {
+ /**
+ * 对齐方式
+ * ALIGN_UNDEFINED/ALIGN_LEFT/ALIGN_CENTER/ALIGN_RIGHT/ALIGN_JUSTIFIED/ALIGN_TOP/ALIGN_MIDDLE/ALIGN_BOTTOM/ALIGN_BASELINE/ALIGN_JUSTIFIED_ALL
+ */
+ public String alignText;
+
+ /**
+ * 左缩进(单位:像素)
+ */
+ public Double indentationLeft;
+
+ /**
+ * 右缩进(单位:像素)
+ */
+ public Double indentationRight;
+
+
+ /**
+ * 首行缩进
+ */
+ public Double firstLineIndent;
+
+ /**
+ * 是否禁止分页时拆分段落(true 表示整段必须在同一页)]
+ */
+ public Boolean keepTogether;
+ /**
+ * 段前间距
+ */
+ public Double spacingBefore;
+
+ /**
+ * 段后间距
+ */
+ public Double spacingAfter;
+
+ /**
+ * 上边距
+ */
+ public Double paddingTop;
+
+ /**
+ * 行间距(固定值)
+ */
+ public Double fixedLeading;
+ /**
+ * 行间距(倍数)
+ */
+ public Double multipliedLeading;
+
+ public String getAlignText() {
+ return alignText;
+ }
+
+ public void setAlignText(String alignText) {
+ this.alignText = alignText;
+ }
+
+ public Double getIndentationLeft() {
+ return indentationLeft;
+ }
+
+ public void setIndentationLeft(Double indentationLeft) {
+ this.indentationLeft = indentationLeft;
+ }
+
+ public Double getIndentationRight() {
+ return indentationRight;
+ }
+
+ public void setIndentationRight(Double indentationRight) {
+ this.indentationRight = indentationRight;
+ }
+
+ public Double getFirstLineIndent() {
+ return firstLineIndent;
+ }
+
+ public void setFirstLineIndent(Double firstLineIndent) {
+ this.firstLineIndent = firstLineIndent;
+ }
+
+ public Boolean getKeepTogether() {
+ return keepTogether;
+ }
+
+ public void setKeepTogether(Boolean keepTogether) {
+ this.keepTogether = keepTogether;
+ }
+
+ public Double getSpacingBefore() {
+ return spacingBefore;
+ }
+
+ public void setSpacingBefore(Double spacingBefore) {
+ this.spacingBefore = spacingBefore;
+ }
+
+ public Double getSpacingAfter() {
+ return spacingAfter;
+ }
+
+ public void setSpacingAfter(Double spacingAfter) {
+ this.spacingAfter = spacingAfter;
+ }
+
+ public Double getPaddingTop() {
+ return paddingTop;
+ }
+
+ public void setPaddingTop(Double paddingTop) {
+ this.paddingTop = paddingTop;
+ }
+
+ public Double getFixedLeading() {
+ return fixedLeading;
+ }
+
+ public void setFixedLeading(Double fixedLeading) {
+ this.fixedLeading = fixedLeading;
+ }
+
+ public Double getMultipliedLeading() {
+ return multipliedLeading;
+ }
+
+ public void setMultipliedLeading(Double multipliedLeading) {
+ this.multipliedLeading = multipliedLeading;
+ }
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextParagraphStructure.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextParagraphStructure.java
new file mode 100644
index 000000000..8731dd076
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextParagraphStructure.java
@@ -0,0 +1,120 @@
+package com.netease.lowcode.lib.structure;
+
+import com.netease.lowcode.core.annotation.NaslStructure;
+
+@NaslStructure
+public class ITextParagraphStructure {
+ /**
+ * 对齐方式
+ * ALIGN_UNDEFINED/ALIGN_LEFT/ALIGN_CENTER/ALIGN_RIGHT/ALIGN_JUSTIFIED/ALIGN_TOP/ALIGN_MIDDLE/ALIGN_BOTTOM/ALIGN_BASELINE/ALIGN_JUSTIFIED_ALL
+ */
+ public String alignText;
+ /**
+ * 左缩进(单位:像素)
+ */
+ public Double indentationLeft;
+
+ /**
+ * 右缩进(单位:像素)
+ */
+ public Double indentationRight;
+
+ /**
+ * 首行缩进
+ */
+ public Double firstLineIndent;
+
+ /**
+ * 段前间距
+ */
+ public Double spacingBefore;
+
+ /**
+ * 段后间距
+ */
+ public Double spacingAfter;
+ /**
+ * 是否禁止分页时拆分段落(true 表示整段必须在同一页)]
+ */
+ public Boolean keepTogether;
+ /**
+ * 行间距(固定值)
+ */
+ public Double fixedLeading;
+ /**
+ * 行间距(倍数)
+ */
+ public Double multipliedLeading;
+
+ public String getAlignText() {
+ return alignText;
+ }
+
+ public void setAlignText(String alignText) {
+ this.alignText = alignText;
+ }
+
+ public Double getIndentationLeft() {
+ return indentationLeft;
+ }
+
+ public void setIndentationLeft(Double indentationLeft) {
+ this.indentationLeft = indentationLeft;
+ }
+
+ public Double getIndentationRight() {
+ return indentationRight;
+ }
+
+ public void setIndentationRight(Double indentationRight) {
+ this.indentationRight = indentationRight;
+ }
+
+ public Double getFirstLineIndent() {
+ return firstLineIndent;
+ }
+
+ public void setFirstLineIndent(Double firstLineIndent) {
+ this.firstLineIndent = firstLineIndent;
+ }
+
+ public Double getSpacingBefore() {
+ return spacingBefore;
+ }
+
+ public void setSpacingBefore(Double spacingBefore) {
+ this.spacingBefore = spacingBefore;
+ }
+
+ public Double getSpacingAfter() {
+ return spacingAfter;
+ }
+
+ public void setSpacingAfter(Double spacingAfter) {
+ this.spacingAfter = spacingAfter;
+ }
+
+ public Double getFixedLeading() {
+ return fixedLeading;
+ }
+
+ public void setFixedLeading(Double fixedLeading) {
+ this.fixedLeading = fixedLeading;
+ }
+
+ public Double getMultipliedLeading() {
+ return multipliedLeading;
+ }
+
+ public void setMultipliedLeading(Double multipliedLeading) {
+ this.multipliedLeading = multipliedLeading;
+ }
+
+ public Boolean getKeepTogether() {
+ return keepTogether;
+ }
+
+ public void setKeepTogether(Boolean keepTogether) {
+ this.keepTogether = keepTogether;
+ }
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextPhraseStructure.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextPhraseStructure.java
new file mode 100644
index 000000000..111a86f26
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextPhraseStructure.java
@@ -0,0 +1,32 @@
+package com.netease.lowcode.lib.structure;
+
+import com.netease.lowcode.core.annotation.NaslStructure;
+
+@NaslStructure
+public class ITextPhraseStructure {
+
+ /**
+ * 行间距(固定值)
+ */
+ public Double fixedLeading;
+ /**
+ * 行间距(倍数)
+ */
+ public Double multipliedLeading;
+
+ public Double getFixedLeading() {
+ return fixedLeading;
+ }
+
+ public void setFixedLeading(Double fixedLeading) {
+ this.fixedLeading = fixedLeading;
+ }
+
+ public Double getMultipliedLeading() {
+ return multipliedLeading;
+ }
+
+ public void setMultipliedLeading(Double multipliedLeading) {
+ this.multipliedLeading = multipliedLeading;
+ }
+}
diff --git a/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextTableStructure.java b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextTableStructure.java
new file mode 100644
index 000000000..bcfc3f3b6
--- /dev/null
+++ b/itextpdf-tool/src/main/java/com/netease/lowcode/lib/structure/ITextTableStructure.java
@@ -0,0 +1,154 @@
+package com.netease.lowcode.lib.structure;
+
+import com.netease.lowcode.core.annotation.NaslStructure;
+
+import java.util.List;
+
+@NaslStructure
+public class ITextTableStructure {
+ /**
+ * 表格宽度占页面宽度的百分比(如 100%)
+ */
+ public Double widthPercentage;
+ /**
+ * 表格绝对宽度(单位:像素)
+ */
+ public Double totalWidth;
+ /**
+ * 是否固定表格宽度(true 时忽略自动调整)
+ */
+ public Boolean lockedWidth;
+ /**
+ * 水平对齐方式(ALIGN_UNDEFINED/ALIGN_LEFT/ALIGN_CENTER/ALIGN_RIGHT/ALIGN_JUSTIFIED/ALIGN_TOP/ALIGN_MIDDLE/ALIGN_BOTTOM/ALIGN_BASELINE/ALIGN_JUSTIFIED_ALL)
+ */
+ public String horizontalAlignmentText;
+ /**
+ * 表格前的空白间距
+ */
+ public Double spacingBefore;
+ /**
+ * 表格后的空白间距
+ */
+ public Double spacingAfter;
+ /**
+ * 表格列数
+ */
+ public Integer numColumns;
+ /**
+ * 每列宽度比例,数组大小与numColumns一致(如 new Double[]{1, 2, 3} 表示三列比例为1:2:3)
+ */
+ public List widths;
+ /**
+ * 指定前N行为表头(跨页时自动重复)
+ */
+ public Integer headerRows;
+ /**
+ * 指定后N行为表尾(跨页时自动重复)
+ */
+ public Integer footerRows;
+
+ /**
+ * 是否允许拆分单元格(false 时强制整单元格换页)
+ */
+ public Boolean splitLate;
+ /**
+ * 是否跳过第一页的表头(用于连续表格)
+ */
+ public Boolean skipFirstHeader;
+
+ public Integer getNumColumns() {
+ return numColumns;
+ }
+
+ public void setNumColumns(Integer numColumns) {
+ this.numColumns = numColumns;
+ }
+
+ public Double getWidthPercentage() {
+ return widthPercentage;
+ }
+
+ public void setWidthPercentage(Double widthPercentage) {
+ this.widthPercentage = widthPercentage;
+ }
+
+ public Double getTotalWidth() {
+ return totalWidth;
+ }
+
+ public void setTotalWidth(Double totalWidth) {
+ this.totalWidth = totalWidth;
+ }
+
+ public Boolean getLockedWidth() {
+ return lockedWidth;
+ }
+
+ public void setLockedWidth(Boolean lockedWidth) {
+ this.lockedWidth = lockedWidth;
+ }
+
+ public String getHorizontalAlignmentText() {
+ return horizontalAlignmentText;
+ }
+
+ public void setHorizontalAlignmentText(String horizontalAlignmentText) {
+ this.horizontalAlignmentText = horizontalAlignmentText;
+ }
+
+ public Double getSpacingBefore() {
+ return spacingBefore;
+ }
+
+ public void setSpacingBefore(Double spacingBefore) {
+ this.spacingBefore = spacingBefore;
+ }
+
+ public Double getSpacingAfter() {
+ return spacingAfter;
+ }
+
+ public void setSpacingAfter(Double spacingAfter) {
+ this.spacingAfter = spacingAfter;
+ }
+
+ public List getWidths() {
+ return widths;
+ }
+
+ public void setWidths(List widths) {
+ this.widths = widths;
+ }
+
+ public Integer getHeaderRows() {
+ return headerRows;
+ }
+
+ public void setHeaderRows(Integer headerRows) {
+ this.headerRows = headerRows;
+ }
+
+ public Integer getFooterRows() {
+ return footerRows;
+ }
+
+ public void setFooterRows(Integer footerRows) {
+ this.footerRows = footerRows;
+ }
+
+ public Boolean getSplitLate() {
+ return splitLate;
+ }
+
+ public void setSplitLate(Boolean splitLate) {
+ this.splitLate = splitLate;
+ }
+
+ public Boolean getSkipFirstHeader() {
+ return skipFirstHeader;
+ }
+
+ public void setSkipFirstHeader(Boolean skipFirstHeader) {
+ this.skipFirstHeader = skipFirstHeader;
+ }
+}
diff --git a/itextpdf-tool/src/main/resources/META-INF/spring.factories b/itextpdf-tool/src/main/resources/META-INF/spring.factories
new file mode 100644
index 000000000..fbebf46ce
--- /dev/null
+++ b/itextpdf-tool/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.netease.lowcode.lib.ItextpdfToolBasicSpringEnvironmentConfiguration
\ No newline at end of file
diff --git a/redis-template-tool/pom.xml b/redis-template-tool/pom.xml
index 32ba1db1d..c3272a748 100644
--- a/redis-template-tool/pom.xml
+++ b/redis-template-tool/pom.xml
@@ -5,7 +5,7 @@
com.netease.lowcode
redis-template-tool
- 1.1.0
+ 1.2.0
org.springframework.boot
spring-boot-starter-parent
@@ -63,12 +63,6 @@
org.apache.commons
commons-pool2
-
- com.alibaba.fastjson2
- fastjson2
- 2.0.47
- provided
-
org.apache.commons
commons-lang3
@@ -90,7 +84,7 @@
1.5.1
false
-
+ true
diff --git a/redis-template-tool/src/main/java/com/netease/lib/redistemplatetool/util/RedisTool.java b/redis-template-tool/src/main/java/com/netease/lib/redistemplatetool/util/RedisTool.java
index f29969847..877edb351 100644
--- a/redis-template-tool/src/main/java/com/netease/lib/redistemplatetool/util/RedisTool.java
+++ b/redis-template-tool/src/main/java/com/netease/lib/redistemplatetool/util/RedisTool.java
@@ -1,6 +1,6 @@
package com.netease.lib.redistemplatetool.util;
-import com.alibaba.fastjson2.JSONObject;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.netease.lowcode.core.annotation.NaslLogic;
import org.slf4j.Logger;
@@ -197,6 +197,32 @@ public Boolean setIfAbsentWithExpire(String key, String value, Long timeout) {
return ops.setIfAbsent(key, value, timeout, TimeUnit.SECONDS);
}
+
+ /**
+ * 如果 Redis 中不存在指定 key,则设置为指定字符串,并设置过期时间
+ *
+ * @param key Redis 中的键
+ * @param value Redis 中的值
+ * @param timeout 过期时间,单位s
+ * @param retryTimeout 重试持续时间,单位ms。默认无
+ * @param retryIntervalTime 重试间隔时间,单位ms。默认10ms
+ * @return 如果设置成功,返回 true,否则返回 false
+ */
+ @NaslLogic
+ public Boolean setIfAbsentWithExpireRetry(String key, String value, Long timeout, Long retryTimeout, Long retryIntervalTime) {
+ if (retryTimeout == null || retryTimeout <= 0) {
+ return setIfAbsentWithExpire(key, value, timeout);
+ }
+
+ while (retryTimeout > 0) {
+ retryTimeout -= retryIntervalTime;
+ if (setIfAbsentWithExpire(key, value, timeout)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* 如果 Redis 中存在指定 key,则设置为指定字符串,并设置过期时间
*
@@ -445,7 +471,7 @@ public List getHashTopN(String hashKey, Integer limit, String keywordVal
} else {
return allList.stream().filter(json -> {
try {
- JSONObject jsonObject = mapper.readValue(json, JSONObject.class);
+ JsonNode jsonObject = mapper.readValue(json, JsonNode.class);
String nameValue = getKeywordValue(keywordName, jsonObject);
return !StringUtils.isEmpty(nameValue) && nameValue.contains(keywordValue);
} catch (Exception e) {
@@ -457,9 +483,9 @@ public List getHashTopN(String hashKey, Integer limit, String keywordVal
// 创建中文拼音排序器
Collator collator = Collator.getInstance(Locale.CHINA);
// 获取两个json对象的中文名
- JSONObject jsonObject1 = mapper.readValue(json1, JSONObject.class);
+ JsonNode jsonObject1 = mapper.readValue(json1, JsonNode.class);
String name1 = getKeywordValue(keywordName, jsonObject1);
- JSONObject jsonObject2 = mapper.readValue(json2, JSONObject.class);
+ JsonNode jsonObject2 = mapper.readValue(json2, JsonNode.class);
String name2 = getKeywordValue(keywordName, jsonObject2);
// 使用Collator比较中文名
return collator.compare(name1, name2);
@@ -472,24 +498,43 @@ public List getHashTopN(String hashKey, Integer limit, String keywordVal
}
}
- private String getKeywordValue(String keywordName, JSONObject jsonObject) {
+ /**
+ * 使用Jackson JsonNode获取嵌套JSON字段值
+ *
+ * @param keywordName 字段路径,如 "user.profile.name"
+ * @param jsonNode Jackson JsonNode对象
+ * @return 字段值,如果不存在返回null
+ */
+ private String getKeywordValue(String keywordName, JsonNode jsonNode) {
+ if (jsonNode == null || keywordName == null || keywordName.isEmpty()) {
+ return null;
+ }
+
String[] keywordNames = keywordName.split("\\.");
- String nameValue;
- if (keywordNames.length > 1) {
- JSONObject keywordJsonObject = jsonObject;
- for (int i = 0; i < keywordNames.length - 1; i++) {
- String name = keywordNames[i];
- if (keywordJsonObject.containsKey(name)) {
- keywordJsonObject = keywordJsonObject.getJSONObject(name);
- } else {
- break;
- }
+ JsonNode currentNode = jsonNode;
+
+ // 遍历路径的中间部分
+ for (int i = 0; i < keywordNames.length - 1; i++) {
+ String name = keywordNames[i];
+ if (currentNode.has(name) && currentNode.get(name).isObject()) {
+ currentNode = currentNode.get(name);
+ } else {
+ return null; // 路径中间部分不存在或不是对象
+ }
+ }
+
+ // 获取最后一级字段的值
+ String lastName = keywordNames[keywordNames.length - 1];
+ if (currentNode.has(lastName)) {
+ JsonNode valueNode = currentNode.get(lastName);
+ if (valueNode.isTextual()) {
+ return valueNode.asText();
+ } else {
+ return valueNode.toString(); // 非文本类型转换为字符串
}
- nameValue = keywordJsonObject.getString(keywordNames[keywordNames.length - 1]);
- } else {
- nameValue = jsonObject.getString(keywordName);
}
- return nameValue;
+
+ return null;
}
/**
@@ -521,7 +566,7 @@ public List getHashTopNByKeyMap(String hashKey, Integer limit, Map {
try {
- JSONObject jsonObject = mapper.readValue(json, JSONObject.class);
+ JsonNode jsonObject = mapper.readValue(json, JsonNode.class);
return keywordMap.entrySet().stream().allMatch(entry -> {
String jsonValue = getKeywordValue(entry.getKey(), jsonObject);
return !StringUtils.isEmpty(jsonValue) &&
@@ -536,11 +581,11 @@ public List getHashTopNByKeyMap(String hashKey, Integer limit, Map redisTemplate;
+
+ /**
+ * 设置候选列表(自动处理列表变化)
+ */
+ @NaslLogic
+ public Boolean pollingSetCandidates(String nodeId, List candidates) {
+ String candidateKey = "node:" + nodeId + ":candidates";
+ // 删除旧列表,设置新列表
+ redisTemplate.delete(candidateKey);
+ if (!candidates.isEmpty()) {
+ redisTemplate.opsForList().rightPushAll(candidateKey, candidates);
+ }
+ return true;
+ }
+
+ /**
+ * 选择下一个候选
+ */
+ @NaslLogic
+ public String pollingSelectNext(String nodeId) {
+ String candidateKey = "node:" + nodeId + ":candidates";
+ String lastKey = "node:" + nodeId + ":last";
+
+ // 获取当前列表
+ List candidates = redisTemplate.opsForList().range(candidateKey, 0, -1);
+ if (candidates == null || candidates.isEmpty()) {
+ throw new RuntimeException("没有可用的候选人");
+ }
+
+ // 获取上次分配的人
+ String last = redisTemplate.opsForValue().get(lastKey);
+
+ // 找到下一个候选人
+ String next = findNextCandidate(candidates, last);
+
+ // 记录这次分配的人
+ redisTemplate.opsForValue().set(lastKey, next);
+
+ return next;
+ }
+
+ /**
+ * 简单的轮询查找逻辑
+ */
+ private String findNextCandidate(List candidates, String last) {
+ if (last == null || last.isEmpty()) {
+ return candidates.get(0); // 第一次从第一个开始
+ }
+
+ // 找上次分配的人在列表中的位置
+ int lastIndex = -1;
+ for (int i = 0; i < candidates.size(); i++) {
+ if (candidates.get(i).equals(last)) {
+ lastIndex = i;
+ break;
+ }
+ }
+
+ // 如果没找到或者是在最后,就从第一个开始
+ if (lastIndex == -1 || lastIndex == candidates.size() - 1) {
+ return candidates.get(0);
+ }
+
+ // 否则选下一个
+ return candidates.get(lastIndex + 1);
+ }
+
+ /**
+ * 获取当前候选人列表
+ */
+ public List getCandidates(String nodeId) {
+ String candidateKey = "node:" + nodeId + ":candidates";
+ return redisTemplate.opsForList().range(candidateKey, 0, -1);
+ }
+}
\ No newline at end of file
diff --git a/xxl-job/pom.xml b/xxl-job/pom.xml
index d315d23de..2802ef834 100644
--- a/xxl-job/pom.xml
+++ b/xxl-job/pom.xml
@@ -12,7 +12,7 @@
com.netease.lowcode
xxljob-tool
- 2.0.0
+ 2.1.0
8
@@ -78,7 +78,7 @@
1.5.1
false
- true
+
diff --git a/xxl-job/src/main/java/com/netease/lowcode/xxljob/task/InterfaceJobHandler.java b/xxl-job/src/main/java/com/netease/lowcode/xxljob/task/InterfaceJobHandler.java
index d998fc2ff..d87e097e9 100644
--- a/xxl-job/src/main/java/com/netease/lowcode/xxljob/task/InterfaceJobHandler.java
+++ b/xxl-job/src/main/java/com/netease/lowcode/xxljob/task/InterfaceJobHandler.java
@@ -1,6 +1,7 @@
package com.netease.lowcode.xxljob.task;
import com.netease.lowcode.xxljob.model.JobHandlerInterfaceModel;
+import com.netease.lowcode.xxljob.util.NamingUtils;
import com.netease.lowcode.xxljob.util.SpringContextUtil;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.IJobHandler;
@@ -56,24 +57,25 @@ public void execute() {
return;
}
Class> aClass = AopUtils.getTargetClass(bean);
+ String logicName = NamingUtils.toLowerCamelCase(model.getLogicName());
try {
// 无参
- Method targetMethod = aClass.getDeclaredMethod(model.getLogicName());
+ Method targetMethod = aClass.getDeclaredMethod(logicName);
targetMethod.invoke(bean);
} catch (NoSuchMethodException exception) {
try {
- Method targetMethod = aClass.getDeclaredMethod(model.getLogicName(), String.class);
+ Method targetMethod = aClass.getDeclaredMethod(logicName, String.class);
targetMethod.invoke(bean, param);
} catch (Exception e) {
- log.error("Execute job failed: " + handlerName, ((InvocationTargetException) e).getTargetException().getMessage());
- XxlJobHelper.log("任务执行失败: " + ((InvocationTargetException) e).getTargetException().getMessage());
- XxlJobHelper.handleFail("任务执行失败: " + ((InvocationTargetException) e).getTargetException().getMessage());
+ log.error("Execute job failed: " + handlerName, e.getCause());
+ XxlJobHelper.log("任务执行失败: " + e.getCause());
+ XxlJobHelper.handleFail("任务执行失败: " + e.getCause());
return;
}
} catch (Exception e) {
log.error("Execute job failed: " + handlerName, e);
- XxlJobHelper.log("任务执行失败: " + ((InvocationTargetException) e).getTargetException().getMessage());
- XxlJobHelper.handleFail("任务执行失败: " + ((InvocationTargetException) e).getTargetException().getMessage());
+ XxlJobHelper.log("任务执行失败: " + e.getCause());
+ XxlJobHelper.handleFail("任务执行失败: " + e.getCause());
return;
}
}
diff --git a/xxl-job/src/main/java/com/netease/lowcode/xxljob/util/NamingUtils.java b/xxl-job/src/main/java/com/netease/lowcode/xxljob/util/NamingUtils.java
new file mode 100644
index 000000000..ded3fc226
--- /dev/null
+++ b/xxl-job/src/main/java/com/netease/lowcode/xxljob/util/NamingUtils.java
@@ -0,0 +1,36 @@
+package com.netease.lowcode.xxljob.util;
+
+public class NamingUtils {
+ public static String toLowerCamelCase(String str) {
+ if (str == null || str.isEmpty()) {
+ return str;
+ }
+
+ // 如果已经是首字母小写,直接返回
+ if (Character.isLowerCase(str.charAt(0))) {
+ return str;
+ }
+
+ // 处理多单词的驼峰命名
+ StringBuilder result = new StringBuilder();
+ boolean firstWord = true;
+
+ // 分割单词(根据大写字母分割)
+ for (int i = 0; i < str.length(); i++) {
+ char c = str.charAt(i);
+ if (Character.isUpperCase(c) && i > 0) {
+ if (firstWord) {
+ result.append(Character.toLowerCase(c));
+ firstWord = false;
+ } else {
+ result.append(c);
+ }
+ } else {
+ result.append(firstWord ? Character.toLowerCase(c) : c);
+ firstWord = false;
+ }
+ }
+
+ return result.toString();
+ }
+}