Word 转手写体

news/2025/1/8 19:41:15/

文章目录

  • 介绍与准备
    • 声明
    • Tkinter 指南
    • 开发环境
  • Word 转手写体代码
    • 主程序
      • 背景图像
      • 字体下载与设置
      • 主程序啦!!
      • 代码测试与结果
    • 链接 Word 文档
      • 从 Word 文档获取输入的代码
      • 新建一个 Word 测试文档
      • 代码调试
      • 代码运行准备
  • GUI 设计
    • 整体代码
      • GUI 骨架
      • 文件准备
      • 代码
  • 软件的使用与后续开发
    • 使用效果
    • 后续完善

介绍与准备

声明

要源码请关注后留言
不用做任何商业用途
赠人玫瑰,手有余香,点个赞再走呗

Tkinter 指南

这里使用的是 Tkinter 来开发一款软件的,如果不是用 Tkinter,请移步。
首先推荐一些 Tkinter 的教程吧:
Tkinter GUI 01
Tkinter GUI 02
Tkinter GUI 03
Tkinter GUI 04
Tkinter GUI 05
Tkinter GUI 06
Tkinter GUI 07
Tkinter GUI 08

然后,是一些有用的参考资料和书籍的下载地址:
Python GUI Tkinter 参考资料

再安装如下模块;

  • handright
  • docx:用于打开 Word
  • PIL:用于保存图像,创建手写字体模板

开发环境

这里使用 Eclipse 的 PyDev 开发我们的 GUI 应用,有关 Eclipse 的内容,可以参阅:
Eclipse 安装教程、PyDev 插件下载教程与有关设置

另外 PyDev 由于是一个插件,所以,其版本更新需要我们重新卸载、再安装。读者们下载了 PyDev 插件之后,过一阵子,就会弹出下面的一个对话框:
PyDev 更新提示

Word 转手写体代码

首先,我们忽略 GUI,看一下我们的 Word 转手写体代码。首先,在 Eclipse 中新建一个 Python 的项目:
新建Python 项目

主程序

首先,在我们新建的项目中,添加一个 .py 文件,如下所示:
添加一个 .py 文件

背景图像

为了让 Word 转换起来更像是手写体,我们还要添加一个背景,这里用的背景是我们童年的回忆——猫狗本,如下所示。将这个背景图像,命名为 background_01.jpg,并放在我们项目文件的 src(需要自己新建)文件中。
猫狗本背景,命名为 background_01.jpg
放在项目文件中的src文件夹中

字体下载与设置

首先要转换成手写体,我们需要用一个·手写体的字体,再将其加入随机扰动,来模拟手写体的效果。

大家可以从这个网站上下载一些手写体,免费的:

  • 手写体下载地址
  • 推荐一款瘦金体:http://www.zhaozi.cn/html/fonts/china/ruizibige/2020-05-25/26424.html

然后,同样在项目文件夹中,添加一个 font 文件夹。然后,将下载完的手写体字体,放到文件夹中。我大概是下载了这么多的手写体字体,大家可以参考一下:
手写体字体下载图片

主程序啦!!

之后,我们要往这个文件中,加入我们的主要程序代码,如下所示:

from PIL import Image, ImageFont
import numpy as np
from handright import Template, handwrite
from multiprocessing import Pool
import time
text = """
鸟飞鹅跳,月上中梢,目上朱砂,已异非巳,勺旁傍白,万事开头,工戈不全,雨下挚友,称断人和
"""background=Image.open(r'./src/background_01.jpg')
width, height = background.size
background = background.resize((np.int(3*width),np.int(2.5*height)),resample=Image.LANCZOS)if __name__ == "__main__":time_start = time.time()    #计时开始template = Template(background=background,   #选择北京font_size=100,    #选择字体大小font=ImageFont.truetype(r".\font\瘦金简体.ttf"),  #选择字体line_spacing=150,fill=0,  # 字体“颜色”left_margin=250,    #选择字体于背景边缘的间距。top_margin=-30,right_margin=100,bottom_margin=100,word_spacing=15,line_spacing_sigma=6,  # 行间距随机扰动font_size_sigma=1,  # 字体大小随机扰动word_spacing_sigma=3,  # 字间距随机扰动end_chars=",。;、“” ",  # 防止特定字符因排版算法的自动换行而出现在行首perturb_x_sigma=3,  # 笔画横向偏移随机扰动perturb_y_sigma=3,  # 笔画纵向偏移随机扰动perturb_theta_sigma=0.03,  # 笔画旋转偏移随机扰动)with Pool() as p:    #多线程,加快程序的运行。images = handwrite(text, template, mapper=p.map)for i, im in enumerate(images):    #输出结果assert isinstance(im, Image.Image)im.save(r".\output\{}.jpg".format(i))    #将输出结果保存到路径中time_end = time.time()    #计时结束print(time_end-time_start)    #输出运行时间

