前段时间换工作,在BOSS直聘上看到一个有意思的功能,上传docx格式的简历,能够以图片的形式在线预览,并且图片还添加了水印,笔者对此比较感兴趣,就摸索着实现方法。网上逛了一圈,对于docx直接转图片这种转换没有什么有效的信息,不过结合获取的信息,笔者倒是想了一个曲线救国的法子,docx->pdf, pdf->image,通过两步可以最终实现该转换。

一、环境准备

首先进行环境准备工作,把需要用到的工具都安装配置好:

1、安装pandoc

Pandoc是由John MacFarlane开发的标记语言转换工具,可实现不同标记语言间的格式转换,堪称该领域中的“瑞士军刀”。该工具使用Haskell语言编写,以命令行形式实现与用户的交互,可支持多种操作系统。简而言之,pandoc就是一款转换工具,可以把格式A的文档转换为格式B的文档,就本文的目标来讲,主要用它实现了docx->pdf的转换。

根据系统类别选择对应的安装包,安装完成后将其安装目录添加到系统路径中,windows版本的安装完成后会自动添加,不用额外设置。

2、安装miktex

MikTex是一款基于Tex的排版引擎。TeX 在不同的硬件和操作系统上有不同的实现版本。这就像C语言,在不同的操作系统中有不同的编译系统,例如Linux 下的gcc,Windows 下的Visual C++ 等。有时,一种操作系统里也会有好几种的TeX系统。目前常见的Unix/Linux 下的TeX系统是Texlive,Windows 下则有MiKTeX和fpTeX。CTeX指的是CTeX中文套装的简称,是把MiKTeX和一些常用的相关工具,如GSview,WinEdt 等包装在一起制作的一个简易安装程序,并对其中的中文支持部分进行了配置,使得安装后马上就可以使用中文。

该排版工具是docx->pdf转换时不可获取的,否则无法实现转换。

下载地址:https://miktex.org/   根据系统类别选择合适的版本,安装完成后将安装目录添加到系统路径。

3、安装poppler

代码实现中引用了一个开源库pdf2image,而这个库又引用了poppler,所以需要安装该环境。Poppler是一个基于xpdf-3.0的用于操作pdf的库,其中poppler-utils是建立在Poppler的库API上的工具集合,用于管理PDF和提取内容,主要工具如下:

pdfdetach – 从PDF中提取嵌入式文档;

pdffonts – 列出PDF中使用的字体;

pdfimages – 从PDF中以原始分辨率提取所有嵌入式图像;

pdfinfo – 列出PDF的所有信息;

pdfseparate – 从PDF中提取单个页面;

pdftocairo – 使用cairo将单页从PDF转换为矢量或位图格式;

pdftohtml – 将PDF转换为HTML格式的保留格式;

pdftoppm – 将PDF页面转换为位图;

pdftops – 将PDF转换为可打印的PS格式;

pdftotext – 从PDF中提取所有文本;

pdfunite – 合并几个PDF。

pdf2image使用了pdftoppm和pdfinfo这两个工具,用来将pdf转换为图片和获取pdf的文档页数,代码稍后会列出。

下载地址:https://github.com/Belval/pdf2image  根据系统类别选择对应的版本,安装完成后将安装目录下的bin目录添加到系统路径。

4、字体下载

用来生成水印时使用,笔者下载的是SimSun宋体,下载地址:https://www.fontke.com/font/10132367/download/,也可以下载其他字体,看个人选择。

完成上边四步环境就配置好了,笔者的电脑是Windows10,当然以上工具同样支持linux和mac环境,每个工具的官档都标注了支持的系统版本。环境配置好后接下来就是coding环节了,通过程序来实现转换过程的自动化调用,笔者会列出代码的目录结构,并依次说明其用途。

二、代码实现

关于实现这部分,笔者使用的开发语言是python36,以下为目录结构:

1、font目录

该目录存放的是下载好的字体文件,供代码中引用。

2、pdf目录

该目录存放转换之后的pdf文件。

3、target目录

该目录存放最终转换后的图片,图片为以时间戳命名的png图片,格式可以指定。

4、config.py

配置文件,存放了目录、图片大小、水印等配置,代码如下:

