头图

PDFium is Chromium's PDF rendering engine, the license agreement is BSD 3-Clause. Unlike Mozilla's PDF.js based on HTML5, PDFium is based on the rendering code of Foxit Software, which is open sourced by Google in cooperation with it.

In addition, the Qt PDF module also selects PDFium, see QtWebEngine / QtPdf .

This article will introduce how to use PDFium to implement a simple PDF reader. For the code, see: https://github.com/ikuokuo/pdfium-reader .

Compile PDFium

Use precompiled library: https://github.com/bblanchon/pdfium-binaries

Otherwise, refer to 161a9dca415b70 PDFium / README to compile by yourself. The practical steps are as follows:

# 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 during the period are as follows:

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)

Use PDFium

Read PDFium / Getting Started to learn how to initialize PDFium and load documents. The steps are as follows, or see 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;
}

getting information

For examples, see pdf_info.cc , which can print PDF metadata, page information, etc.

Such as FPDF_GetMetaText obtain metadata ( UTF-16LE code):

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;
  }
}

Render the page

For examples, see pdf_render.cc , which can render PDF pages and save them as PNG.

FPDF_RenderPageBitmap render a page:

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 saved as 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;
}

Implement the UI

PDFium Reader code given in this article, using ImGui + GLFW + OpenGL3 can realize the UI across the three major desktop systems.

If you want to know more, you can directly look at the code, compile and run according to README.

GoCoding personal practice experience sharing, please follow the official account!

GoCoding
88 声望5 粉丝

Go coding in my way :)