毕业论文超清pdf带标签导出

Word直接导出的pdf不够清晰,使用打印导出的pdf又不带书签以及目录跳转功能这一问题,查阅网上资料使用Adobe DC似乎能够解决但是下载安装比较麻烦,于是写了python程序解决该问题。

解决思路: 使用python脚本对两个pdf文件进行合并,合并小体积的带书签的pdf和高清版本的pdf文件。

Step1:准备带书签的pdf,并命名为999.pdf (该名称与程序对应)

使用word导出功能,选择最小文件导出,并勾选书签导出选项。

Step2:打印输出高清pdf文件,并命名为a.pdf

打印使用福昕pdf的虚拟打印机,最高支持2400dpi的清晰度,我一般选1200dpi,已经足够清晰了。(也可以使用自带的Microsoft pdf 打印输出 但最高只支持600dpi)

打印导出文件,并命名为a.pdf

如果遇到导出的pdf文件太大可以不勾选word选项中的“不压缩文件中的图像”来进一步限制打印输出文件大小。

###使用Microsoft pdf打印输出文件也可以用代码替代执行,但最高只有600dpi,不如福昕pdf

import win32com.client
from pathlib import Path
import os
import timedef convert_docx_to_hd_pdf(input_docx, output_pdf):# 确保输出路径存在output_path = Path(output_pdf).parentos.makedirs(output_path, exist_ok=True)# 创建Word应用对象word = win32com.client.DispatchEx("Word.Application")word.Visible = Truetry:# 打开文档doc = word.Documents.Open(str(Path(input_docx).resolve()))# 获取文档总页数total_pages = doc.ComputeStatistics(2)  # 2 = wdStatisticPagesprint(f"文档共 {total_pages} 页,开始转换...")# 输出路径output_pdf_abs = str(Path(output_pdf).resolve())# 设置打印机为Microsoft Print to PDFword.ActivePrinter = "Microsoft Print to PDF"# 设置打印选项word.Options.PrintBackground = Trueword.Application.Options.PrintDraft = Falseword.Application.Options.PrintProperties = False# 执行打印print("开始高质量打印...")word.ActiveDocument.PrintOut(OutputFileName=output_pdf_abs,Range=0,Item=0,Copies=1,Pages="",PageType=0,PrintToFile=True,Collate=False)# 等待打印完成print("正在等待打印完成...")# 等待文件生成max_wait = 30  # 最多等待30秒start_time = time.time()while time.time() - start_time < max_wait:if os.path.exists(output_pdf) and os.path.getsize(output_pdf) > 0:print(f"PDF导出完成: {output_pdf}")print(f"文件大小: {os.path.getsize(output_pdf)} 字节")return Truetime.sleep(1)print("等待超时,请检查输出文件")return Falseexcept Exception as e:print(f"转换失败: {str(e)}")return Falsefinally:try:doc.Close(SaveChanges=False)except:password.Quit()# 执行转换
if __name__ == "__main__":convert_docx_to_hd_pdf('88.docx', '999.pdf')  ## 88.docx为word文件名

总之会得到两个pdf文件如下

Step3:创建虚拟环境,并执行文件合并

conda create -n xxx python=3.9conda activate xxxcd xxxxxpip install pikepdf pathlib os

然后运行程序,在此之前需在程序中指定目录页码范围,比如我的word目录对应9-10页

修改如下部分

运行