Python

import os

# 根目录

BASE_DIR = os.path.dirname(__file__)

# pdf默认输出路径

DEFAULT_OUTPUT_DIR = os.path.join(BASE_DIR, 'pdf')

# 最终生成的图片存放在此目录

TARGET_DIR = os.path.join(BASE_DIR, 'target')

# 水印字体

FONT_FILE = os.path.join(BASE_DIR, "font/simsun.ttf")

# 单张图片的宽高,PIL支持图片的最大宽度1700

IMAGE_WIDTH = 1700

IMAGE_HEIGHT = 2200

# 水印位置-left和top

WATERMARK_LEFT = 0

WATERMARK_TOP = 1800

# 水印图片背景色-白色透明,最后一位表示透明度0~255,0为透明

BACKGROUND_COLOR = (255, 255, 255, 0)

#水印字体颜色-红色 60透明度

FONT_COLOR = (255, 0, 0, 60)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

importos

# 根目录

BASE_DIR=os.path.dirname(__file__)

# pdf默认输出路径

DEFAULT_OUTPUT_DIR=os.path.join(BASE_DIR,'pdf')

# 最终生成的图片存放在此目录

TARGET_DIR=os.path.join(BASE_DIR,'target')

# 水印字体

FONT_FILE=os.path.join(BASE_DIR,"font/simsun.ttf")

# 单张图片的宽高,PIL支持图片的最大宽度1700

IMAGE_WIDTH=1700

IMAGE_HEIGHT=2200

# 水印位置-left和top

WATERMARK_LEFT=0

WATERMARK_TOP=1800

# 水印图片背景色-白色透明,最后一位表示透明度0~255,0为透明

BACKGROUND_COLOR=(255,255,255,0)

#水印字体颜色-红色 60透明度

FONT_COLOR=(255,0,0,60)

5、doc2pdf.py

该模块封装了pandoc命令行工具的调用,实现了docx文档到pdf的转换过程

Python

import os

import uuid

from subprocess import Popen, PIPE

def _load_doc(doc):

doc = str(doc)

if not os.path.exists(doc):

raise FileNotFoundError("file {} not found".format(doc))

_, doc_name = os.path.split(doc)

if not doc_name.endswith('.docx'):

raise TypeError("Only support word doc with suffix '.docx'")

return doc

def _build_command(doc_path, output_dir, pdf_name=None, pdf_engine=None):

args = ['pandoc', '--pdf-engine=xelatex']

# args = ['pandoc', '--pdf-engine=lualatex']

if pdf_engine is not None:

# MikTex包含了两种pandoc支持的Tex

if pdf_engine not in ("xelatex", "lualatex"):

raise ValueError("not supported pdf-engine: {}".format(pdf_engine))

else:

args[1] = '--pdf-engine={}'.format(pdf_engine)

if pdf_name is None:

pdf_name = str(uuid.uuid4()) + '.pdf'

if not os.path.isdir(output_dir):

raise NotADirectoryError("{} is not a existed directory".format(output_dir))

pdf_path = os.path.join(output_dir, pdf_name)

args.append('-o')

args.append(pdf_path)

args.append(doc_path)

# 设置字体避免中文乱码无法输出问题,SimSun是宋体, MicrosoftYaHei是微软雅黑

args.append('-V')

args.append('mainfont="MicrosoftYaHei"')

# args.append('-V')

# args.append('margin-left=0in')

return args, pdf_path

def doc2pdf(source, pdf_path):

doc_path = _load_doc(source)

cmd, output_pdf = _build_command(doc_path, pdf_path)

# run cmd

print("doc to pdf, converting...")

Popen(cmd, stderr=PIPE, stdout=PIPE).communicate()

print("job done!")

return output_pdf

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

importos

importuuid

fromsubprocessimportPopen,PIPE

def_load_doc(doc):

doc=str(doc)

ifnotos.path.exists(doc):

raiseFileNotFoundError("file {} not found".format(doc))

_,doc_name=os.path.split(doc)

ifnotdoc_name.endswith('.docx'):

raiseTypeError("Only support word doc with suffix '.docx'")

returndoc

