news 2026/9/24 20:41:08

golang PDF转图片,图片转PDF(无cgo,以及其他二进制程序依赖)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
golang PDF转图片,图片转PDF(无cgo,以及其他二进制程序依赖)

直接上代码和效果:

packagemainimport("fmt"_"image/png""os""path/filepath""regexp""sort""strconv""sync""github.com/jung-kurt/gofpdf""github.com/pdfcpu/pdfcpu/pkg/api""github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model")// PDFToImages// pdfPath: 输入PDF路径// outputDir: 图片输出目录// maxWorkers: 最大并发协程数(0表示不限制)funcPDFToImages(pdfPath,outputDirstring,maxWorkers...int)error{// 1. 创建输出目录iferr:=os.MkdirAll(outputDir,0755);err!=nil{returnfmt.Errorf("创建输出目录失败: %w",err)}// 2. 配置PDF渲染参数conf:=model.NewDefaultConfiguration()conf.Cmd=model.WMImage// 3. 获取PDF页数pageCount,err:=api.PageCountFile(pdfPath)iferr!=nil{returnfmt.Errorf("获取PDF页数失败: %w",err)}ifpageCount==0{returnfmt.Errorf("PDF文件无页面")}// 4. 设置并发参数workers:=pageCountiflen(maxWorkers)>0&&maxWorkers[0]>0{workers=maxWorkers[0]ifworkers>pageCount{workers=pageCount}}// 5. 创建任务通道和结果通道tasks:=make(chanint,pageCount)errChan:=make(chanerror,pageCount)varwg sync.WaitGroup// 6. 启动工作协程fori:=0;i<workers;i++{wg.Add(1)gofunc(workerIDint){deferwg.Done()forpageNum:=rangetasks{// 转换单页PDF为图片err:=api.ExtractImagesFile(pdfPath,outputDir,[]string{fmt.Sprintf("%d",pageNum)},conf)iferr!=nil{errChan<-fmt.Errorf("转换第%d页失败: %w",pageNum,err)continue}fmt.Printf("第%d页已保存\n",pageNum)}}(i)}// 7. 发送任务forpageNum:=1;pageNum<=pageCount;pageNum++{tasks<-pageNum}close(tasks)// 8. 等待所有协程完成wg.Wait()close(errChan)// 9. 检查错误forerr:=rangeerrChan{iferr!=nil{returnerr}}returnnil}// extractPageNum 从图片文件名中提取页码funcextractPageNum(pdfFileNamestring,filenamestring)int{// 定义正则表达式匹配页码部分re:=regexp.MustCompile(pdfFileName+`_(\d+)_Im0\.(jpg|jpeg|png)`)matches:=re.FindStringSubmatch(filename)iflen(matches)<2{return0}pageNum,err:=strconv.Atoi(matches[1])iferr!=nil{return0}returnpageNum}// ImagesToPDF 图片批量转回PDFfuncImagesToPDF(imgDir,pdfPathstring,sortByNamebool)error{varimgFiles[]stringerr:=filepath.Walk(imgDir,func(pathstring,info os.FileInfo,errerror)error{iferr!=nil{returnerr}ifinfo.IsDir(){returnnil}ext:=filepath.Ext(path)ifext==".png"||ext==".jpg"||ext==".jpeg"{imgFiles=append(imgFiles,path)}returnnil})iferr!=nil{returnfmt.Errorf("遍历图片目录失败: %w",err)}iflen(imgFiles)==0{returnfmt.Errorf("未找到图片文件")}ifsortByName{// 根据页码数字排序sort.Slice(imgFiles,func(i,jint)bool{pageNumI:=extractPageNum("D3D9_DEV",filepath.Base(imgFiles[i]))// 记得把D3D9_DEV替换为你的PDF文件名,只要文件名,不要文件后缀pageNumJ:=extractPageNum("D3D9_DEV",filepath.Base(imgFiles[j]))// 记得把D3D9_DEV替换为你的PDF文件名,只要文件名,不要文件后缀returnpageNumI<pageNumJ})}pdf:=gofpdf.New("P","mm","A4","")pdf.SetMargins(0,0,0)for_,imgPath:=rangeimgFiles{pdf.AddPage()pdf.Image(imgPath,0,0,210,297,false,"",0,"")// A4尺寸 210x297mmfmt.Printf("添加图片到PDF:%s\n",imgPath)}iferr:=pdf.OutputFileAndClose(pdfPath);err!=nil{returnfmt.Errorf("保存PDF失败: %w",err)}returnnil}funcmain(){// PDF转图片err:=PDFToImages("D3D9_DEV.pdf","pdf_images",10)iferr!=nil{fmt.Printf("PDF转图片失败: %v\n",err)return}// 图片转回PDFerr=ImagesToPDF("pdf_images","output_from_images.pdf",true)iferr!=nil{fmt.Printf("图片转PDF失败: %v\n",err)return}fmt.Println("操作完成!")}

在main函数中进行操作:

