news 2026/6/10 8:54:17

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/6/10 1:17:14

基于matlab的凸轮轮廓的设计计算与绘图 计算此结构的最优化参数,根据其原理输出推程和回程的...

基于matlab的凸轮轮廓的设计计算与绘图 计算此结构的最优化参数&#xff0c;根据其原理输出推程和回程的最大压力角、最小曲率半径等相关结果。 程序已调通&#xff0c;可直接运行。打开MATLAB的脚本编辑器&#xff0c;迎面扑来的是熟悉的蓝色界面。咱们今天要折腾的这个凸轮设…

作者头像 李华
网站建设 2026/6/9 19:01:02

经验失灵:当IT老手在AI时代求职遇冷

在科技行业&#xff0c;经验曾是求职时最坚实的后盾。然而&#xff0c;不少拥有多年工作经历的IT人&#xff0c;却在最近的求职季中遭遇了意想不到的挑战&#xff1a;他们引以为傲的经验&#xff0c;在面对“是否熟悉AIGC工具”、“能否用AI重构工作流”等问题时&#xff0c;显…

作者头像 李华
网站建设 2026/6/7 4:23:21

单元测试的10个最佳实践

在软件开发的生命周期中&#xff0c;单元测试是确保代码健壮性和可维护性的基石。随着敏捷开发和持续集成的普及&#xff0c;高效的单元测试已成为测试从业者的必备技能。本文针对软件测试从业者&#xff0c;总结了10个经过验证的最佳实践&#xff0c;涵盖测试设计、执行到维护…

作者头像 李华
网站建设 2026/6/9 20:41:22

MATLAB基础应用精讲-【自动驾驶】SORT目标跟踪算法(附python代码实现)

目录 前言 算法原理 什么是SORT 算法思想 SORT原理 (1)目标检测(Object Detection) (2)卡尔曼滤波(Kalman Filter) (3)匈牙利算法(Hungarian Algorithm) SORT算法实现过程 算法步骤 步骤1:目标检测 步骤2:轨迹预测 步骤3:数据关联 步骤4:状态更新…

作者头像 李华
网站建设 2026/6/8 6:01:36

虫害预警怎样更及时?虫情测报仪夜间自动诱捕拍照,助力植保提前规划

虫害的发生往往具有隐蔽性和突发性&#xff0c;等到田间出现明显为害症状时再防治&#xff0c;有时可能已造成一定影响。如何更早地发现害虫出现迹象&#xff0c;实现植保工作的提前部署&#xff0c;是种植管理中希望改善的环节。虫情测报仪在害虫监测预警方面提供了一种技术手…

作者头像 李华