import pikepdf
from pathlib import Path
import osstart_page = 8
end_page = 9def merge_pdfs_with_bookmarks(image_pdf_path, bookmark_pdf_path, output_pdf_path):"""合并两个PDF文件,保留第二个PDF的书签信息和目录页,其他页面使用第一个PDF的内容参数:image_pdf_path: 包含高质量图片的PDF路径bookmark_pdf_path: 包含书签信息的PDF路径output_pdf_path: 输出PDF的路径"""print(f"开始合并PDF文件...")print(f"图片源PDF: {image_pdf_path}")print(f"书签源PDF: {bookmark_pdf_path}")try:# 确保输出目录存在output_dir = Path(output_pdf_path).parentos.makedirs(output_dir, exist_ok=True)# 打开两个PDF文件with pikepdf.open(image_pdf_path) as image_pdf, pikepdf.open(bookmark_pdf_path) as bookmark_pdf:# 检查页数是否一致if len(image_pdf.pages) != len(bookmark_pdf.pages):print(f"警告: 两个PDF的页数不一致! 图片PDF: {len(image_pdf.pages)}页, 书签PDF: {len(bookmark_pdf.pages)}页")print("继续合并,但可能导致书签指向错误的页面")# 创建一个新的PDF,以bookmark_pdf为基础merged_pdf = pikepdf.Pdf.open(bookmark_pdf_path)# 创建页面映射表,记录原始页面和新页面的对应关系page_map = {}# 替换除了第8-9页以外的所有页面for i in range(len(merged_pdf.pages)):# 页码从0开始,所以第9-10页对应索引8-9if (i > end_page or  i < start_page) and i < len(image_pdf.pages): # i != 10-12# 保存原始页面的引用old_page = merged_pdf.pages[i]old_objgen = old_page.obj.objgen# 使用正确的方法替换页面# pikepdf不支持直接删除页面,但可以直接替换merged_pdf.pages[i] = pikepdf.Page(image_pdf.pages[i])# 记录页面映射关系page_map[old_objgen[0]] = merged_pdf.pages[i].obj# 修复文档内部链接(目录页链接)print("正在修复文档内部链接...")# 特别处理目录页(第9-10页,索引8-9)for i in [start_page, end_page]:if '/Annots' in merged_pdf.pages[i]:annots = merged_pdf.pages[i]['/Annots']if isinstance(annots, pikepdf.Array):# print(annots)for annot in annots:# 检查是否是链接注释if annot.get('/Subtype') == '/Link':# 处理直接目标if '/Dest' in annot:dest = annot['/Dest']if isinstance(dest, pikepdf.Array) and len(dest) > 0:if hasattr(dest[0], 'objgen'):ref_id = dest[0].objgen[0]if ref_id in page_map:dest[0] = page_map[ref_id]# 处理动作目标elif '/A' in annot and isinstance(annot['/A'], pikepdf.Dictionary) and '/D' in annot['/A']:dest = annot['/A']['/D']if isinstance(dest, pikepdf.Array) and len(dest) > 0:if hasattr(dest[0], 'objgen'):ref_id = dest[0].objgen[0]if ref_id in page_map:dest[0] = page_map[ref_id]# 提取书签信息bookmarks = []if hasattr(bookmark_pdf, 'Root') and '/Outlines' in bookmark_pdf.Root:print("正在提取书签信息...")# 递归提取书签def extract_bookmarks(outline, bookmarks, depth=0):if '/First' not in outline:returncurrent = outline['/First']while True:title = str(current.get('/Title', ''))dest = current.get('/Dest', None)page_num = 0if dest is not None and isinstance(dest, pikepdf.Array) and len(dest) > 0:# 查找目标页面page_ref = dest[0]for i, page in enumerate(bookmark_pdf.pages):# 使用对象ID比较而不是same_as方法if hasattr(page.obj, 'objgen') and hasattr(page_ref, 'objgen'):if page.obj.objgen == page_ref.objgen:page_num = ibreak# 创建书签项bookmark = {'title': title,'page': page_num,'children': []}bookmarks.append(bookmark)# 处理子书签if '/First' in current:extract_bookmarks(current, bookmark['children'], depth + 1)# 移动到下一个书签if '/Next' not in current:breakcurrent = current['/Next']# 提取所有书签extract_bookmarks(bookmark_pdf.Root['/Outlines'], bookmarks)print(f"提取了 {len(bookmarks)} 个顶级书签")# 如果有提取到书签,添加到新PDFif bookmarks:print("正在将书签添加到新PDF...")# 递归创建书签def create_bookmarks(pdf, bookmarks, parent=None):if not bookmarks:return Nonefirst = Nonelast = Noneprev = Nonefor bookmark in bookmarks:# 创建新书签current = pdf.make_indirect(pikepdf.Dictionary({'/Title': pikepdf.String(bookmark['title']),'/Parent': parent}))# 设置目标页面page_idx = bookmark['page']if page_idx < len(pdf.pages):dest = [pdf.pages[page_idx].obj, pikepdf.Name('/Fit')]current['/Dest'] = pdf.make_indirect(pikepdf.Array(dest))# 处理链接关系if first is None:first = currentif prev is not None:prev['/Next'] = currentcurrent['/Prev'] = prevprev = currentlast = current# 处理子书签if bookmark['children']:children_first = create_bookmarks(pdf, bookmark['children'], current)if children_first:current['/First'] = children_first# 找到最后一个子书签children_last = children_firstwhile '/Next' in children_last:children_last = children_last['/Next']current['/Last'] = children_lastcurrent['/Count'] = len(bookmark['children'])return first# 创建书签字典outlines = merged_pdf.make_indirect(pikepdf.Dictionary({'/Type': pikepdf.Name('/Outlines'),'/Count': len(bookmarks)}))# 创建书签树first = create_bookmarks(merged_pdf, bookmarks, outlines)if first:outlines['/First'] = first# 找到最后一个书签last = firstwhile '/Next' in last:last = last['/Next']outlines['/Last'] = last# 添加到PDFmerged_pdf.Root['/Outlines'] = outlinesprint("成功添加书签到新PDF")# 保存合并后的PDFmerged_pdf.save(output_pdf_path)print(f"PDF合并完成! 输出文件: {output_pdf_path}")print(f"文件大小: {os.path.getsize(output_pdf_path)} 字节")return Trueexcept Exception as e:print(f"合并PDF时出错: {str(e)}")import tracebacktraceback.print_exc()return Falseif __name__ == "__main__":# 文件路径image_pdf = ".\a.pdf"  # 包含高质量图片的PDFbookmark_pdf = ".\999.pdf"  # 包含书签信息的PDFoutput_pdf = ".\out.pdf"  # 输出文件# 执行合并merge_pdfs_with_bookmarks(image_pdf, bookmark_pdf, output_pdf)

