PDF转doc(golang 跨平台版)

PDF转doc(golang 跨平台版)

git地址

	码云地址: https://gitee.com/mr_yanghw/go-utils.git

使用到的技术

  • golang
  • tesseract

实现

1. 代码


// 打包文件参考 https://blog.csdn.net/daily886/article/details/97527117
// mac :  go build -o pdf2Doc-mac cmd/pdf2img.go
// linux: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o pdf2Doc-mac cmd/pdf2img.go
// windows: CC=x86_64-w64-mingw32-gcc CGO_ENABLED=1 GOOS=windows go build -o pdf2Doc-win.exe cmd/pdf2img.go (需要安装 brew install mingw-w64)
func main() {
	welcome := `================================================================================

                         欢迎使用疯狂的 "IT小🐑" PDF转图片工具

================================================================================`
	fmt.Println(welcome)
	fmt.Println(`共有 5 个参数:
	参数1:目标pdf文件路径 
	参数2:目标文件名称
	参数3:电脑系统 1:mac 2:windows
	参数4:可选,100 - 800,代表的图片清晰度,数值越大,图片越清晰,相对执行时间也会变长,建议:400
	参数5:1/0,是否关闭无痕模式,1关闭 0开启,默认 开启
	例如:/Users/yixia/Documents 大学英语B统考1 1 400 0`)
	flag.Parse()

	var splitStr string = "/"
	path := flag.Arg(0)
	if path == "" {
		fmt.Println("🐑温馨提示:缺少参数, 请参考上述提示~~~")
		return
	}
	fileName := flag.Arg(1)
	if fileName == "" {
		fmt.Println("🐑温馨提示:缺少参数, 请参考上述提示~~~")
		return
	}
	// 1 mac 2 windows 默认为 1
	osMac := flag.Arg(2)
	if osMac != "" && osMac != "1" && osMac != "2" {
		fmt.Println("🐑温馨提示:电脑系统参数错误, 请参考上述提示~~~")
		return
	}
	if osMac == "" {
		osMac = "1"
		splitStr = "/"
	} else if osMac == "1" {
		splitStr = "/"
	} else if osMac == "2" {
		splitStr = "\\"
	}
	var imgDpi float64 = 400
	imgDpiStr := flag.Arg(3)
	if imgDpiStr != "" {
		imgDpiRt := utils.ToFloat64(imgDpi)
		if imgDpiRt != 0 {
			imgDpi = imgDpiRt
		}
	}
	closeClear := flag.Arg(4)
	clearStr := "开启"
	if closeClear == "1" {
		clearStr = "关闭"
	}
	var outTempPath string
	var outImgPath string
	var outDocPath string

	if !PathExists(path) {
		fmt.Println("🐑: 抱歉,输入地址有误,请检查~~~")
		return
	}
	if strings.HasSuffix(path, splitStr) {
		path = path[0 : len(path)-1]
	}
	outImgPath = CreateDateDir(fmt.Sprintf("%s%s%s", path, splitStr, "image"))
	outDocPath = CreateDateDir(fmt.Sprintf("%s%s%s", path, splitStr, "doc"))
	outTempPath = CreateDateDir(fmt.Sprintf("%s%s%s", path, splitStr, "temp"))
	if strings.HasSuffix(fileName, "pdf") {
		fileName = fileName[0 : len(fileName)-4]
	}
	fmt.Println("")
	fmt.Println(fmt.Sprintf("输入目标文件:%s%s%s.pdf", path, splitStr, fileName))
	fmt.Println(fmt.Sprintf("输出图片路径:%s", outImgPath))
	fmt.Println(fmt.Sprintf("输出txt路径:%s", outTempPath))
	fmt.Println(fmt.Sprintf("输出Doc路径:%s", outDocPath))
	fmt.Println(fmt.Sprintf("清晰度:%f", imgDpi))
	fmt.Println(fmt.Sprintf("无痕模式:%s", clearStr))
	fmt.Println("")
	fmt.Println("         🐑 🐑 🐑 开始干活了,驾驾驾 ~~~ 🐑 🐑 🐑 ")
	fmt.Println("")
	fmt.Println("         	🐑 🐑 🐑PDF转化为图片开始~~~🐑 🐑 🐑 ")

	// 处理PDF->image
	imagePathArr, err := pdfDeal(path, fileName, outImgPath, imgDpi, splitStr)
	if err != nil {
		fmt.Println("🐑: 处理PDF->image失败了, 😭😭😭~~~")
		return
	}
	fmt.Println("🐑 🐑 🐑 ✌️✌️✌️ 阶段 1 完美收工 ~~~ ✌️✌️✌️ 🐑 🐑 🐑 ")
	fmt.Println("")
	// 处理image->doc
	txtTempPathArr, err := batchRunImg2Doc(outImgPath, fileName, outDocPath, outTempPath, splitStr, osMac)
	if err != nil {
		fmt.Println("🐑: 处理image->doc失败了, 😭😭😭~~~")
		return
	}
	if closeClear != "1" {
		clean(imagePathArr, txtTempPathArr)
	}
	fmt.Println("🐑 🐑 🐑 ✌️✌️✌️ 任务执行成功了 ~~~ ✌️✌️✌️ 🐑 🐑 🐑")
}

