首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

自制PDF阅读器

我们可以使用 go-fitz 很轻易的制作一款 pdf 文档阅读器,原理是使用 go-fitz 将 pdf 转换为 html 内容,然后使用 go 搭建一个 web 服务,之后再通过浏览器进行文档阅读。相比直接使用浏览器阅读 pdf 文档的好处是原本一些不支持在 pdf 文档中使用的浏览器插件变得可以使用了,我们可以很轻松在阅读 pdf 文档时使用文本翻译,文本语音合成,GPT文章总结……这些功能。

本教程将引导你使用 Go 和 go-fitz 库构建一个 PDF 阅读器。go-fitz 是一个流行的 Go 库,它允许你操作 PDF 文档,包括将它们转换为 HTML。

1. 导入依赖项

首先,你需要导入必要的依赖项:

import?(

"flag"

"fmt"

"log"

"net/http"

"os"

"os/signal"

"strconv"

"syscall"

"github.com/gen2brain/go-fitz"

)

flag:用于处理命令行参数。

fmt:用于格式化和打印输出。

log:用于记录信息。

net/http:用于创建 Web 服务器。

os:用于处理文件。

os/signal:用于处理操作系统信号。

strconv:用于将字符串转换为其他数据类型。

syscall:用于处理系统调用。

github.com/gen2brain/go-fitz:go-fitz 库。

2. 定义常量和模板

接下来,定义一个 HTML 框架和一个常量:

const?frame?=?`

body{background-color:slategray}

div{position:relative;background-color:white;margin:1em?auto;box-shadow:1px?1px?8px?-2px?black}

div#pages{background-color:#0000;margin:0;box-shadow:none}

p{position:absolute;white-space:pre;margin:0}

async?function?load_page(position,?page_ix=-1)?{

const?pages?=?document.querySelector('#pages');

if?(page_ix?==?-1)?{

if?(position?==?'beforeEnd')?{

page_ix?=?Number(

pages.lastElementChild.id.substr('page'.length))?+?1;

}?else?if?(position?==?'afterBegin')?{

page_ix?=?Number(

pages.firstElementChild.id.substr('page'.length))?-?1;

}

}

const?url?=?'/page?ix='?+?page_ix;

const?resp?=?await?fetch(url);

if?(!resp.ok)?{

return;

}

const?page?=?await?resp.text()

pages.insertAdjacentHTML(position,?page);

if?(position?==?'afterBegin')?{

window.scrollTo(0,?pages.firstChild.offsetHeight);

}

}

function?load_more()?{

const?autoload?=?async?()?=>?{

if?((window.innerHeight?+?window.scrollY)?>=?document.body.parentNode.offsetHeight)?{

window.removeEventListener("scroll",?autoload);

console.log("loading?page?to?end...");

await?load_page('beforeEnd');

window.addEventListener("scroll",?autoload);

}?else?if?(window.scrollY?==?0)?{

window.removeEventListener("scroll",?autoload);

console.log("loading?page?to?begin...");

await?load_page('afterBegin');

window.addEventListener("scroll",?autoload);

}

}

window.addEventListener("scroll",?autoload);

}

window.onload?=?async?()?=>?{

const?page_ix?=?(new?URLSearchParams(location.search)).get('start')?||?0;

await?load_page('beforeEnd',?page_ix);

await?load_page('afterBegin',?page_ix?-?1);

load_more();

}

`

frame:这是网页的 HTML 框架。它定义了页面的布局、样式和包含 PDF 内容的容器。

pages:这是将 PDF 页面插入的 HTML 容器的 ID。

3. 处理命令行参数

接下来,从命令行参数获取 PDF 文件名:

filename?:=?flag.String("filename",?"",?"pdf?filename")

flag.Parse()

这会获取一个名为 --filename 的命令行参数,它表示要打开的 PDF 文件的路径。

4. 打开 PDF 文档

使用 go-fitz 打开 PDF 文档:

var?doc?*fitz.Document

doc,?err?=?fitz.New(*filename)

if?err?!=?nil?{

log.Fatal(err)

}

这会打开给定路径的 PDF 文档并将其存储在 doc 变量中。

5. 处理系统信号

设置一个信号通道来监听 SIGINT 信号(通常是按 Ctrl+C 触发):

signal_chan?:=?make(chan?os.Signal,?1)

signal.Notify(signal_chan,?syscall.SIGINT)

并设置一个 goroutine 来处理信号并关闭 PDF 文档:

go?func()?{

<-signal_chan

if?doc?!=?nil?{

doc.Close()

}

os.Exit(0)

}()

这将确保在收到 SIGINT 信号时关闭 PDF 文档并退出程序。

6. 处理页面请求

设置一个 HTTP 处理程序来处理 /page 路由,它将提供 PDF 的单个页面:

func?page_handler(w?http.ResponseWriter,?r?*http.Request)?{

values?:=?r.URL.Query()

page_ix,?err?:=?strconv.Atoi(values.Get("ix"))

if?err?!=?nil?{

http.Error(w,?"page?number?valid?faild",?http.StatusBadRequest)

return

}

log.Println("page?ix:",?page_ix)

if?page_ix?<?0?||?page_ix?>=?doc.NumPage()?{

http.Error(w,?"load?page?faild",?http.StatusNotFound)

return

}

html,?err?:=?doc.HTML(page_ix,?false)

if?err?!=?nil?{

http.Error(w,?"load?page?faild",?http.StatusInternalServerError)

return

}

fmt.Fprint(w,?html)

}