大功告成

该文件大小一般与高清pdf文件大小相当...

特别说明:该程序对正文中的图跳转未作程序编写。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/76885.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NOIP2012提高组.同余方程

目录 题目算法标签: 数论, 扩展欧几里得算法思路代码 题目 203. 同余方程 算法标签: 数论, 扩展欧几里得算法 思路 简单的扩展欧几里得算法应用题, 扩展欧几里得算法可以直接计算同余方程的通解, 因为求得是最小正整数解, 因此需要取模转换为正整数 a x b y ≡ 1 ax by …

C++学习-入门到精通-【0】计算机和C++简介

C学习-入门到精通-[0]计算机和C简介 计算机和C简介 C学习-入门到精通-[0]计算机和C简介一、计算机的组成二、硬件和软件三、数据的层次结构四、机器语言、汇编语言和高级语言五、C标准库六、面向对象技术 一、计算机的组成 计算机是由多个不同功能的逻辑单元组成的&#xff1a…

macOS 系统设置息屏情况下,PHP等后台脚本继续执行

在 macOS 系统下&#xff0c;当屏幕息屏或合上盖子时&#xff0c;后台脚本程序是否会继续运行&#xff0c;主要取决于以下几个因素&#xff1a; 1. 系统睡眠状态的影响 默认情况&#xff1a;合盖/息屏后&#xff0c;Mac 会进入「睡眠模式」&#xff08;部分硬件休眠&#xff…

SpringBoot集成ActiveMQ异常处理机制:若未捕获异常,消息会被重新投递

一、问题描述 SpringBoot项目集成AvtiveMQ&#xff0c;作为消息消费者。如果在消费消息的方法中&#xff0c;抛出异常&#xff0c;会产生什么效果&#xff1f; 二、ActiveMQ异常处理机制&#xff08;AI问答仅供参考&#xff09; 在Spring Boot项目集成ActiveMQ作为消息消费者…

【Java学习笔记】random的使用

random使用方法 使用说明&#xff1a;返回的是(0<n<1)这个范围中的任意带正号的double值 代码实例 public class helloworld{public static void main(String[] args){System.out.println(Math.random());} }生成0-100中的任意数代码示例 public class Main {public …

(三)垂直分库架构、分布式数据库

文章目录 垂直分库架构/分布式数据库什么是垂直分库架构架构模型优缺点优点缺点 技术案例分布式数据库架构模型优缺点优点缺点 技术案例 垂直分库架构/分布式数据库 什么是垂直分库架构 根据业务的模块划分&#xff0c; 将不同业务的数据放到不同的数据库中。 比如一个电子商城…

数据结构线性表的顺序存储结构

线性表是由零个或多个数据元素组成的有序序列。 特点&#xff1a; 数据元素间是有顺序的&#xff1b; 数据元素的个数是有限的&#xff1b; 一般来说&#xff0c;数据元素的类型是相同的&#xff08;强类型语言&#xff09;。c/c是强类型语言&#xff0c;必须指定数据类型。…

扣子空间试用:生成五一骑行规划+notion文章编写

今天试用了一下扣子空间&#xff0c;正好五一快到了&#xff0c;让它帮忙做了五一骑行规划&#xff0c;效果不赖&#xff01; 生成五一骑行规划 点击前往网站查看效果 prompt 如下&#xff1a; 帮我做一个五一上海骑行规划 要求&#xff1a; - 风景优美 - 人少 - 100km总路程…

最新得物小程序sign签名加密,请求参数解密,响应数据解密逆向分析

点击精选&#xff0c;出现https://app.dewu.com/api/v1/h5/index/fire/index 这个请求 直接搜索sign的话不容易定位 直接搜newAdvForH5就一个&#xff0c;进去再搜sign&#xff0c;打上断点 可以看到t.params就是没有sign的请求参数&#xff0c; 经过Object(a.default)该函数…