def_build_command(doc_path,output_dir,pdf_name=None,pdf_engine=None):

args=['pandoc','--pdf-engine=xelatex']

# args = ['pandoc', '--pdf-engine=lualatex']

ifpdf_engineisnotNone:

# MikTex包含了两种pandoc支持的Tex

ifpdf_enginenotin("xelatex","lualatex"):

raiseValueError("not supported pdf-engine: {}".format(pdf_engine))

else:

args[1]='--pdf-engine={}'.format(pdf_engine)

ifpdf_nameisNone:

pdf_name=str(uuid.uuid4())+'.pdf'

ifnotos.path.isdir(output_dir):

raiseNotADirectoryError("{} is not a existed directory".format(output_dir))

pdf_path=os.path.join(output_dir,pdf_name)

args.append('-o')

args.append(pdf_path)

args.append(doc_path)

# 设置字体避免中文乱码无法输出问题,SimSun是宋体, MicrosoftYaHei是微软雅黑

args.append('-V')

args.append('mainfont="MicrosoftYaHei"')

# args.append('-V')

# args.append('margin-left=0in')

returnargs,pdf_path

defdoc2pdf(source,pdf_path):

doc_path=_load_doc(source)

cmd,output_pdf=_build_command(doc_path,pdf_path)

# run cmd

print("doc to pdf, converting...")

Popen(cmd,stderr=PIPE,stdout=PIPE).communicate()

print("job done!")

returnoutput_pdf

6、pdf2image.py

一个开源的python库,只包含了一个文件,笔者直接复制过来使用的。主要封装了poppler的命令行调用,实现pdf到图片的转换。

Python

"""

pdf2image is a light wrapper for the poppler-utils tools that can convert your

PDFs into Pillow images.

"""

import os

import re

import tempfile

import uuid

from io import BytesIO

from subprocess import Popen, PIPE

from PIL import Image

def convert_from_path(pdf_path, dpi=200, output_folder=None, first_page=None, last_page=None, fmt='ppm', thread_count=1, userpw=None):

"""

Description: Convert PDF to Image will throw whenever one of the condition is reached

Parameters:

pdf_path -> Path to the PDF that you want to convert

dpi -> Image quality in DPI (default 200)

output_folder -> Write the resulting images to a folder (instead of directly in memory)

first_page -> First page to process

last_page -> Last page to process before stopping

fmt -> Output image format

thread_count -> How many threads we are allowed to spawn for processing

userpw -> PDF's password

"""

page_count = __page_count(pdf_path, userpw)

if thread_count < 1:

thread_count = 1

if first_page is None:

first_page = 1

if last_page is None or last_page > page_count:

last_page = page_count

# Recalculate page count based on first and last page

page_count = last_page - first_page + 1

if thread_count > page_count:

thread_count = page_count

reminder = page_count % thread_count

current_page = first_page

processes = []

for _ in range(thread_count):

# A unique identifier for our files if the directory is not empty

uid = str(uuid.uuid4())

# Get the number of pages the thread will be processing

thread_page_count = page_count // thread_count + int(reminder > 0)

# Build the command accordingly

args, parse_buffer_func = __build_command(['pdftoppm', '-r', str(dpi), pdf_path], output_folder, current_page, current_page + thread_page_count - 1, fmt, uid, userpw)

# Update page values

current_page = current_page + thread_page_count

reminder -= int(reminder > 0)

# Spawn the process and save its uuid

processes.append((uid, Popen(args, stdout=PIPE, stderr=PIPE)))

images = []

for uid, proc in processes:

data, _ = proc.communicate()

if output_folder is not None:

images += __load_from_output_folder(output_folder, uid)

else:

images += parse_buffer_func(data)

return images

def convert_from_bytes(pdf_file, dpi=200, output_folder=None, first_page=None, last_page=None, fmt='ppm', thread_count=1, userpw=None):

"""

Description: Convert PDF to Image will throw whenever one of the condition is reached

Parameters:

pdf_file -> Bytes representing the PDF file

dpi -> Image quality in DPI

output_folder -> Write the resulting images to a folder (instead of directly in memory)

first_page -> First page to process

last_page -> Last page to process before stopping

fmt -> Output image format

thread_count -> How many threads we are allowed to spawn for processing

userpw -> PDF's password

"""

