create_pdf.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. from reportlab.pdfbase import pdfmetrics # 注册字体
  2. from reportlab.pdfbase.ttfonts import TTFont # 字体类
  3. from reportlab.platypus import Table, SimpleDocTemplate, Paragraph, Image, Frame, PageTemplate, Spacer # 报告内容相关类
  4. from reportlab.lib.pagesizes import A4 # 页面的标志尺寸(8.5*inch, 11*inch)
  5. from reportlab.lib.styles import getSampleStyleSheet # 文本样式
  6. from reportlab.lib import colors # 颜色模块
  7. from reportlab.graphics.charts.barcharts import VerticalBarChart # 图表类
  8. from reportlab.graphics.charts.legends import Legend # 图例类
  9. from reportlab.graphics.shapes import Drawing # 绘图工具
  10. from reportlab.lib.units import cm # 单位:cm
  11. import argparse
  12. import re
  13. pdfmetrics.registerFont(TTFont('SimSun', './py/SimSun.ttf'))
  14. class Graphs:
  15. # 绘制标题
  16. @staticmethod
  17. def draw_title(title: str, linespace=50, fontsize=18):
  18. # 获取所有样式表
  19. style = getSampleStyleSheet()
  20. # 拿到标题样式
  21. ct = style['Heading2']
  22. # 单独设置样式相关属性
  23. ct.fontName = 'SimSun' # 字体名
  24. ct.fontSize = fontsize # 字体大小
  25. ct.leading = linespace # 行间距
  26. ct.textColor = colors.green # 字体颜色
  27. ct.alignment = 1 # 居中
  28. ct.bold = True
  29. # 创建标题对应的段落,并且返回
  30. return Paragraph(title, ct)
  31. # 绘制小标题
  32. @staticmethod
  33. def draw_little_title(title: str):
  34. # 获取所有样式表
  35. style = getSampleStyleSheet()
  36. # 拿到标题样式
  37. ct = style['Normal']
  38. # 单独设置样式相关属性
  39. ct.fontName = 'SimSun' # 字体名
  40. ct.fontSize = 15 # 字体大小
  41. ct.leading = 30 # 行间距
  42. ct.textColor = colors.red # 字体颜色
  43. # 创建标题对应的段落,并且返回
  44. return Paragraph(title, ct)
  45. # 绘制普通段落内容
  46. @staticmethod
  47. def draw_text(text: str,alignment=0,firstLineIndent=32):
  48. # 获取所有样式表
  49. style = getSampleStyleSheet()
  50. # 获取普通样式
  51. ct = style['Normal']
  52. ct.fontName = 'SimSun'
  53. ct.fontSize = 12
  54. ct.wordWrap = 'CJK' # 设置自动换行
  55. ct.alignment = alignment # 左对齐
  56. ct.firstLineIndent = firstLineIndent # 第一行开头空格
  57. ct.leading = 25
  58. return Paragraph(text, ct)
  59. # 绘制表格
  60. @staticmethod
  61. def draw_info_table(*args, col_width=120):
  62. # 列宽度
  63. col_width = col_width
  64. style = [
  65. ('FONTNAME', (0, 0), (-1, -1), 'SimSun'), # 字体
  66. ('FONTSIZE', (0, 0), (-1, 0), 12), # 第一行的字体大小
  67. ('FONTSIZE', (0, 1), (-1, -1), 12), # 第二行到最后一行的字体大小
  68. ('BACKGROUND', (0, 0), (-1, 0), '#d5dae6'), # 设置第一行背景颜色
  69. ('ALIGN', (0, 0), (-1, -1), 'CENTER'), # 第一行水平居中
  70. ('ALIGN', (0, 1), (-1, -1), 'CENTER'), # 第二行到最后一行左右左对齐
  71. ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), # 所有表格上下居中对齐
  72. ('TEXTCOLOR', (0, 0), (-1, -1), colors.darkslategray), # 设置表格内文字颜色
  73. ('GRID', (0, 0), (-1, -1), 0.5, colors.black), # 设置表格框线为grey色,线宽为0.5
  74. # ('SPAN', (0, 1), (0, 2)), # 合并第一列二三行
  75. # ('SPAN', (0, 3), (0, 4)), # 合并第一列三四行
  76. # ('SPAN', (0, 5), (0, 6)), # 合并第一列五六行
  77. # ('SPAN', (0, 7), (0, 8)), # 合并第一列五六行
  78. ]
  79. table = Table(args, colWidths=col_width, rowHeights=(1*cm, 1*cm),style=style)
  80. return table
  81. # 绘制表格
  82. @staticmethod
  83. def draw_result_table(*args, col_width=120):
  84. # 列宽度
  85. col_width = col_width
  86. style = [
  87. ('FONTNAME', (0, 0), (-1, -1), 'SimSun'), # 字体
  88. ('FONTSIZE', (0, 0), (-1, 0), 12), # 第一行的字体大小
  89. ('FONTSIZE', (0, 1), (-1, -1), 12), # 第二行到最后一行的字体大小
  90. ('BACKGROUND', (0, 0), (-1, 0), '#d5dae6'), # 设置第一行背景颜色
  91. ('ALIGN', (0, 0), (-1, -1), 'CENTER'), # 第一行水平居中
  92. ('ALIGN', (0, 1), (-1, -1), 'CENTER'), # 第二行到最后一行左右左对齐
  93. ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), # 所有表格上下居中对齐
  94. ('TEXTCOLOR', (0, 0), (-1, -1), colors.darkslategray), # 设置表格内文字颜色
  95. ('GRID', (0, 0), (-1, -1), 0.5, colors.black), # 设置表格框线为grey色,线宽为0.5
  96. # ('SPAN', (0, 1), (0, 2)), # 合并第一列二三行
  97. # ('SPAN', (0, 3), (0, 4)), # 合并第一列三四行
  98. # ('SPAN', (0, 5), (0, 6)), # 合并第一列五六行
  99. # ('SPAN', (0, 7), (0, 8)), # 合并第一列五六行
  100. ]
  101. table = Table(args, colWidths=col_width, style=style)
  102. return table
  103. @staticmethod
  104. def draw_explanation_table(*args, col_width=120):
  105. # 列宽度
  106. col_width = col_width
  107. style = [
  108. ('FONTNAME', (0, 0), (-1, -1), 'SimSun'), # 字体
  109. ('FONTSIZE', (0, 0), (-1, 0), 12), # 第一行的字体大小
  110. ('FONTSIZE', (0, 1), (-1, -1), 12), # 第二行到最后一行的字体大小
  111. ('BACKGROUND', (0, 0), (-1, 0), '#d5dae6'), # 设置第一行背景颜色
  112. ('ALIGN', (0, 0), (-1, -1), 'CENTER'), # 第一行水平居中
  113. ('ALIGN', (0, 1), (-1, -1), 'CENTER'), # 第二行到最后一行左右左对齐
  114. ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), # 所有表格上下居中对齐
  115. ('TEXTCOLOR', (0, 0), (-1, -1), colors.darkslategray), # 设置表格内文字颜色
  116. ('GRID', (0, 0), (-1, -1), 0.5, colors.black), # 设置表格框线为grey色,线宽为0.5
  117. # ('SPAN', (0, 1), (0, 2)), # 合并第一列二三行
  118. # ('SPAN', (0, 3), (0, 4)), # 合并第一列三四行
  119. # ('SPAN', (0, 5), (0, 6)), # 合并第一列五六行
  120. # ('SPAN', (0, 7), (0, 8)), # 合并第一列五六行
  121. ]
  122. table = Table(args, colWidths=(100,350), style=style)
  123. return table
  124. # 创建图表
  125. @staticmethod
  126. def draw_bar(bar_data: list, ax: list, items: list):
  127. drawing = Drawing(500, 250)
  128. bc = VerticalBarChart()
  129. bc.x = 45 # 整个图表的x坐标
  130. bc.y = 45 # 整个图表的y坐标
  131. bc.height = 200 # 图表的高度
  132. bc.width = 350 # 图表的宽度
  133. bc.data = bar_data
  134. bc.strokeColor = colors.black # 顶部和右边轴线的颜色
  135. bc.valueAxis.valueMin = 5000 # 设置y坐标的最小值
  136. bc.valueAxis.valueMax = 26000 # 设置y坐标的最大值
  137. bc.valueAxis.valueStep = 2000 # 设置y坐标的步长
  138. bc.categoryAxis.labels.dx = 2
  139. bc.categoryAxis.labels.dy = -8
  140. bc.categoryAxis.labels.angle = 20
  141. bc.categoryAxis.categoryNames = ax
  142. # 图示
  143. leg = Legend()
  144. leg.fontName = 'SimSun'
  145. leg.alignment = 'right'
  146. leg.boxAnchor = 'ne'
  147. leg.x = 475 # 图例的x坐标
  148. leg.y = 240
  149. leg.dxTextSpace = 10
  150. leg.columnMaximum = 3
  151. leg.colorNamePairs = items
  152. drawing.add(leg)
  153. drawing.add(bc)
  154. return drawing
  155. # 绘制图片
  156. @staticmethod
  157. def draw_img(path):
  158. img = Image(path) # 读取指定路径下的图片
  159. img.drawWidth = 12*cm # 设置图片的宽度
  160. img.drawHeight = 6*cm # 设置图片的高度
  161. return img
  162. def footer(canvas, doc):
  163. canvas.saveState()
  164. canvas.setFont('SimSun', 12)
  165. canvas.line(cm, cm * 1.2, A4[0] - cm, cm * 1.2)
  166. canvas.drawCentredString(A4[0]/2, cm * 0.7, "本报告仅供临床参考,不做诊断证明之用")
  167. canvas.restoreState()
  168. def header(canvas, doc):
  169. canvas.saveState()
  170. canvas.drawImage(logo, 50, doc.height + 50,2*cm,2*cm)
  171. canvas.setFont('SimSun', 12)
  172. canvas.line(cm, cm * 1.2, A4[0] - cm, cm * 1.2)
  173. canvas.drawCentredString(A4[0]/2, cm * 0.7, "本报告仅供临床参考,不做诊断证明之用")
  174. canvas.restoreState()
  175. if __name__ == '__main__':
  176. parse = argparse.ArgumentParser()
  177. parse.add_argument('filename', type=str)
  178. parse.add_argument('logo', type=str)
  179. parse.add_argument('output', type=str)
  180. args = parse.parse_args()
  181. with open(args.filename, 'rb') as fp:
  182. data = fp.readlines()
  183. d = eval(data[0])
  184. global logo, output_path
  185. logo = args.logo
  186. output_path = args.output
  187. gender = ['男','女']
  188. # 创建内容对应的空列表
  189. content = list()
  190. # 添加标题
  191. content.append(Graphs.draw_title(d['userEntity']['institutionName'],linespace=20,fontsize=14))
  192. content.append(Graphs.draw_title(d['name']))
  193. # 基本信息
  194. content.append(Graphs.draw_text('【基本信息】',firstLineIndent=0))
  195. data = [
  196. ('姓 名', '性 别', '出生日期'),
  197. (d['userEntity']['petName'],gender[int(d['userEntity']['gender'])],d['userEntity']['birthday'])
  198. ]
  199. content.append(Graphs.draw_info_table(*data, col_width=150))
  200. # 添加段落文字
  201. s = Spacer(width=0, height=1*cm)
  202. content.append(s)
  203. content.append(Graphs.draw_text('【测试结果】',firstLineIndent=0))
  204. # 添加图片
  205. # content.append(Graphs.draw_img('1.png'))
  206. data = [('项目', '结果','常模参考值')]
  207. for i in d['result']:
  208. data.append((i['name'],i['score'], i['reference']))
  209. content.append(Graphs.draw_result_table(*data,col_width=150))
  210. s = Spacer(width=0, height=1*cm)
  211. content.append(s)
  212. content.append(Graphs.draw_text('【结果说明】',firstLineIndent=0))
  213. # 添加表格
  214. styles = getSampleStyleSheet()
  215. styleC = styles["BodyText"]
  216. styleC.fontName = 'SimSun'
  217. styleC.fontSize = 12 # 字体大小
  218. styleC.alignment = 1 # 居中
  219. styleC.textColor = colors.darkslategray
  220. styles = getSampleStyleSheet()
  221. styleL = styles["BodyText"]
  222. styleL.fontName = 'SimSun'
  223. styleL.fontSize = 12 # 字体大小
  224. styleL.alignment = 0 # 左对齐
  225. styleL.textColor = colors.darkslategray
  226. for i in d['result']:
  227. if len(i['nameExplain'])>1:
  228. explanation = Paragraph(i['nameExplain'], styleL)
  229. else:
  230. explanation = Paragraph(i['nameExplain'], styleC)
  231. if len(i['suggestion'])>1:
  232. suggestion = Paragraph(i['suggestion'], styleL)
  233. else:
  234. suggestion = Paragraph(i['suggestion'], styleC)
  235. data = [
  236. ('维度名称', i['name']),
  237. ('得 分', i['score']),
  238. ('结 论', i['symptom']),
  239. ('维度说明', explanation),
  240. ('建 议',suggestion)
  241. ]
  242. content.append(Graphs.draw_explanation_table(*data))
  243. s = Spacer(width=0, height=2*cm)
  244. content.append(s)
  245. content.append(Graphs.draw_text(f'医生签名:__________________ 测评日期:{d["reportTime"]}',2))
  246. # 生成pdf文件
  247. output_name = f"{output_path}"
  248. doc = SimpleDocTemplate(output_name, pagesize=A4)
  249. frame = Frame(36, 36, A4[0] - 72, A4[1] - 72)
  250. header_frame = Frame(36, 36, A4[0] - 72, A4[1] - 72)
  251. footer_template = PageTemplate(id='footer', frames=[frame], onPage=footer)
  252. header_template = PageTemplate(id='header', frames=[header_frame], onPage=header)
  253. doc.addPageTemplates([header_template, footer_template])
  254. doc.build(content,onFirstPage=footer, onLaterPages=footer)