ofdrw 库是一个用于操作 OFD(Open Fixed-layout Document)格式文件的强大工具。本指南将详细介绍如何使用 ofdrw 库进行常见操作,如生成 OFD 文件、转换格式、设置文本和图片等。
<dependency>
<groupId>org.ofdrwgroupId>
<artifactId>ofdrw-fullartifactId>
<version>2.3.5version>
dependency>
protected void generateOfd(String filePath) {
if (StringUtils.isBlank(filePath)) {
return;
}
try (OFDDoc ofdDoc = new OFDDoc(Paths.get(filePath))) {
PageLayout pageLayout = ofdDoc.getPageLayout();
pageLayout.setHeight(OFD_BODY_HEIGHT);
pageLayout.setWidth(OFD_BODY_WIDTH);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
protected ByteArrayOutputStream generateOfdBos() {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (OFDDoc ofdDoc = new OFDDoc(bos)) {
PageLayout pageLayout = ofdDoc.getPageLayout();
pageLayout.setHeight(OFD_BODY_HEIGHT);
pageLayout.setWidth(OFD_BODY_WIDTH);
} catch (IOException e) {
throw new RuntimeException(e);
}
return bos;
}
ofd 库并未提供直接转换图片的功能,需要将生成的图片暂存到本地,转成输出流后再删除文件,这里需要注意的是,转换图片所需要的参数不是文件名,而是一个文件夹路径,生成的图片会使用默认的命名方式保存在这个文件夹(0.png,1.png)。
public ByteArrayOutputStream covertOfdBosToPng(ByteArrayOutputStream bos) {
ByteArrayInputStream ofdInput = new ByteArrayInputStream(bos.toByteArray());
try {
String pngFolderPath = String.format("%s/%s/", TEMPORARY_IMAGE_FOLDER, UUID.randomUUID());
ImageExporter exporter = new ImageExporter(ofdInput, Paths.get(pngFolderPath));
exporter.export();
List<Path> pathList = exporter.getImgFilePaths();
if (CollectionUtils.isNotEmpty(pathList)) {
ByteArrayOutputStream pngBos = convertFileToByteArray(exporter.getImgFilePaths().get(0).toString());
deleteFolder(pngFolderPath);
return pngBos;
}
log.warn("图片转换失败");
return null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void deleteFolder(String folderPath) {
deleteFolder(new File(folderPath));
}
private void deleteFolder(File folder) {
if (folder.isDirectory()) {
File[] files = folder.listFiles();
if (files != null) {
for (File file : files) {
deleteFolder(file); // 递归删除子文件/子文件夹
}
}
}
folder.delete(); // 删除当前文件或空文件夹
}
@Override
public String covertBosToBase64Str(ByteArrayOutputStream bos) {
return Base64.encode(bos.toByteArray());
}
这里需要注意,段落不直接支持设置垂直方向上的对齐方式,需要自己通过内外边距的方式设置垂直对齐。
public static Paragraph generateParagraph(OFDPositionEnum positionEnu, String text) {
Font font1 = new Font(positionEnu.getFont().getCode(), Paths.get(positionEnu.getFont().getPath()));
if (OFDPositionEnum.INDEX_VALUE.equals(positionEnu)) {
font1.setPrintableAsciiWidthMap(FontWidthConstants.NOTO_PRINTABLE_ASCII_WIDTH_MAP);
}
Paragraph paragraph = new Paragraph()
.setPosition(Position.Absolute)
// .setBorder(0.5d)
.setXY(positionEnu.getX(), positionEnu.getY())
.setWidth(positionEnu.getWidth())
.setHeight(positionEnu.getHeight());
Span span = new Span(font1, positionEnu.getFontSize(), text);
if (OFDPositionEnum.INDEX_VALUE.equals(positionEnu)) {
span.setLetterSpacing(0.1d);
}
paragraph.add(span);
switch (positionEnu.getAlignModeHor()) {
case HORIZONTAL_LEFT -> paragraph.setTextAlign(TextAlign.left);
case HORIZONTAL_CENTER -> paragraph.setTextAlign(TextAlign.center);
case HORIZONTAL_RIGHT -> paragraph.setTextAlign(TextAlign.right);
}
switch (positionEnu.getAlignModeVer()) {
case VERTICAL_TOP -> paragraph.setPaddingTop(0d);
case VERTICAL_CENTER -> paragraph.setPaddingTop(paragraph.getHeight() / 2 - span.getFontSize() / 2);
case VERTICAL_BOTTOM -> paragraph.setPaddingTop(paragraph.getHeight() - span.getFontSize());
}
return paragraph;
}
public static Canvas generateCanvas(OFDAlignModeEnum alignModeHor, OFDAlignModeEnum alignModeVer, double width, double height, double x, double y, int fontThick, double fontSize, FontEnum font, String text) {
Canvas canvas = new Canvas(width, height)
.setPosition(Position.Absolute)
// .setBorder(0.5d)
.setXY(x, y);
canvas.setDrawer(ctx -> {
String fontName = font.getCode();
// FontManager.getInstance().addFont(Font.loadFont("fonts/SimHei.ttf"));
ctx.addFont(fontName, Paths.get(font.getPath()));
ctx.font = String.format("bold %d %fmm %s", fontThick, fontSize, FontName.SimHei);
ctx.fillStyle = OFDLayoutConstants.FONT_COLOR;
double textWidth = ctx.measureText(text).width;
double textX = getTextXY(alignModeHor, width, height, textWidth, fontSize);
double textY = getTextXY(alignModeVer, width, height, textWidth, fontSize);
StringBuilder currentLine = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
currentLine.append(text.charAt(i));
double currentLineWidth = ctx.measureText(currentLine.toString()).width;
if (currentLineWidth >= width - fontSize) {
ctx.fillText(currentLine.toString(), textX, textY);
currentLine = new StringBuilder();
textY += fontSize * 3 / 2;
}
}
if (!currentLine.isEmpty()) {
ctx.fillText(currentLine.toString(), textX, textY);
}
});
return canvas;
}
private static Img generatorImg(String filePath, double width, double height, double x, double y) {
try {
Path imgPath = Paths.get(filePath);
return new Img(width, height, imgPath)
.setPosition(Position.Absolute)
// .setBorder(0d)
// .setPadding(0d)
.setX(x).setY(y);
} catch (IOException e) {
System.out.println("生成图片失败");
throw new RuntimeException(e);
}
}
public static ByteArrayOutputStream generateOfdBos(OfdAuthCertDataSource dataSource) {
if (Objects.isNull(dataSource) || CollectionUtils.isEmpty(dataSource.getOfdGeneralLayout())) {
return new ByteArrayOutputStream();
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (OFDDoc ofdDoc = new OFDDoc(bos)) {
PageLayout pageLayout = ofdDoc.getPageLayout();
pageLayout.setHeight(OFDLayoutConstants.OFD_BODY_HEIGHT);
pageLayout.setWidth(OFDLayoutConstants.OFD_BODY_WIDTH);
VirtualPage virtualPage = new VirtualPage(pageLayout);
virtualPage.add(generatorImg(...));
ofdDoc.addVPage(virtualPage);
} catch (IOException e) {
throw new RuntimeException(e);
}
return bos;
}
private static Img generatorImg(String filePath, double width, double height, double x, double y) {
try {
Path imgPath = Paths.get(filePath);
return new Img(width, height, imgPath)
.setPosition(Position.Absolute)
// .setBorder(0d)
// .setPadding(0d)
.setX(x).setY(y);
} catch (IOException e) {
System.out.println("生成图片失败");
throw new RuntimeException(e);
}
}
public static ByteArrayOutputStream generateOfdBos(OfdAuthCertDataSource dataSource) {
if (Objects.isNull(dataSource) || CollectionUtils.isEmpty(dataSource.getOfdGeneralLayout())) {
return new ByteArrayOutputStream();
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (OFDDoc ofdDoc = new OFDDoc(bos)) {
PageLayout pageLayout = ofdDoc.getPageLayout();
pageLayout.setHeight(OFDLayoutConstants.OFD_BODY_HEIGHT);
pageLayout.setWidth(OFDLayoutConstants.OFD_BODY_WIDTH);
VirtualPage virtualPage = new VirtualPage(pageLayout);
virtualPage.add(generatorImg(...));
ofdDoc.addVPage(virtualPage);
} catch (IOException e) {
throw new RuntimeException(e);
}
return bos;
}
@Getter
@AllArgsConstructor
public enum FontEnum {
SIM_HEI("simHei", "黑体", "D:/data/font/simhei.ttf"),
// SIM_SUN("simSun", "宋体", "D:/data/font/simsun.ttf"),
// MS_YH("msyh", "微软雅黑", "D:/data/font/msyh.ttc"),
// MS_YH_BD("msyhbd", "微软雅黑-粗体", "D:/data/font/msyhbd.ttc"),
// MS_YH_L("msyhl", "微软雅黑-细", "D:/data/font/msyhl.ttc"),
// CALI_BRI("calibri", "Calibri", "D:/data/font/calibri.ttf"),
;
private final String code;
private final String desc;
private final String path;
}
@Getter
@AllArgsConstructor
public enum OFDAlignModeEnum {
HORIZONTAL_LEFT("0", "水平居左"),
HORIZONTAL_CENTER("1", "水平居中"),
HORIZONTAL_RIGHT("2", "水平居右"),
VERTICAL_TOP("3", "垂直居上"),
VERTICAL_CENTER("4", "垂直居中"),
VERTICAL_BOTTOM("5", "垂直居下"),
;
private final String code;
private final String desc;
}
public class FontWidthConstants {
/**
* 可打印的ASCII表字母宽度所占用百分比
*
* ASCII区间: [32,126]
*
* 其中空格特殊处理,默认为半个字符宽度也就是 0.5
*/
public static final double[] NOTO_PRINTABLE_ASCII_WIDTH_MAP = {
// 空格 (ASCII 32)
0.5,
// 标点符号及特殊字符 (ASCII 33-47)
0.3125, // '!' (ASCII 33)
0.435546875, // '"' (ASCII 34)
0.63818359375, // '#' (ASCII 35)
0.58642578125, // '$' (ASCII 36)
0.8896484375, // '%' (ASCII 37)
0.8701171875, // '&' (ASCII 38)
0.25634765625, // ''' (ASCII 39)
0.333984375, // '(' (ASCII 40)
0.333984375, // ')' (ASCII 41)
0.455078125, // '*' (ASCII 42)
0.74169921875, // '+' (ASCII 43)
0.24072265625, // ',' (ASCII 44)
0.4326171875, // '-' (ASCII 45)
0.24072265625, // '.' (ASCII 46)
0.42724609375, // '/' (ASCII 47)
// 数字 (ASCII 48-57)
// 数字 '0' (ASCII 48) 的宽度从 0.58642578125 调整为 0.5
0.5, // '0' (ASCII 48)
0.58642578125, // '1' (ASCII 49)
0.58642578125, // '2' (ASCII 50)
0.58642578125, // '3' (ASCII 51)
0.58642578125, // '4' (ASCII 52)
0.58642578125, // '5' (ASCII 53)
0.58642578125, // '6' (ASCII 54)
0.58642578125, // '7' (ASCII 55)
0.58642578125, // '8' (ASCII 56)
0.58642578125, // '9' (ASCII 57)
// 标点符号及特殊字符 (ASCII 58-64)
0.24072265625, // ':' (ASCII 58)
0.24072265625, // ';' (ASCII 59)
0.74169921875, // '<' (ASCII 60)
0.74169921875, // '=' (ASCII 61)
0.74169921875, // '>' (ASCII 62)
0.48291015625, // '?' (ASCII 63)
1.03125, // '@' (ASCII 64)
// 大写字母 (ASCII 65-90)
0.51318359375, // 'A' (ASCII 65)
0.51318359375, // 'B' (ASCII 66)
0.51318359375, // 'C' (ASCII 67)
0.51318359375, // 'D' (ASCII 68)
0.5498046875, // 'E' (ASCII 69)
0.53125, // 'F' (ASCII 70)
0.51318359375, // 'G' (ASCII 71)
0.51318359375, // 'H' (ASCII 72)
0.51318359375, // 'I' (ASCII 73)
0.51318359375, // 'J' (ASCII 74)
0.51318359375, // 'K' (ASCII 75)
0.51318359375, // 'L' (ASCII 76)
0.51318359375, // 'M' (ASCII 77)
0.51318359375, // 'N' (ASCII 78)
// 大写字母 'O' (ASCII 79) 的宽度从 0.74365234375 调整为 0.6
0.51318359375, // 'O' (ASCII 79)
0.51318359375, // 'P' (ASCII 80)
0.61181640625, // 'Q' (ASCII 81)
0.51318359375, // 'R' (ASCII 82)
0.65283203125, // 'S' (ASCII 83)
0.5771484375, // 'T' (ASCII 84)
0.5732421875, // 'U' (ASCII 85)
0.51318359375, // 'V' (ASCII 86)
0.67626953125, // 'W' (ASCII 87)
0.65283203125, // 'X' (ASCII 88)
// 标点符号及特殊字符 (ASCII 91-96)
0.64501953125, // '[' (ASCII 91)
0.603515625, // '\' (ASCII 92)
0.6201171875, // ']' (ASCII 93)
0.333984375, // '^' (ASCII 94)
0.416015625, // '_' (ASCII 95)
0.333984375, // '`' (ASCII 96)
// 小写字母 (ASCII 97-122)
0.74169921875, // 'a' (ASCII 97)
0.7734375, // 'b' (ASCII 98)
0.2939453125, // 'c' (ASCII 99)
0.39599609375, // 'd' (ASCII 100)
0.634765625, // 'e' (ASCII 101)
0.51318359375, // 'f' (ASCII 102)
0.97705078125, // 'g' (ASCII 103)
0.81298828125, // 'h' (ASCII 104)
0.81494140625, // 'i' (ASCII 105)
0.61181640625, // 'j' (ASCII 106)
0.81494140625, // 'k' (ASCII 107)
0.65283203125, // 'l' (ASCII 108)
// 小写字母 'm' (ASCII 109) 的宽度从 0.6396484375 调整为 0.55
0.55, // 'm' (ASCII 109)
0.6396484375, // 'n' (ASCII 110)
0.5673828125, // 'o' (ASCII 111)
0.3466796875, // 'p' (ASCII 112)
0.6396484375, // 'q' (ASCII 113)
0.61572265625, // 'r' (ASCII 114)
0.26611328125, // 's' (ASCII 115)
0.26708984375, // 't' (ASCII 116)
0.54443359375, // 'u' (ASCII 117)
0.26611328125, // 'v' (ASCII 118)
0.93701171875, // 'w' (ASCII 119)
0.6162109375, // 'x' (ASCII 120)
0.6357421875, // 'y' (ASCII 121)
0.638671875, // 'z' (ASCII 122)
// 标点符号及特殊字符 (ASCII 123-126)
0.6396484375, // '{' (ASCII 123)
0.3818359375, // '|' (ASCII 124)
0.462890625, // '}' (ASCII 125)
0.37255859375, // '~' (ASCII 126)
};
}
@Getter
@AllArgsConstructor
public enum OFDPositionEnum {
TITLE("标题", OFDLayoutConstants.OFD_BODY_WIDTH, 18.5d, 0d, 41d, 600, OFDLayoutConstants.FONT_SIZE_32MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_CENTER, OFDAlignModeEnum.VERTICAL_TOP),
INDEX_NAME("编号标题:", 60d, 23.5d, 0d, 59.5d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
INDEX_VALUE("编号填充", 133d, 5d, 61d, 59d, 500, OFDLayoutConstants.FONT_SIZE_14MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_LEFT, OFDAlignModeEnum.VERTICAL_TOP),
DATA_SOURCE_NAME("资源数据名称标题", 65.5d, 15d, 0d, 83d, 500, OFDLayoutConstants.FONT_SIZE_15MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
DATA_SOURCE_VALUE("数据资源名称填充", 123d, 12d, 70d, 80d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_LEFT, OFDAlignModeEnum.VERTICAL_CENTER),
DATA_SOURCE_VALUE_2("数据资源名称填充", 123d, 12d, 70d, 80d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_LEFT, OFDAlignModeEnum.VERTICAL_TOP),
REGISTER_SUBJECT_NAME("登记主体标题", 65.5d, 14d, 0d, 98d, 500, OFDLayoutConstants.FONT_SIZE_15MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
REGISTER_SUBJECT_VALUE("登记主体填充", 123d, 18d, 70d, 92d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_LEFT, OFDAlignModeEnum.VERTICAL_CENTER),
INDUSTRY_TYPE_NAME("行业分类标题", 65.5d, 15d, 0d, 112d, 500, OFDLayoutConstants.FONT_SIZE_15MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
INDUSTRY_TYPE_VALUE("行业分类填充", 123d, 12d, 70d, 112d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_LEFT, OFDAlignModeEnum.VERTICAL_TOP),
DATA_CONTENT_INTRODUCTION_NAME("数据内容标题", 65.5d, 24d, 0d, 127d, 500, OFDLayoutConstants.FONT_SIZE_15MM,FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
DATA_CONTENT_INTRODUCTION_VALUE("数据内容简介填充", 123d, 20d, 70d, 127d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_LEFT, OFDAlignModeEnum.VERTICAL_TOP),
VALID_DATE_NAME("有效期标题", 65.5d, 15d, 0d, 151d, 500, OFDLayoutConstants.FONT_SIZE_15MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
VALID_DATE_VALUE("有效期填充", 123d, 6d, 70d, 151d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_LEFT, OFDAlignModeEnum.VERTICAL_TOP),
REGISTER_INST_NAME("登记机构标题", 65.5d, 61d, 0d, 166d, 500, OFDLayoutConstants.FONT_SIZE_15MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
REGISTER_INST_VALUE("登记机构填充", 123d, 12d, 70d, 166d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_LEFT, OFDAlignModeEnum.VERTICAL_TOP),
QR_CODE("二维码", 28d, 28d, 40d, 227d, 0, 0d, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_CENTER, OFDAlignModeEnum.VERTICAL_CENTER),
QR_CODE_Name("二维码标题", 28d, 5d, 40d, 257.5d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_CENTER, OFDAlignModeEnum.VERTICAL_CENTER),
STAMP_UNIT("盖章单位", OFDLayoutConstants.STAMP_CENTER_X, 8d, 0d, 236d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
STAMP_TIME("盖章时间", OFDLayoutConstants.STAMP_CENTER_X, 11.5, 0d, 244d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
ELECTRONIC_SIGNAL("电子签章", 40d, 40d, 147d - 20d, 245d - 20d, 0, 0d, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_CENTER, OFDAlignModeEnum.VERTICAL_CENTER),
BACK_PNG("背景图片", OFDLayoutConstants.OFD_BODY_WIDTH, OFDLayoutConstants.OFD_BODY_HEIGHT, 0d, 0d, 0, 0d, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_CENTER, OFDAlignModeEnum.VERTICAL_CENTER),
DATA_SOURCE_NAME_PRO("数据产品和服务名称标题", 76.5d, 23d, 0d, 83d, 500, OFDLayoutConstants.FONT_SIZE_15MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
DATA_SOURCE_VALUE_PRO("数据产品和服务名称填充", 112d, 12d, 80d, 80d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_LEFT, OFDAlignModeEnum.VERTICAL_CENTER),
DATA_SOURCE_VALUE_PRO_2("数据产品和服务名称填充", 112d, 12d, 80d, 80d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_LEFT, OFDAlignModeEnum.VERTICAL_TOP),
REGISTER_SUBJECT_NAME_PRO("登记主体标题", 76.5d, 15d, 0d, 106d, 500, OFDLayoutConstants.FONT_SIZE_15MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
REGISTER_SUBJECT_VALUE_PRO("登记主体填充", 112d, 18d, 80d, 100d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_LEFT, OFDAlignModeEnum.VERTICAL_CENTER),
INDUSTRY_TYPE_NAME_PRO("行业分类标题", 76.5d, 15d, 0d, 121d, 500, OFDLayoutConstants.FONT_SIZE_15MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
INDUSTRY_TYPE_VALUE_PRO("行业分类填充", 112d, 12d, 80d, 121d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_LEFT, OFDAlignModeEnum.VERTICAL_TOP),
DATA_CONTENT_INTRODUCTION_NAME_PRO("产品和服务简介标题", 76.5d, 23d, 0d, 136d, 500, OFDLayoutConstants.FONT_SIZE_15MM,FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
DATA_CONTENT_INTRODUCTION_VALUE_PRO("产品和服务简介填充", 112d, 20d, 80d, 136d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_LEFT, OFDAlignModeEnum.VERTICAL_TOP),
VALID_DATE_NAME_PRO("有效期标题", 76.5d, 14d, 0d, 159d, 500, OFDLayoutConstants.FONT_SIZE_15MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
VALID_DATE_VALUE_PRO("有效期填充", 112d, 6d, 80d, 159d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_LEFT, OFDAlignModeEnum.VERTICAL_TOP),
REGISTER_INST_NAME_PRO("登记机构标题", 76.5d, 61d, 0d, 173d, 500, OFDLayoutConstants.FONT_SIZE_15MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
REGISTER_INST_VALUE_PRO("登记机构填充", 112d, 12d, 80d, 173d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_LEFT, OFDAlignModeEnum.VERTICAL_TOP),
QR_CODE_PRO("二维码", 28d, 28d, 40d, 234d, 0, 0d, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_CENTER, OFDAlignModeEnum.VERTICAL_CENTER),
QR_CODE_Name_PRO("二维码标题", 28d, 5d, 40d, 264.5d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_CENTER, OFDAlignModeEnum.VERTICAL_CENTER),
STAMP_UNIT_PRO("盖章单位", OFDLayoutConstants.STAMP_CENTER_X, 8d, 0d, 243d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
STAMP_TIME_PRO("盖章时间", OFDLayoutConstants.STAMP_CENTER_X, 11.5, 0d, 251d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.SIM_HEI, OFDAlignModeEnum.HORIZONTAL_RIGHT, OFDAlignModeEnum.VERTICAL_TOP),
// QR_CODE("二维码", 28d, 28d, 40d, 227d, 0, 0d, FontEnum.MS_YH, OFDAlignModeEnum.HORIZONTAL_CENTER, OFDAlignModeEnum.VERTICAL_CENTER),
// QR_CODE_Name("二维码标题", 28d, OFDLayoutConstants.FONT_HEIGHT_12MM, 40d, 257d, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.MS_YH, OFDAlignModeEnum.HORIZONTAL_CENTER, OFDAlignModeEnum.VERTICAL_CENTER),
//
// STAMP_UNIT("盖章单位", 210d, OFDLayoutConstants.FONT_HEIGHT_12MM, OFDLayoutConstants.STAMP_CENTER_X, OFDLayoutConstants.STAMP_UNIT_Y, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.MS_YH_BD, OFDAlignModeEnum.HORIZONTAL_CENTER, OFDAlignModeEnum.VERTICAL_CENTER),
// STAMP_TIME("盖章时间", 210d, OFDLayoutConstants.FONT_HEIGHT_12MM, OFDLayoutConstants.STAMP_CENTER_X, OFDLayoutConstants.STAMP_TIME_Y, 500, OFDLayoutConstants.FONT_SIZE_12MM, FontEnum.MS_YH_BD, OFDAlignModeEnum.HORIZONTAL_CENTER, OFDAlignModeEnum.VERTICAL_CENTER),
// ELECTRONIC_SIGNAL("电子签章", 40d, 40d, 147d - 20d, 245d - 20d, 0, 0d, FontEnum.MS_YH, OFDAlignModeEnum.HORIZONTAL_CENTER, OFDAlignModeEnum.VERTICAL_CENTER),
// BACK_PNG("背景图片", OFDLayoutConstants.OFD_BODY_WIDTH, OFDLayoutConstants.OFD_BODY_HEIGHT, 0d, 0d, 0, 0d, FontEnum.MS_YH, OFDAlignModeEnum.HORIZONTAL_CENTER, OFDAlignModeEnum.VERTICAL_CENTER),
;
private final String desc;
private final Double width;
private final Double height;
private final Double x;
private final Double y;
private final int fontThick;
private final Double fontSize;
private final FontEnum font;
private final OFDAlignModeEnum alignModeHor;
private final OFDAlignModeEnum alignModeVer;
}
@Data
public class OfdData {
/**
* 数据类型
*/
private AuthType authType;
/**
* 登记资源|产品 标题
*/
private String title;
/**
* 登记资源|产品 编号
*/
private String code;
/**
* 登记资源|产品 名称
*/
private String name;
/**
* 登记主体
*/
private String registerSubject;
/**
* 行业分类
*/
private String industryType;
/**
* 数据内容简介
*/
private String contentIntroduction;
/**
* 证书有效期
*/
private String dateValid;
/**
* 登记机构
*/
private String registerInst;
/**
* 盖章单位
*/
private String stampUnit;
/**
* 盖章时间
*/
private String stampTime;
/**
* 背景图片地址
*/
private String backUrl;
/**
* 二维码图片地址
*/
private String qrCodeUrl;
/**
* 二维码图片地址名称
*/
private String qrCodeUrlName;
/**
* 保存地址
*/
private String savePath;
/**
* 600 最多有1个登记主体,5个共有主体,每个登记主体名称长度为100汉字,多个之前用“、”分隔
*
* @param subjectList
*/
public void setRegisterSubject(List<String> subjectList) {
if (CollectionUtils.isNotEmpty(subjectList)) {
String registerSubject = subjectList.stream()
.limit(6)
.filter(StringUtils::isNotBlank)
.map(str -> str.length() > 100 ? str.substring(0, 100) : str)
.collect(Collectors.joining(","));
this.registerSubject = registerSubject.length() > 600 ? registerSubject.substring(0, 600) : registerSubject;
}
}
public void setRegisterSubject(String registerSubject) {
this.registerSubject = registerSubject;
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Item {
private String text;
private OFDPositionEnum position;
}
public interface AuthCertGenerator {
String generateOfdBase64(OfdData data);
byte[] generateOfdByteArray(OfdData data);
ByteArrayOutputStream generateOfd(OfdData data);
void generateOfd(String filePath, OfdData data);
ByteArrayOutputStream covertBase64StrToBos(String str);
String covertBosToBase64Str(ByteArrayOutputStream bos);
ByteArrayOutputStream covertOfdBosToPng(ByteArrayOutputStream bos);
ByteArrayOutputStream covertOfdToPng(OfdData data);
ByteArrayOutputStream covertOfdBosToPdf(ByteArrayOutputStream bos);
ByteArrayOutputStream covertOfdToPdf(OfdData data);
void saveFile(String filePath, ByteArrayOutputStream bos);
}
@Slf4j
public abstract class AbstractAuthCertGenerator implements AuthCertGenerator{
// 页面整体宽度
public static final Double OFD_BODY_WIDTH = 210d;
// 页面整体高度
protected static final Double OFD_BODY_HEIGHT = 297d;
// 字体颜色
public static final String FONT_COLOR = "#000000";
public static final String SIGN_FILE_PATH = "/data/template/zmb.png";
public static final String TEMPORARY_IMAGE_FOLDER = "D:/data/OFD/PNG";
@Override
public ByteArrayOutputStream covertBase64StrToBos(String str) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// 检查 Base64 字符串是否有效
if (StringUtils.isBlank(str)) {
log.warn("Base64 String is null or empty");
return null;
}
// 解码 Base64 字符串
byte[] pdfBytes = Base64.decode(str);
try {
bos.write(pdfBytes);
} catch (IOException e) {
throw new UncheckedIOException("编码失败", e);
}
return bos;
}
@Override
public String covertBosToBase64Str(ByteArrayOutputStream bos) {
return Base64.encode(bos.toByteArray());
}
@Override
public ByteArrayOutputStream covertOfdBosToPdf(ByteArrayOutputStream bos) {
// 关键步骤:将OFD输出流转为输入流
ByteArrayInputStream ofdInput = new ByteArrayInputStream(bos.toByteArray());
ByteArrayOutputStream pdfOutput = new ByteArrayOutputStream();
ConvertHelper.toPdf(ofdInput, pdfOutput); // 核心转换
return pdfOutput;
}
@Override
public ByteArrayOutputStream covertOfdBosToPng(ByteArrayOutputStream bos) {
ByteArrayInputStream ofdInput = new ByteArrayInputStream(bos.toByteArray());
try {
String pngFolderPath = String.format("%s/%s/", TEMPORARY_IMAGE_FOLDER, UUID.randomUUID());
ImageExporter exporter = new ImageExporter(ofdInput, Paths.get(pngFolderPath));
exporter.export();
List<Path> pathList = exporter.getImgFilePaths();
if (CollectionUtils.isNotEmpty(pathList)) {
ByteArrayOutputStream pngBos = convertFileToByteArray(exporter.getImgFilePaths().get(0).toString());
deleteFolder(pngFolderPath);
return pngBos;
}
log.warn("图片转换失败");
return null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void saveFile(String filePath, ByteArrayOutputStream bos) {
Path path = Paths.get(filePath);
try {
Files.write(path, bos.toByteArray());
System.out.println("OFD 文件已生成并保存至: " + path.toAbsolutePath());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
protected ByteArrayOutputStream generateOfdBos(List<Item> itemList) {
if (CollectionUtils.isEmpty(itemList)) {
return null;
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (OFDDoc ofdDoc = new OFDDoc(bos)) {
PageLayout pageLayout = ofdDoc.getPageLayout();
pageLayout.setHeight(OFD_BODY_HEIGHT);
pageLayout.setWidth(OFD_BODY_WIDTH);
VirtualPage virtualPage = generateVirtualPage(itemList, pageLayout);
ofdDoc.addVPage(virtualPage);
} catch (IOException e) {
throw new RuntimeException(e);
}
return bos;
}
protected void generateOfd(String filePath, List<Item> itemList) {
if (StringUtils.isBlank(filePath) || CollectionUtils.isEmpty(itemList)) {
return;
}
try (OFDDoc ofdDoc = new OFDDoc(Paths.get(filePath))) {
PageLayout pageLayout = ofdDoc.getPageLayout();
pageLayout.setHeight(OFD_BODY_HEIGHT);
pageLayout.setWidth(OFD_BODY_WIDTH);
VirtualPage virtualPage = generateVirtualPage(itemList, pageLayout);
ofdDoc.addVPage(virtualPage);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static VirtualPage generateVirtualPage(List<Item> items, PageLayout pageLayout) {
VirtualPage virtualPage = new VirtualPage(pageLayout);
for (Item item : items) {
if (Objects.nonNull(item)) {
OFDPositionEnum positionEnu = item.getPosition();
Div div = switch (positionEnu) {
case BACK_PNG ->
generatorBackgroundImage(item.getText(), positionEnu.getWidth(), positionEnu.getHeight()); // 背景图层要最后添加
case ELECTRONIC_SIGNAL ->
generatorImg(SIGN_FILE_PATH, positionEnu.getWidth(), positionEnu.getHeight(), positionEnu.getX(), positionEnu.getY());// 电子签章,用于mock数据,实际签章调用平台接口
case QR_CODE, QR_CODE_PRO ->
generatorImg(item.getText(), positionEnu.getWidth(), positionEnu.getHeight(), positionEnu.getX(), positionEnu.getY());// 二维码图片
case STAMP_UNIT, STAMP_UNIT_PRO ->
generateParagraphStampInfo(positionEnu, item.getText());// TODO 盖章单位,待确认
case STAMP_TIME, STAMP_TIME_PRO ->
generateParagraphStampInfo(positionEnu, item.getText());// 盖章时间,签章当天时间
default ->
generateParagraph(positionEnu, item.getText());
};
virtualPage.add(div);
}
}
return virtualPage;
}
public static Paragraph generateParagraph(OFDPositionEnum positionEnu, String text) {
Font font1 = new Font(positionEnu.getFont().getCode(), Paths.get(positionEnu.getFont().getPath()));
if (OFDPositionEnum.INDEX_VALUE.equals(positionEnu)) {
font1.setPrintableAsciiWidthMap(FontWidthConstants.NOTO_PRINTABLE_ASCII_WIDTH_MAP);
}
Paragraph paragraph = new Paragraph()
.setPosition(Position.Absolute)
// .setBorder(0.5d)
.setXY(positionEnu.getX(), positionEnu.getY())
.setWidth(positionEnu.getWidth())
.setHeight(positionEnu.getHeight());
Span span = new Span(font1, positionEnu.getFontSize(), text);
if (OFDPositionEnum.INDEX_VALUE.equals(positionEnu)) {
span.setLetterSpacing(0.1d);
}
paragraph.add(span);
switch (positionEnu.getAlignModeHor()) {
case HORIZONTAL_LEFT -> paragraph.setTextAlign(TextAlign.left);
case HORIZONTAL_CENTER -> paragraph.setTextAlign(TextAlign.center);
case HORIZONTAL_RIGHT -> paragraph.setTextAlign(TextAlign.right);
}
switch (positionEnu.getAlignModeVer()) {
case VERTICAL_TOP -> paragraph.setPaddingTop(0d);
case VERTICAL_CENTER -> paragraph.setPaddingTop(paragraph.getHeight() / 2 - span.getFontSize() / 2);
case VERTICAL_BOTTOM -> paragraph.setPaddingTop(paragraph.getHeight() - span.getFontSize());
}
return paragraph;
}
private static Paragraph generateParagraphStampInfo(OFDPositionEnum positionEnu, String text) {
Font font1 = new Font(positionEnu.getFont().getCode(), Paths.get(positionEnu.getFont().getPath()));
font1.setPrintableAsciiWidthMap(FontName.TIMES_NEW_ROMAN_PRINTABLE_ASCII_MAP);
Paragraph paragraph = new Paragraph()
.setPosition(Position.Absolute)
// .setBorder(0.5d)
.setXY(OFD_BODY_WIDTH - 64d - positionEnu.getWidth() / 2, positionEnu.getY())
.setWidth(positionEnu.getWidth())
.setHeight(positionEnu.getHeight())
.setTextAlign(TextAlign.center)
.setPaddingTop(0d);
Span span = new Span(font1, positionEnu.getFontSize(), text);
paragraph.add(span);
return paragraph;
}
private static Img generatorImg(String filePath, double width, double height, double x, double y) {
try {
Path imgPath = Paths.get(filePath);
return new Img(width, height, imgPath)
.setPosition(Position.Absolute)
// .setBorder(0d)
// .setPadding(0d)
.setX(x).setY(y);
} catch (IOException e) {
System.out.println("生成图片失败");
throw new RuntimeException(e);
}
}
/**
* 生成背景图片
*
* @param filePath
* @param width
* @param height
* @return
* @throws IOException
*/
private static Img generatorBackgroundImage(String filePath, double width, double height) {
try {
Path imgPath = Paths.get(filePath);
Img img = new Img(width, height, imgPath)
.setPosition(Position.Absolute)
// .setBorder(0.1d)
.setX(0d).setY(0d) // 坐标原点
.setLayer(Type.Background);
return img;
} catch (IOException e) {
System.out.println("生成图片失败");
throw new RuntimeException(e);
}
}
private ByteArrayOutputStream convertFileToByteArray(String filePath) {
File file = new File(filePath);
try (FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
return bos;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void deleteFolder(String folderPath) {
deleteFolder(new File(folderPath));
}
private void deleteFolder(File folder) {
if (folder.isDirectory()) {
File[] files = folder.listFiles();
if (files != null) {
for (File file : files) {
deleteFolder(file); // 递归删除子文件/子文件夹
}
}
}
folder.delete(); // 删除当前文件或空文件夹
}
}
public class AuthCertOPFGenerator extends AbstractAuthCertGenerator{
@Override
public String generateOfdBase64(OfdData data) {
byte[] bytes = generateOfdByteArray(data);
if (Objects.isNull(bytes) || bytes.length == 0) {
return null;
}
return Base64.encode(bytes);
}
@Override
public byte[] generateOfdByteArray(OfdData data) {
ByteArrayOutputStream bos = generateOfd(data);
if (Objects.isNull(bos)) {
return null;
}
return bos.toByteArray();
}
@Override
public ByteArrayOutputStream generateOfd(OfdData data) {
List<Item> itemList = dataInit(data);
if (CollectionUtils.isEmpty(itemList)) {
return null;
}
return generateOfdBos(itemList);
}
@Override
public void generateOfd(String filePath, OfdData data) {
List<Item> itemList = dataInit(data);
if (CollectionUtils.isEmpty(itemList)) {
return;
}
generateOfd(filePath, itemList);
}
@Override
public ByteArrayOutputStream covertOfdToPng(OfdData data) {
ByteArrayOutputStream bos = generateOfd(data);
if (Objects.isNull(bos)) {
return null;
}
return covertOfdBosToPng(bos);
}
@Override
public ByteArrayOutputStream covertOfdToPdf(OfdData data) {
ByteArrayOutputStream bos = generateOfd(data);
if (Objects.isNull(bos)) {
return null;
}
return covertOfdBosToPdf(bos);
}
private List<Item> dataInit(OfdData data) {
if (Objects.nonNull(data.getAuthType())) {
return switch (data.getAuthType()) {
case TENURE -> tenureInit(data);
case PROCESS -> processInit(data);
};
}
return null;
}
private List<Item> tenureInit(OfdData data) {
List<Item> ofdGeneralLayout = new ArrayList<>();
ofdGeneralLayout.add(new Item(data.getTitle(), OFDPositionEnum.TITLE));
ofdGeneralLayout.add(new Item("编号 :", OFDPositionEnum.INDEX_NAME));
ofdGeneralLayout.add(new Item(data.getCode(), OFDPositionEnum.INDEX_VALUE));
ofdGeneralLayout.add(new Item("数据资源名称 :", OFDPositionEnum.DATA_SOURCE_NAME));
if (data.getName().length() >= 27) {
ofdGeneralLayout.add(new Item(data.getName(), OFDPositionEnum.DATA_SOURCE_VALUE_2));
} else {
ofdGeneralLayout.add(new Item(data.getName(), OFDPositionEnum.DATA_SOURCE_VALUE));
}
ofdGeneralLayout.add(new Item("登记主体 :", OFDPositionEnum.REGISTER_SUBJECT_NAME));
ofdGeneralLayout.add(new Item(data.getRegisterSubject(), OFDPositionEnum.REGISTER_SUBJECT_VALUE));
ofdGeneralLayout.add(new Item("行业分类 :", OFDPositionEnum.INDUSTRY_TYPE_NAME));
ofdGeneralLayout.add(new Item(data.getIndustryType(), OFDPositionEnum.INDUSTRY_TYPE_VALUE));
ofdGeneralLayout.add(new Item("数据内容简介 :", OFDPositionEnum.DATA_CONTENT_INTRODUCTION_NAME));
String contentIntroduction = data.getContentIntroduction().length() >= 76 ? data.getContentIntroduction().substring(0, 76) + "..." : data.getContentIntroduction();
ofdGeneralLayout.add(new Item(contentIntroduction, OFDPositionEnum.DATA_CONTENT_INTRODUCTION_VALUE));
ofdGeneralLayout.add(new Item("有效期 :", OFDPositionEnum.VALID_DATE_NAME));
ofdGeneralLayout.add(new Item(data.getDateValid(), OFDPositionEnum.VALID_DATE_VALUE));
ofdGeneralLayout.add(new Item("登记机构 :", OFDPositionEnum.REGISTER_INST_NAME));
ofdGeneralLayout.add(new Item(data.getRegisterInst(), OFDPositionEnum.REGISTER_INST_VALUE));
ofdGeneralLayout.add(new Item(data.getQrCodeUrl(), OFDPositionEnum.QR_CODE));
ofdGeneralLayout.add(new Item("查验", OFDPositionEnum.QR_CODE_Name));
ofdGeneralLayout.add(new Item(data.getStampUnit(), OFDPositionEnum.STAMP_UNIT));
ofdGeneralLayout.add(new Item(data.getStampTime(), OFDPositionEnum.STAMP_TIME));
ofdGeneralLayout.add(new Item(data.getBackUrl(), OFDPositionEnum.BACK_PNG));
return ofdGeneralLayout;
}
private List<Item> processInit(OfdData data) {
List<Item> ofdGeneralLayout = new ArrayList<>();
ofdGeneralLayout.add(new Item(data.getTitle(), OFDPositionEnum.TITLE));
ofdGeneralLayout.add(new Item("编号 :", OFDPositionEnum.INDEX_NAME));
ofdGeneralLayout.add(new Item(data.getCode(), OFDPositionEnum.INDEX_VALUE));
ofdGeneralLayout.add(new Item("数据产品和服务名称 :", OFDPositionEnum.DATA_SOURCE_NAME_PRO));
if (data.getName().length() >= 27) {
ofdGeneralLayout.add(new Item(data.getName(), OFDPositionEnum.DATA_SOURCE_VALUE_PRO_2));
} else {
ofdGeneralLayout.add(new Item(data.getName(), OFDPositionEnum.DATA_SOURCE_VALUE_PRO));
}
ofdGeneralLayout.add(new Item("登记主体 :", OFDPositionEnum.REGISTER_SUBJECT_NAME_PRO));
ofdGeneralLayout.add(new Item(data.getRegisterSubject(), OFDPositionEnum.REGISTER_SUBJECT_VALUE_PRO));
ofdGeneralLayout.add(new Item("行业分类 :", OFDPositionEnum.INDUSTRY_TYPE_NAME_PRO));
ofdGeneralLayout.add(new Item(data.getIndustryType(), OFDPositionEnum.INDUSTRY_TYPE_VALUE_PRO));
ofdGeneralLayout.add(new Item("产品和服务简介 :", OFDPositionEnum.DATA_CONTENT_INTRODUCTION_NAME_PRO));
String contentIntroduction = data.getContentIntroduction().length() >= 76 ? data.getContentIntroduction().substring(0, 76) + "..." : data.getContentIntroduction();
ofdGeneralLayout.add(new Item(contentIntroduction, OFDPositionEnum.DATA_CONTENT_INTRODUCTION_VALUE_PRO));
ofdGeneralLayout.add(new Item("有效期 :", OFDPositionEnum.VALID_DATE_NAME_PRO));
ofdGeneralLayout.add(new Item(data.getDateValid(), OFDPositionEnum.VALID_DATE_VALUE_PRO));
ofdGeneralLayout.add(new Item("登记机构 :", OFDPositionEnum.REGISTER_INST_NAME_PRO));
ofdGeneralLayout.add(new Item(data.getRegisterInst(), OFDPositionEnum.REGISTER_INST_VALUE_PRO));
ofdGeneralLayout.add(new Item(data.getQrCodeUrl(), OFDPositionEnum.QR_CODE_PRO));
ofdGeneralLayout.add(new Item("查验", OFDPositionEnum.QR_CODE_Name_PRO));
ofdGeneralLayout.add(new Item(data.getStampUnit(), OFDPositionEnum.STAMP_UNIT_PRO));
ofdGeneralLayout.add(new Item(data.getStampTime(), OFDPositionEnum.STAMP_TIME_PRO));
ofdGeneralLayout.add(new Item(data.getBackUrl(), OFDPositionEnum.BACK_PNG));
return ofdGeneralLayout;
}
}