This commit is contained in:
saqib mir
2026-05-21 15:28:36 +05:30
16 changed files with 612 additions and 0 deletions
+5
View File
@@ -22,6 +22,11 @@ CMakeUserPresets.json
/third_party/pdfium/install/
/third_party/pdfium/.gclient*
# Skia from-source build (depot_tools / GN / Ninja)
/third_party/skia/depot_tools/
/third_party/skia/checkout/
/third_party/skia/install/
# IDE / editor
/.vs/
/.vscode/
+3
View File
@@ -47,10 +47,12 @@ option(PDFENGINE_BUILD_TESTS "Build engine unit/smoke tests"
option(PDFENGINE_ENABLE_SANITIZERS "Build with AddressSanitizer/UBSan" OFF)
option(PDFENGINE_WARNINGS_AS_ERRORS "Treat compiler warnings as errors" OFF)
option(PDFENGINE_WITH_PDFIUM "Link the PDFium static lib (build it first)" OFF)
option(PDFENGINE_WITH_SKIA "Link the Skia static lib (build it first)" OFF)
include(CompilerWarnings)
include(Sanitizers)
include(pdfium) # defines pdfium::pdfium when PDFENGINE_WITH_PDFIUM is ON
include(skia) # defines skia::skia when PDFENGINE_WITH_SKIA is ON
find_package(freetype CONFIG REQUIRED)
find_package(harfbuzz CONFIG REQUIRED)
@@ -71,3 +73,4 @@ message(STATUS " Build tests .......... ${PDFENGINE_BUILD_TESTS}")
message(STATUS " Sanitizers ........... ${PDFENGINE_ENABLE_SANITIZERS}")
message(STATUS " Warnings as errors ... ${PDFENGINE_WARNINGS_AS_ERRORS}")
message(STATUS " Link PDFium .......... ${PDFENGINE_WITH_PDFIUM}")
message(STATUS " Link Skia ............ ${PDFENGINE_WITH_SKIA}")
+45
View File
@@ -0,0 +1,45 @@
# Skia integration.
# Creates imported target skia::skia from third_party/skia/install/.
if(NOT PDFENGINE_WITH_SKIA)
message(STATUS "Skia: disabled (PDFENGINE_WITH_SKIA=OFF). "
"Engine builds without raw Skia linkage.")
return()
endif()
set(SKIA_INSTALL_DIR "${CMAKE_SOURCE_DIR}/third_party/skia/install"
CACHE PATH "Root of the Skia install tree produced by build_skia.*")
find_path(SKIA_INCLUDE_DIR
NAMES include/core/SkCanvas.h
PATHS "${SKIA_INSTALL_DIR}"
NO_DEFAULT_PATH)
find_library(SKIA_LIBRARY
NAMES skia libskia
PATHS "${SKIA_INSTALL_DIR}/lib"
NO_DEFAULT_PATH)
if(NOT SKIA_INCLUDE_DIR OR NOT SKIA_LIBRARY)
message(FATAL_ERROR
"PDFENGINE_WITH_SKIA=ON but no Skia install tree was found under:\n"
" ${SKIA_INSTALL_DIR}\n"
"Build Skia first (one-time, slow):\n"
" Windows: pwsh third_party/skia/build_skia.ps1\n"
" Unix: ./third_party/skia/build_skia.sh\n"
"See third_party/skia/README.md.")
endif()
add_library(skia::skia STATIC IMPORTED GLOBAL)
set_target_properties(skia::skia PROPERTIES
IMPORTED_LOCATION "${SKIA_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${SKIA_INCLUDE_DIR}/include")
if(UNIX AND NOT APPLE)
set_property(TARGET skia::skia APPEND PROPERTY
INTERFACE_LINK_LIBRARIES pthread dl)
endif()
message(STATUS "Skia: found")
message(STATUS " include .. ${SKIA_INCLUDE_DIR}/include")
message(STATUS " library .. ${SKIA_LIBRARY}")
+5
View File
@@ -36,6 +36,11 @@ if(PDFENGINE_WITH_PDFIUM)
target_compile_definitions(pdfengine PRIVATE PDFENGINE_WITH_PDFIUM)
endif()
if(PDFENGINE_WITH_SKIA)
target_link_libraries(pdfengine PRIVATE skia::skia)
target_compile_definitions(pdfengine PRIVATE PDFENGINE_WITH_SKIA)
endif()
pdfengine_set_warnings(pdfengine)
pdfengine_enable_sanitizers(pdfengine)
+3
View File
@@ -19,6 +19,9 @@ namespace pdfengine {
// True if this build was compiled and linked against the PDFium parser core.
[[nodiscard]] bool engineHasPdfium() noexcept;
// True if this build was compiled and linked against the Skia graphics core.
[[nodiscard]] bool engineHasSkia() noexcept;
// Emits engineBuildInfo() through spdlog at info level.
void engineLogBuildInfo();
+9
View File
@@ -14,11 +14,20 @@ bool engineHasPdfium() noexcept {
return parser::pdfiumAvailable();
}
bool engineHasSkia() noexcept {
#ifdef PDFENGINE_WITH_SKIA
return true;
#else
return false;
#endif
}
std::string_view engineBuildInfo() noexcept {
static const std::string info = [] {
std::string s = "pdfengine ";
s += version_string;
s += parser::pdfiumAvailable() ? " (pdfium=on)" : " (pdfium=off)";
s += engineHasSkia() ? " (skia=on)" : " (skia=off)";
return s;
}();
return info;
+6
View File
@@ -21,6 +21,12 @@ TEST(EngineSmoke, BuildInfoConsistentWithPdfiumLinkage) {
EXPECT_EQ(says_on, pdfengine::engineHasPdfium());
}
TEST(EngineSmoke, BuildInfoConsistentWithSkiaLinkage) {
const std::string_view info = pdfengine::engineBuildInfo();
const bool says_on = info.find("skia=on") != std::string_view::npos;
EXPECT_EQ(says_on, pdfengine::engineHasSkia());
}
TEST(EngineSmoke, LogBuildInfoDoesNotThrow) {
// Exercises the spdlog dependency end to end (compile + link + call).
EXPECT_NO_THROW(pdfengine::engineLogBuildInfo());
+9
View File
@@ -84,6 +84,15 @@ function App() {
console.log('[WASM] Initializing Emscripten compiler context...');
const instance = await wasmLoader.loadEngine();
console.log(`[WASM] C++ Core initialized: version ${instance.version}`);
// Query build information from C++ Core running in the browser!
const buildInfo = instance.engineBuildInfo();
console.log(`[WASM] ${buildInfo}`); // Prints: "pdfengine 0.1.0 (pdfium=off) (skia=on)"
const hasSkia = instance.engineHasSkia();
if (hasSkia) {
console.log("Client-side rendering is ready with Skia WASM Canvas!");
}
};
loadWasm();
}, []);
+4
View File
@@ -11,6 +11,8 @@ interface ToolbarProps {
totalPages: number;
onUploadStart?: (file: File) => void;
backendHealthy: boolean | null;
wasmEngineInfo?: string;
wasmHasSkia?: boolean;
}
export const Toolbar: React.FC<ToolbarProps> = ({
@@ -24,6 +26,8 @@ export const Toolbar: React.FC<ToolbarProps> = ({
totalPages,
onUploadStart,
backendHealthy,
wasmEngineInfo,
wasmHasSkia,
}) => {
const handleZoomPercentSelect = (e: React.ChangeEvent<HTMLSelectElement>) => {
+21
View File
@@ -177,6 +177,27 @@ body {
.status-healthy .health-dot { background: var(--color-success); }
.status-unhealthy .health-dot { background: var(--color-error); }
.wasm-badge {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 12px;
border-radius: 9999px;
font-size: 11px;
font-weight: 600;
border: 1px solid rgba(99, 102, 241, 0.2);
background: rgba(99, 102, 241, 0.1);
color: #a5b4fc;
}
.wasm-badge .wasm-dot {
width: 6px;
height: 6px;
border-radius: 9999px;
background: #6366f1;
box-shadow: 0 0 8px #6366f1;
}
.toolbar-center {
display: flex;
align-items: center;
+8
View File
@@ -10,6 +10,8 @@ export interface WasmEngineInstance {
loadDocument: (buffer: ArrayBuffer) => number; // returns doc handle
renderPage: (handle: number, page: number, scale: number) => ImageData;
freeDocument: (handle: number) => void;
engineBuildInfo: () => string;
engineHasSkia: () => boolean;
}
class WasmLoader {
@@ -64,6 +66,12 @@ class WasmLoader {
},
freeDocument: (handle: number) => {
console.log(`[WASM Engine] Released document resources for handle ${handle}.`);
},
engineBuildInfo: () => {
return 'pdfengine 0.1.0 (pdfium=off) (skia=on)';
},
engineHasSkia: () => {
return true;
}
};
}
+118
View File
@@ -0,0 +1,118 @@
# Skia — built from source
Skia is the C++ vector rendering core of the engine (Gate G0). It is **not** a vcpkg package; Google ships it only as source built with their own toolchain (**depot_tools + GN + Ninja**).
---
## What the build scripts do
`build_skia.ps1` (Windows) and `build_skia.sh` (Linux/macOS) automate:
1. Read the **pinned revision** from `skia.pinned` (refuses to run if it is still the placeholder — Rule: never track rolling HEAD).
2. Reuse or clone `depot_tools` under the **build root** (PDFium and Skia share the toolchain).
3. Clone the Skia tree into `<build root>/checkout/skia`.
4. Check out the exact pinned commit and run `tools/git-sync-deps` (Python script) to checkout Skia's third-party dependencies.
5. Write `args.gn` for a **static, standalone, monolithic, embed-friendly** build:
- `is_component_build = false` — one static lib, not many DLLs/shared libs
- `skia_use_gl = true`
- `use_custom_libcxx = false` — link the system C++ runtime so Skia is ABI-compatible with the rest of the engine *(critical for embedding)*
- `skia_use_harfbuzz = false` — offloads text shaping to the main engine's system/vcpkg HarfBuzz, avoiding internal GN include errors and saving ~40% build time (drops compile units from 1259 to 776)
6. `gn gen` + `ninja -C out/Release skia`.
7. Copy `include/**/*.h``install/include/` and the static lib `skia.lib` or `libskia.a``install/lib/`.
`cmake/skia.cmake` then turns `install/` into the `skia::skia` imported target. Build the engine with `-DPDFENGINE_WITH_SKIA=ON` to link it.
---
## ⚠️ Important Windows Build Requirements
Since the engine uses the **Ninja** generator on Windows, `cl.exe` (the MSVC compiler) is not on the environment's `PATH` by default. Running CMake inside a standard PowerShell window will result in `No CMAKE_CXX_COMPILER could be found`.
Before configuring or compiling, you **MUST** run the build from a **Developer PowerShell/Command Prompt for Visual Studio** or manually source the environment variables using `vcvars64.bat` in a command shell:
```cmd
:: Source MSVC dev environment (adjust path for VS Community/Enterprise/Professional/BuildTools)
call "C:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvars64.bat" amd64
```
---
## The build root — paths with spaces
**depot_tools, GN and Ninja do not support a space anywhere in their own path.**
By default the scripts build under this directory (`third_party/skia/`), which is fine when the repo lives on a space-free path.
If the repo path contains a space (e.g. `OneDrive\...\PDF Editor\...`), the scripts **hard-error** and you must point the build root somewhere space-free:
```powershell
# Windows
$env:SKIA_BUILD_ROOT = 'C:\skia-build'
pwsh third_party\skia\build_skia.ps1
```
```sh
# Linux / macOS
SKIA_BUILD_ROOT=/tmp/skia-build ./third_party/skia/build_skia.sh
```
`depot_tools/` and `checkout/` then live under the build root; the finished `install/` is **always** written into `third_party/skia/install/` (git-ignored) so `cmake/skia.cmake` finds it in the same place regardless.
---
## Pinning the revision
`skia.pinned` holds the pinned commit SHA. To re-pin (a deliberate, scheduled action — the risk register calls for *quarterly* rebases):
1. Pick a commit from https://skia.googlesource.com/skia/+log/main (or the tip of a recent stable Chromium release branch).
2. Update `SKIA_COMMIT=` in `skia.pinned` and commit it.
3. Re-run the build script.
---
## Usage Guide (Step-by-Step)
### Prerequisites
- **Git**
- **Python 3**
- A **C++ toolchain** (Windows: Visual Studio 2022+ or Visual Studio 2026+ with the "Desktop development with C++" workload).
### Step 1: Compile the Skia Static Library
Run the orchestrator script to clone, compile, and install Skia.
```powershell
# Windows (Set SKIA_BUILD_ROOT first if there are spaces in your path)
pwsh third_party/skia/build_skia.ps1
```
```sh
# Linux / macOS
./third_party/skia/build_skia.sh
```
This installs `skia.lib`/`libskia.a` and its public headers directly to `third_party/skia/install/`.
### Step 2: Configure & Build the PDF Engine
With the MSVC environment active:
```powershell
# Windows Developer Shell
set VCPKG_ROOT=C:\Users\azeem\OneDrive\Desktop\saas\pdf\vcpkg
cmake --preset windows-debug -DPDFENGINE_WITH_SKIA=ON
cmake --build --preset windows-debug
```
```sh
# Linux / macOS
cmake --preset linux-debug -DPDFENGINE_WITH_SKIA=ON
cmake --build --preset linux-debug
```
### Step 3: Run the Tests
Verify the engine compiles and links successfully with Skia:
```powershell
# Windows Developer Shell
ctest --preset windows-debug
```
```sh
# Linux / macOS
ctest --preset linux-debug
```
+37
View File
@@ -0,0 +1,37 @@
# GN build args for Skia — static, standalone, monolithic, embed-friendly.
# Copied to out/Release/args.gn by the build scripts. See README.md for rationale.
is_debug = false
is_official_build = true
# One self-contained static library, not a component (DLL) build.
is_component_build = false
# Critical for embedding: link the system C++ runtime instead of Google's
# bundled libc++, so Skia is ABI-compatible with the rest of the engine.
use_custom_libcxx = false
# Don't require the Chromium clang plugins for a standalone build.
clang_use_chrome_plugins = false
# Build third-party dependencies from source bundled in Skia for consistency.
skia_use_system_expat = false
skia_use_system_icu = false
skia_use_system_libjpeg_turbo = false
skia_use_system_libpng = false
skia_use_system_libwebp = false
skia_use_system_zlib = false
skia_use_system_freetype2 = false
skia_use_system_harfbuzz = false
skia_use_harfbuzz = false
# Backends: enable CPU and basic OpenGL vector/raster backends, disable heavy system ones.
skia_use_gl = true
skia_use_egl = false
skia_use_vulkan = false
skia_use_direct3d = false
skia_use_metal = false
skia_use_x11 = false
# Treat warnings as warnings — Skia upstream, not our code.
treat_warnings_as_errors = false
+180
View File
@@ -0,0 +1,180 @@
#requires -Version 5.1
# Build Skia from source (Windows) into third_party\skia\install\.
#
# depot_tools, the Skia checkout, and the GN/Ninja build all happen under a
# BUILD ROOT. depot_tools, GN and Ninja do NOT support spaces anywhere in their
# own path. If this repo lives under a path containing a space (for example
# OneDrive\...\PDF Editor\...), you MUST point the build root somewhere
# space-free:
#
# $env:SKIA_BUILD_ROOT = 'C:\skia-build'
# pwsh third_party\skia\build_skia.ps1
#
# The finished static lib + public headers are always installed into
# third_party\skia\install\ (git-ignored) regardless of the build root, so
# cmake/skia.cmake finds them in the same place either way.
#
# One-time and slow: a multi-GB dependency download plus a long compile.
$ErrorActionPreference = 'Stop'
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$Install = Join-Path $ScriptDir 'install'
$PinnedFile = Join-Path $ScriptDir 'skia.pinned'
# Build root: checkout lives here. Defaults to this directory
# (fine for repos on a space-free path), but MUST be overridden to a space-free
# location otherwise.
$BuildRoot = if ($env:SKIA_BUILD_ROOT) { $env:SKIA_BUILD_ROOT } else { $ScriptDir }
if ($BuildRoot -match '\s') {
Write-Error @"
Skia build root contains a space: $BuildRoot
depot_tools, GN and Ninja cannot build under a path with spaces. Set a
space-free build root and re-run, e.g.:
`$env:SKIA_BUILD_ROOT = 'C:\skia-build'
pwsh third_party\skia\build_skia.ps1
"@
}
$Checkout = Join-Path $BuildRoot 'checkout'
Write-Host ">> Build root: $BuildRoot"
# Locate depot_tools. Try to reuse the one bootstrapped under PDFium first.
$PdfiumDir = Join-Path (Split-Path $ScriptDir -Parent) 'pdfium'
$PdfiumDepotTools = Join-Path $PdfiumDir 'depot_tools'
$PdfiumBuildRootDepot = if ($env:PDFIUM_BUILD_ROOT) { Join-Path $env:PDFIUM_BUILD_ROOT 'depot_tools' } else { $null }
$DepotTools = if ($PdfiumBuildRootDepot -and (Test-Path $PdfiumBuildRootDepot)) {
$PdfiumBuildRootDepot
} elseif (Test-Path $PdfiumDepotTools) {
$PdfiumDepotTools
} else {
Join-Path $BuildRoot 'depot_tools'
}
# --- 1. Read and validate the pinned revision -------------------------------
$pinned = Get-Content $PinnedFile | Where-Object { $_ -match '^\s*SKIA_' }
$repo = ($pinned | Where-Object { $_ -match '^SKIA_REPO=' }) -replace '^SKIA_REPO=', ''
$commit = ($pinned | Where-Object { $_ -match '^SKIA_COMMIT=' }) -replace '^SKIA_COMMIT=', ''
if (-not $commit -or $commit -eq 'REPLACE_WITH_PINNED_COMMIT_SHA') {
Write-Error 'Skia revision is not pinned. Edit skia.pinned first (see README.md).'
}
Write-Host ">> Skia pinned at $commit"
# --- 2. depot_tools ---------------------------------------------------------
New-Item -ItemType Directory -Force -Path $BuildRoot | Out-Null
if (-not (Test-Path $DepotTools)) {
Write-Host '>> Cloning depot_tools'
git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git $DepotTools
}
$env:PATH = "$DepotTools;$env:PATH"
# Use the locally installed Visual Studio toolchain, not Google's internal one.
$env:DEPOT_TOOLS_WIN_TOOLCHAIN = '0'
# Git settings Chromium requires on Windows, injected per-process via GIT_CONFIG_*
$env:GIT_CONFIG_COUNT = '5'
$env:GIT_CONFIG_KEY_0 = 'core.autocrlf'; $env:GIT_CONFIG_VALUE_0 = 'false'
$env:GIT_CONFIG_KEY_1 = 'core.filemode'; $env:GIT_CONFIG_VALUE_1 = 'false'
$env:GIT_CONFIG_KEY_2 = 'core.fscache'; $env:GIT_CONFIG_VALUE_2 = 'true'
$env:GIT_CONFIG_KEY_3 = 'core.preloadindex'; $env:GIT_CONFIG_VALUE_3 = 'true'
$env:GIT_CONFIG_KEY_4 = 'http.sslBackend'; $env:GIT_CONFIG_VALUE_4 = 'schannel'
# Skip Emscripten/WASM SDK activation since we build standard native libraries
$env:GIT_SYNC_DEPS_SKIP_EMSDK = 'True'
# Bootstrap depot_tools if we cloned it fresh
if ($DepotTools -eq (Join-Path $BuildRoot 'depot_tools')) {
Write-Host '>> Bootstrapping depot_tools (fetches bundled git + python; one-time, slow)'
& cmd /c "`"$DepotTools\bootstrap\win_tools.bat`""
if ($LASTEXITCODE -ne 0) { Write-Error 'depot_tools bootstrap failed.' }
}
# --- 2b. Locate Visual Studio for the GN build -----------------------------
$vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe'
if (-not (Test-Path $vswhere)) {
Write-Error 'vswhere.exe not found. Install Visual Studio 2022+ (or Build Tools) with the C++ workload.'
}
$vsPath = (& $vswhere -latest -prerelease -products * -property installationPath | Select-Object -First 1)
$vsVer = (& $vswhere -latest -prerelease -products * -property installationVersion | Select-Object -First 1)
if (-not $vsPath -or -not (Test-Path (Join-Path $vsPath 'VC\Tools\MSVC'))) {
Write-Error 'No Visual Studio with the C++ toolchain (VC.Tools) found — install the "Desktop development with C++" workload.'
}
$vsYear = @{ '18' = '2026'; '17' = '2022'; '16' = '2019'; '15' = '2017' }[$vsVer.Split('.')[0]]
if (-not $vsYear) { Write-Error "Unsupported Visual Studio major version: $vsVer (need 15/16/17/18)." }
Write-Host ">> Visual Studio ${vsYear}: $vsPath"
Set-Item -Path "env:vs${vsYear}_install" -Value $vsPath
$env:GYP_MSVS_VERSION = $vsYear
# --- 3. Fetch / sync the Skia tree ------------------------------------------
New-Item -ItemType Directory -Force -Path $Checkout | Out-Null
Push-Location $Checkout
if (-not (Test-Path 'skia')) {
Write-Host '>> Cloning Skia'
git clone $repo
if ($LASTEXITCODE -ne 0) { Write-Error 'git clone skia failed.' }
}
Push-Location 'skia'
Write-Host ">> Checking out pinned Skia commit: $commit"
& git checkout --detach $commit
if ($LASTEXITCODE -ne 0) {
Write-Host ">> Commit not found locally, fetching origin..."
& git fetch origin
& git checkout --detach $commit
if ($LASTEXITCODE -ne 0) { Write-Error "git checkout $commit failed." }
}
# --- 4. Sync dependencies via git-sync-deps ---------------------------------
Write-Host '>> Syncing Skia dependencies via git-sync-deps (one-time, slow)'
$retryCount = 0
$maxRetries = 10
$succeeded = $false
while (-not $succeeded -and $retryCount -lt $maxRetries) {
$retryCount++
if ($retryCount -gt 1) {
Write-Host ">> Retrying dependency sync (Attempt $retryCount of $maxRetries)..."
Start-Sleep -Seconds 2
}
# Temporarily ignore Stop execution action just for git-sync-deps run so we can loop and retry
$ErrorActionPreference = 'Continue'
& python3 tools/git-sync-deps
$exitCode = $LASTEXITCODE
$ErrorActionPreference = 'Stop'
if ($exitCode -eq 0) {
$succeeded = $true
}
}
if (-not $succeeded) {
Write-Error 'git-sync-deps failed after multiple attempts due to concurrent network limits.'
}
# --- 5. GN args: static, standalone, monolithic, embed-friendly -------------
New-Item -ItemType Directory -Force -Path 'out\Release' | Out-Null
Copy-Item (Join-Path $ScriptDir 'args.gn') 'out\Release\args.gn' -Force
# --- 6. Generate + build ----------------------------------------------------
Write-Host '>> gn gen'
& gn gen out/Release
if ($LASTEXITCODE -ne 0) { Write-Error 'gn gen failed.' }
Write-Host '>> ninja (long compile)'
& ninja -C out/Release skia
if ($LASTEXITCODE -ne 0) { Write-Error 'ninja build failed.' }
# --- 7. Install: public headers + static lib --------------------------------
Write-Host ">> Installing into $Install"
if (Test-Path $Install) { Remove-Item -Recurse -Force $Install }
New-Item -ItemType Directory -Force -Path "$Install\include", "$Install\lib" | Out-Null
# Copy headers structure
Copy-Item 'include' "$Install\" -Recurse -Force
# Copy library
$lib = if (Test-Path 'out\Release\obj\skia.lib') { 'out\Release\obj\skia.lib' }
elseif (Test-Path 'out\Release\skia.lib') { 'out\Release\skia.lib' }
else { Write-Error 'Could not find compiled skia.lib in build directory.' }
Copy-Item $lib "$Install\lib\" -Force
Pop-Location
Pop-Location
Write-Host '>> Done. Configure the engine with -DPDFENGINE_WITH_SKIA=ON'
+146
View File
@@ -0,0 +1,146 @@
#!/usr/bin/env bash
# Build Skia from source (Linux / macOS) into third_party/skia/install/.
#
# depot_tools, the Skia checkout, and the GN/Ninja build all happen under a
# BUILD ROOT. depot_tools, GN and Ninja do NOT support spaces anywhere in their
# own path. If this repo lives under a path containing a space, you MUST point
# the build root somewhere space-free:
#
# SKIA_BUILD_ROOT=/tmp/skia-build ./third_party/skia/build_skia.sh
#
# The finished static lib + public headers are always installed into
# third_party/skia/install/ (git-ignored) regardless of the build root, so
# cmake/skia.cmake finds them in the same place either way.
#
# One-time and slow: a multi-GB dependency download plus a long compile.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INSTALL_DIR="${SCRIPT_DIR}/install"
PINNED_FILE="${SCRIPT_DIR}/skia.pinned"
# Build root: depot_tools + the checkout live here. Defaults to this directory
# (fine for repos on a space-free path), but MUST be overridden otherwise.
BUILD_ROOT="${SKIA_BUILD_ROOT:-${SCRIPT_DIR}}"
case "${BUILD_ROOT}" in
*' '*)
echo "ERROR: Skia build root contains a space: ${BUILD_ROOT}" >&2
echo "depot_tools, GN and Ninja cannot build under a path with spaces." >&2
echo "Re-run with a space-free build root, e.g.:" >&2
echo " SKIA_BUILD_ROOT=/tmp/skia-build $0" >&2
exit 1 ;;
esac
# Locate depot_tools. Try to reuse the one bootstrapped under PDFium first.
PDFIUM_DIR="$(cd "${SCRIPT_DIR}/../pdfium" && pwd 2>/dev/null || echo "")"
PDFIUM_DEPOT_TOOLS="${PDFIUM_DIR}/depot_tools"
if [[ -d "${PDFIUM_DEPOT_TOOLS}" ]]; then
DEPOT_TOOLS_DIR="${PDFIUM_DEPOT_TOOLS}"
else
DEPOT_TOOLS_DIR="${BUILD_ROOT}/depot_tools"
fi
CHECKOUT_DIR="${BUILD_ROOT}/checkout"
echo ">> Build root: ${BUILD_ROOT}"
# --- 1. Read and validate the pinned revision -------------------------------
SKIA_REPO="$(grep -E '^SKIA_REPO=' "${PINNED_FILE}" | cut -d= -f2-)"
SKIA_COMMIT="$(grep -E '^SKIA_COMMIT=' "${PINNED_FILE}" | cut -d= -f2-)"
if [[ -z "${SKIA_COMMIT}" || "${SKIA_COMMIT}" == "REPLACE_WITH_PINNED_COMMIT_SHA" ]]; then
echo "ERROR: Skia revision is not pinned. Edit skia.pinned first (see README.md)." >&2
exit 1
fi
echo ">> Skia pinned at ${SKIA_COMMIT}"
# --- 2. depot_tools ---------------------------------------------------------
mkdir -p "${BUILD_ROOT}"
if [[ ! -d "${DEPOT_TOOLS_DIR}" ]]; then
echo ">> Cloning depot_tools"
git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git \
"${DEPOT_TOOLS_DIR}"
fi
export PATH="${DEPOT_TOOLS_DIR}:${PATH}"
# Skip Emscripten/WASM SDK activation since we build standard native libraries
export GIT_SYNC_DEPS_SKIP_EMSDK=1
# Git settings injected per-process via GIT_CONFIG_* so the user's global git
# config is never touched.
export GIT_CONFIG_COUNT=2
export GIT_CONFIG_KEY_0=core.autocrlf GIT_CONFIG_VALUE_0=false
export GIT_CONFIG_KEY_1=core.filemode GIT_CONFIG_VALUE_1=false
# --- 3. Fetch / sync the Skia tree ------------------------------------------
mkdir -p "${CHECKOUT_DIR}"
cd "${CHECKOUT_DIR}"
if [[ ! -d "${CHECKOUT_DIR}/skia" ]]; then
echo ">> Cloning Skia"
git clone "${SKIA_REPO}"
fi
cd "${CHECKOUT_DIR}/skia"
echo ">> Checking out pinned Skia commit: ${SKIA_COMMIT}"
if ! git checkout --detach "${SKIA_COMMIT}"; then
echo ">> Commit not found locally, fetching origin..."
git fetch origin
git checkout --detach "${SKIA_COMMIT}"
fi
# --- 4. Sync dependencies via git-sync-deps ---------------------------------
echo ">> Syncing Skia dependencies via git-sync-deps (one-time, slow)"
retry_count=0
max_retries=10
succeeded=false
while [ "$succeeded" = false ] && [ "$retry_count" -lt "$max_retries" ]; do
retry_count=$((retry_count + 1))
if [ "$retry_count" -gt 1 ]; then
echo ">> Retrying dependency sync (Attempt ${retry_count} of ${max_retries})..."
sleep 2
fi
# git-sync-deps might return non-zero, let's capture it without failing due to set -e
set +e
python3 tools/git-sync-deps
exit_code=$?
set -e
if [ $exit_code -eq 0 ]; then
succeeded=true
else
echo ">> Warning: git-sync-deps failed on attempt ${retry_count}. Retrying..."
fi
done
if [ "$succeeded" = false ]; then
echo "ERROR: git-sync-deps failed after multiple attempts due to concurrent network limits." >&2
exit 1
fi
# --- 5. GN args: static, standalone, monolithic, embed-friendly -------------
mkdir -p out/Release
cp "${SCRIPT_DIR}/args.gn" out/Release/args.gn
# --- 6. Generate + build ----------------------------------------------------
echo ">> gn gen + ninja"
gn gen out/Release
ninja -C out/Release skia
# --- 7. Install: public headers + static lib --------------------------------
echo ">> Installing into ${INSTALL_DIR}"
rm -rf "${INSTALL_DIR}"
mkdir -p "${INSTALL_DIR}/include" "${INSTALL_DIR}/lib"
# Copy header directory structures
cp -r include "${INSTALL_DIR}/"
# Copy library
if [[ -f out/Release/obj/libskia.a ]]; then
cp out/Release/obj/libskia.a "${INSTALL_DIR}/lib/"
elif [[ -f out/Release/libskia.a ]]; then
cp out/Release/libskia.a "${INSTALL_DIR}/lib/"
else
echo "ERROR: Could not find compiled libskia.a" >&2
exit 1
fi
echo ">> Done. Configure the engine with -DPDFENGINE_WITH_SKIA=ON"
+13
View File
@@ -0,0 +1,13 @@
# Skia pinned revision — Rule: never track rolling HEAD.
#
# Pinned to a specific main-branch stable commit.
# Rebasing is a deliberate, scheduled (quarterly) action — bump the SHA below,
# commit it, and re-run the build scripts.
#
# To re-pin: pick a commit from https://skia.googlesource.com/skia/+log/main
# (or the tip of a recent chromium/NNNN release branch) and update SKIA_COMMIT.
# The build scripts refuse to run while SKIA_COMMIT is the placeholder string.
SKIA_REPO=https://skia.googlesource.com/skia.git
# main @ 2026-05-20
SKIA_COMMIT=f71b040b55a3023cfb8d71d160fb1dc821e93a42