这里要注意,im.save(r".\output\{}.jpg".format(i)) 将手写体弄成 jpg 图片之后,就会保存到一个叫 output 的文件夹中,并以 0 开始,命名图像。这里的 output 文件夹,要事先创建好,否则报错。当然,也可以添加一条代码来防止这样的情况。这条代码是什么呢?评论区走起~~

代码测试与结果

然后,运行一下我们的代码,会跳出一下错误

File “E:\java-2020-03\eclipse\workspace\Word2write_Principle\principle1.py”, line 7
SyntaxError: Non-UTF-8 code starting with ‘\xc4’ in file E:\java-2020-03\eclipse\workspace\Word2write_Principle\principle1.py on line 8, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

这是因为,我们的代码的第7句,text = """ 鸟飞鹅跳,月上中梢,目上朱砂,已异非巳,勺旁傍白,万事开头,工戈不全,雨下挚友,称断人和 """出现了中文,所以识别不出来。只要在代码的开头,加入一句 #coding=gbk或者# -*- coding:utf-8 -*,再次运行即可。

效果如下:
运行结果
怎么样?心旷神怡对不对?除了有点东倒西歪以外,基本上是满足了我们的需求啊。大家也可以用其他字体,来体验一下效果。这里用李国夫手写体,来实验一下,效果如下:
李国夫手写体效果

链接 Word 文档

我们的需求是,输入一个 Word 文档,再将其转换为手写体。而不是每一次运行,都要设置 text 变量。为了实现这个目的,我们需要另外写一个程序,来读写 Word 文档。

从 Word 文档获取输入的代码

再次新建一个 .py 文件,命名为 principle2.py ,并键入如下代码:

# coding: utf-8
import docx
def read_docx(docx_path = r'./input/demo.docx'):docStr = docx.Document(docx_path)txt = []for para in docStr.paragraphs:    # 如果检测到 word 中有居中的,就在两边加上空格。parStr = para.textif_center = para.paragraph_format.alignmentif if_center:parStr = parStr.center(48)else:parStr = '    ' + parStrtxt.append(parStr)return '\n'.join(txt)if __name__ == "__main__":txt = read_docx()

新建一个 Word 测试文档

为了保证程序的正确运行,我们还需要在项目文件夹中,创建一个新的文件夹,命名为 input。之后,再在里面添加一个 .docx 文件,命名为 demo.docx,输入以下文字:
demo.docx 的内容

代码调试

之后,我们可以运行一下 principle2.py 文件,用 debug 来观察程序是否运行顺畅。首先,在 return 处设置断点,按 F11 进行调试,可以看到程序运行到断点出,停止运行。并在 Varialbe 窗口出现一些变量(右边)
设置断点
我们可以直接在 Variable 窗口,点击 txt,来观察变量 txt 的取值。也可以在 command 窗口,输入变量名 txt,来观察变量的取值。结果如下:

[’ 谜题一个 ‘, ’ 鸟飞鹅跳,月上中梢,目上朱砂,已异非巳,勺旁傍白,万事开头,工戈不全,雨下挚友,称断人和。’, ’ 答案是:—— 我要用自己的方式来爱你!!!’]

之后,在按一下 F5(step into吧,好像是),再按一次(共两次),就可以结束了。有关调试的内容,就不再过多说明了。

代码运行准备

测试完毕,效果可嘉。为了让上述的 txt 变量读入到 principle1.py 中,以供我们的程序使用,我们还要在 principle1.py 文件中,添加一些代码,以保证程序的运行,如下:

......
from principle2 import *
#text = """
#鸟飞鹅跳,月上中梢,目上朱砂,已异非巳,勺旁傍白,万事开头,工戈不全,雨下挚友,称断人和
#"""
......
text = read_docx(r'./input/demo.docx')
......

再次运行 principle1.py,在 console 那里,可以看到程序运行总时间为 4.211 秒,不知道是字数多了,还是程序变复杂的缘故?想知道答案吗?评论区提问一下吧!

来看一下输出结果,在 output 文件夹中,打开图像 0.jpg:
output文件夹0.jpg
课题看到,我们在 docx 文件中,第一行即标题行,并没有用两个回车,但是为什么会空了一行,我们的理想效果应该是这样的:
理想效果
那么,怎么做呢?评论区回答或者提问一下吧!

GUI 设计

从代码中,可以看出,我们需要的输入变量一共有:

text:docx 文件路径或者直接输入
background:背景
fontsize:字体大小
font :字体
line_spacing : 行间距
fill : 字体颜色

当然,还有其他的。为了简化设计,我们将不考虑其他东西~

为此,我们画出 GUI 的草图,如下所示:
草图
草图要有草图的样子,不要笑了[笑哭]。上面的草图是页面1,页面2 我打算弄成一个预览窗口。这个窗口能够展示一个 demo,从而方便我们调整字体大小等参数。

整体代码

我们再次新建一个项目,命名为 Word2write,并在项目文件下面,新建一个 GUI 文件夹,再在文件夹下创建两个 .py 文件: Tab1.py,Tab2.py。

GUI 骨架

至于具体实现,我们大致可以按照如下骨架来弄,如果觉得下图很难看懂,那么就乖乖看一下上面罗列的教程吧:
Tab1 的骨架
Tab2 骨架

文件准备

首先,我们的项目文件已经有 GUI 文件夹,此后还有 Tab1.py Tab2.py
之后,我们还需要创建一个 Fun 文件夹,用来保存一些逻辑功能。我们在下面创建两个文件: fun1.py fun2.py
之后,还需要在项目文件中,创建 Input、Output、Font、Background 文件夹。
在 Backgroun 文件夹中放入背景图片文件:background_01.jpg
并在项目文件中,放入 test.jpg 文件
文件夹

代码

首先是 fun1.py

