Windows下静态链接编译pdfium和使用

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 安装和更新

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

demo

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博客
请先登录后发表评论
  • 最新评论
  • 总共0条评论
  • 本博客使用免费开源的 laravel-bjyblog v5.5.1.1 搭建 © 2014-2018 lokie.wang 版权所有 ICP证:沪ICP备18016993号
  • 联系邮箱:kitche1985@hotmail.com