type FileEntity struct {
	Idx      int    `json:"idx"`
	FileName string `json:"file_name"`
}

func FileExist(path string) bool {
	_, err := os.Lstat(path)
	return !os.IsNotExist(err)
}

func PathExists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	if err != nil {
		panic("🐑: 抱歉,输入地址有误,请检查~~~")
	}
	return false
}

func CreateDateDir(path string) string {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		// 必须分成两步:先创建文件夹、再修改权限
		os.Mkdir(path, 0777) //0777也可以os.ModePerm
		os.Chmod(path, 0777)
	}
	return path
}

func clean(imagePathArr []string, txtTempPathArr []string) {
	var errr error
	for _, s := range imagePathArr {
		errr = os.Remove(s)
	}
	for _, s := range txtTempPathArr {
		errr = os.Remove(s)
	}
	if errr != nil {
		fmt.Println("🐑: 抱歉过程文件删除失败了,请手动清理吧 😭😭😭~~~")
	}
}
func pdfDeal(path string, fileName string, outImgPath string, imgDpi float64, splitStr string) ([]string, error) {
	doc, err := fitz.New(fmt.Sprintf("%s%s%s.pdf", path, splitStr, fileName))
	if err != nil {
		return nil, err
		panic(err)
	}
	defer doc.Close()
	// Extract pages as images
	imgList := make([]string, 0)
	totalNum := doc.NumPage()
	for n := 0; n < totalNum; n++ {
		imgPath := fmt.Sprintf("%s%s%s_%d.jpg", outImgPath, splitStr, fileName, n)
		imgList = append(imgList, imgPath)
		if FileExist(imgPath) {
			fmt.Println(fmt.Sprintf("🐑: 文件存在跳过,path:%s", imgPath))
			continue
		}
		img, err := doc.ImageDPI(n, imgDpi)
		if err != nil {
			return nil, err
		}
		f, err := os.Create(imgPath)
		if err != nil {
			return nil, err
		}
		err = jpeg.Encode(f, img, &jpeg.Options{jpeg.DefaultQuality})
		if err != nil {
			return nil, err
		}
		f.Close()
		fmt.Println(fmt.Sprintf("🐑: 一共需要转换 %d 张图, 已完成 %d 张,请耐心等待一下~~~", totalNum, n))
	}
	fmt.Println(fmt.Sprintf("🐑: PDF转化的图片存放地址:%s", outImgPath))
	return imgList, nil
}