这会获取请求的页面索引 (ix 查询参数),验证索引是否有效,然后使用 go-fitz 将页面转换为 HTML 并发送给客户端。

7. 处理首页请求

设置一个 HTTP 处理程序来处理 / 路由,它将提供带有 PDF 内容的 HTML 框架:

func?index_handler(w?http.ResponseWriter,?r?*http.Request)?{

fmt.Fprint(w,?frame)

}

这会将 frame 框架发送给客户端,其中包含用于加载 PDF 页面的脚本。

8. 启动 Web 服务器

最后,启动一个在端口 8000 上监听的 Web 服务器:

http.HandleFunc("/page",?page_handler)

http.HandleFunc("/",?index_handler)

log.Fatal(http.ListenAndServe(":8000",?nil))

这将启动服务器并允许客户端通过浏览器访问 PDF 内容。

9. 使用 PDF 阅读器

现在,你可以通过访问 http://localhost:8000 在浏览器中打开 PDF 阅读器。该页面将显示 PDF 的第一页。你可以使用滚动条浏览页面,或者使用键盘快捷键(如 和 )进行导航。你还可以使用浏览器的插件,如翻译器和语音合成,来增强阅读体验。

完整示例:

package?main

import?(

"flag"

"fmt"

"log"

"net/http"

"os"

"os/signal"

"strconv"

"syscall"

"github.com/gen2brain/go-fitz"

)

var?doc?*fitz.Document

const?frame?=?`

body{background-color:slategray}

div{position:relative;background-color:white;margin:1em?auto;box-shadow:1px?1px?8px?-2px?black}

div#pages{background-color:#0000;margin:0;box-shadow:none}

p{position:absolute;white-space:pre;margin:0}

async?function?load_page(position,?page_ix=-1)?{

const?pages?=?document.querySelector('#pages');

if?(page_ix?==?-1)?{

if?(position?==?'beforeEnd')?{

page_ix?=?Number(

pages.lastElementChild.id.substr('page'.length))?+?1;

}?else?if?(position?==?'afterBegin')?{

page_ix?=?Number(

pages.firstElementChild.id.substr('page'.length))?-?1;

}

}

const?url?=?'/page?ix='?+?page_ix;

const?resp?=?await?fetch(url);

if?(!resp.ok)?{

return;

}

const?page?=?await?resp.text()

pages.insertAdjacentHTML(position,?page);

if?(position?==?'afterBegin')?{

window.scrollTo(0,?pages.firstChild.offsetHeight);

}

}

function?load_more()?{

const?autoload?=?async?()?=>?{

if?((window.innerHeight?+?window.scrollY)?>=?document.body.parentNode.offsetHeight)?{

window.removeEventListener("scroll",?autoload);

console.log("loading?page?to?end...");

await?load_page('beforeEnd');

window.addEventListener("scroll",?autoload);

}?else?if?(window.scrollY?==?0)?{

window.removeEventListener("scroll",?autoload);

console.log("loading?page?to?begin...");

await?load_page('afterBegin');

window.addEventListener("scroll",?autoload);

}

}

window.addEventListener("scroll",?autoload);

}

window.onload?=?async?()?=>?{

const?page_ix?=?(new?URLSearchParams(location.search)).get('start')?||?0;

await?load_page('beforeEnd',?page_ix);

await?load_page('afterBegin',?page_ix?-?1);

load_more();

}

`

func?page_handler(w?http.ResponseWriter,?r?*http.Request)?{

values?:=?r.URL.Query()

page_ix,?err?:=?strconv.Atoi(values.Get("ix"))

if?err?!=?nil?{

http.Error(w,?"page?number?valid?faild",?http.StatusBadRequest)

return

}

log.Println("page?ix:",?page_ix)

if?page_ix?<?0?||?page_ix?>=?doc.NumPage()?{

http.Error(w,?"load?page?faild",?http.StatusNotFound)

return

}

html,?err?:=?doc.HTML(page_ix,?false)

if?err?!=?nil?{

http.Error(w,?"load?page?faild",?http.StatusInternalServerError)

return

}

fmt.Fprint(w,?html)

}

func?index_handler(w?http.ResponseWriter,?r?*http.Request)?{

fmt.Fprint(w,?frame)

}

func?main()?{

filename?:=?flag.String("filename",?"",?"pdf?filename")

flag.Parse()

var?err?error

doc,?err?=?fitz.New(*filename)

if?err?!=?nil?{

log.Fatal(err)

}

signal_chan?:=?make(chan?os.Signal,?1)

signal.Notify(signal_chan,?syscall.SIGINT)

go?func()?{

<-signal_chan

if?doc?!=?nil?{

doc.Close()

}

os.Exit(0)

}()

http.HandleFunc("/page",?page_handler)

http.HandleFunc("/",?index_handler)

log.Fatal(http.ListenAndServe(":8000",?nil))

}

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OtaHAkKanDTHLOxU2T1v9avA0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券
http://www.vxiaotou.com