# coding: utf-8
from PIL import Image, ImageFont
import numpy as np
from handright import Template, handwrite
from multiprocessing import Pool
import time
from Fun.fun2 import *def trans(input_path,output_path,font_path,line_spacing,if_test=False):background=Image.open(r'../Background/background_01.jpg')width, height = background.sizebackground = background.resize((np.int(3*width),np.int(2.5*height)),resample=Image.LANCZOS)if not if_test:text = read_docx(input_path)else:text = """卿寻鲤影剔浮英, 我恨浮英掩玉卿。难教芳心知我心, 孤烛半影又天明。"""time_start = time.time()template = Template(background=background,font_size=100,font=ImageFont.truetype(font_path),line_spacing=line_spacing,fill=0,  # ���塰��ɫ��left_margin=250,top_margin=-30,right_margin=100,bottom_margin=100,word_spacing=15,line_spacing_sigma=6,  # �м������Ŷ�font_size_sigma=1,  # �����С����Ŷ�word_spacing_sigma=3,  # �ּ������Ŷ�end_chars=",。;、“”",  # ��ֹ�ض��ַ����Ű��㷨���Զ����ж�����������perturb_x_sigma=3,  # �ʻ�����ƫ������Ŷ�perturb_y_sigma=3,  # �ʻ�����ƫ������Ŷ�perturb_theta_sigma=0.03,  # �ʻ���תƫ������Ŷ�)with Pool() as p:images = handwrite(text, template,mapper=p.map)for i, im in enumerate(images):assert isinstance(im, Image.Image)if not if_test:im.save(output_path+"\{}.jpg".format(i))else:im.save(r"../test.jpg")time_end = time.time()print(time_end-time_start)

然后是 fun2.py

import docx
def read_docx(docx_path):docStr = docx.Document(docx_path)txt = []for para in docStr.paragraphs:parStr = para.textif_center = para.paragraph_format.alignmentif if_center:parStr = parStr.center(30)else:parStr = '    ' + parStrtxt.append(parStr)return '\n'.join(txt)if __name__ == "__main__":txt = read_docx()

然后是 Tab1.py:

# -*- coding: utf-8 -*-import tkinter as tk
from tkinter import ttk
from tkinter import messagebox as mBox
from tkinter import Menu
from sys import exit
from threading import Thread
from time import sleep
from queue import Queue
import os
from os import path
from tkinter import filedialog as fd
from Tab2 import Tab2
import sys
sys.path.append('../')
from PIL import Image, ImageFont
import numpy as np
from handright import Template, handwrite
from multiprocessing import Pool
import time
from Fun.fun1 import *
from PIL import ImageTk,Imageclass OOP():def __init__(self):self.win = tk.Tk()self.win.title('妙笔生花')self.win.iconbitmap(r'./app.ico')self._createWidget()def _runTrans(self,input_path,output_path,font_path,line_spacing): trans(input_path,output_path,font_path,line_spacing)def _testTrans(self,input_path,output_path,font_path,line_spacing): trans(input_path,output_path,font_path,line_spacing,if_test=True)self.tab2Widget.createImage()self.tab2Widget._image = ImageTk.PhotoImage(self.tab2Widget.pil_image)self.tab2Widget.canvas.create_image(20,20,anchor='nw',image=self.tab2Widget._image)def createRunThread(self):input_path = str(self.inEntry.get())output_path = str(self.outEntry.get())font_path = str(self.fntEntry.get())  line_spacing = float(str(self.lineSpcEty.get())) run = Thread(target=self._runTrans,args=(input_path,output_path,font_path,line_spacing))    #创建一个线程,线程运行 methodInAThreadrun.setDaemon(True)    #将线程设置成守护线程run.start()def createTestThread(self):input_path = str(self.inEntry.get())output_path = str(self.outEntry.get())font_path = str(self.fntEntry.get())  line_spacing = float(str(self.lineSpcEty.get())) test = Thread(target=self._testTrans,args=(input_path,output_path,font_path,line_spacing))test.setDaemon(True)test.start()def _clickRunBut(self):self.createRunThread()def _clickTstBut(self):self.createTestThread()def _getFileName(self):fDir = os.path.join( os.path.dirname(__file__),'../..')  #���ϼ��ļ�Ŀ¼��fName = fd.askopenfilename(parent=self.inOuFrm,initialdir=fDir)fPath = path.dirname(fName)self.inEntry.delete(0,tk.END)   self.inEntry.insert(0,fName)   def _getFileName2(self):fDir = os.path.join( os.path.dirname(__file__),'..')  #���ϼ��ļ�Ŀ¼��fName = fd.askdirectory(parent=self.inOuFrm,initialdir=fDir)fPath = path.dirname(fName)self.outEntry.delete(0,tk.END)   self.outEntry.insert(0,fName)   def _getFileName3(self):fDir = os.path.join( os.path.dirname(__file__),'..')  #���ϼ��ļ�Ŀ¼��fName = fd.askopenfilename(parent=self.inOuFrm,initialdir=fDir)fPath = path.dirname(fName)self.fntEntry.delete(0,tk.END)   self.fntEntry.insert(0,fName) def _quit(self):self.win.quit()self.win.destroy()exit()def _createWidget(self):self.menuBar = Menu(self.win)self.win.configure(menu=self.menuBar)self.startMenu = Menu(self.menuBar,tearoff=0)self.startMenu.add_command(label='保存为')self.startMenu.add_separator()self.startMenu.add_command(label='退出',command=self._quit)self.menuBar.add_cascade(label='开始',menu=self.startMenu)self.helpMenu = Menu(self.menuBar,tearoff=0)self.helpMenu.add_command(label='帮助')self.helpMenu.add_command(label='关于')self.menuBar.add_cascade(label='其他',menu=self.helpMenu)self.tabControl = ttk.Notebook(self.win)self.tab1 = ttk.LabelFrame(self.tabControl)self.tab2 = ttk.LabelFrame(self.tabControl)self.tabControl.add(self.tab1,text='参数设置')self.tabControl.add(self.tab2,text='效果预览')self.tabControl.pack(fill='both',expand=1)self.zhuo = ttk.Frame(self.tab1)self.zhuo.grid(column=0,row=0)self.inOuFrm = ttk.LabelFrame(self.zhuo,text='文件管理')self.inOuFrm.grid(column=0,row=0,sticky='W')self.inBut = ttk.Button(self.inOuFrm,text='输入文件',command=self._getFileName)self.inBut.grid(column=0,row=0)self.fDir = os.path.abspath(os.path.join( os.path.dirname(__file__),".."))self.default_value = tk.StringVar()self.default_value.set(self.fDir+'\Input\demo.docx')self.default_value2 = tk.StringVar()self.default_value2.set(self.fDir+'\Output')self.default_value3 = tk.StringVar()self.default_value3.set(self.fDir+'\Font\瘦金简体.ttf')self.inEntry = ttk.Entry(self.inOuFrm,width=60,textvariable=self.default_value)self.inEntry.grid(column=1,row=0)self.inEntry.focus()self.outBut = ttk.Button(self.inOuFrm,text='输出文件夹',command=self._getFileName2)self.outBut.grid(column=0,row=1)self.outEntry = ttk.Entry(self.inOuFrm,width=60,textvariable=self.default_value2)self.outEntry.grid(column=1,row=1)self.fntBut = ttk.Button(self.inOuFrm,text='搜索字体',command=self._getFileName3)self.fntBut.grid(column=0,row=2)self.fntEntry = ttk.Entry(self.inOuFrm,width=60,textvariable=self.default_value3)self.fntEntry.grid(column=1,row=2)for child in self.inOuFrm.winfo_children():child.grid_configure(padx=10,pady=10,sticky='W')self.miniCon = tk.Frame(self.zhuo)self.miniCon.grid(column=0,row=1,sticky='W')ttk.Label(self.miniCon,text='字体大小').grid(column=0,row=0)self.fntSizeCom = ttk.Combobox(self.miniCon)self.fntSizeCom['value']=(80,90,100)self.fntSizeCom.grid(column=1,row=0)self.fntSizeCom.current(2)ttk.Label(self.miniCon,text='字体颜色').grid(column=2,row=0)self.fntColCom = ttk.Combobox(self.miniCon)self.fntColCom['value']=(0)self.fntColCom.grid(column=3,row=0,sticky='W')self.fntColCom.current(0)ttk.Label(self.miniCon,text='行间距').grid(column=0,row=1)self.default_value4 = tk.StringVar()self.default_value4.set(150)self.lineSpcEty = ttk.Entry(self.miniCon,textvariable=self.default_value4)self.lineSpcEty.grid(column=1,row=1)    self.runBut = ttk.Button(self.miniCon,text='转换',command=self._clickRunBut)self.runBut.grid(column=0,row=2)self.testBut = ttk.Button(self.miniCon,text='测试',command=self._clickTstBut)self.testBut.grid(column=1,row=2)for child in self.miniCon.winfo_children():child.grid_configure(padx=10,pady=10,sticky='W')        """页面2开发"""self.tab2Widget = Tab2(self.tab2)  
oop = OOP()
oop.win.mainloop()

然后是Tab2.py

# -*- coding: utf-8 -*-import tkinter as tk
from tkinter import ttkfrom PIL import ImageTk,Image
class Tab2():def __init__(self,tab):self._createWidget(tab)def _createWidget(self,tab):self.zhuo = tk.Frame(tab,bg='red')self.zhuo.grid(column=0,row=0)self.canvas = tk.Canvas(self.zhuo)self.createImage()self.image = ImageTk.PhotoImage(self.pil_image)    #这里就不是 tk.PhotoImage 了self.canvas.create_image(20,20,anchor='nw',image=self.image)    #打开图像self.canvas.grid(row=0,column=0,columnspan=2)def _resize(self,w, h, w_box, h_box, pil_image):  f1 = 1.0*w_box/w f2 = 1.0*h_box/h  factor = min([f1, f2])  width = int(w*factor)  height = int(h*factor)  self.pil_image = pil_image.resize((width, height), Image.ANTIALIAS) def createImage(self):        pil_image = Image.open(r'../test.jpg')    #记得在缩放图像之前,要用 Image 模块打开。w, h = pil_image.sizew_box = 400   #大伙可以调节这个,来设置图片的大小。当然,也建议读者们把他设为 公有的。h_box = 500self._resize(w,h,w_box,h_box,pil_image)

运行 Tab1.py,再点击运行按钮时,会出现多个弹窗的情况,如下所示:
在这里插入图片描述
而我们要的效果是,点击后不能出现这样的弹窗。原因在哪里呢?,请到评论区提问或回答,我会回复的哦。

软件的使用与后续开发

使用效果

解决完上述弹窗问题之后,我们就来验证一下我们的软件吧:

首先运行 Tab1.py,就会弹出软件窗口,再点击测试,并在效果预览页面观察效果图,如下所示:
在这里插入图片描述
也可以修改字体,点击搜索字体,选择李国夫手写体,再点测试:
在这里插入图片描述
在这里插入图片描述
在 Input 中新建一个 demo.docx 文件,并任意输入内容,我这里输入的是:
在这里插入图片描述
然后,点击转换,就可以在 Input 文件夹中,生成一个 0.jpg 的文件,就可以啦:
在这里插入图片描述

后续完善

其实还有很多要完善的地方。比如运行的时候,显示进度条。而且还要提高运行效率。除此之外,一些参数的设置要更加完善。如果大家喜欢这个软件的话,可以关注我。我把 Github 源码链接发给你们,咱们一起开发和完善。

另外,还要转换成 exe 文件,这里就不再展示了。大家可以参考上面罗列的教程。

为了提高效率、以及实用性,我们还需要用 Java 来开发一个一模一样的。


http://www.ppmy.cn/news/356450.html

相关文章

Microsoft Teams免费版本初体验

Microsoft Teams推出有一段时间了,如果想要体验Teams,必须需要有Office365的订阅。最近微软为了进一步推广Teams,突然宣布Teams免费了。使用过Teams的读者知道Teams是基于Office365账号和组的,那它免费后,不使用Office…

vsto与word基础介绍-新建第一个word加载项〔1〕

系列文章目录 专栏目录:专栏目录 上节链接:word加载项 vsto从入门到精通–专栏目录 vs2019社区版:VS2019社区版免费下载 转载或转发请注明出处,有问题请私聊博主! 文章目录 系列文章目录一、word简介1、word窗口简介2…

mathtype7.4免费用 + office2019 成功配置过程

1.安装mathtype 我们直接去官网下载最新版本的mathtype即可 官网 关闭word,之后我们正常安装,注意记住安装路径,一会需要进入安装路径的文件 安装完成后,我们打开你安装的路径,新建一个文本文件(.txt&…

word在html中预览,在网页中预览word和excel

毫无疑问,若想在网页中预览word文档和excel表格,必然先将其转换成html格式。在windows office中是可以将word和excel另存为html文件的,但是由于正版需要购买和安装体积较大的原因对于项目来说部署成本有点高了,而且只能运行在windows之上。这里给大家推荐一个更易用的东西:…

python读入excel,进行excel处理的基础操作

1.合并多个Excel文件的sheet 可以使用Python中的pandas库来读取和合并多个Excel文件的sheet。 首先需要安装pandas库,可以使用以下命令进行安装: pip install pandas 接下来,可以使用以下代码来读取和合并多个Excel文件的sheet&#xff1a…

weboffice 6版本实现在线word

公司最近需要开发一个在线word功能,开始用pageoffice开发的功能被否决因为pageoffice的版权问题,后采用点聚weboffice的免费版开发.因为多个页面需要使用在线word功能,对于weboffice提供的activeX对象进行一定的封装,减少代码的冗余 和开发时的方便. 一 简单分析 weboffice是…

基于BES DSP 音频EQ算法C程序实现

+ hezkz17进数字音频系统研究开发交流答疑 用C语言如何写一个音频EQ 算法程序? 2023/6/12 下午11:45:07 下面是一个简单的用 C 语言写音频 EQ 算法程序的 例子: 1该程序使用了 C 语言实现了一个基本的均衡器。代码分为以下几个部分: 2打开输入文件:使用 fopen 函数打开音…

kotlin协程flow retry功能函数返回失败后重试(4)

kotlin协程flow retry功能函数返回失败后重试&#xff08;4&#xff09; import kotlinx.coroutines.delay import kotlinx.coroutines.flow.* import kotlinx.coroutines.runBlockingfun main(args: Array<String>) {var count 0 //重试计数runBlocking {load().onEach…