func batchRunImg2Doc(targetDir string, filePre string, outfileDir string, outTempPath string, splitStr string, osMac string) ([]string, error) {
	fmt.Println("")
	fmt.Println("         	🐑 🐑 🐑图片转化为txt文件开始~~~🐑 🐑 🐑 ")
	files, err := ioutil.ReadDir(targetDir)
	if err != nil {
		fmt.Println("打开文件夹失败")
	}
	if len(files) == 0 {
		return nil, nil
	}
	fileArr := make([]FileEntity, 0)
	for _, file := range files {
		fileName := file.Name()
		if !strings.HasPrefix(fileName, filePre) {
			continue
		}
		idx := strings.Split(strings.Split(fileName, ".")[0], "_")[1]
		fileArr = append(fileArr, FileEntity{
			Idx:      utils.ToInt(idx),
			FileName: fileName,
		})
		// 排序
		sort.Sort(IdxSort(fileArr))
	}
	if len(files) == 0 {
		return nil, nil
	}
	outFileArr := make([]string, 0)
	for i, fileVo := range fileArr {
		fileName := fileVo.FileName
		outFileName := fmt.Sprintf("%s%s%s_%d", outTempPath, splitStr, filePre, i)
		outFileArr = append(outFileArr, outFileName+".txt")
		if FileExist(outFileName + ".txt") {
			fmt.Println(fmt.Sprintf("🐑: 文件存在跳过,path:%s", outFileName+".txt"))
			continue
		}
		cmd := "tesseract"
		if osMac == "1" {
			cmd = "tesseract.ext"
		}
		_, err = runCmd(cmd, []string{fmt.Sprintf("%s%s%s", targetDir, splitStr, fileName), outFileName, "-l", "chi_sim+eng"})
		if err != nil {
			fmt.Println(fmt.Sprintf("🐑: 图片转换失败:%s", fileName))
			return nil, err
		}
		fmt.Println(fmt.Sprintf("🐑: 完成 => %s", fileVo.FileName))
	}
	fmt.Println("🐑 🐑 🐑 ✌️✌️✌️ 阶段 2 完美收工 ~~~ ✌️✌️✌️ 🐑 🐑 🐑 ")
	fmt.Println("")
	fmt.Println("         	🐑 🐑 🐑txt文件合并开始~~~🐑 🐑 🐑 ")
	if strings.HasSuffix(outfileDir, splitStr) {
		outfileDir = outfileDir + filePre + ".docx"
	} else {
		outfileDir = outfileDir + splitStr + filePre + ".docx"
	}
	//创建一个新文件,写入内容 5 句 “http://c.biancheng.net/golang/”
	filePath := outfileDir
	_, err = os.Create(filePath)
	if err != nil {
		fmt.Println("文件创建失败", err)
		return nil, err
	}
	for _, path := range outFileArr {
		file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND, 0666)
		if err != nil {
			fmt.Println("文件打开失败", err)
		}
		//写入文件时,使用带缓存的 *Writer
		write := bufio.NewWriter(file)
		//及时关闭file句柄
		defer file.Close()
		write.WriteString(ReadFile2String(path))
		//Flush将缓存的文件真正写入到文件中
		write.Flush()
	}
	fmt.Println(fmt.Sprintf("🐑: doc文件存放地址:%s", filePath))
	return outFileArr, nil
}

func ReadFile2String(readPath string) string {
	//获得一个file
	f, err := os.Open(readPath)
	if err != nil {
		fmt.Println("read fail")
		return ""
	}

	//把file读取到缓冲区中
	defer f.Close()
	var chunk []byte
	buf := make([]byte, 1024)

	for {
		//从file读取到buf中
		n, err := f.Read(buf)
		if err != nil && err != io.EOF {
			fmt.Println("read buf fail", err)
			return ""
		}
		//说明读取结束
		if n == 0 {
			break
		}
		//读取到最终的缓冲区中
		chunk = append(chunk, buf[:n]...)
	}

	return string(chunk)
	//fmt.Println(string(chunk))
}

