前言:
现在同学们对“pythondpi”可能比较讲究,大家都需要剖析一些“pythondpi”的相关文章。那么小编在网络上网罗了一些有关“pythondpi””的相关知识,希望看官们能喜欢,各位老铁们一起来了解一下吧!前言
本篇文章是笔者在学习了python-docx库后就一直想写的一篇,但是因为时间关系一直没机会动笔,最近正好不忙就和大家一起分享一下。本篇可以看成是办公自动化Word系列的最后一篇,python-docx库官方使用文档的后续翻译以后应该也不会有了。
办公自动化这个系列后续还会有Excel篇,应该也会分为官方使用文档的说明和一到两篇使用实例。其实就使用频率来说Excel的自动化是笔者使用最多也最有价值的,但就是因为使用太过频繁、应用场景太过广泛,所以笔者一时也不知如何动笔,不知道怎样才能将其讲好。不过身为一个强迫症,在PPT实战篇也完成后,笔者就会开始慢慢更新Excel系列,有需要的读者可以期待一下。
项目介绍
笔者因为公司电脑会对Office文档自动加密的缘故,平常较少使用Word进行文字记录,基本所有的个人笔记内容都是通过Markdown来进行记录的,在此也向读者安利一个Markdown文本编辑器Typora,用过之后笔者相信大部分人都不会再想用Word来记录非正式的笔记内容了。习惯了编辑器自动排版,笔者现在是完全不想浪费时间在Word排版上,虽然通过应用模板的方式Word自身也能实现部分自动排版功能,但是并不能仅通过键入字符的方式简单完成,不够优雅。
Markdown虽好,但在正式场合还是有局限性的,自己个人使用当然是千般好,不过给别人看的时候就只能笑笑了。网上其实有现成的软件可以将md格式的文件转为docx格式,但是笔者个人认为功能还是不够完善,排版的自定义程度并不是很够用。
既然没有现成满意的那就只好自己动手了,万幸大部分轮子都有大佬搭好了,而笔者只需要做个装配工就成了,拼辆车还是很简单的。
功能目标
采用Markdown的语法结构为基础,进行部分简化和拓展,最终实现在txt文本编辑器中键入纯文本后运行脚本即可得到指定样式的Word文档。
因为Markdown原本的语法全部实现较为费时,并且笔者本来的目的也不是实现md和docx的互转,仅仅只是想要通过纯文本的方式实现Word文档的优雅编写,所以在此对Markdown语法进行部分保留和拓展:
#:此为Markdown中的标题语法,笔者在此进行保留,但是只支持到四级标题。-:此为Markdown中的无须项目符号语法,笔者在此保留并进行拓展,和标题类似,可以键入多个减号来实现多级项目符号,最多三级。**:此为Markdown中的粗体语法,笔者在此保留并简化,规定此语法只能在正文段落中使用。$:此为Markdown中的Latex公式语法,笔者在此保留,为了便于添加到Word中,此处将公式转化为图片。!:此为Markdown中的图片语法的简化写法其他Markdown语法全部舍弃,例如引用、表格、代码块等,其中表格功能后续可能会添加进去。
笔者保留的语法都是完成一篇Word文档必不可少的部分,当然表格由于实现起来成本较高被舍去了。项目目标到此已经相当明确,下面笔者将为读者讲解具体的实现过程。
完成思路
首先简化Word文档,我们将Word文档看成是由若干段落组成的纯文本,同时规定上述所有的语法符号除**外都只能出现在段落开头。接下来思路就很清晰了,每一段作为一个最小单元,通过段落开头的语法符号分别调用不同的函数进行格式排版即可。这样我们只需要构建一个类,在类中为每一个语法符号编写一个方法就可以了。
源码展示
代码的讲解通过注释的形式来完成,读者如果有不明白的地方可以评论或者私信笔者,当然,常识性的问题不予解答,下面是代码部分:
import os, sysimport numpy as npimport matplotlib.pyplot as pltfrom PIL import Image, ImageOpsfrom docx import Documentfrom docx.shared import Pt, Cmfrom docx.shared import RGBColorfrom docx.enum.style import WD_STYLE_TYPEfrom docx.enum.text import WD_LINE_SPACINGfrom docx.enum.text import WD_ALIGN_PARAGRAPHclass Word(object):#定义类 def __init__(self):#创建类实例时自动执行的初始化方法 self.__document = Document()#此处变量前__符号表示该变量仅支持类内部调用,外部无法修改该变量 self.__filename = '炜智能.docx' styles = self.__document.styles #定义段落样式:一级标题,用于文章标题 style = styles.add_style('h1', WD_STYLE_TYPE.PARAGRAPH) style.base_style = styles['Normal'] paragraph_format = style.paragraph_format font = style.font size = 22#二号 #字号和磅对应关系如下:42磅对应初号、copy36磅对应小初、26磅对应一号、24磅对应小一号、22磅对应二号、18磅对应zd小二号、16磅对应三号、15磅对应小三号、14磅对应四号、12磅对应小四号、10.5磅对应五号、9磅对应小五号、7.5磅对应六号、6.5磅对应小六号、5.5磅对应七号、5磅对应八号。 paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER#设置段落居中对齐 paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE#设置一点五倍行距 paragraph_format.space_before = Pt(size / 2)#设置段前段后为0.5倍行距 paragraph_format.space_after = Pt(size / 2) paragraph_format.left_indent = 0#左缩进 font.name = '黑体'#设置字体名称 font.size = Pt(size)#设置字体大小 font.color.rgb = RGBColor(0, 0, 0)#设置字体颜色为黑 font.bold = True#是否粗体 font.italic = False#是否斜体 style.hidden = False#是否隐藏 style.quick_style = True#是否显示 style.priority = 1#样式排序 #定义段落样式:二级标题 style = styles.add_style('h2', WD_STYLE_TYPE.PARAGRAPH) style.base_style = styles['Heading 1'] paragraph_format = style.paragraph_format font = style.font size = 16#三号 paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT#设置段落居左对齐 paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE#设置一点五倍行距 paragraph_format.space_before = Pt(size / 2)#设置段前段后为0.5倍行距 paragraph_format.space_after = Pt(size / 2) paragraph_format.left_indent = 0#左缩进 font.name = '黑体'#设置字体名称 font.size = Pt(size)#设置字体大小 font.color.rgb = RGBColor(0, 0, 0)#设置字体颜色为黑 font.bold = True#是否粗体 font.italic = False#是否斜体 style.hidden = False#是否隐藏 style.quick_style = True#是否显示 style.priority = 2#样式排序 #定义段落样式:三级标题 style = styles.add_style('h3', WD_STYLE_TYPE.PARAGRAPH) style.base_style = styles['Heading 2'] paragraph_format = style.paragraph_format font = style.font size = 14#四号 paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT#设置段落居左对齐 paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE#设置一点五倍行距 paragraph_format.space_before = Pt(size / 2)#设置段前段后为0.5倍行距 paragraph_format.space_after = Pt(size / 2) paragraph_format.left_indent = Pt(24)#左缩进 font.name = '黑体'#设置字体名称 font.size = Pt(size)#设置字体大小 font.color.rgb = RGBColor(0, 0, 0)#设置字体颜色为黑 font.bold = True#是否粗体 font.italic = False#是否斜体 style.hidden = False#是否隐藏 style.quick_style = True#是否显示 style.priority = 3#样式排序 #定义段落样式:四级标题 style = styles.add_style('h4', WD_STYLE_TYPE.PARAGRAPH) style.base_style = styles['Heading 3'] paragraph_format = style.paragraph_format font = style.font size = 12#小四号 paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT#设置段落居左对齐 paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE#设置一点五倍行距 paragraph_format.space_before = Pt(size / 2)#设置段前段后为0.5倍行距 paragraph_format.space_after = Pt(size / 2) paragraph_format.left_indent = Pt(24)#左缩进 font.name = '黑体'#设置字体名称 font.size = Pt(size)#设置字体大小 font.color.rgb = RGBColor(0, 0, 0)#设置字体颜色为黑 font.bold = True#是否粗体 font.italic = False#是否斜体 style.hidden = False#是否隐藏 style.quick_style = True#是否显示 style.priority = 4#样式排序 #定义段落样式:二级标题 style = styles.add_style('h2', WD_STYLE_TYPE.PARAGRAPH) style.base_style = styles['Heading 1'] paragraph_format = style.paragraph_format font = style.font size = 16#三号 paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT#设置段落居左对齐 paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE#设置一点五倍行距 paragraph_format.space_before = Pt(size / 2)#设置段前段后为0.5倍行距 paragraph_format.space_after = Pt(size / 2) paragraph_format.left_indent = 0#左缩进 font.name = '黑体'#设置字体名称 font.size = Pt(size)#设置字体大小 font.color.rgb = RGBColor(0, 0, 0)#设置字体颜色为黑 font.bold = True#是否粗体 font.italic = False#是否斜体 style.hidden = False#是否隐藏 style.quick_style = True#是否显示 style.priority = 2#样式排序 #定义段落样式:三级标题 style = styles.add_style('h3', WD_STYLE_TYPE.PARAGRAPH) style.base_style = styles['Heading 2'] paragraph_format = style.paragraph_format font = style.font size = 14#四号 paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT#设置段落居左对齐 paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE#设置一点五倍行距 paragraph_format.space_before = Pt(size / 2)#设置段前段后为0.5倍行距 paragraph_format.space_after = Pt(size / 2) paragraph_format.left_indent = Pt(24)#左缩进 font.name = '黑体'#设置字体名称 font.size = Pt(size)#设置字体大小 font.color.rgb = RGBColor(0, 0, 0)#设置字体颜色为黑 font.bold = True#是否粗体 font.italic = False#是否斜体 style.hidden = False#是否隐藏 style.quick_style = True#是否显示 style.priority = 3#样式排序 #定义段落样式:四级标题 style = styles.add_style('h4', WD_STYLE_TYPE.PARAGRAPH) style.base_style = styles['Heading 3'] paragraph_format = style.paragraph_format font = style.font size = 12#小四号 paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT#设置段落居左对齐 paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE#设置一点五倍行距 paragraph_format.space_before = Pt(size / 2)#设置段前段后为0.5倍行距 paragraph_format.space_after = Pt(size / 2) paragraph_format.left_indent = Pt(24)#左缩进 font.name = '黑体'#设置字体名称 font.size = Pt(size)#设置字体大小 font.color.rgb = RGBColor(0, 0, 0)#设置字体颜色为黑 font.bold = True#是否粗体 font.italic = False#是否斜体 style.hidden = False#是否隐藏 style.quick_style = True#是否显示 style.priority = 4#样式排序 #定义段落样式:五级标题,用于图片、表格等的说明文字 style = styles.add_style('h5', WD_STYLE_TYPE.PARAGRAPH) style.base_style = styles['Normal'] paragraph_format = style.paragraph_format font = style.font size = 10.5#五号 paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER#设置段落居中对齐 paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE#设置一点五倍行距 paragraph_format.space_before = Pt(size / 2)#设置段前段后为0.5倍行距 paragraph_format.space_after = Pt(size / 2) paragraph_format.left_indent = 0#左缩进 font.name = '黑体'#设置字体名称 font.size = Pt(size)#设置字体大小 font.color.rgb = RGBColor(0, 0, 0)#设置字体颜色为黑 font.bold = False#是否粗体 font.italic = False#是否斜体 style.hidden = False#是否隐藏 style.quick_style = True#是否显示 style.priority = 5#样式排序 #定义段落样式:正文样式 style = styles.add_style('body', WD_STYLE_TYPE.PARAGRAPH) style.base_style = styles['Body Text'] paragraph_format = style.paragraph_format font = style.font size = 12#小四号 paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT#设置段落居左对齐 paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE#设置一点五倍行距 paragraph_format.space_before = 0#设置段前为0倍行距 paragraph_format.space_after = Pt(size / 2) paragraph_format.first_line_indent = Pt(size * 2)#首行缩进 font.name = '宋体'#设置字体名称 font.size = Pt(size)#设置字体大小 font.color.rgb = RGBColor(0, 0, 0)#设置字体颜色为黑 font.bold = False#是否粗体 font.italic = False#是否斜体 style.hidden = False#是否隐藏 style.quick_style = True#是否显示 style.priority = 6#样式排序 #定义段落样式:一级无序项目符号 style = styles.add_style('l1', WD_STYLE_TYPE.PARAGRAPH) style.base_style = styles['List Bullet'] paragraph_format = style.paragraph_format font = style.font size = 12#小四号 paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT#设置段落居左对齐 paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE#设置一点五倍行距 paragraph_format.space_before = 0#设置段前段后为0倍行距 paragraph_format.space_after = 0 font.name = '宋体'#设置字体名称 font.size = Pt(size)#设置字体大小 font.color.rgb = RGBColor(0, 0, 0)#设置字体颜色为黑 font.bold = False#是否粗体 font.italic = False#是否斜体 style.hidden = False#是否隐藏 style.quick_style = True#是否显示 style.priority = 7#样式排序 #定义段落样式:二级无序项目符号 style = styles.add_style('l2', WD_STYLE_TYPE.PARAGRAPH) style.base_style = styles['List Bullet 2'] paragraph_format = style.paragraph_format font = style.font size = 12#小四号 paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT#设置段落居左对齐 paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE#设置一点五倍行距 paragraph_format.space_before = 0#设置段前段后为0倍行距 paragraph_format.space_after = 0 font.name = '宋体'#设置字体名称 font.size = Pt(size)#设置字体大小 font.color.rgb = RGBColor(0, 0, 0)#设置字体颜色为黑 font.bold = False#是否粗体 font.italic = False#是否斜体 style.hidden = False#是否隐藏 style.quick_style = True#是否显示 style.priority = 8#样式排序 #定义段落样式:三级无序项目符号 style = styles.add_style('l3', WD_STYLE_TYPE.PARAGRAPH) style.base_style = styles['List Bullet 3'] paragraph_format = style.paragraph_format font = style.font size = 12#小四号 paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT#设置段落居左对齐 paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE#设置一点五倍行距 paragraph_format.space_before = 0#设置段前段后为0倍行距 paragraph_format.space_after = 0 font.name = '宋体'#设置字体名称 font.size = Pt(size)#设置字体大小 font.color.rgb = RGBColor(0, 0, 0)#设置字体颜色为黑 font.bold = False#是否粗体 font.italic = False#是否斜体 style.hidden = False#是否隐藏 style.quick_style = True#是否显示 style.priority = 9#样式排序 def save(self, filename = None):#保存文件 if filename == None: filename = self.__filename self.__document.save(filename) def add_heading(self, text, level):#添加标题段落,输入为标题文本和标题级别 if level in [1, 2, 3, 4, 5]: paragraph = self.__document.add_paragraph(style = 'h' + str(level))#添加段落,选择对应级别的段落样式 run = paragraph.add_run(text)#在段落中添加文本 if level == 1:#如果是正文标题的话就更改内置变量__filename的值,方便后面保存文件时命名 self.__filename = text + '.docx' else: raise SyntaxError(u'标题等级为1-5') def add_body(self, text):#添加正文段落 paragraph = self.__document.add_paragraph(style = 'body')#添加段落 texts = text.split('**')#以**来对文本分段 for x in range(len(texts)): if x % 2 != 0:#索引值为奇数 paragraph.add_run(texts[x]).font.bold = True#**之间的文本加粗 else: paragraph.add_run(texts[x])#在段落中添加文本 def add_list(self, text, level):#添加无序列表符号段落 if level in [1, 2, 3]: paragraph = self.__document.add_paragraph(style = 'l' + str(level))#添加段落,选择对应级别的段落样式 run = paragraph.add_run(text)#在段落中添加文本 else: raise SyntaxError(u'项目符号等级为1-3') def __latex_to_pic(self, text):#将Latex文本公式转为图片 math_formula = '$%s$' % text#将公式转为matplotlib库支持的语句 plt.rcParams['font.sans-serif']=['SimHei'] # 用来正常显示中文标签 plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号 fig = plt.figure(figsize = (20, 4), dpi = 160)#创建窗口 ax = fig.add_axes([0,0,1,1])#添加坐标轴,便于后面文字定位 ax.text(0.5, 0.5, math_formula, horizontalalignment='center', verticalalignment='center', fontsize=20, color='black', transform=ax.transAxes)#在中心添加文字 ax.set_axis_off()#隐藏坐标轴 plt.savefig('炜智能.png')#保存图片 padding = (10,10,10,10)#边缘保留宽度 color = (255, 255, 255)#白色RGB值 img = Image.open('炜智能.png')#打开图片 img = img.convert('RGB')#将图片转为RGB格式 ivt_img = ImageOps.invert(img)#将颜色反转,方便后面getbbox()截取黑边 bbox = ivt_img.getbbox()#截去黑色边缘 left = bbox[0] - padding[0]#计算截取边界 top = bbox[1] - padding[1] right = bbox[2] + padding[2] bottom = bbox[3] + padding[3] img = img.crop([left, top, right, bottom])#截取图片 img = img.convert('RGBA')#转换图片格式为RGBA,增加一个透明度通道 arr = np.array(np.asarray(img))#将图片数组化 r, g ,b, a = np.rollaxis(arr, axis = -1) mask = (r == color[0]) & (g == color[1]) & (b == color[2])#筛选白色的像素点 arr[mask, 3] = 0#将白点透明度调为0 img = Image.fromarray(arr, mode = 'RGBA')#将数组转会图片 img.save('炜智能.png')#保存图片 def __picture(self, filename, width = None, height = None):#添加图片 try:#将宽度值转为cm width = Cm(width) except: pass try:#将高度值转为cm height = Cm(height) except: pass paragraph = self.__document.add_paragraph() run = paragraph.add_run() paragraph_format = paragraph.paragraph_format font = run.font size = 12#小四号 paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER#将图像居中 paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE#设置一点五倍行距 paragraph_format.space_before = 0#设置段前为0倍行距 paragraph_format.space_after = Pt(size / 2) font.name = '宋体'#设置字体名称 font.size = Pt(size)#设置字体大小 font.color.rgb = RGBColor(0, 0, 0)#设置字体颜色为黑 font.bold = False#是否粗体 font.italic = False#是否斜体 run.add_picture(filename, width = width, height = height)#添加图像 def add_latex(self, text):#添加公式段落 self.__latex_to_pic(text) self.__picture('炜智能.png', height = 0.6) os.remove('炜智能.png')#删除插入公式的图片文件 def add_picture(self, filename):#添加图片段落 self.__picture(filename, width = 8.5) def read_text(self, filename):#读取txt文档转为Word with open(filename, 'r', encoding = 'UTF-8') as f: lines = f.readlines() for line in lines: line = line.rstrip('\n')#去掉字符串最后的换行符 if line.startswith('# '): line = line.lstrip('# ') self.add_heading(line, 1) elif line.startswith('## '): line = line.lstrip('## ') self.add_heading(line, 2) elif line.startswith('### '): line = line.lstrip('### ') self.add_heading(line, 3) elif line.startswith('#### '): line = line.lstrip('#### ') self.add_heading(line, 4) elif line.startswith('##### '): line = line.lstrip('##### ') self.add_heading(line, 5) elif line.startswith('- '): line = line.lstrip('- ') self.add_list(line, 1) elif line.startswith('-- '): line = line.lstrip('-- ') self.add_list(line, 2) elif line.startswith('--- '): line = line.lstrip('--- ') self.add_list(line, 3) elif line.startswith('! '): line = line.lstrip('! ') try: self.add_picture(line) except: self.add_body('添加图片失败(%s)' % line) elif line.startswith('!'): line = line.lstrip('!') try: self.add_picture(line) except: self.add_body('添加图片失败(%s)' % line) elif line.startswith('$ '): line = line.lstrip('$ ') try: self.add_latex(line) except: self.add_body('添加公式失败(%s)' % line) elif line.startswith('¥ '): line = line.lstrip('¥ ') try: self.add_latex(line) except: self.add_body('添加公式失败(%s)' % line) else: self.add_body(line) self.save()def main():#主函数 os.chdir(os.path.abspath(os.path.dirname(sys.argv[0])))#切换工作目录至脚本所在位置 doc = Word()#创建上面定义的类 doc.read_text('demo.txt')#选择要转换的txt文件if __name__ == '__main__': main()
代码中的样式定义语句其实也可以通过直接编写Word模板的方式实现,只需在最开始的self.__document = Document()语句中填入模板文件的地址即可,笔者之所以没有采用这种方式是因为不想外带一个模板文件,不介意的读者完全可以通过在模板中编写样式的方式来实现更多的样式设定。
代码中最后一个方法使用了大量的if条件判定并用了大量的重复代码,整体看上去略显繁琐,究其原因是笔者懒得想方法简化了,觉得不够优雅的读者可以自行改造一下,有好的思路的话也可以与笔者分享一下。
实例讲解
上面的代码看着复杂,其实大部分代码行都是用来定义样式风格和文本转换语法的,读者可以直接用笔者定义好的样式和文本转换语法,也可以以上面的代码为框架自己进行样式自定义或功能拓展,比如表格的支持。
学习使用的最好方式就是实例,下面笔者给出一份txt文件的编写实例,如果公司电脑有加密系统的话,此处建议使用notepad++来进行文件编写:
# 一级标题## 二级标题### 三级标题#### 四级标题正文效果展示(微信公众号:**炜智能**)- 一级项目符号-- 二级项目符号--- 三级级项目符号- 一级项目符号-- 二级项目符号--- 三级级项目符号$ \mathit{\alpha = \beta +\gamma }! weizhineng.png##### 五级标题标题分为五级,用以若干#加单空格开头;一级标题用于文章标题,其文本会直接被用作Word文档文件名,此处需要注意,如果脚本所在工作目录下有同名Word文档,该文档会被直接替换;五级标题用于图片、表格等的说明;项目符号分为三级,以若干-加单空格开头;数学公式以$或¥加单空格开头,公式语法为Latex,如果公式错误的话会在生成的Word文档中提示;图片以!或!加单空格开头,如果图片与脚本文件在同一工作目录下可以只填写图片文件名,不然需要加上绝对路径,图片地址填写错误的话同样会在生成的Word文档中提示;没有以上述特殊语法开头的段落一律视为正文内容;**为正文中的粗体语法,笔者为了编写方便,将其设为了分隔符,也就是说正文中是无法显示这两个星号的,而且当出现了奇数个双星号时会自动在最后补齐,使最后一个双星号后的字符也变为粗体。
笔者建议将脚本文件、待转换txt文件、需插入图片都放置在同一工作目录下,示意图如下:
执行脚本只需右击脚本,选择打开方式为Python即可,示意图如下:
执行完脚本后会在脚本所在工作目录下生成Word文档,此文档因为是用python-docx库编写的,所以即使是使用带加密功能的公司电脑生成也不会被加密,具体效果如下:
标签: #pythondpi