Add a flag for selecting renderer at runtime
Introduce an experimental config flag in FPDF_LIBRARY_CONFIG to allow
for changing the renderer at runtime. This flag only takes effect if the
config version is 4 or higher. When using a config version of 4, it is
necessary to specify a valid renderer type that will be used to specify
the renderer type at runtime. This is required even if a build does not
include Skia definitions and only has AGG as the available renderer
type.
Expose this flag in the pdfium_test sample, via the --use-renderer
command line option. This option is only available for builds which
are Skia enabled.
Bug: pdfium:1878
Change-Id: I971f8f7d420015a919316b1b3d57fe3d7bea4ce4
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/96904
Reviewed-by: Nigi <nigi@chromium.org>
Commit-Queue: Alan Screen <awscreen@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/build_overrides/pdfium.gni b/build_overrides/pdfium.gni
index 6037a63..8553398 100644
--- a/build_overrides/pdfium.gni
+++ b/build_overrides/pdfium.gni
@@ -21,9 +21,11 @@
# Default: Use PartitionAlloc unless it is a MSVC build.
pdf_use_partition_alloc_override = !is_win || is_clang
-# Build PDFium against skia (experimental) rather than agg, replacing all PDFium
-# graphics.
-# Default: Use agg.
+# Build PDFium to use Skia (experimental) for all PDFium graphics.
+# If enabled, coexists in build with AGG graphics and the default
+# renderer is selectable at runtime.
+# The default is to use AGG when both `pdf_use_skia_override` and
+# `pdf_use_skia_paths_override` are set to their defaults of false.
pdf_use_skia_override = false
# Build PDFium against skia (experimental) rather than agg, adding only path
diff --git a/core/fxge/cfx_defaultrenderdevice.cpp b/core/fxge/cfx_defaultrenderdevice.cpp
index 90ee030..d50d4a1 100644
--- a/core/fxge/cfx_defaultrenderdevice.cpp
+++ b/core/fxge/cfx_defaultrenderdevice.cpp
@@ -8,12 +8,32 @@
#include "core/fxge/dib/cfx_dibitmap.h"
+namespace {
+
+// When build variant is Skia then it is assumed as the default, but might be
+// overridden at runtime.
+#if defined(_SKIA_SUPPORT_)
+CFX_DefaultRenderDevice::RendererType g_default_renderer_type =
+ CFX_DefaultRenderDevice::RendererType::kSkia;
+#endif
+
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+bool IsSkiaVariant() {
+#if defined(_SKIA_SUPPORT_)
+ return g_default_renderer_type ==
+ CFX_DefaultRenderDevice::RendererType::kSkia;
+#elif defined(_SKIA_SUPPORT_PATHS_)
+ return true;
+#endif
+}
+#endif // defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+
+} // namespace
+
// static
bool CFX_DefaultRenderDevice::SkiaIsDefaultRenderer() {
#if defined(_SKIA_SUPPORT_)
- // TODO(crbug.com/pdfium/1878) This will become variable-based once a method
- // is provided to set the default at runtime.
- return true;
+ return g_default_renderer_type == RendererType::kSkia;
#else
return false;
#endif
@@ -22,19 +42,25 @@
// static
bool CFX_DefaultRenderDevice::SkiaPathsIsDefaultRenderer() {
#if defined(_SKIA_SUPPORT_PATHS_)
- // TODO(crbug.com/pdfium/1878) This will become variable-based once a method
- // is provided to set the default at runtime.
return true;
#else
return false;
#endif
}
+#if defined(_SKIA_SUPPORT_)
+// static
+void CFX_DefaultRenderDevice::SetDefaultRenderer(RendererType renderer_type) {
+ g_default_renderer_type = renderer_type;
+}
+#endif
+
CFX_DefaultRenderDevice::CFX_DefaultRenderDevice() = default;
CFX_DefaultRenderDevice::~CFX_DefaultRenderDevice() {
#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
- Flush(true);
+ if (IsSkiaVariant())
+ Flush(true);
#endif
}
@@ -62,11 +88,12 @@
RetainPtr<CFX_DIBitmap> pBackdropBitmap,
bool bGroupKnockout) {
#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
- return AttachSkiaImpl(pBitmap, bRgbByteOrder, pBackdropBitmap,
- bGroupKnockout);
-#else
- return AttachAggImpl(pBitmap, bRgbByteOrder, pBackdropBitmap, bGroupKnockout);
+ if (IsSkiaVariant()) {
+ return AttachSkiaImpl(pBitmap, bRgbByteOrder, pBackdropBitmap,
+ bGroupKnockout);
+ }
#endif
+ return AttachAggImpl(pBitmap, bRgbByteOrder, pBackdropBitmap, bGroupKnockout);
}
bool CFX_DefaultRenderDevice::Create(int width,
@@ -74,8 +101,8 @@
FXDIB_Format format,
RetainPtr<CFX_DIBitmap> pBackdropBitmap) {
#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
- return CreateSkia(width, height, format, pBackdropBitmap);
-#else
- return CreateAgg(width, height, format, pBackdropBitmap);
+ if (IsSkiaVariant())
+ return CreateSkia(width, height, format, pBackdropBitmap);
#endif
+ return CreateAgg(width, height, format, pBackdropBitmap);
}
diff --git a/core/fxge/cfx_defaultrenderdevice.h b/core/fxge/cfx_defaultrenderdevice.h
index 3ffbe04..0e7d77f 100644
--- a/core/fxge/cfx_defaultrenderdevice.h
+++ b/core/fxge/cfx_defaultrenderdevice.h
@@ -49,6 +49,20 @@
// Runtime check to see if SkiaPaths is the renderer variant in use.
static bool SkiaPathsIsDefaultRenderer();
+#if defined(_SKIA_SUPPORT_)
+ // This internal definition of renderer types must stay updated with respect
+ // to the public definition of `FPDF_RENDERER_TYPE`, so that all public
+ // definition values can be mapped to a value in
+ // `CFX_DefaultRenderDevice::RendererType`.
+ enum class RendererType {
+ kAgg = 0,
+ kSkia = 1,
+ };
+
+ // Update default renderer.
+ static void SetDefaultRenderer(RendererType renderer_type);
+#endif // defined(_SKIA_SUPPORT_)
+
private:
bool AttachImpl(RetainPtr<CFX_DIBitmap> pBitmap,
bool bRgbByteOrder,
diff --git a/fpdfsdk/fpdf_view.cpp b/fpdfsdk/fpdf_view.cpp
index 90d0f68..62b96ca 100644
--- a/fpdfsdk/fpdf_view.cpp
+++ b/fpdfsdk/fpdf_view.cpp
@@ -47,6 +47,7 @@
#include "fpdfsdk/cpdfsdk_renderpage.h"
#include "fxjs/ijs_runtime.h"
#include "public/fpdf_formfill.h"
+#include "third_party/base/check_op.h"
#include "third_party/base/numerics/safe_conversions.h"
#include "third_party/base/ptr_util.h"
#include "third_party/base/span.h"
@@ -96,10 +97,45 @@
"WindowsPrintMode::kPostScript3Type42PassThrough value mismatch");
#endif // BUILDFLAG(IS_WIN)
+#if defined(_SKIA_SUPPORT_)
+// These checks are here because core/ and public/ cannot depend on each other.
+static_assert(static_cast<int>(CFX_DefaultRenderDevice::RendererType::kAgg) ==
+ FPDF_RENDERERTYPE_AGG,
+ "CFX_DefaultRenderDevice::RendererType::kAGG value mismatch");
+static_assert(static_cast<int>(CFX_DefaultRenderDevice::RendererType::kSkia) ==
+ FPDF_RENDERERTYPE_SKIA,
+ "CFX_DefaultRenderDevice::RendererType::kSkia value mismatch");
+#endif // defined(_SKIA_SUPPORT_)
+
namespace {
bool g_bLibraryInitialized = false;
+void UseRendererType(FPDF_RENDERER_TYPE public_type) {
+ // Internal definition of renderer types must stay updated with respect to
+ // the public definition, such that all public definitions can be mapped to
+ // an internal definition in `CFX_DefaultRenderDevice`. A public definition
+ // value might not be meaningful for a particular build configuration, which
+ // would mean use of that value is an error for that build.
+
+ // AGG is always present in a build. |FPDF_RENDERERTYPE_SKIA| is valid to use
+ // only if it is included in the build.
+#if defined(_SKIA_SUPPORT_)
+ // This build configuration has the option for runtime renderer selection.
+ if (public_type == FPDF_RENDERERTYPE_AGG ||
+ public_type == FPDF_RENDERERTYPE_SKIA) {
+ CFX_DefaultRenderDevice::SetDefaultRenderer(
+ static_cast<CFX_DefaultRenderDevice::RendererType>(public_type));
+ return;
+ }
+ CHECK(false);
+#else
+ // `FPDF_RENDERERTYPE_AGG` is used for fully AGG builds as well as for the
+ // _SKIA_SUPPORT_PATHS_ build configuration.
+ CHECK_EQ(public_type, FPDF_RENDERERTYPE_AGG);
+#endif
+}
+
const CPDF_Object* GetXFAEntryFromDocument(const CPDF_Document* doc) {
const CPDF_Dictionary* root = doc->GetRoot();
if (!root)
@@ -196,6 +232,9 @@
void* platform = config->version >= 3 ? config->m_pPlatform : nullptr;
IJS_Runtime::Initialize(config->m_v8EmbedderSlot, config->m_pIsolate,
platform);
+
+ if (config->version >= 4)
+ UseRendererType(config->m_RendererType);
}
g_bLibraryInitialized = true;
}
diff --git a/pdfium.gni b/pdfium.gni
index 43800d5..b571a33 100644
--- a/pdfium.gni
+++ b/pdfium.gni
@@ -39,8 +39,9 @@
# Build PDFium with PartitionAlloc as the memory allocator.
pdf_use_partition_alloc = pdf_use_partition_alloc_override
- # Build PDFium against Skia (experimental) rather than AGG. Use Skia to draw
- # everything.
+ # Build PDFium to use Skia (experimental) for all PDFium graphics.
+ # If enabled, coexists in build with AGG graphics and the default
+ # renderer is selectable at runtime.
pdf_use_skia = pdf_use_skia_override
# Build PDFium against Skia (experimental) rather than AGG. Use Skia to draw
diff --git a/public/fpdfview.h b/public/fpdfview.h
index da4fdc3..9a540e4 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -234,6 +234,15 @@
// future.
FPDF_EXPORT void FPDF_CALLCONV FPDF_InitLibrary();
+// PDF renderer types - Experimental.
+// Selection of 2D graphics library to use for rendering to FPDF_BITMAPs.
+typedef enum {
+ // Anti-Grain Geometry - https://sourceforge.net/projects/agg/
+ FPDF_RENDERERTYPE_AGG = 0,
+ // Skia - https://skia.org/
+ FPDF_RENDERERTYPE_SKIA = 1,
+} FPDF_RENDERER_TYPE;
+
// Process-wide options for initializing the library.
typedef struct FPDF_LIBRARY_CONFIG_ {
// Version number of the interface. Currently must be 2.
@@ -257,11 +266,21 @@
// embedders.
unsigned int m_v8EmbedderSlot;
- // Version 3 - Experimantal,
+ // Version 3 - Experimental.
// Pointer to the V8::Platform to use.
void* m_pPlatform;
+ // Version 4 - Experimental.
+
+ // Explicit specification of core renderer to use. |m_RendererType| must be
+ // a valid value for |FPDF_LIBRARY_CONFIG| versions of this level or higher,
+ // or else the initialization will fail with an immediate crash.
+ // Note that use of a specified |FPDF_RENDERER_TYPE| value for which the
+ // corresponding render library is not included in the build will similarly
+ // fail with an immediate crash.
+ FPDF_RENDERER_TYPE m_RendererType;
+
} FPDF_LIBRARY_CONFIG;
// Function: FPDF_InitLibraryWithConfig
diff --git a/samples/pdfium_test.cc b/samples/pdfium_test.cc
index b69eedb..e35db65 100644
--- a/samples/pdfium_test.cc
+++ b/samples/pdfium_test.cc
@@ -128,6 +128,7 @@
bool save_thumbnails = false;
bool save_thumbnails_decoded = false;
bool save_thumbnails_raw = false;
+ absl::optional<FPDF_RENDERER_TYPE> use_renderer_type;
#ifdef PDF_ENABLE_V8
bool disable_javascript = false;
std::string js_flags; // Extra flags to pass to v8 init.
@@ -182,6 +183,14 @@
return flags;
}
+FPDF_RENDERER_TYPE BuildDefaultRendererType() {
+#if defined(_SKIA_SUPPORT_)
+ return FPDF_RENDERERTYPE_SKIA;
+#else
+ return FPDF_RENDERERTYPE_AGG;
+#endif
+}
+
absl::optional<std::string> ExpandDirectoryPath(const std::string& path) {
#if defined(WORDEXP_AVAILABLE)
wordexp_t expansion;
@@ -483,6 +492,23 @@
options->save_thumbnails_decoded = true;
} else if (cur_arg == "--save-thumbs-raw") {
options->save_thumbnails_raw = true;
+#if defined(_SKIA_SUPPORT_)
+ } else if (ParseSwitchKeyValue(cur_arg, "--use-renderer=", &value)) {
+ if (options->use_renderer_type.has_value()) {
+ fprintf(stderr, "Duplicate --use-renderer argument\n");
+ return false;
+ }
+ if (value == "agg") {
+ options->use_renderer_type = FPDF_RENDERERTYPE_AGG;
+ } else if (value == "skia") {
+ options->use_renderer_type = FPDF_RENDERERTYPE_SKIA;
+ } else {
+ fprintf(stderr,
+ "Invalid --use-renderer argument, value must be one of agg or "
+ "skia\n");
+ return false;
+ }
+#endif // defined(_SKIA_SUPPORT_)
#ifdef PDF_ENABLE_V8
} else if (cur_arg == "--disable-javascript") {
options->disable_javascript = true;
@@ -1133,9 +1159,12 @@
"<pdf-name>.thumbnail.decoded.<page-number>.png\n"
" --save-thumbs-raw - write page thumbnails' raw stream data"
"<pdf-name>.thumbnail.raw.<page-number>.png\n"
+#if defined(_SKIA_SUPPORT_)
+ " --use-renderer - renderer to use, one of [agg | skia]\n"
+#endif
#ifdef PDF_ENABLE_V8
" --disable-javascript - do not execute JS in PDF files\n"
- " --js-flags=<flags> - additional flags to pas to V8"
+ " --js-flags=<flags> - additional flags to pass to V8\n"
#ifdef PDF_ENABLE_XFA
" --disable-xfa - do not process XFA forms\n"
#endif // PDF_ENABLE_XFA
@@ -1198,11 +1227,13 @@
}
FPDF_LIBRARY_CONFIG config;
- config.version = 3;
+ config.version = 4;
config.m_pUserFontPaths = nullptr;
config.m_pIsolate = nullptr;
config.m_v8EmbedderSlot = 0;
config.m_pPlatform = nullptr;
+ config.m_RendererType =
+ options.use_renderer_type.value_or(BuildDefaultRendererType());
std::function<void()> idler = []() {};
#ifdef PDF_ENABLE_V8