Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b3d74fd
Add pdf exporter.
OnionsYu Mar 24, 2026
643f038
Add pdf test.
OnionsYu Mar 24, 2026
3a74988
Add pdf command.
OnionsYu Mar 24, 2026
dedb48b
Fix clip path transform and fill rule support in PDF exporter.
OnionsYu Mar 24, 2026
99a2b04
Bake accumulated CTM into pattern matrices so gradient and image patt…
OnionsYu Mar 24, 2026
935d059
Replace scientific notation with decimal format in PDF float output.
OnionsYu Mar 24, 2026
37d7375
Add soft mask support for alpha and luminance mask types in PDF expor…
OnionsYu Mar 24, 2026
248f99a
Add alpha channel support for transparent images in PDF exporter.
OnionsYu Mar 24, 2026
13fc702
Add PAG_BUILD_PDF cmake option for PDF export support.
OnionsYu Mar 24, 2026
c51731d
Extract shared GlyphPath utility from duplicated SVG and PDF glyph pa…
OnionsYu Mar 25, 2026
bba08b1
Rename GlyphPathUtil to ExporterUtils and deduplicate shared SVG and …
OnionsYu Mar 25, 2026
ad4cfa0
Move shared image and mask helper functions from SVG and PDF exporter…
OnionsYu Mar 25, 2026
b3ef5d7
Refactor PDFExporter gradient and pattern code for clarity and consis…
OnionsYu Mar 25, 2026
95b1f89
Add blend mode support to PDF exporter.
OnionsYu Mar 25, 2026
ec500dc
Add mirror tile mode support to PDF pattern exporter.
OnionsYu Mar 25, 2026
ad53bd6
Add gradient alpha soft mask support to PDF exporter.
OnionsYu Mar 25, 2026
c686895
Refactor PDF exporter with ScopedTransform RAII guard and reduce dupl…
OnionsYu Mar 25, 2026
8ef29cb
Add CID font and UTF-16BE encoding support for non-ASCII text in PDF …
OnionsYu Mar 25, 2026
bf382a3
Optimize PDF exporter with reduced string allocations and cleaner con…
OnionsYu Mar 25, 2026
bbbd4f3
Remove redundant writeLayerContents wrapper and inline its calls.
OnionsYu Mar 26, 2026
1226089
Extract emitPaintOp to eliminate duplicated fill and stroke logic in …
OnionsYu Mar 26, 2026
9e75ba3
Unify shape writing methods into a single writeShape with shared emit…
OnionsYu Mar 26, 2026
422fe43
Add comprehensive test cases for PAGX PDF export functionality.
OnionsYu Mar 26, 2026
4aa9cc9
Add comprehensive test cases for PAGX PDF export functionality.
OnionsYu Mar 26, 2026
fc75e4b
Fix code formatting in PAGX test files.
OnionsYu Mar 26, 2026
0671e90
Add unit tests for ExporterUtils and ComputeGlyphPaths.
OnionsYu Mar 26, 2026
7c6a302
Fix include order and code formatting in PAGXSVGTest.
OnionsYu Mar 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ option(PAG_USE_C "Enable c API" OFF)
option(PAG_BUILD_PAGX "Enable PAGX format support" OFF)
option(PAG_BUILD_CLI "Enable building the command-line tool" OFF)
option(PAG_BUILD_SVG "Enable SVG import/export support" OFF)
option(PAG_BUILD_PDF "Enable PDF export support" OFF)

# EMSCRIPTEN_PTHREADS can be set by vendor tools for building the wasm-mt architecture.
if (NOT WEB OR EMSCRIPTEN_PTHREADS)
Expand Down Expand Up @@ -99,6 +100,9 @@ endif ()
if (PAG_BUILD_SVG)
set(PAG_BUILD_PAGX ON)
endif ()
if (PAG_BUILD_PDF)
set(PAG_BUILD_PAGX ON)
endif ()

