使用golang实现PDF图片提取

Written by

in

文章目录
  • 简单来说,这个工具干了三件事: 读 PDF:用 pdfcpu 库解析 PDF 文件结构。 找图片:遍历每一页,找出所有嵌入的图像对象(不管是 JPG、PNG 还是其他格式)。 存图片:把每张图按 名称-页码-对象ID.格式 的规则命名,存到你指定的文件夹里。 小知识:PDF 里的图片其实是“嵌入对象”,每个都有唯一 ID 和所属页码。我们就是靠这些信息精准定位每张图!
  • 假设你有个文件叫 report.pdf,只需一行命令: extractimages report.pdf 它就会自动创建一个叫 report.pdf.images 的文件夹,里面是这样的: chart-5-42.jpgdiagram-12-108.pngimage-3-17.jpg… 想换个目录?加个 -o 就行: extractimages -o my_pics report.pdf 连帮助都贴心准备好了: extractimages –help
  • 用 unsafe “偷看”私有字段:pdfcpu.Image 的内部字段是私有的,但我们用 unsafe.Pointer 强行转成自定义结构体 MyImage,就能拿到图片名、页码、对象 ID等关键信息!(别怕,这在 Go 里是合法“黑科技”) 自动防重名:如果文件名冲突,自动加上时间戳,绝不覆盖! 智能默认输出目录:不指定 -o?没问题,自动用 PDF文件名.images 当文件夹!
  • 只要你会装 Go,三步搞定: 安装依赖: go mod init extractimages go get github.com/pdfcpu/pdfcpu 把下面的代码保存为 main.go 编译运行: go build -o extractimages main.go ./extractimages your_file.pdf 从此,PDF 再也不是图片的"监 狱"——而是你的"图库仓库"!
  • package main import ( “flag” // 用于处理命令行参数 “fmt” // 用于格式化输出 “io” // 提供基础的I/O接口 “log” // 提供日志功能 “os” // 提供操作系统功能接口 “path/filepath” // 用于处理文件路径 “strings” // 提供字符串处理函数 “time” // 用于计时和时间戳 “unsafe” // 用于不安全的内存操作 “github.com/pdfcpu/pdfcpu/pkg/api” // pdfcpu库的API接口 “github.com/pdfcpu/pdfcpu/pkg/pdfcpu” // pdfcpu库的核心功能 ) // MyImage 是对pdfcpu.Image的封装,用于访问图片的详细信息 type MyImage struct { io.Reader // 嵌入的Reader接口,用于读取图片数据 Name string // 图片名称 FileType string // 文件类型(如jpg、png等) PageNr int // 图片所在的PDF页码 ObjNr int // 图片在PDF中的对象编号 } // 全局变量,用于统计提取的图片数量 var extractedImagesCount int // main 程序入口函数 func main() { // 设置日志格式 log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) log.Println(“PDF图片提取工具启动”) // 解析命令行参数 var help bool var outputDir string flag.BoolVar(&help, “h”, false, “显示帮助信息”) flag.BoolVar(&help, “help”, false, “显示帮助信息”) flag.StringVar(&outputDir, “o”, “”, “指定输出图片的目录”) flag.StringVar(&outputDir, “output”, “”, “指定输出图片的目录”) flag.Parse() // 获取剩余的非选项参数(即PDF文件路径) args := flag.Args() if help || len(args) < 1 { showHelp() return } // 获取PDF文件的绝对路径 pdfFilename, err := filepath.Abs(args[0]) if err != nil { log.Fatalf(“获取文件绝对路径失败: %v”, err) } // 检查文件是否存在 if _, err := os.Stat(pdfFilename); os.IsNotExist(err) { log.Fatalf(“PDF文件不存在: %s”, pdfFilename) } // 检查文件扩展名是否为pdf if !strings.HasSuffix(strings.ToLower(pdfFilename), “.pdf”) { log.Printf(“警告: 文件扩展名不是.pdf,可能不是有效的PDF文件: %sn”, pdfFilename) } // 确定输出目录 if outputDir == “” { // 如果未指定输出目录,使用默认目录(PDF文件名.images) pdfName := strings.TrimSuffix(filepath.Base(pdfFilename), “.pdf”) outputDir = pdfName + “.images” } // 获取输出目录的绝对路径 outputDir, err = filepath.Abs(outputDir) if err != nil { log.Fatalf(“获取输出目录绝对路径失败: %v”, err) } // 创建输出目录(如果不存在) if err := os.MkdirAll(outputDir, 0755); err != nil { log.Fatalf(“创建输出目录失败: %v”, err) } log.Printf(“开始处理PDF文件: %s”, pdfFilename) log.Printf(“图片将保存到目录: %s”, outputDir) startTime := time.Now() // 打开 PDF 文件 pdfFile, err := os.Open(pdfFilename) if err != nil { log.Fatalf(“打开PDF文件失败: %v”, err) } // 确保程序结束时关闭文件 defer pdfFile.Close() // 解析 PDF,将提取的图片写入指定目录 // 使用saveImageToDir函数作为回调函数处理每个提取到的图片 if err := api.ExtractImages(pdfFile, nil, saveImageToDir(outputDir), nil); err != nil { log.Fatalf(“提取图片过程中出错: %v”, err) } elapsedTime := time.Since(startTime) log.Printf(“图片提取完成,共提取 %d 张图片,耗时: %v”, extractedImagesCount, elapsedTime) log.Printf(“所有图片已保存到目录: %s”, outputDir) } // showHelp 显示帮助信息 func showHelp() { fmt.Println(“PDF图片提取工具 – 从PDF文件中提取所有图片并保存到指定目录”) fmt.Println(“n用法:”) fmt.Println(” extractimages [选项] <PDF文件路径>”) fmt.Println(“n选项:”) fmt.Println(” -h, –help 显示此帮助信息”) fmt.Println(” -o, –output <目录路径> 指定输出图片的目录(默认: PDF文件名.images)”) fmt.Println(“n示例:”) fmt.Println(” extractimages document.pdf”) fmt.Println(” extractimages -o images_folder document.pdf”) fmt.Println(“n说明:”) fmt.Println(” 1. 程序会从指定的PDF文件中提取所有图片”) fmt.Println(” 2. 提取的图片将保存到指定目录或默认目录中”) fmt.Println(” 3. 图片文件名格式: 图片名称-页码-对象编号.文件类型”) fmt.Println(” 4. 如果指定的目录不存在,将自动创建”) } // saveImageToDir 创建一个回调函数,用于将提取的图片保存到指定目录 // 参数 outputDir 是保存图片的目标目录路径 // 返回值是一个函数,该函数将在pdfcpu库提取到图片时被调用 func saveImageToDir(outputDir string) func(pdfcpu.Image, bool, int) error { return func(img pdfcpu.Image, singleImgPerPage bool, maxPageDigits int) error { // 使用unsafe包将pdfcpu.Image类型强制转换为MyImage类型,以便访问私有字段 myImg := (*MyImage)(unsafe.Pointer(&img)) // 处理空文件名的情况 name := myImg.Name if name == “” { name = “image” } // 处理未知文件类型的情况 fileType := myImg.FileType if fileType == “” { fileType = “jpg” // 默认使用jpg格式 } // 生成图片文件名,格式为:图片名称-页码-对象编号.文件类型 filename := fmt.Sprintf(“%s-%d-%d.%s”, name, myImg.PageNr, myImg.ObjNr, fileType) // 生成完整的文件路径 filePath := filepath.Join(outputDir, filename) // 检查文件是否已存在,如果存在则添加时间戳 if _, err := os.Stat(filePath); err == nil { timestamp := time.Now().Format(“20060102_150405”) filename = fmt.Sprintf(“%s-%d-%d_%s.%s”, name, myImg.PageNr, myImg.ObjNr, timestamp, fileType) filePath = filepath.Join(outputDir, filename) log.Printf(“图片文件已存在,将使用新文件名: %s”, filename) } // 创建目标文件 outFile, err := os.Create(filePath) if err != nil { log.Printf(“创建图片文件失败: %v”, err) return err } // 确保文件关闭 defer outFile.Close() // 将图片数据写入文件 n, err := io.Copy(outFile, img.Reader) if err != nil { log.Printf(“写入图片数据失败: %v”, err) return err } // 增加统计计数 extractedImagesCount++ // 输出成功处理的图片文件名和大小 log.Printf(“成功提取图片 #%d: %s (大小: %d 字节)”, extractedImagesCount, filename, n) return nil } } 从此 PDF 里的图,都是你的! 快去试试吧,说不定下一个被你提取到的,就是价值千金的设计图呢~ 到此这篇关于使用golang实现PDF图片提取的文章就介绍到这了,更多相关go提取PDF图片内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: Go语言实现生成样式美观的PDF文件 Go实现将任何网页转化为PDF 基于golang编写一个word/excel/ppt转pdf的工具 Go语言如何生成PDF文件实例探究 golang 实现 pdf 转高清晰度 jpeg的处理方法 Go处理PDF的实现代码
  • 目录
    • 它是怎么工作的
    • 用起来有多爽
    • 技术小彩蛋
    • 快来试试吧
    • 完整代码(直接复制就能用!)

    ““PDF 里的图,难道只能看不能拿?””

    —— 别急,今天教你用 Go 写个"“图片提取器”",把 PDF 里的图统统打包带走!

    你有没有遇到过这种情况:

    • 看到一份超赞的 PDF 报告,里面的图表清晰又专业,想保存下来做参考?
    • 老师发的课件里有张神图,但右键不能保存,复制还糊成马赛克?
    • 想批量提取 PDF 中的所有插图,却只能一张张截图?

    别慌!今天我们就用 Go 语言 + pdfcpu 库,手搓一个超实用的 PDF 图片提取工具,一键把 PDF 里的所有图片“扒”出来,整齐码好,命名清晰,绝不手软!

    简单来说,这个工具干了三件事:

    • 读 PDF:用 pdfcpu 库解析 PDF 文件结构。
    • 找图片:遍历每一页,找出所有嵌入的图像对象(不管是 JPG、PNG 还是其他格式)。
    • 存图片:把每张图按 名称-页码-对象ID.格式 的规则命名,存到你指定的文件夹里。

    小知识:PDF 里的图片其实是“嵌入对象”,每个都有唯一 ID 和所属页码。我们就是靠这些信息精准定位每张图!

    假设你有个文件叫 report.pdf,只需一行命令:

    extractimages report.pdf
    

    它就会自动创建一个叫 report.pdf.images 的文件夹,里面是这样的:

    chart-5-42.jpg
    diagram-12-108.png
    image-3-17.jpg

    想换个目录?加个 -o 就行:

    extractimages -o my_pics report.pdf
    

    连帮助都贴心准备好了:

    extractimages --help
    

    用 unsafe “偷看”私有字段:pdfcpu.Image 的内部字段是私有的,但我们用 unsafe.Pointer 强行转成自定义结构体 MyImage,就能拿到图片名、页码、对象

    ID等关键信息!(别怕,这在 Go 里是合法“黑科技”)

    自动防重名:如果文件名冲突,自动加上时间戳,绝不覆盖!

    智能默认输出目录:不指定 -o?没问题,自动用 PDF文件名.images 当文件夹!

    只要你会装 Go,三步搞定:

    安装依赖:

    go mod init extractimages
    go get github.com/pdfcpu/pdfcpu
    

    把下面的代码保存为 main.go

    编译运行:

    go build -o extractimages main.go
    ./extractimages your_file.pdf
    

    从此,PDF 再也不是图片的"监 狱"——而是你的"图库仓库"!

    package main
    
    import (
    	"flag"               // 用于处理命令行参数
    	"fmt"               // 用于格式化输出
    	"io"                // 提供基础的I/O接口
    	"log"               // 提供日志功能
    	"os"                // 提供操作系统功能接口
    	"path/filepath"     // 用于处理文件路径
    	"strings"           // 提供字符串处理函数
    	"time"              // 用于计时和时间戳
    	"unsafe"            // 用于不安全的内存操作
    
    	"github.com/pdfcpu/pdfcpu/pkg/api"      // pdfcpu库的API接口
    	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu"   // pdfcpu库的核心功能
    )
    
    // MyImage 是对pdfcpu.Image的封装,用于访问图片的详细信息
    type MyImage struct {
    	io.Reader  // 嵌入的Reader接口,用于读取图片数据
    	Name     string // 图片名称
    	FileType string // 文件类型(如jpg、png等)
    	PageNr   int    // 图片所在的PDF页码
    	ObjNr    int    // 图片在PDF中的对象编号
    }
    
    // 全局变量,用于统计提取的图片数量
    var extractedImagesCount int
    
    // main 程序入口函数
    func main() {
    	// 设置日志格式
    	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
    	log.Println("PDF图片提取工具启动")
    
    	// 解析命令行参数
    	var help bool
    	var outputDir string
    	flag.BoolVar(&help, "h", false, "显示帮助信息")
    	flag.BoolVar(&help, "help", false, "显示帮助信息")
    	flag.StringVar(&outputDir, "o", "", "指定输出图片的目录")
    	flag.StringVar(&outputDir, "output", "", "指定输出图片的目录")
    	flag.Parse()
    
    	// 获取剩余的非选项参数(即PDF文件路径)
    	args := flag.Args()
    	if help || len(args) < 1 {
    		showHelp()
    		return
    	}
    
    	// 获取PDF文件的绝对路径
    	pdfFilename, err := filepath.Abs(args[0])
    	if err != nil {
    		log.Fatalf("获取文件绝对路径失败: %v", err)
    	}
    
    	// 检查文件是否存在
    	if _, err := os.Stat(pdfFilename); os.IsNotExist(err) {
    		log.Fatalf("PDF文件不存在: %s", pdfFilename)
    	}
    
    	// 检查文件扩展名是否为pdf
    	if !strings.HasSuffix(strings.ToLower(pdfFilename), ".pdf") {
    		log.Printf("警告: 文件扩展名不是.pdf,可能不是有效的PDF文件: %sn", pdfFilename)
    	}
    
    	// 确定输出目录
    	if outputDir == "" {
    		// 如果未指定输出目录,使用默认目录(PDF文件名.images)
    		pdfName := strings.TrimSuffix(filepath.Base(pdfFilename), ".pdf")
    		outputDir = pdfName + ".images"
    	}
    	
    	// 获取输出目录的绝对路径
    	outputDir, err = filepath.Abs(outputDir)
    	if err != nil {
    		log.Fatalf("获取输出目录绝对路径失败: %v", err)
    	}
    
    	// 创建输出目录(如果不存在)
    	if err := os.MkdirAll(outputDir, 0755); err != nil {
    		log.Fatalf("创建输出目录失败: %v", err)
    	}
    
    	log.Printf("开始处理PDF文件: %s", pdfFilename)
    	log.Printf("图片将保存到目录: %s", outputDir)
    	startTime := time.Now()
    
    	// 打开 PDF 文件
    	pdfFile, err := os.Open(pdfFilename)
    	if err != nil {
    		log.Fatalf("打开PDF文件失败: %v", err)
    	}
    	// 确保程序结束时关闭文件
    	defer pdfFile.Close()
    
    	// 解析 PDF,将提取的图片写入指定目录
    	// 使用saveImageToDir函数作为回调函数处理每个提取到的图片
    	if err := api.ExtractImages(pdfFile, nil, saveImageToDir(outputDir), nil); err != nil {
    		log.Fatalf("提取图片过程中出错: %v", err)
    	}
    
    	elapsedTime := time.Since(startTime)
    	log.Printf("图片提取完成,共提取 %d 张图片,耗时: %v", extractedImagesCount, elapsedTime)
    	log.Printf("所有图片已保存到目录: %s", outputDir)
    }
    
    // showHelp 显示帮助信息
    func showHelp() {
    	fmt.Println("PDF图片提取工具 - 从PDF文件中提取所有图片并保存到指定目录")
    	fmt.Println("n用法:")
    	fmt.Println("  extractimages [选项] <PDF文件路径>")
    	fmt.Println("n选项:")
    	fmt.Println("  -h, --help              显示此帮助信息")
    	fmt.Println("  -o, --output <目录路径>  指定输出图片的目录(默认: PDF文件名.images)")
    	fmt.Println("n示例:")
    	fmt.Println("  extractimages document.pdf")
    	fmt.Println("  extractimages -o images_folder document.pdf")
    	fmt.Println("n说明:")
    	fmt.Println("  1. 程序会从指定的PDF文件中提取所有图片")
    	fmt.Println("  2. 提取的图片将保存到指定目录或默认目录中")
    	fmt.Println("  3. 图片文件名格式: 图片名称-页码-对象编号.文件类型")
    	fmt.Println("  4. 如果指定的目录不存在,将自动创建")
    }
    
    // saveImageToDir 创建一个回调函数,用于将提取的图片保存到指定目录
    // 参数 outputDir 是保存图片的目标目录路径
    // 返回值是一个函数,该函数将在pdfcpu库提取到图片时被调用
    func saveImageToDir(outputDir string) func(pdfcpu.Image, bool, int) error {
    	return func(img pdfcpu.Image, singleImgPerPage bool, maxPageDigits int) error {
    		// 使用unsafe包将pdfcpu.Image类型强制转换为MyImage类型,以便访问私有字段
    		myImg := (*MyImage)(unsafe.Pointer(&img))
    		
    		// 处理空文件名的情况
    		name := myImg.Name
    		if name == "" {
    			name = "image"
    		}
    		
    		// 处理未知文件类型的情况
    		fileType := myImg.FileType
    		if fileType == "" {
    			fileType = "jpg" // 默认使用jpg格式
    		}
    		
    		// 生成图片文件名,格式为:图片名称-页码-对象编号.文件类型
    		filename := fmt.Sprintf("%s-%d-%d.%s", name, myImg.PageNr, myImg.ObjNr, fileType)
    		
    		// 生成完整的文件路径
    		filePath := filepath.Join(outputDir, filename)
    		
    		// 检查文件是否已存在,如果存在则添加时间戳
    		if _, err := os.Stat(filePath); err == nil {
    			timestamp := time.Now().Format("20060102_150405")
    			filename = fmt.Sprintf("%s-%d-%d_%s.%s", name, myImg.PageNr, myImg.ObjNr, timestamp, fileType)
    			filePath = filepath.Join(outputDir, filename)
    			log.Printf("图片文件已存在,将使用新文件名: %s", filename)
    		}
    		
    		// 创建目标文件
    		outFile, err := os.Create(filePath)
    		if err != nil {
    			log.Printf("创建图片文件失败: %v", err)
    			return err
    		}
    		
    		// 确保文件关闭
    		defer outFile.Close()
    		
    		// 将图片数据写入文件
    		n, err := io.Copy(outFile, img.Reader)
    		if err != nil {
    			log.Printf("写入图片数据失败: %v", err)
    			return err
    		}
    		
    		// 增加统计计数
    		extractedImagesCount++
    		
    		// 输出成功处理的图片文件名和大小
    		log.Printf("成功提取图片 #%d: %s (大小: %d 字节)", extractedImagesCount, filename, n)
    		return nil
    	}
    }
    

    从此 PDF 里的图,都是你的!

    快去试试吧,说不定下一个被你提取到的,就是价值千金的设计图呢~

    到此这篇关于使用golang实现PDF图片提取的文章就介绍到这了,更多相关go提取PDF图片内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

    您可能感兴趣的文章:

    • Go语言实现生成样式美观的PDF文件
    • Go实现将任何网页转化为PDF
    • 基于golang编写一个word/excel/ppt转pdf的工具
    • Go语言如何生成PDF文件实例探究
    • golang 实现 pdf 转高清晰度 jpeg的处理方法
    • Go处理PDF的实现代码

    站内搜索