先把图片转回PDF注释掉:

funcmain(){// PDF转图片err:=PDFToImages("D3D9_DEV.pdf","pdf_images",10)iferr!=nil{fmt.Printf("PDF转图片失败: %v\n",err)return}// 图片转回PDF// err = ImagesToPDF("pdf_images", "output_from_images.pdf", true)// if err != nil {// fmt.Printf("图片转PDF失败: %v\n", err)// return// }fmt.Println("操作完成!")}


因为采用了协程,所以效率非常高。

之后把图片转PDF的注释打开,把PDF转图片注释,注意要修改的地方:

funcmain(){// PDF转图片// err := PDFToImages("D3D9_DEV.pdf", "pdf_images", 10)// if err != nil {// fmt.Printf("PDF转图片失败: %v\n", err)// return// }// 图片转回PDFerr:=ImagesToPDF("pdf_images","output_from_images.pdf",true)iferr!=nil{fmt.Printf("图片转PDF失败: %v\n",err)return}fmt.Println("操作完成!")}

因为我的pdf文件就叫D3D9_DEV.pdf,你们在还原的时候,需要根据你们的pdf文件名进行修改:

ifsortByName{// 根据页码数字排序sort.Slice(imgFiles,func(i,jint)bool{pageNumI:=extractPageNum("D3D9_DEV",filepath.Base(imgFiles[i]))// 记得把D3D9_DEV替换为你的PDF文件名,只要文件名,不要文件后缀pageNumJ:=extractPageNum("D3D9_DEV",filepath.Base(imgFiles[j]))// 记得把D3D9_DEV替换为你的PDF文件名,只要文件名,不要文件后缀returnpageNumI<pageNumJ})}


因为现在AI越来越强大了,希望能够有更多的人可以把图片进行修复,比如文字更加平滑,清晰,图片更加精美漂亮,等每张图片都修复好了,再把他们合成最终的pdf文件。

老旧PDF图书大多都不太清晰,希望能多加利用这个案例,修复更多的pdf,给后来人留下更多的数字遗产。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 0:27:28

金属锯床采用台达PLC与威纶通触摸屏,程序成熟稳定并带有注释

金属锯床采用台达PLC和威纶通触摸屏&#xff0c;带有注释。 程序成熟稳定。最近在调试一台老款金属锯床时发现这套设备挺有意思——台达DVP系列PLC配威纶通MT8071iE触摸屏的组合&#xff0c;运行五年多居然连个死机记录都没有。这种工控组合现在看可能不算新潮&#xff0c;但胜…

作者头像 李华
网站建设 2026/9/23 10:15:58

Qwen-Image单图训练LoRA全解析

Qwen-Image单图训练LoRA全解析&#xff1a;基于MMDiT架构的高保真图像生成与精准控制 2025年&#xff0c;AIGC创作正从“通用生成”迈向“个性可控”的新阶段。阿里云发布的 Qwen-Image 模型&#xff0c;凭借其200亿参数的 MMDiT&#xff08;Multimodal Diffusion Transformer&…

作者头像 李华
网站建设 2026/9/25 5:50:29

业务规则提取与验证方法指南

一、业务规则的定义与测试价值 业务规则是组织在特定领域内约束行为、定义逻辑的规范集合&#xff0c;通常以条件-结果语句&#xff08;如“若用户积分≥1000&#xff0c;则自动升级为VIP”&#xff09;形式存在。对测试人员而言&#xff0c;业务规则具有三重核心价值&#xf…

作者头像 李华
网站建设 2026/9/24 3:17:45

冒烟测试 vs 验收测试:从构建验证到业务交付的全链路质量保障

测试金字塔中的关键分层 在软件测试体系中&#xff0c;冒烟测试与验收测试作为不同阶段的验证手段&#xff0c;常被项目团队混淆或重叠使用。本文基于软件工程实践&#xff0c;结合敏捷测试模型&#xff0c;系统分析二者的本质区别与协作关系。通过明确分界&#xff0c;帮助测…

作者头像 李华
网站建设 2026/9/24 8:13:31

用例场景法在业务流程测试中的应用‌

在软件开发周期中&#xff0c;业务流程测试旨在验证系统从起点到终点的完整操作流程是否符合业务需求。用例场景法作为一种黑盒测试技术&#xff0c;通过构建真实用户场景来模拟端到端的业务操作&#xff0c;广泛应用于金融、电商、医疗等领域的系统测试中。它不仅提高了测试的…

作者头像 李华
网站建设 2026/9/24 8:13:21

Qwen3-8B显存不足?五招亲测解决

Qwen3-8B显存不足&#xff1f;五招亲测解决 你有没有过这样的经历&#xff1a;兴冲冲地拉下 Qwen3-8B 的镜像&#xff0c;满怀期待准备在自己的 RTX 3060 或 4090 上跑个本地 AI 助手&#xff0c;结果刚一启动——“CUDA out of memory”&#x1f4a5;&#xff1f; 明明是号称“…

作者头像 李华