PDFium 渲染
PDFium 是 Chromium 的 PDF 渲染引擎,许可协议为 BSD 3-Clause。不同于 Mozilla 基于 HTML5 的 PDF.js,PDFium 是基于 Foxit Software (福昕软件)的渲染代码,Google 与其合作开源出的。
此外,Qt PDF 模块也选用了 PDFium ,可见 QtWebEngine / QtPdf。
本文将介绍如何用 PDFium 实现一个简单的 PDF 阅读器,代码见:https://github.com/ikuokuo/pdfium-reader 。
编译 PDFium
使用预编译库:https://github.com/bblanchon/pdfium-binaries
不然,参考 PDFium / README 自己编译,实践步骤如下:
# get depot_tools, contains: gclient, ninja, gn, ...
git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH="$PATH:$HOME/Codes/Star/depot_tools"
# get pdfium
cd pdfium-reader/
mkdir -p third_party/chromium
cd third_party/chromium
gclient config --unmanaged https://pdfium.googlesource.com/pdfium.git
gclient sync
cd pdfium
# get deps
# on linux, install additional build dependencies
./build/install-build-deps.sh
# gn config
# args see the following `out/Release/args.gn`
gn args out/Release
# ninja build
# pdfium
ninja -C out/Release pdfium
# pdfium_test
ninja -C out/Release pdfium_test
# run sample: pdf > ppm
./out/Release/pdfium_test --ppm path/to/myfile.pdf
期间 out/Release/args.gn
内容如下:
use_goma = false # Googlers only. Make sure goma is installed and running first.
is_debug = false # Enable debugging features.
# Set true to enable experimental Skia backend.
pdf_use_skia = false
# Set true to enable experimental Skia backend (paths only).
pdf_use_skia_paths = false
pdf_enable_xfa = false # Set false to remove XFA support (implies JS support).
pdf_enable_v8 = false # Set false to remove Javascript support.
pdf_is_standalone = true # Set for a non-embedded build.
pdf_is_complete_lib = true # Set for a static library build.
is_component_build = false # Disable component build (Though it should work)
使用 PDFium
阅读 PDFium / Getting Started,了解如何初始化 PDFium 及载入文档。步骤如下,或见 pdfium_start.c:
#include <fpdfview.h>
#include <stdio.h>
int main(int argc, char const *argv[]) {
FPDF_STRING test_doc = "test_doc.pdf";
if (argc >= 2) {
test_doc = argv[1];
}
printf("test_doc: %s\n", test_doc);
FPDF_InitLibrary();
FPDF_DOCUMENT doc = FPDF_LoadDocument(test_doc, NULL);
if (!doc) {
unsigned long err = FPDF_GetLastError();
// Load pdf docs unsuccessful: ...
goto EXIT;
}
FPDF_CloseDocument(doc);
EXIT:
FPDF_DestroyLibrary();
return 0;
}
获取信息
样例见 pdf_info.cc,可打印 PDF 元数据、页面信息等。
如 FPDF_GetMetaText
获取元数据(UTF-16LE
编码):
void PrintPdfMetaData(FPDF_DOCUMENT doc) {
static constexpr const char *kMetaTags[] = {
"Title", "Author", "Subject", "Keywords",
"Creator", "Producer", "CreationDate", "ModDate"};
for (const char *meta_tag : kMetaTags) {
const unsigned long len = FPDF_GetMetaText(doc, meta_tag, nullptr, 0);
if (!len)
continue;
std::vector<char16_t> buf(len);
FPDF_GetMetaText(doc, meta_tag, buf.data(), buf.size());
auto text = strings::FromUtf16(std::u16string(buf.data()));
if (strcmp(meta_tag, "CreationDate") == 0 ||
strcmp(meta_tag, "ModDate") == 0) {
text = fpdf::DateToRFC3399(text);
}
std::cout << " " << meta_tag << ": " << text << std::endl;
}
}
渲染页面
样例见 pdf_render.cc,可渲染 PDF 页面并保存为 PNG。
FPDF_RenderPageBitmap
渲染某一页:
void PdfRenderPage(const std::string &pdf_name, FPDF_DOCUMENT doc, int index) {
Timer t;
FPDF_PAGE page = FPDF_LoadPage(doc, index);
double scale = 1.0;
// double scale = 2.0;
int width = static_cast<int>(FPDF_GetPageWidth(page) * scale);
int height = static_cast<int>(FPDF_GetPageHeight(page) * scale);
int alpha = FPDFPage_HasTransparency(page) ? 1 : 0;
ScopedFPDFBitmap bitmap(FPDFBitmap_Create(width, height, alpha)); // BGRx
if (bitmap) {
FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, fill_color);
int rotation = 0;
int flags = FPDF_ANNOT;
FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, width, height,
rotation, flags);
auto t_render = t.Elapsed();
int stride = FPDFBitmap_GetStride(bitmap.get());
void *buffer = FPDFBitmap_GetBuffer(bitmap.get());
char img_name[256];
int chars_formatted = snprintf(
img_name, sizeof(img_name), "%s.%d.png", pdf_name.c_str(), index);
if (chars_formatted < 0 ||
static_cast<size_t>(chars_formatted) >= sizeof(img_name)) {
fprintf(stderr, "Filename is too long: %s\n", img_name);
exit(EXIT_FAILURE);
}
auto ok = PdfWritePng(img_name, buffer, width, height, stride);
if (!ok) {
fprintf(stderr, "Write png failed: %s\n", img_name);
exit(EXIT_FAILURE);
}
auto t_write = t.Elapsed();
fprintf(stdout, "%s\n", img_name);
fprintf(stdout, " %02d: %dx%d, render=%lldms, write=%lldms\n",
index, width, height, t_render, t_write);
} else {
fprintf(stderr, "Page was too large to be rendered.\n");
exit(EXIT_FAILURE);
}
FPDF_ClosePage(page);
}
stb_image_write.h 存为 PNG:
bool PdfWritePng(const std::string &img_name, void *buffer,
int width, int height, int stride) {
// BGRA > RGBA
auto buf = reinterpret_cast<uint8_t *>(buffer);
for (int r = 0; r < height; ++r) {
for (int c = 0; c < width; ++c) {
auto pixel = buf + (r*stride) + (c*4);
auto b = pixel[0];
pixel[0] = pixel[2]; // b = r
pixel[2] = b; // r = b
}
}
return stbi_write_png(img_name.c_str(), width, height, 4, buf, stride) != 0;
}
实现 UI
本文给出的 PDFium Reader 代码,用的 ImGui+GLFW+OpenGL3 实现的 UI,可跨三大桌面系统。
想进一步了解的,可以直接看代码,编译运行依照 README。
GoCoding 个人实践的经验分享,可关注公众号!
PDFium 渲染的更多相关文章
- pdfium 之二
https://www.foxitsoftware.cn/products/premium-pdfium/feature.php 基于谷歌PDFium开源代码 谷歌采用福昕的PDF技术为其PDF开源项 ...
- IE6、7下html标签间存在空白符,导致渲染后占用多余空白位置的原因及解决方法
直接上图:原因:该div包含的内容是靠后台进行print操作,输出的.如果没有输出任何内容,浏览器会默认给该空白区域添加空白符.在IE6.7下,浏览器解析渲染时,会认为空白符也是占位置的,默认其具有字 ...
- 【前端性能】高性能滚动 scroll 及页面渲染优化
最近在研究页面渲染及web动画的性能问题,以及拜读<CSS SECRET>(CSS揭秘)这本大作. 本文主要想谈谈页面优化之滚动优化. 主要内容包括了为何需要优化滚动事件,滚动与页面渲染的 ...
- HTML渲染过程详解
无意中看到寒冬关于前端的九个问题,细细想来我也只是对第一.二.九问有所了解,正好也趁着这个机会梳理一下自己的知识体系.由于本人对http协议以及dns对url的解析问题并不了解,所以这里之探讨url请 ...
- 【原】实时渲染中常用的几种Rendering Path
[原]实时渲染中常用的几种Rendering Path 本文转载请注明出处 —— polobymulberry-博客园 本文为我的图形学大作业的论文部分,介绍了一些Rendering Path,比较简 ...
- geotrellis使用(二十八)栅格数据色彩渲染(多波段真彩色)
目录 前言 实现过程 总结 一.前言 上一篇文章介绍了如何使用Geotrellis渲染单波段的栅格数据,已然很是头疼,这几天不懈努力之后工作又进了一步,整清楚了如何使用Geotrelli ...
- CSS 3 学习——transform 3D转换渲染
以下内容根据官方规范翻译,没有翻译关于SVG变换的内容和关于矩阵计算的内容. 一般情况下,元素在一个无景深无立体感的平面(flat plane)上渲染,这个平面就是其包含块所处的平面.同时,页面上的其 ...
- 高级渲染技巧和代码示例 GPU Pro 7
下载代码示例 移动设备正呈现着像素越来越高,屏幕尺寸越来越小的发展趋势. 由于像素着色的能耗非常大,因此 DPI 的增加以及移动设备固有的功耗受限环境为降低像素着色成本带来了巨大的压力. MSAA 有 ...
- 【Web动画】CSS3 3D 行星运转 && 浏览器渲染原理
承接上一篇:[CSS3进阶]酷炫的3D旋转透视 . 最近入坑 Web 动画,所以把自己的学习过程记录一下分享给大家. CSS3 3D 行星运转 demo 页面请戳:Demo.(建议使用Chrome打开 ...
随机推荐
- 不可错过的stm32单片机直流电机驱动与测速详解
stm32直流电机驱动与测速 说实话就现在的市场应用中stm32已经占到了绝对住到的地位,51已经成为过去式,32的功能更加强大,虽然相应的难度有所增加,但是依然阻止不了大家学习32的脚步,不说大话了 ...
- nodejs 连接 mysql 查询事务处理
自己用 mysql 很多次的,然后又是主玩nodejs的.专门写一篇文章来说说nodejs连接mysql数据库.在使用之前,请检查计算机是否具有一下环境! nodejs 执行环境. mysql数据库环 ...
- systemverilog 字符串类型
转载:https://blog.csdn.net/Holden_Liu/article/details/100727957 传统的Veriog仅仅支持文字表述上的字符串, 而SystemVerilog ...
- mac bigsur 安装mysql步骤
我首先下载的是mysql8.x,安装完后,在偏好设置里面,双击mysql图标,弹窗:未能载入偏好设置面板MySQL,重启无果,查攻略说是要安装5.7.x,在mysql官网上,下载5.7.29 强烈建议 ...
- python的分支结构
python分支结构 if结构 python的 if 选择分支结构的基础语法如下,需要注意的是判断条件后面是半角的分号,它的作用相当于Java中的小括号 if 判断条件 : 代码块 elif 判断条件 ...
- python进阶(22)pydantic--数据类型校验
pydantic库的作用 pydantic库是一种常用的用于数据接口schema定义与检查的库. Pydantic 在运行时强制执行类型提示,并在数据无效时提供用户友好的错误信息. pydantic安 ...
- 网关服务spring cloud zuul
Zuul例子配置文件 spring.application.name=switch-gateway server.port=5555 请求路由 传统路由方式 zuul.routes.api-a-url ...
- 『动善时』JMeter基础 — 57、Linux系统中运行JMeter脚本
目录 1.Linux系统中安装Java环境 (1)解压Java安装包 (2)配置Java环境变量 (3)验证Java环境是否配置成功 2.Linux系统中安装JMeter (1)下载JMeter (2 ...
- CentOS服务器的网络配置与部署
1.系统安装与软件安装 1.1选择CentOs7.9release版本用作所研发系统部署服务器,官网以及所选择镜像为地址为:http://ftp.sjtu.edu.cn/centos/7.9.2009 ...
- rz安装
rpm -ivh http://www.rpmfind.net/linux/centos/6.10/os/x86_64/Packages/lrzsz-0.12.20-27.1.el6.x86_64.r ...