with tempfile.NamedTemporaryFile('wb') as f:

f.write(pdf_file)

f.flush()

return convert_from_path(f.name, dpi=dpi, output_folder=output_folder, first_page=first_page, last_page=last_page, fmt=fmt, thread_count=thread_count, userpw=userpw)

def pdf_page_count(pdf, password=None):

return __page_count(pdf, password)

def __build_command(args, output_folder, first_page, last_page, fmt, uid, userpw):

if first_page is not None:

args.extend(['-f', str(first_page)])

if last_page is not None:

args.extend(['-l', str(last_page)])

parsed_format, parse_buffer_func = __parse_format(fmt)

if parsed_format != 'ppm':

args.append('-' + parsed_format)

if output_folder is not None:

args.append(os.path.join(output_folder, uid))

if userpw is not None:

args.extend(['-upw', userpw])

return args, parse_buffer_func

def __parse_format(fmt):

if fmt[0] == '.':

fmt = fmt[1:]

if fmt == 'jpeg' or fmt == 'jpg':

return 'jpeg', __parse_buffer_to_jpeg

if fmt == 'png':

return 'png', __parse_buffer_to_png

# Unable to parse the format so we'll use the default

return 'ppm', __parse_buffer_to_ppm

def __parse_buffer_to_ppm(data):

images = []

index = 0

while index < len(data):

code, size, rgb = tuple(data[index:index + 40].split(b'\n')[0:3])

size_x, size_y = tuple(size.split(b' '))

file_size = len(code) + len(size) + len(rgb) + 3 + int(size_x) * int(size_y) * 3

images.append(Image.open(BytesIO(data[index:index + file_size])))

index += file_size

return images

def __parse_buffer_to_jpeg(data):

return [

Image.open(BytesIO(image_data + b'\xff\xd9'))

for image_data in data.split(b'\xff\xd9')[:-1] # Last element is obviously empty

]

def __parse_buffer_to_png(data):

images = []

index = 0

while index < len(data):

file_size = data[index:].index(b'IEND') + 8 # 4 bytes for IEND + 4 bytes for CRC

images.append(Image.open(BytesIO(data[index:index+file_size])))

index += file_size

return images

def __page_count(pdf_path, userpw=None):

try:

if userpw is not None:

proc = Popen(["pdfinfo", pdf_path, '-upw', userpw], stdout=PIPE, stderr=PIPE)

else:

proc = Popen(["pdfinfo", pdf_path], stdout=PIPE, stderr=PIPE)

out, err = proc.communicate()

except:

raise Exception('Unable to get page count. Is poppler installed and in PATH?')

try:

# This will throw if we are unable to get page count

return int(re.search(r'Pages:\s+(\d+)', out.decode("utf8", "ignore")).group(1))

except:

raise Exception('Unable to get page count. %s' % err.decode("utf8", "ignore"))

def __load_from_output_folder(output_folder, uid):

return [Image.open(os.path.join(output_folder, f)) for f in sorted(os.listdir(output_folder)) if uid in f]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

"""

pdf2image is a light wrapper for the poppler-utils tools that can convert your

PDFs into Pillow images.

"""

importos

importre

importtempfile

importuuid

fromioimportBytesIO

fromsubprocessimportPopen,PIPE

fromPILimportImage

defconvert_from_path(pdf_path,dpi=200,output_folder=None,first_page=None,last_page=None,fmt='ppm',thread_count=1,userpw=None):

"""

Description: Convert PDF to Image will throw whenever one of the condition is reached

Parameters:

pdf_path -> Path to the PDF that you want to convert

dpi -> Image quality in DPI (default 200)

output_folder -> Write the resulting images to a folder (instead of directly in memory)

first_page -> First page to process

last_page -> Last page to process before stopping

fmt -> Output image format

thread_count -> How many threads we are allowed to spawn for processing

userpw -> PDF's password

"""

page_count=__page_count(pdf_path,userpw)

ifthread_count<1:

thread_count=1

iffirst_pageisNone:

first_page=1

iflast_pageisNoneorlast_page>page_count:

last_page=page_count

# Recalculate page count based on first and last page

page_count=last_page-first_page+1

ifthread_count>page_count:

thread_count=page_count

reminder=page_count%thread_count

current_page=first_page

processes=[]

for_inrange(thread_count):

# A unique identifier for our files if the directory is not empty

uid=str(uuid.uuid4())

# Get the number of pages the thread will be processing

thread_page_count=page_count//thread_count+int(reminder>0)

# Build the command accordingly

args,parse_buffer_func=__build_command(['pdftoppm','-r',str(dpi),pdf_path],output_folder,current_page,current_page+thread_page_count-1,fmt,uid,userpw)

# Update page values

current_page=current_page+thread_page_count

reminder-=int(reminder>0)

# Spawn the process and save its uuid

processes.append((uid,Popen(args,stdout=PIPE,stderr=PIPE)))

images=[]

foruid,procinprocesses:

data,_=proc.communicate()

ifoutput_folderisnotNone:

images+=__load_from_output_folder(output_folder,uid)

else:

images+=parse_buffer_func(data)

returnimages

defconvert_from_bytes(pdf_file,dpi=200,output_folder=None,first_page=None,last_page=None,fmt='ppm',thread_count=1,userpw=None):

"""

Description: Convert PDF to Image will throw whenever one of the condition is reached

Parameters:

pdf_file -> Bytes representing the PDF file

dpi -> Image quality in DPI

output_folder -> Write the resulting images to a folder (instead of directly in memory)

first_page -> First page to process

last_page -> Last page to process before stopping

fmt -> Output image format

thread_count -> How many threads we are allowed to spawn for processing

userpw -> PDF's password

"""

withtempfile.NamedTemporaryFile('wb')asf:

f.write(pdf_file)

f.flush()

returnconvert_from_path(f.name,dpi=dpi,output_folder=output_folder,first_page=first_page,last_page=last_page,fmt=fmt,thread_count=thread_count,userpw=userpw)

defpdf_page_count(pdf,password=None):

return__page_count(pdf,password)

def__build_command(args,output_folder,first_page,last_page,fmt,uid,userpw):

iffirst_pageisnotNone:

args.extend(['-f',str(first_page)])

iflast_pageisnotNone:

args.extend(['-l',str(last_page)])

parsed_format,parse_buffer_func=__parse_format(fmt)

ifparsed_format!='ppm':

args.append('-'+parsed_format)

ifoutput_folderisnotNone:

args.append(os.path.join(output_folder,uid))

ifuserpwisnotNone:

args.extend(['-upw',userpw])

returnargs,parse_buffer_func

def__parse_format(fmt):

iffmt[0]=='.':

fmt=fmt[1:]

iffmt=='jpeg'orfmt=='jpg':

return'jpeg',__parse_buffer_to_jpeg

iffmt=='png':

return'png',__parse_buffer_to_png

# Unable to parse the format so we'll use the default

return'ppm',__parse_buffer_to_ppm

def__parse_buffer_to_ppm(data):

images=[]

index=0

whileindex

code,size,rgb=tuple(data[index:index+40].split(b'\n')[0:3])

size_x,size_y=tuple(size.split(b' '))

file_size=len(code)+len(size)+len(rgb)+3+int(size_x)*int(size_y)*3

images.append(Image.open(BytesIO(data[index:index+file_size])))

index+=file_size

returnimages

def__parse_buffer_to_jpeg(data):

return[

Image.open(BytesIO(image_data+b'\xff\xd9'))

forimage_dataindata.split(b'\xff\xd9')[:-1]# Last element is obviously empty

]

def__parse_buffer_to_png(data):

images=[]

index=0

whileindex

file_size=data[index:].index(b'IEND')+8# 4 bytes for IEND + 4 bytes for CRC

images.append(Image.open(BytesIO(data[index:index+file_size])))

index+=file_size

returnimages

def__page_count(pdf_path,userpw=None):

try:

ifuserpwisnotNone:

proc=Popen(["pdfinfo",pdf_path,'-upw',userpw],stdout=PIPE,stderr=PIPE)

else:

proc=Popen(["pdfinfo",pdf_path],stdout=PIPE,stderr=PIPE)

