方案简单说明
目前考虑的方案是用reportBro官网的demo项目配合好模板,保存到我们自己的数据库中,然后我们利用java代码封装业务数据,随模板信息一起调用reportBro-lib(reportBro的Python脚本),生成pdf文件。而后将其传给前端展示并打印。
不利因素
就目前这种方案,可替代产品有crystalReport和ireport,但这两个软件都是客户端软件,不支持BS架构。后续如果想提供web版的模板绘制页面非常困难。而reportBro可以支持BS实现(代码完全开源,但是使用有限制,除非付费)。
reportBro相关资料
官网:
https://www.reportbro.com
demo应用源码:
https://github.com/jobsta/albumapp-django
reportBro-lib源码:
https://github.com/jobsta/reportbro-lib
demo应用截图:
时序图
java调用Python脚本Demo
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.File;
/**
* Created by keyleaf on 2020/12/21.
*/
public class MainTest {
public static void main(String[] args) {
String report_definition = "{'docElements': [{'elementType': 'text', 'id': 7, 'containerId': '0_content', 'x': 50, 'y': 130, 'width': 255, 'height': 30, 'content': 'Page ${page_number} / ${page_count}', 'eval': False, 'styleId': '', 'bold': False, 'italic': False, 'underline': False, 'strikethrough': False, 'horizontalAlignment': 'right', 'verticalAlignment': 'middle', 'textColor': '#666666', 'backgroundColor': '', 'font': 'helvetica', 'fontSize': 12, 'lineSpacing': 1, 'borderColor': '#000000', 'borderWidth': 1, 'borderAll': False, 'borderLeft': False, 'borderTop': False, 'borderRight': False, 'borderBottom': False, 'paddingLeft': 2, 'paddingTop': 2, 'paddingRight': 2, 'paddingBottom': 2, 'printIf': '', 'removeEmptyElement': False, 'alwaysPrintOnSamePage': True, 'pattern': '', 'link': '', 'cs_condition': '', 'cs_styleId': '', 'cs_bold': False, 'cs_italic': False, 'cs_underline': False, 'cs_strikethrough': False, 'cs_horizontalAlignment': 'left', 'cs_verticalAlignment': 'top', 'cs_textColor': '#000000', 'cs_backgroundColor': '', 'cs_font': 'helvetica', 'cs_fontSize': 12, 'cs_lineSpacing': 1, 'cs_borderColor': '#000000', 'cs_borderWidth': '1', 'cs_borderAll': False, 'cs_borderLeft': False, 'cs_borderTop': False, 'cs_borderRight': False, 'cs_borderBottom': False, 'cs_paddingLeft': 2, 'cs_paddingTop': 2, 'cs_paddingRight': 2, 'cs_paddingBottom': 2, 'spreadsheet_hide': False, 'spreadsheet_column': '', 'spreadsheet_colspan': '', 'spreadsheet_addEmptyRow': False}, {'elementType': 'text', 'id': 37, 'containerId': '0_content', 'x': 370, 'y': 280, 'width': 100, 'height': 20, 'content': '${current_date}', 'eval': False, 'styleId': '', 'bold': False, 'italic': False, 'underline': False, 'strikethrough': False, 'horizontalAlignment': 'left', 'verticalAlignment': 'top', 'textColor': '#000000', 'backgroundColor': '', 'font': 'helvetica', 'fontSize': 12, 'lineSpacing': 1, 'borderColor': '#000000', 'borderWidth': 1, 'borderAll': False, 'borderLeft': False, 'borderTop': False, 'borderRight': False, 'borderBottom': False, 'paddingLeft': 2, 'paddingTop': 2, 'paddingRight': 2, 'paddingBottom': 2, 'printIf': '', 'removeEmptyElement': False, 'alwaysPrintOnSamePage': True, 'pattern': '', 'link': '', 'cs_condition': '', 'cs_styleId': '', 'cs_bold': False, 'cs_italic': False, 'cs_underline': False, 'cs_strikethrough': False, 'cs_horizontalAlignment': 'left', 'cs_verticalAlignment': 'top', 'cs_textColor': '#000000', 'cs_backgroundColor': '', 'cs_font': 'helvetica', 'cs_fontSize': 12, 'cs_lineSpacing': 1, 'cs_borderColor': '#000000', 'cs_borderWidth': '1', 'cs_borderAll': False, 'cs_borderLeft': False, 'cs_borderTop': False, 'cs_borderRight': False, 'cs_borderBottom': False, 'cs_paddingLeft': 2, 'cs_paddingTop': 2, 'cs_paddingRight': 2, 'cs_paddingBottom': 2, 'spreadsheet_hide': False, 'spreadsheet_column': '', 'spreadsheet_colspan': '', 'spreadsheet_addEmptyRow': False}], 'parameters': [{'id': 1, 'name': 'page_count', 'type': 'number', 'arrayItemType': 'string', 'eval': False, 'nullable': False, 'pattern': '', 'expression': '', 'showOnlyNameType': True, 'testData': ''}, {'id': 2, 'name': 'page_number', 'type': 'number', 'arrayItemType': 'string', 'eval': False, 'nullable': False, 'pattern': '', 'expression': '', 'showOnlyNameType': True, 'testData': ''}, {'id': 9, 'name': 'current_date', 'type': 'date', 'arrayItemType': 'string', 'eval': False, 'nullable': False, 'pattern': 'd. MMMM yyyy, H:mm', 'expression': '', 'showOnlyNameType': False, 'testData': ''}, {'id': 10, 'name': 'year', 'type': 'number', 'arrayItemType': 'string', 'eval': False, 'nullable': True, 'pattern': '0', 'expression': '', 'showOnlyNameType': False, 'testData': ''}, {'id': 12, 'name': 'albums', 'type': 'array', 'arrayItemType': 'string', 'eval': False, 'nullable': False, 'pattern': '', 'expression': '', 'showOnlyNameType': False, 'testData': '[{\"name\":\"¿Dónde Jugarán las Niñas?\",\"artist\":\"Molotov\",\"year\":\"1997\",\"best_of_compilation\":\"\"},{\"name\":\"Big Ones\",\"artist\":\"Aerosmith\",\"year\":\"1995\",\"best_of_compilation\":\"1\"},{\"name\":\"The Greatest Hits\",\"artist\":\"INXS\",\"year\":\"1996\",\"best_of_compilation\":\"true\"},{\"name\":\"Coming Home\",\"artist\":\"Pain\",\"year\":\"2016\",\"best_of_compilation\":\"\"}]', 'children': [{'id': 13, 'name': 'name', 'type': 'string', 'arrayItemType': 'string', 'eval': False, 'nullable': False, 'pattern': '', 'expression': '', 'showOnlyNameType': False, 'testData': ''}, {'id': 14, 'name': 'artist', 'type': 'string', 'arrayItemType': 'string', 'eval': False, 'nullable': False, 'pattern': '', 'expression': '', 'showOnlyNameType': False, 'testData': ''}, {'id': 15, 'name': 'year', 'type': 'number', 'arrayItemType': 'string', 'eval': False, 'nullable': True, 'pattern': '', 'expression': '', 'showOnlyNameType': False, 'testData': ''}, {'id': 16, 'name': 'best_of_compilation', 'type': 'boolean', 'arrayItemType': 'string', 'eval': False, 'nullable': False, 'pattern': '', 'expression': '', 'showOnlyNameType': False, 'testData': ''}]}], 'styles': [{'id': 33, 'name': 'Table Header', 'bold': True, 'italic': False, 'underline': False, 'strikethrough': False, 'horizontalAlignment': 'center', 'verticalAlignment': 'middle', 'textColor': '#000000', 'backgroundColor': '', 'font': 'helvetica', 'fontSize': '10', 'lineSpacing': 1, 'borderColor': '#000000', 'borderWidth': '1', 'borderAll': False, 'borderLeft': False, 'borderTop': False, 'borderRight': False, 'borderBottom': False, 'paddingLeft': '2', 'paddingTop': '2', 'paddingRight': '2', 'paddingBottom': '2'}, {'id': 34, 'name': 'Table Content', 'bold': False, 'italic': False, 'underline': False, 'strikethrough': False, 'horizontalAlignment': 'left', 'verticalAlignment': 'middle', 'textColor': '#000000', 'backgroundColor': '', 'font': 'helvetica', 'fontSize': '9', 'lineSpacing': 1, 'borderColor': '#000000', 'borderWidth': '1', 'borderAll': False, 'borderLeft': False, 'borderTop': False, 'borderRight': False, 'borderBottom': False, 'paddingLeft': '2', 'paddingTop': '2', 'paddingRight': '2', 'paddingBottom': '2'}, {'id': 35, 'name': 'Table Content Highlight', 'bold': True, 'italic': False, 'underline': False, 'strikethrough': False, 'horizontalAlignment': 'center', 'verticalAlignment': 'middle', 'textColor': '#3d85c6', 'backgroundColor': '', 'font': 'helvetica', 'fontSize': '9', 'lineSpacing': 1, 'borderColor': '#000000', 'borderWidth': '1', 'borderAll': False, 'borderLeft': False, 'borderTop': False, 'borderRight': False, 'borderBottom': False, 'paddingLeft': '2', 'paddingTop': '2', 'paddingRight': '2', 'paddingBottom': '2'}], 'version': 3, 'documentProperties': {'pageFormat': 'A4', 'pageWidth': '', 'pageHeight': '', 'unit': 'mm', 'orientation': 'landscape', 'contentHeight': '', 'marginLeft': '10', 'marginTop': '10', 'marginRight': '10', 'marginBottom': '10', 'header': False, 'headerSize': '80', 'headerDisplay': 'always', 'footer': False, 'footerSize': '30', 'footerDisplay': 'always', 'patternLocale': 'en', 'patternCurrencySymbol': '$'}}";
String data = "{'current_date': '', 'year': '', 'albums': [{'name': '¿Dónde Jugarán las Niñas?', 'artist': 'Molotov', 'year': '1997', 'best_of_compilation': ''}, {'name': 'Big Ones', 'artist': 'Aerosmith', 'year': '1995', 'best_of_compilation': '1'}, {'name': 'The Greatest Hits', 'artist': 'INXS', 'year': '1996', 'best_of_compilation': 'true'}, {'name': 'Coming Home', 'artist': 'Pain', 'year': '2016', 'best_of_compilation': ''}]}";
Process proc;
try {
// 指定虚拟环境目录下的python3,否则执行generateFilesByReportBro.py时候找不到依赖的包导致无法成功执行Python脚本
String[] args1 = new String[]{"/Users/keyleaf/Documents/jobs/open-source/reportbro/albumapp-django/env/bin/python3",
"/Users/keyleaf/Documents/jobs/open-source/reportbro/albumapp-django/generateFilesByReportBro.py",
"-rd", report_definition,
"-d", data};
String fileDir = "/Users/keyleaf/Documents/jobs/open-source/reportbro/albumapp-django/";
proc = Runtime.getRuntime().exec(args1, null, new File(fileDir));
//用输入输出流来截取结果
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line = null;
String fileKey = null;
while ((line = in.readLine()) != null) {
fileKey = line;
}
System.out.println("fileKey is : " + fileKey);
in.close();
int exitValue = proc.waitFor();
if (0 != exitValue) {
System.out.println("call Python failed. error code is : " + exitValue);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
python调用reportBro脚本
import uuid
import ast
import argparse
from reportbro import Report, ReportBroError
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument("-rd", "--report_definition", default="", help="模板定义")
parser.add_argument("-d", "--data", default="", help="业务参数")
parser.add_argument("-af", "--additional_fonts", default="", help="额外字体")
args = parser.parse_args()
return args
args = parse_args()
report_definition = ast.literal_eval(args.report_definition)
data = ast.literal_eval(args.data)
if args.additional_fonts is None or args.additional_fonts == '':
additional_fonts = []
else:
additional_fonts = ast.literal_eval(args.additional_fonts)
is_test_data = True
try:
report = Report(report_definition, data, is_test_data, additional_fonts=additional_fonts)
except Exception as e:
print(e)
key = str(uuid.uuid4())
if report.errors:
print(report.errors)
report_file = report.generate_pdf()
# 打开文件 wb:以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
fo = open(key + ".pdf", "wb")
# 打印文件名,可以被Java程序捕获
print(key)
fo.write(report_file)
# 关闭文件
fo.close()