Merge branch 'dev' of https://gitea.maskantech.in/gitea_admin/pdf into saqib
This commit is contained in:
@@ -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/
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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}")
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
}, []);
|
||||
|
||||
@@ -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>) => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Vendored
+118
@@ -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
|
||||
```
|
||||
|
||||
Vendored
+37
@@ -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
|
||||
Vendored
+180
@@ -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'
|
||||
Vendored
+146
@@ -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"
|
||||
Vendored
+13
@@ -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
|
||||
Reference in New Issue
Block a user