# PAG_BUILD_PAGX enables HarfBuzz
if (PAG_BUILD_PAGX)
Expand All @@ -118,6 +122,7 @@ if (PAG_BUILD_TESTS)
set(PAG_BUILD_PAGX ON)
set(PAG_BUILD_CLI ON)
set(PAG_BUILD_SVG ON)
set(PAG_BUILD_PDF ON)
set(PAG_USE_HARFBUZZ ON)
set(PAG_USE_SYSTEM_LZ4 OFF)
set(PAG_BUILD_SHARED OFF)
Expand All @@ -131,6 +136,7 @@ message("PAG_USE_HARFBUZZ: ${PAG_USE_HARFBUZZ}")
message("PAG_BUILD_PAGX: ${PAG_BUILD_PAGX}")
message("PAG_BUILD_CLI: ${PAG_BUILD_CLI}")
message("PAG_BUILD_SVG: ${PAG_BUILD_SVG}")
message("PAG_BUILD_PDF: ${PAG_BUILD_PDF}")
message("PAG_USE_SYSTEM_LZ4: ${PAG_USE_SYSTEM_LZ4}")
message("PAG_USE_C: ${PAG_USE_C}")
message("PAG_BUILD_SHARED: ${PAG_BUILD_SHARED}")
Expand Down Expand Up @@ -199,6 +205,10 @@ if (PAG_BUILD_PAGX)
list(APPEND PAG_FILES src/pagx/svg/SVGImporter.cpp src/pagx/svg/SVGParserContext.h)
list(APPEND PAG_FILES src/pagx/svg/SVGExporter.cpp src/pagx/svg/SVGTextLayout.cpp)
endif ()

if (PAG_BUILD_PDF)
list(APPEND PAG_FILES src/pagx/pdf/PDFExporter.cpp)
endif ()
endif ()
file(GLOB COMMON_FILES src/platform/*.*)
list(APPEND PAG_FILES ${COMMON_FILES})
Expand Down
60 changes: 60 additions & 0 deletions include/pagx/PDFExporter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// Tencent is pleased to support the open source community by making libpag available.
//
// Copyright (C) 2026 Tencent. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// unless required by applicable law or agreed to in writing, software distributed under the
// license is distributed on an "as is" basis, without warranties or conditions of any kind,
// either express or implied. see the license for the specific language governing permissions
// and limitations under the license.
//
/////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#include <string>
#include "pagx/PAGXDocument.h"

namespace pagx {

/**
* Export options for PDFExporter.
*/
struct PDFExportOptions {
/**
* Whether to convert text elements to path elements using pre-shaped glyph outlines. When
* enabled, text with GlyphRun data is rendered as PDF path operators instead of text operators,
* ensuring identical rendering across platforms without font dependency. Falls back to Helvetica
* text operators when glyph outline data is unavailable. The default value is true.
*/
bool convertTextToPath = true;
};

/**
* PDFExporter converts a PAGXDocument into PDF 1.4 format.
* Supports vector shapes, solid/gradient fills, strokes, transforms, opacity, clipping, and text.
*/
class PDFExporter {
public:
using Options = PDFExportOptions;

/**
* Exports a PAGXDocument to a PDF string (binary content).
*/
static std::string ToPDF(const PAGXDocument& document, const Options& options = {});

/**
* Exports a PAGXDocument to a PDF file.
* Returns true on success.
*/
static bool ToFile(const PAGXDocument& document, const std::string& filePath,
const Options& options = {});
};

} // namespace pagx
63 changes: 60 additions & 3 deletions src/cli/CommandConvert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <vector>
#include "pagx/PAGXExporter.h"
#include "pagx/PAGXImporter.h"
#include "pagx/PDFExporter.h"
#include "pagx/SVGExporter.h"
#include "pagx/SVGImporter.h"

Expand All @@ -40,12 +41,17 @@ struct SVGImportOptions {
bool preserveUnknown = false;
};

struct PDFExportOpts {
bool noConvertTextToPath = false;
};

struct ConvertOptions {
std::string inputFile = {};
std::string outputFile = {};
std::string outputFormat = {};
SVGExportOptions svgExport = {};
SVGImportOptions svgImport = {};
PDFExportOpts pdfExport = {};
};