在C#串口通信中,一发一收的场景,如何处理不同功能码的帧数据比较合理,代码结构好

在 C# 串口通信的一发一收场景里&#xff0c;处理不同功能码的帧数据可采用以下合理的代码结构&#xff0c;它能让代码更具可读性、可维护性和可扩展性。 实现思路 定义帧结构&#xff1a;创建一个类来表示通信帧&#xff0c;其中包含功能码、数据等信息。功能码处理逻辑&…

【C++】vector扩容缩容

vector扩容缩容 1 扩容 一般来说&#xff0c;主要是重新分配内存 2 缩容 resize 缩小后&#xff0c;vector 的容量&#xff08;capacity()&#xff09;可能保持不变&#xff0c;需要显式调用 shrink_to_fit() 来释放内存。 验证代码&#xff1a; #include <vector>…

java中,线程的执行状态有哪些

在 Java 里&#xff0c;线程有 6 种执行状态&#xff0c;这些状态都在 java.lang.Thread.State 枚举类中被定义。下面为你详细介绍这些状态&#xff1a; 1. NEW&#xff08;新建状态&#xff09; 当你创建了一个 Thread 对象&#xff0c;却还未调用其 start() 方法时&#xf…

MATLAB 控制系统设计与仿真 - 41

鲁棒控制的其他函数 - 回路成型函数 loopsyn 灵敏度问题由鲁棒控制工具箱中的loopsyn就可以直接求解,该函数采用H无穷回路成型算法设计控制器,函数的调用格式为: [K,CL,gamma,info] = loopsyn(G,Gd) % G为受控对象模型% Gd为期望的回路传递函数% K为回路成型控制器模型% C…

查询Hologres或postgresql中的数据

因Hologres使用postgresql的语法.所以两者查询一样. 方案1: import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.ArrayList; import java.util.List;/*** 一个使用简单连接池管理PostgreSQL连接的工具类。*/ publi…

OpenBayes 一周速览|EasyControl 高效控制 DiT 架构,助力吉卜力风图像一键生成;TripoSG 单图秒变高保真 3D 模型

公共资源速递 10 个教程&#xff1a; * 一键部署 R1-OneVision * UNO&#xff1a;通用定制化图像生成 * TripoSG&#xff1a;单图秒变高保真 3D * 使用 VASP 进行机器学习力场训练 * InfiniteYou 高保真图像生成 Demo * VenusFactory 蛋白质工程设计平台 * Qwen2.5-0mni…

中兴云电脑W102D_晶晨S905X2_2+16G_mt7661无线_安卓9.0_线刷固件包

中兴云电脑W102D_晶晨S905X2_216G_mt7661无线_安卓9.0_线刷固件包 准备工作&#xff1a; 工具和设备在开始刷机之前&#xff0c;确保你已经准备好以下物品&#xff1a;双公头USB线&#xff1a;选择一根30-50厘米长的USB线&#xff0c;长度适中&#xff0c;方便操作&#xff0c;…

Rust 学习笔记:安装 Rust

Rust 学习笔记&#xff1a;安装 Rust Rust 学习笔记&#xff1a;安装 Rust在 Windows 上安装 Rust命令行创建 Rust 项目在 Mac/Linux 上安装 Rust一些命令升级卸载cargo -hrustc -h 安装 RustRoverrust-analyzer Rust 学习笔记&#xff1a;安装 Rust 在 Windows 上安装 Rust …

Opencv图像处理:轮廓检测、轮廓近似、绘制外接圆外接矩形

文章目录 一、图像轮廓检测1、比较2、常见的轮廓检测方法1&#xff09;基于梯度的方法2&#xff09;基于边缘检测器的方法3&#xff09;基于阈值的方法 3、查找轮廓与绘制轮廓4、参数解释4、代码解释1&#xff09;读取原图像灰度图并用二值化显示2&#xff09;轮廓绘制3&#x…

精益数据分析(17/126):精益画布与创业方向抉择

精益数据分析&#xff08;17/126&#xff09;&#xff1a;精益画布与创业方向抉择 大家好&#xff01;一直以来&#xff0c;我都希望能和大家一起在创业和数据分析的领域中不断探索、共同进步。今天&#xff0c;我们接着深入学习《精益数据分析》&#xff0c;这次聚焦于精益画…

每天五分钟深度学习PyTorch:图像的处理的上采样和下采样

本文重点 在pytorch中封装了上采样和下采样的方法,我们可以使用封装好的方法可以很方便的完成采样任务,采样分为上采样和下采样。 上采样和下采样 下采样(缩小图像)的主要目的有两个:1、使得图像符合显示区域的大小;2、生成对应图像的缩略图。 下采样( 放大图像)的…