直接上代码和效果:
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,给后来人留下更多的数字遗产。