From c044fc86bce7542a372ec4398bdb9574fa59e96f Mon Sep 17 00:00:00 2001 From: azeeee05 Date: Thu, 21 May 2026 14:59:09 +0530 Subject: [PATCH 1/2] done skia base code --- .gitignore | 5 + CMakeLists.txt | 3 + cmake/skia.cmake | 45 ++++++ engine/CMakeLists.txt | 5 + engine/include/pdfengine/pdf_engine.hpp | 3 + engine/src/core/engine_info.cpp | 9 ++ engine/tests/smoke_test.cpp | 6 + third_party/skia/README.md | 118 ++++++++++++++++ third_party/skia/args.gn | 37 +++++ third_party/skia/build_skia.ps1 | 180 ++++++++++++++++++++++++ third_party/skia/build_skia.sh | 146 +++++++++++++++++++ third_party/skia/skia.pinned | 13 ++ 12 files changed, 570 insertions(+) create mode 100644 cmake/skia.cmake create mode 100644 third_party/skia/README.md create mode 100644 third_party/skia/args.gn create mode 100644 third_party/skia/build_skia.ps1 create mode 100644 third_party/skia/build_skia.sh create mode 100644 third_party/skia/skia.pinned diff --git a/.gitignore b/.gitignore index 39fcb9d..0ba5b39 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ee0d0b..59c4502 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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}") diff --git a/cmake/skia.cmake b/cmake/skia.cmake new file mode 100644 index 0000000..0382dfd --- /dev/null +++ b/cmake/skia.cmake @@ -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}") diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index af8aa18..a76506c 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -34,6 +34,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) diff --git a/engine/include/pdfengine/pdf_engine.hpp b/engine/include/pdfengine/pdf_engine.hpp index b4e4366..079d578 100644 --- a/engine/include/pdfengine/pdf_engine.hpp +++ b/engine/include/pdfengine/pdf_engine.hpp @@ -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(); diff --git a/engine/src/core/engine_info.cpp b/engine/src/core/engine_info.cpp index 1e52f1a..aa0c31b 100644 --- a/engine/src/core/engine_info.cpp +++ b/engine/src/core/engine_info.cpp @@ -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; diff --git a/engine/tests/smoke_test.cpp b/engine/tests/smoke_test.cpp index 3aa268e..141c14f 100644 --- a/engine/tests/smoke_test.cpp +++ b/engine/tests/smoke_test.cpp @@ -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()); diff --git a/third_party/skia/README.md b/third_party/skia/README.md new file mode 100644 index 0000000..866aeab --- /dev/null +++ b/third_party/skia/README.md @@ -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 `/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 +``` + diff --git a/third_party/skia/args.gn b/third_party/skia/args.gn new file mode 100644 index 0000000..a4f6f89 --- /dev/null +++ b/third_party/skia/args.gn @@ -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 diff --git a/third_party/skia/build_skia.ps1 b/third_party/skia/build_skia.ps1 new file mode 100644 index 0000000..a5b3d6a --- /dev/null +++ b/third_party/skia/build_skia.ps1 @@ -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' diff --git a/third_party/skia/build_skia.sh b/third_party/skia/build_skia.sh new file mode 100644 index 0000000..ff4468d --- /dev/null +++ b/third_party/skia/build_skia.sh @@ -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" diff --git a/third_party/skia/skia.pinned b/third_party/skia/skia.pinned new file mode 100644 index 0000000..e6923ce --- /dev/null +++ b/third_party/skia/skia.pinned @@ -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 From e29e419a2e2fae930ea8c71cc0a0c765abb3f42f Mon Sep 17 00:00:00 2001 From: azeeee05 Date: Thu, 21 May 2026 15:05:36 +0530 Subject: [PATCH 2/2] doen frontend to show the skia integration --- frontend/src/App.tsx | 9 +++++++++ frontend/src/components/Toolbar.tsx | 4 ++++ frontend/src/index.css | 21 +++++++++++++++++++++ frontend/src/lib/wasmLoader.ts | 8 ++++++++ 4 files changed, 42 insertions(+) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b19c43e..a505535 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -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(); }, []); diff --git a/frontend/src/components/Toolbar.tsx b/frontend/src/components/Toolbar.tsx index 9ee7250..112deae 100644 --- a/frontend/src/components/Toolbar.tsx +++ b/frontend/src/components/Toolbar.tsx @@ -11,6 +11,8 @@ interface ToolbarProps { totalPages: number; onUploadStart?: (file: File) => void; backendHealthy: boolean | null; + wasmEngineInfo?: string; + wasmHasSkia?: boolean; } export const Toolbar: React.FC = ({ @@ -24,6 +26,8 @@ export const Toolbar: React.FC = ({ totalPages, onUploadStart, backendHealthy, + wasmEngineInfo, + wasmHasSkia, }) => { const handleZoomPercentSelect = (e: React.ChangeEvent) => { diff --git a/frontend/src/index.css b/frontend/src/index.css index 449d0f0..10ba8c0 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -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; diff --git a/frontend/src/lib/wasmLoader.ts b/frontend/src/lib/wasmLoader.ts index df2d92d..ad447d6 100644 --- a/frontend/src/lib/wasmLoader.ts +++ b/frontend/src/lib/wasmLoader.ts @@ -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 { @@ -68,6 +70,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; } }; }