out,err=proc.communicate()

except:

raiseException('Unable to get page count. Is poppler installed and in PATH?')

try:

# This will throw if we are unable to get page count

returnint(re.search(r'Pages:\s+(\d+)',out.decode("utf8","ignore")).group(1))

except:

raiseException('Unable to get page count. %s'%err.decode("utf8","ignore"))

def__load_from_output_folder(output_folder,uid):

return[Image.open(os.path.join(output_folder,f))forfinsorted(os.listdir(output_folder))ifuidinf]

7、main.py

入口主程序,传入doc文档,得到png图片^_^

Python

import os

import time

from PIL import Image

from PIL import ImageFont

from PIL import ImageDraw

from config import *

from pdf2image import convert_from_path, pdf_page_count

from doc2pdf import doc2pdf

def doc2image(doc, fmt='png', output_dir=None, watermark=None, fpage=None, lpage=None):

if output_dir is None:

output_dir = TARGET_DIR

target_name = os.path.join(output_dir, "{}.{}".format(int(time.time()), fmt))

temp_pdf = doc2pdf(doc, pdf_path=DEFAULT_OUTPUT_DIR)

if fpage is None:

fpage = 1

if lpage is not None:

page_count = lpage - fpage + 1

else:

page_count = pdf_page_count(temp_pdf)

print("pdf to image, converting...")

to_image = _convert_pdf_to_image(temp_pdf, page_count, fmt, fpage, lpage)

if watermark is not None:

watermark_image = _make_watermark_image(to_image.size, watermark, page_count)

# 合并内容图片和水印图片

out = Image.alpha_composite(to_image, watermark_image)

# out.show()

# 转换为RGB模式才可保存为图片

out.convert("RGB").save(target_name)

else:

to_image.save(target_name)

print("job done!")

def _convert_pdf_to_image(pdf, page_count, fmt, f, l, save=False):

'''

params:

pdf: 待转换的pdf文件路径

page_count: 要转换的页数

fmt: 转换的图片格式

f: 要转换的开始页数

l: 结束页数

save: 是否保留pdf文件,默认不保留

'''

# 根据页数转换为相应数量的图片

image_list = convert_from_path(pdf, fmt=fmt, first_page=f, last_page=l)

target_name = os.path.join(TARGET_DIR, "{}.{}".format(int(time.time()), fmt))

to_image = Image.new('RGBA', (IMAGE_WIDTH, page_count * IMAGE_HEIGHT))

for i, image in enumerate(image_list):

# 计算高度,拼合单张图片到一整张图片上

loc = (0, i * IMAGE_HEIGHT)

to_image.paste(image, loc)

if not save:

try:

os.remove(pdf)

except Exception as e:

print("fail to remove pdf, please check and remove it manually")

return to_image

def _make_watermark_image(image_size, watermark, count=1):

'''

生成水印图片

params:

image_size: 水印图片大小,与底板图片大小一致

watermark: 水印文本

count: 水印数量, 与内容页数保持一致

'''

fnt = ImageFont.truetype(font=FONT_FILE, size=80)

watermark_image = Image.new('RGBA', image_size, color=BACKGROUND_COLOR)

draw = ImageDraw.Draw(watermark_image)

for i in range(1, count+1):

draw.multiline_text((WATERMARK_LEFT, WATERMARK_TOP * i), watermark, font=fnt, fill=FONT_COLOR)

return watermark_image

if __name__ == '__main__':

txt = "玩点coding https://vdcoding.com"

doc = "resume.docx"

doc2image(doc, watermark=txt)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

importos

importtime

fromPILimportImage

fromPILimportImageFont

fromPILimportImageDraw

fromconfigimport*

frompdf2imageimportconvert_from_path,pdf_page_count

fromdoc2pdfimportdoc2pdf

defdoc2image(doc,fmt='png',output_dir=None,watermark=None,fpage=None,lpage=None):

ifoutput_dirisNone:

output_dir=TARGET_DIR

target_name=os.path.join(output_dir,"{}.{}".format(int(time.time()),fmt))

temp_pdf=doc2pdf(doc,pdf_path=DEFAULT_OUTPUT_DIR)

