Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

132 linhas
3.3KB

  1. # © 2016 cole
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  3. from docxtpl import DocxTemplate
  4. import docx
  5. import jinja2
  6. """
  7. 使用一个独立的文件来封装需要支持图片等功能,避免污染report_docx.py
  8. """
  9. def calc_length(s):
  10. """
  11. 把字符串,数字类型的参数转化为docx的长度对象,如:
  12. 12 => Pt(12)
  13. '12' => Pt(12)
  14. '12pt' => Pt(12) 单位为point
  15. '12cm' => Cm(12) 单位为厘米
  16. '12mm' => Mm(12) 单位为毫米
  17. '12inchs' => Inchs(12) 单位为英寸
  18. '12emu' => Emu(12)
  19. '12twips' => Twips(12)
  20. """
  21. if not isinstance(s, str):
  22. # 默认为像素
  23. return docx.shared.Pt(s)
  24. if s.endswith('cm'):
  25. return docx.shared.Cm(float(s[:-2]))
  26. elif s.endswith('mm'):
  27. return docx.shared.Mm(float(s[:-2]))
  28. elif s.endswith('inchs'):
  29. return docx.shared.Inches(float(s[:-5]))
  30. elif s.endswith('pt') or s.endswith('px'):
  31. return docx.shared.Pt(float(s[:-2]))
  32. elif s.endswith('emu'):
  33. return docx.shared.Emu(float(s[:-3]))
  34. elif s.endswith('twips'):
  35. return docx.shared.Twips(float(s[:-5]))
  36. else:
  37. # 默认为像素
  38. return docx.shared.Pt(float(s))
  39. def calc_alignment(s):
  40. """
  41. 把字符串转换为对齐的常量
  42. """
  43. A = docx.enum.text.WD_ALIGN_PARAGRAPH
  44. if s == 'center':
  45. return A.CENTER
  46. elif s == 'left':
  47. return A.LEFT
  48. elif s == 'right':
  49. return A.RIGHT
  50. else:
  51. return A.LEFT
  52. @jinja2.contextfilter
  53. def picture(ctx, data, width=None, height=None, align=None):
  54. """
  55. 把图片的二进制数据(使用了base64编码)转化为一个docx.Document对象
  56. data:图片的二进制数据(使用了base64编码)
  57. width:图片的宽度,可以为:'12cm','12mm','12pt' 等,参考前面的 calc_length()
  58. height:图片的长度,如果没有设置,根据长度自动缩放
  59. align:图片的位置,'left','center','right'
  60. """
  61. if not data:
  62. return None
  63. # 转化为file-like对象
  64. # 在python2.7中,bytes==str,可以直接使用
  65. # 在python3.5中,bytes和str是不同的类型,需要使用base64这个库
  66. # data使用了base64编码,所以这里需要解码
  67. data = data.decode('base64')
  68. import io
  69. data = io.BytesIO(data)
  70. tpl = ctx['tpl']
  71. doc = tpl.new_subdoc()
  72. if width:
  73. width = calc_length(width)
  74. if height:
  75. height = calc_length(height)
  76. p = doc.add_paragraph()
  77. p.alignment = calc_alignment(align)
  78. p.add_run().add_picture(data, width=width, height=height)
  79. return doc
  80. def get_env():
  81. """
  82. 创建一个jinja的enviroment,然后添加一个过滤器
  83. """
  84. jinja_env = jinja2.Environment()
  85. jinja_env.filters['picture'] = picture
  86. return jinja_env
  87. def test():
  88. """
  89. 演示了如何使用,可以直接执行该文件,但是需要使用自己写的docx模版,和图片
  90. """
  91. tpl = DocxTemplate("tpls/test_tpl.docx")
  92. # 读取图片的数据且使用base64编码
  93. data = open('tpls/python_logo.png', 'rb').read().encode('base64')
  94. obj = {'logo': data}
  95. # 需要添加模版对象
  96. ctx = {'obj': obj, 'tpl': tpl}
  97. jinja_env = get_env()
  98. tpl.render(ctx, jinja_env)
  99. tpl.save('tpls/test.docx')
  100. def main():
  101. test()
  102. if __name__ == '__main__':
  103. main()
上海开阖软件有限公司 沪ICP备12045867号-1