func runCmd(cmdName string, argsArr []string) (string, error) {
	cmd := exec.Command(cmdName, argsArr...)
	var ioReader io.ReadCloser
	var err error
	if ioReader, err = cmd.StderrPipe(); err != nil {
		fmt.Println("获取输出对象失败 ~~~")
		return "", err
	}
	defer ioReader.Close()

	if err = cmd.Start(); err != nil {
		fmt.Println("命令执行失败 ~~~")
		return "", err
	}
	var outByte []byte
	if outByte, err = ioutil.ReadAll(ioReader); err != nil {
		fmt.Println("获取执行结果内容失败 ~~~")
		return "", err
	}
	return string(outByte), err
}

type IdxSort []FileEntity

func (mt IdxSort) Len() int      { return len(mt) }
func (mt IdxSort) Swap(i, j int) { mt[i], mt[j] = mt[j], mt[i] }
func (mt IdxSort) Less(i, j int) bool {
	return mt[i].Idx < mt[j].Idx
}

2. 打包

# mac :  
	go build -o pdf2Doc-mac cmd/pdf2img.go
# linux: 
	CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o pdf2Doc-mac cmd/pdf2img.go
# windows: (需要安装 brew install mingw-w64)
	CC=x86_64-w64-mingw32-gcc CGO_ENABLED=1 GOOS=windows go build -o pdf2Doc-win.exe cmd/pdf2img.go 

3. 安装环境

mac安装软件 
	1. homebrew 和git,打开终端执行下述命令 参考(https://zhuanlan.zhihu.com/p/372576355)
		/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"
	2. 切换 homebrew 源
		# 1.修改 brew.git 为阿里源
			git -C "$(brew --repo)" remote set-url origin https://mirrors.aliyun.com/homebrew/brew.git

		# 2.修改 homebrew-core.git 为阿里源
			git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.aliyun.com/homebrew/homebrew-core.git

		# 3.zsh 替换 brew bintray 镜像
			echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles' >> ~/.zshrc
			source ~/.zshrc
	3. 上一步如果没有安装 git 需要安装 git,打开终端执行下述命令
		brew install git
	4. 安装 tesseract,打开终端执行下述命令 参考(https://blog.csdn.net/qq_38680405/article/details/105605928)
		brew install tesseract
		

windows安装软件
	1. 下载 tesseract (https://github.com/UB-Mannheim/tesseract/wiki)
	2. 配置 "tesseract" 环境变量,可参考(https://jingyan.baidu.com/article/00a07f3876cd0582d128dc55.html)
		2.1 找到 tesseract 的安装目录,
		2.2 打开win10系统的“此电脑”文件夹,然后在上面的选项中,点击“计算机”选项,在顶部,
		2.3 接着我们点击打开“系统属性”,进入系统的信息界面,离成功就快了;
	 	2.4 在系统的信息界面,点击左侧的【高级系统设置】按钮打开系统属性,
	  	2.5 在“系统属性”界面的最下面,就有“环境变量”的选项,请点击该选项,进入环境变量的编辑框之中。
  		2.6 打开之后我们就可以进行 添加一个配置项 如 tesseract 的安装目录为 “D:\Users\tesseract” 的话, 找到对应的文件下的bin 目录,如“ D:\Users\tesseract\bin”
	

4.使用


mac: 
   1.打开终端
   2.将文件拉至终端
   3.后边追加参数 增加参数
	   参数1:目标pdf文件路径
	   参数2:目标文件名称
	   参数3:电脑系统 1:mac 2:windows
	   参数4:可选,100 - 800,代表的图片清晰度,数值越大,图片越清晰,相对执行时间也会变长,建议:400
	   参数5:1/0,是否关闭无痕模式,1关闭 0开启,默认开启
	   追加的参数例如:/Users/yixia/Documents 大学英语B统考1 1 400 1
	   命令预览如: /Users/yixia/Desktop/归档/pdf2Doc-mac /Users/yixia/Documents 大学英语B统考 1 400 1
   4. 回车执行命令
window:
   1. 打开CMD
   2. 找到 可执行文件 “pdf2Doc-win.exe”
   3. 执行命令 pdf2Doc-win.exe /Users/yixia/Documents 大学英语B统考 2 400 1
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值