iffpageisNone:

fpage=1

iflpageisnotNone:

page_count=lpage-fpage+1

else:

page_count=pdf_page_count(temp_pdf)

print("pdf to image, converting...")

to_image=_convert_pdf_to_image(temp_pdf,page_count,fmt,fpage,lpage)

ifwatermarkisnotNone:

watermark_image=_make_watermark_image(to_image.size,watermark,page_count)

# 合并内容图片和水印图片

out=Image.alpha_composite(to_image,watermark_image)

# out.show()

# 转换为RGB模式才可保存为图片

out.convert("RGB").save(target_name)

else:

to_image.save(target_name)

print("job done!")

def_convert_pdf_to_image(pdf,page_count,fmt,f,l,save=False):

'''

params:

pdf: 待转换的pdf文件路径

page_count: 要转换的页数

fmt: 转换的图片格式

f: 要转换的开始页数

l: 结束页数

save: 是否保留pdf文件,默认不保留

'''

# 根据页数转换为相应数量的图片

image_list=convert_from_path(pdf,fmt=fmt,first_page=f,last_page=l)

target_name=os.path.join(TARGET_DIR,"{}.{}".format(int(time.time()),fmt))

to_image=Image.new('RGBA',(IMAGE_WIDTH,page_count*IMAGE_HEIGHT))

fori,imageinenumerate(image_list):

# 计算高度,拼合单张图片到一整张图片上

loc=(0,i*IMAGE_HEIGHT)

to_image.paste(image,loc)

ifnotsave:

try:

os.remove(pdf)

exceptExceptionase:

print("fail to remove pdf, please check and remove it manually")

returnto_image

def_make_watermark_image(image_size,watermark,count=1):

'''

生成水印图片

params:

image_size: 水印图片大小,与底板图片大小一致

watermark: 水印文本

count: 水印数量, 与内容页数保持一致

'''

fnt=ImageFont.truetype(font=FONT_FILE,size=80)

watermark_image=Image.new('RGBA',image_size,color=BACKGROUND_COLOR)

draw=ImageDraw.Draw(watermark_image)

foriinrange(1,count+1):

draw.multiline_text((WATERMARK_LEFT,WATERMARK_TOP*i),watermark,font=fnt,fill=FONT_COLOR)

returnwatermark_image

if__name__=='__main__':

txt="玩点coding https://vdcoding.com"

doc="resume.docx"

doc2image(doc,watermark=txt)

三、待完善的地方

笔者从网上下载了份docx格式的简历模板,通过上边的程序转换后得到的图片如下:

从图中可以看到个人信息部分的样式跑偏了,这部分是在docx->pdf过程中的排版引擎决定的,所以想要获得完美的图片,先要好好研究下排版引擎的使用,排版引擎可以加载多种扩展包,用来支持不同的排版格式。笔者只是兴趣使然,在短时间内实现了docx到图片的转换,没有深入研究排版引擎部分。关于这种转换笔者能力有限,只能以这种繁琐的方式实现,不过笔者倒是很好奇BOSS直聘是如何实现的,如果有机会真心希望交流一下。