static void PrintUsage() {
Expand All @@ -55,12 +61,15 @@ static void PrintUsage() {
<< "is inferred from file extensions (e.g. .pagx -> .svg or .svg -> .pagx).\n"
<< "\n"
<< "Options:\n"
<< " --format <format> Override output format (svg, pagx)\n"
<< " --format <format> Override output format (svg, pdf, pagx)\n"
<< "\n"
<< "SVG output options:\n"
<< " --indent <n> Indentation spaces (default: 2, valid range: 0-16)\n"
<< " --no-xml-declaration Omit the <?xml ...?> declaration\n"
<< " --no-convert-text-to-path Keep text as <text> elements instead of <path>\n"
<< " --no-convert-text-to-path Keep text as <text>/<text operators> instead of paths\n"
<< "\n"
<< "PDF output options:\n"
<< " --no-convert-text-to-path Keep text as PDF text operators instead of paths\n"
<< "\n"
<< "SVG input options:\n"
<< " --no-expand-use Do not expand <use> references\n"
Expand All @@ -69,9 +78,10 @@ static void PrintUsage() {
<< "\n"
<< "Examples:\n"
<< " pagx convert input.pagx output.svg # PAGX to SVG\n"
<< " pagx convert input.pagx output.pdf # PAGX to PDF\n"
<< " pagx convert input.svg output.pagx # SVG to PAGX\n"
<< " pagx convert --indent 4 input.pagx out.svg # PAGX to SVG with 4-space indent\n"
<< " pagx convert --format svg input.pagx output # specify SVG output format\n";
<< " pagx convert --format pdf input.pagx output # specify PDF output format\n";
}

static std::string InferFormat(const std::string& path) {
Expand All @@ -81,6 +91,9 @@ static std::string InferFormat(const std::string& path) {
if (ext == "svg") {
return "svg";
}
if (ext == "pdf") {
return "pdf";
}
if (ext == "pagx") {
return "pagx";
}
Expand All @@ -107,6 +120,7 @@ static int ParseOptions(int argc, char* argv[], ConvertOptions* options) {
options->svgExport.noXmlDeclaration = true;
} else if (arg == "--no-convert-text-to-path") {
options->svgExport.noConvertTextToPath = true;
options->pdfExport.noConvertTextToPath = true;
} else if (arg == "--no-expand-use") {
options->svgImport.expandUse = false;
} else if (arg == "--flatten-transforms") {
Expand Down Expand Up @@ -178,6 +192,46 @@ static int ConvertToSVG(const ConvertOptions& options) {
return 0;
}

static int ConvertToPDF(const ConvertOptions& options) {
auto inputFormat = InferFormat(options.inputFile);
std::shared_ptr<PAGXDocument> document;

if (inputFormat == "pagx") {
document = PAGXImporter::FromFile(options.inputFile);
} else if (inputFormat == "svg") {
SVGImporter::Options svgOptions = {};
svgOptions.expandUseReferences = options.svgImport.expandUse;
svgOptions.flattenTransforms = options.svgImport.flattenTransforms;
svgOptions.preserveUnknownElements = options.svgImport.preserveUnknown;
document = SVGImporter::Parse(options.inputFile, svgOptions);
} else {
std::cerr << "pagx convert: error: unsupported input format for '" << options.inputFile
<< "'\n";
return 1;
}

if (document == nullptr) {
std::cerr << "pagx convert: error: failed to load '" << options.inputFile << "'\n";
return 1;
}
if (!document->errors.empty()) {
for (auto& error : document->errors) {
std::cerr << "pagx convert: warning: " << error << "\n";
}
}

PDFExporter::Options pdfOptions = {};
pdfOptions.convertTextToPath = !options.pdfExport.noConvertTextToPath;

if (!PDFExporter::ToFile(*document, options.outputFile, pdfOptions)) {
std::cerr << "pagx convert: error: failed to write '" << options.outputFile << "'\n";
return 1;
}

std::cout << "pagx convert: wrote " << options.outputFile << "\n";
return 0;
}

static int ConvertToPAGX(const ConvertOptions& options) {
auto inputFormat = InferFormat(options.inputFile);
if (inputFormat != "svg") {
Expand Down Expand Up @@ -224,6 +278,9 @@ int RunConvert(int argc, char* argv[]) {
if (options.outputFormat == "svg") {
return ConvertToSVG(options);
}
if (options.outputFormat == "pdf") {
return ConvertToPDF(options);
}
if (options.outputFormat == "pagx") {
return ConvertToPAGX(options);
}
Expand Down
Loading
Loading