pdfium是Google chromium中显示PDF文件的部分是Google和foxit共同开源的一个PDF渲染引擎,可以编译为windows Linux macos android wasm和 iOS,用于显示渲染PDF,也可以通过这个组件渲染PDF文件为图片。
这个组件做为chromium项目的以部分开源在gogolesource上。
项目地址:
https://pdfium.googlesource.com/pdfium/
由于Google下载代码要使用depot_tools,众所周知的原因在国内是非常困难。这里记录编译的过程。
depot_tools是Google自有的源代码下载编译工具。安装方法如下:
首先需要设置科学上网和代理,这里采用v2rayN做为客户端,配置好后,设置为手动添加代理,这样v2rayN会在127.0.0.1:8002上建立http proxy,设置GIT和系统的代理。
Git 代理
set http_proxy="http://127.0.0.1:8002"
set HTTP_PROXY="http://127.0.0.1:8002"
set https_proxy="http://127.0.0.1:8002"
set HTTPS_PROXY="http://127.0.0.1:8002"
git config --global https.proxy http://127.0.0.1:8001
git config --global https.proxy http://127.0.0.1:8001
系统代理:
netsh
winhttp
set proxy 127.0.0.1:8002
下载depot_tools
cd f:\
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
之后把 f:depot_tools 加入到path中,运行如下命令:
gclient
启动更新过程,但是在国内一定会失败在有代理存在的情况下。原因是depot_tools 调用powershell不支持http_proxy这种方式设置代理。解决方案如下:
找到F:depot_tools.cipd_impl.ps1下的代码进行修改。找DownloadFile方法
# Download a file to a particular path.
function Download-File {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]
$UserAgent,
[Parameter(Mandatory = $true)]
[string]
$Url,
[Parameter(Mandatory = $true)]
[string]
$Path
)
$wc = (New-Object System.Net.WebClient)
$wc.Headers.Add("User-Agent", $UserAgent)
$WebProxy = (New-Object System.Net.WebProxy("http://127.0.0.1:8002",$true))
try {
# Download failures were reported on Windows 8.1 without this line.
[System.Net.ServicePointManager]::SecurityProtocol = `
[System.Net.SecurityProtocolType]::Tls12
$wc.Proxy = $WebProxy
$wc.DownloadFile($Url, $Path)
}
catch {
$err = $_.Exception.Message
throw "Failed to download the file, check your network connection, $err"
}
finally {
$wc.Dispose()
}
}
修改完毕后重新gclient会更新cipd client 成功,但是因为网络问题, chrome-infra-packages.appspot.com存在DNS污染问题,需要通过hosts文件修改
142.250.196.148 chrome-infra-packages.appspot.com
环境依赖:
vs2019 with c++ develop feature
windows 10 sdk 10.23 with windows debug tool feature
python 3
获取代码:
mkdir repo
cd repo
gclient config --unmanaged https://pdfium.googlesource.com/pdfium.git
gclient sync
cd pdfium
设置参数:
mkdir dist
gn args dist
参数如下:
use_goma = true # Googlers only. Make sure goma is installed and running first.
is_debug = true # 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 = true # Set false to remove XFA support (implies JS support).
pdf_enable_v8 = true # Set false to remove Javascript support.
pdf_is_standalone = true # Set for a non-embedded build.
is_component_build = false # Disable component build (Though it should work)
pdf_is_complete_lib= true # compile full size static lib
启动编译
ninja -C dist pdfium_all
完成编译后可以从dist/obj/ 下获取pdfium.lib
vs2019 设置
设置inlucde path,头文件可以从pdfium src目录下public中获取获取,复制到项目中,注意,需要修改#include删除public
Link设置:
关于libc++.lib:
由于pdfium采用了gnu的libc++,其实可以从编译结果F:repopdfiumdistobjbuildtoolsthird_partylibc++libc++中通过Lib命令制作
打开vs2019 command line
cd F:\repo\pdfium\dist\obj\buildtools\third_party\libc++\libc++
lib /out libc++.lib *.obj
关于vc runtime采用多线程调试 (/MTd)
demo code:
#include <chrono>
#include "fpdf_edit.h"
#include "fpdf_scopers.h"
#include "fpdfview.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define __STDC_LIB_EXT1__
#include "stb_image_write.h"
#include "helper.h"
void PdfRender(const std::string& pdf_name, FPDF_DOCUMENT doc);
void PdfRenderPage(const std::string& pdf_name, FPDF_DOCUMENT doc, int index);
bool PdfWritePng(const std::string& img_name, void* buffer,
int width, int height, int stride);
int main(int argc, char const* argv[]) {
FPDF_STRING test_doc = "d:\\test11.pdf";
if (argc >= 2) {
test_doc = argv[1];
}
fprintf(stdout, "test_doc: %s\n", test_doc);
FPDF_LIBRARY_CONFIG config;
config.version = 3;
config.m_pUserFontPaths = NULL;
config.m_pIsolate = NULL;
config.m_v8EmbedderSlot = 0;
config.m_pPlatform = NULL;
config.m_pUserFontPaths = NULL;
FPDF_InitLibraryWithConfig(&config);
FPDF_DOCUMENT doc = FPDF_LoadDocument(test_doc, NULL);
if (!doc) {
fpdf::PrintLastError();
goto EXIT;
}
PdfRender(test_doc, doc);
FPDF_CloseDocument(doc);
EXIT:
FPDF_DestroyLibrary();
return 0;
}
class Timer {
public:
using clock = std::chrono::system_clock;
Timer() { Start(); }
~Timer() = default;
void Start() {
time_start_ = clock::now();
}
int64_t Elapsed(bool restart = true) {
using namespace std::chrono; // NOLINT
auto d = duration_cast<milliseconds>(clock::now() - time_start_).count();
if (restart) Start();
return d;
}
private:
clock::time_point time_start_;
};
void PdfRender(const std::string& pdf_name, FPDF_DOCUMENT doc) {
int page_count = FPDF_GetPageCount(doc);
for (int i = 0; i < page_count; ++i) {
PdfRenderPage(pdf_name, doc, i);
}
}
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_PRINTING | 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);
}
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;
}
本文为Lokie.Wang原创文章,转载无需和我联系,但请注明来自lokie博客http://lokie.wang