python将word文档转图片_如何将word文档转换为图片相关推荐

  1. word邮件合并发送记录_如何将Word文档作为电子邮件正文发送

    word邮件合并发送记录 If you prefer to write your emails in Word, there is a way to send them directly to you ...

  2. vba 保存word里面的图片_如何将Word表格保存为图片?这3种方法你用过没?

    如何将Word中的表格另存为图片呢?本期Word妹与大家分享3个快速解决的办法. 第一种:将表格区域转换为图片 选中表格并复制,之后点击开始--剪贴板--粘贴--选择性粘贴,随后在打开的界面中选中[图 ...

  3. 根据坐标如何标记图片_推荐收藏 | 一文搞定SCI论文图片

    SCI论文图片的编辑是一门简单,却不容易的学问.在编辑图片的过程中,涉及到很多软件的配合使用,同时我们的目的不仅是满足投稿杂志的参数要求,还希望尽量做得美观好看. 现基于各大网站.公众号关于SCI作图 ...

  4. python爬虫抓收费图片_简单的抓取淘宝图片的Python爬虫

    写了一个抓taobao图片的爬虫,全是用if,for,while写的,比较简陋,入门作品. 从网页http://mm.taobao.com/json/request_top_list.htm?type ...

  5. python怎么在gui中显示图片_用 PySimpleGUI 做程序(7)--显示图片

    欢迎关注深入 Python 的专栏:恍然大明白​www.zhihu.com 本文代码在此:https://github.com/JiangChuanGo/examples/tree/master/Py ...

  6. word activex部件不能创建对象_如何用Word批量制作员工工作证?1分钟搞定1000份!只需三步...

    工作证是我们工作中很常见的东西,不过由于每个人的名字.部门.照片都不同,很多小伙伴都不知道怎么批量制作,总是傻傻地一张一张制作,这样特别浪费时间,今天小编就来跟大家分享如何批量制作! 一.准备工作 1 ...

  7. word嵌入对象依损坏_出错提示“Word 未能写某些嵌入对象,因为内容或磁盘空间不足”...

    出错提示"Word 未能写某些嵌入对象,因为内容或磁盘空间不足",感开始怀疑是转换软件,经过诊断,一切完好.问题主要出在word上,文档里插入了比较多的公式和图片,而且这些嵌入对象 ...

  8. xml文件转换成图片_如何把pdf文件转换成图片?

    pdf文件怎么转成JPG图片呢?相信有不少人在迷惑,可能还在想着截图等操作来完成转换,但如果是截图成JPG图片后的效果可能很差,并没有之前pdf文件那么清楚.那这样往往打印出来的效果也不怎么理想,那怎 ...

  9. word如何设置上标形式_如何在word中设置特殊页码

    获取更多业界资讯和深度好文● 点击蓝字关注我们 ● 在日常工作中,我们编辑的word文档经常需要设置页码,但有时文档的第一页是封面,第二页才是正文,或者第二页是目录,第三页才是正文,如下图所示,而页码 ...

  10. emwin读取sd图片_第12章emwin(ucgui)jpeg图片显示.pdf

    您所在位置:网站首页 > 海量文档 &nbsp>&nbsp计算机&nbsp>&nbspwindows相关 第12章emwin(ucgui)jpeg图片 ...

最新文章

  1. python以垂直方式输出_python学习笔记
  2. 三十六、Scrapy 中的复写默认管道和Rule扩展
  3. metal分析是什么意思_metal分析.pptx
  4. caffe error: #error This file requires compiler and library support for the ISO C++ 2011 standard
  5. let 和const
  6. 四个变量的图表怎么做_品牌策划方案怎么做?5步图文帮你绘制专业策划图表...
  7. html基础电子文档,html基础
  8. 时光就是一颗巨大的牛轧糖
  9. 一幅图看懂prototype与[[Prototype]]
  10. vs2017添加引用出错:对COM组件的调用返回了错误HRESULT E_FAIL
  11. 支持HomeKit、NFC:智汀智能门锁SL1仅需要149元
  12. Spring AOP术语:连接点和切点的区别。
  13. USB大容量存储设备浅析
  14. ffmpeg命令行使用
  15. 计算机主机配件及图解,电脑主机有哪些配件组成
  16. 哪个106短信服务平台好?这4点要参考!
  17. docker快速安装可道云
  18. 5、6月程序员“薪资被应届生倒挂“现象明显,跳槽还是等待?
  19. wordpress v3.3.1空间上传php,怎么上传wordpress到虚拟主机
  20. php图片平铺,php平铺水印/图片添加水印/图片合成

热门文章

  1. GAN生成手写字体识别
  2. 2022Vue经典面试题及答案汇总(持续更新)
  3. 为报复老东家,程序员编码给自己转账553笔,金额超21万元
  4. 世界各国Google域名后缀对照表
  5. pic12f1823单片机入门_PIC单片机:如何建立PICKit3程序编写电路
  6. 分享一款手机最强Python编程神器,用手机运行Python。天秀!
  7. 在线web表单设计器
  8. OpenStack 从ISO启动并安装VM
  9. SLA技术3D打印机的原理
  10. bio linux 创建_[转载]biolinux包含软件