PHP extension · TrustMark IDs
A cropped screenshot still returns the same ID.
By default, pdfwm writes the string you pass into every tile of an image, or of a rasterized PDF page. Extraction hands that same string back. There is no payload map, and the extension does not expose payload_hex.
Install the Homebrew formula document-embed. It builds the extension against Homebrew php.
brew tap dickwu/tap
brew install dickwu/tap/document-embed
Tile field · example id 10482
Extracted id 10482
API
Five functions. Paths are strings. Options are an array. Extraction returns id, confidence, and votes. PDF extraction also votes across pages.
pdfwm_embed_image_path(string $inputImagePath, string $id, string $outputImagePath, array $options = []): arraypdfwm_extract_image_path(string $imagePath, array $options = []): arraypdfwm_embed_pdf_path(string $inputPdfPath, string $id, string $outputPdfPath, array $options = []): arraypdfwm_extract_pdf_path(string $pdfPath, array $options = []): arraypdfwm_read_metadata(string $pdfPath): array
Tiling
A single whole-image TrustMark is keyed to the entire frame, so a cropped screenshot of that page cannot be decoded. Its residual, a 256×256 pattern stretched across the page, can show up as a faint shadow on white backgrounds. Embedding can split the page into a grid of roughly square tiles and watermark each tile with the same id.
A partial screenshot that contains one whole tile is still traceable. The residual is feathered to zero at tile borders. Tiles are independent, so they are watermarked in parallel across cores. Keep tile_size proportional to dpi so the tile count stays steady as you raise resolution.
Extraction tries the whole image, then sweeps multi-scale sliding windows. Votes alone are not proof of a watermark, because the decoder can emit a consistent bogus id from an unwatermarked region. Constrain results with id_codec and id_max_digits.
| Option | Default | Purpose |
|---|---|---|
tile_size |
1280 |
Target tile side in pixels. 0 disables tiling and writes one whole-image watermark. |
tile_feather |
auto | Residual edge-feather width in pixels inside each tile. 0 derives it from the tile size. |
search_decode |
true |
Run the sliding-window search on extract. Needed to decode tiled output and partial screenshots. |
min_search_votes |
2 |
Agreeing windows required for a high-confidence decode. A single decode is still returned at low confidence. |
max_search_windows |
180 |
Upper bound on windows scanned per image. |
id_max_digits |
unset | Reject decoded numeric ids longer than this. Paired with an id_codec constraint, it discards noise decodes from unwatermarked regions. |
PDF output
PDF embedding rasterizes every page, applies the pixel watermark, and rebuilds an image-only PDF. The output does not guarantee preservation of searchable text, links, forms, bookmarks, attachments, layers, or vector structure. File size can increase substantially. Higher DPI improves raster quality and makes generation slower and larger.
Pages are embedded at the full rasterized resolution, with no silent downsampling, and stored as JPEG. jpeg_quality defaults to 92. dpi defaults to 500. A 500 DPI US-Letter page is roughly 23 MP and about 1–2 MB per page after JPEG compression. Lower dpi, or the PDFWM_DPI environment value, to trade sharpness for smaller, faster output.
Other supported options are id_codec, model_dir, pdfium_lib_path, strength, variant, version, image_format, embed_metadata, max_pages, max_pixels_per_page, and max_id_bytes.
TrustMark is not DRM, encryption, or malicious-removal-proof steganography. It improves leak tracing through screenshots and common image transforms. It cannot stop someone from taking a screenshot, photographing a screen, or attacking the watermark.
Runtime
TrustMark model files must already exist on disk. The extension never downloads models during a PHP request. The Homebrew formula does not bundle them, and the v0.1.6 release tarball does not contain them. The main branch of the source repository includes encoder_Q.onnx and decoder_Q.onnx under models/. Point PDFWM_MODEL_DIR at a directory that contains those two files.
PDFIUM_DYNAMIC_LIB_PATH is required for PDF embed and extract unless Pdfium is available as a system library. It may point at the dynamic library file or at the directory that contains the platform-named Pdfium library. The path below is the example from the README.
export PDFWM_MODEL_DIR=/opt/pdfwm/trustmark-models
export PDFIUM_DYNAMIC_LIB_PATH=/opt/pdfium/libpdfium.dylib
Restart long-running PHP processes after install or upgrade.
ID length
The default TrustMark version is BCH_5, with 61 protected data bits. The first 2 bits are a codec header, so direct IDs are short. id_codec defaults to auto, which tries uint_decimal, then decimal_bcd, then base36, then utf8_raw. An ID that does not fit fails before image or PDF processing with PdfwmIdTooLongException.
| Codec | BCH_5 capacity |
|---|---|
uint_decimal |
0..576460752303423487 without leading zeroes |
decimal_bcd |
Up to 13 digits, preserving leading zeroes |
base36 |
Up to 10 characters in [0-9A-Z], preserving leading zeroes |
utf8_raw |
Up to 7 UTF-8 bytes |
Install
-
Tap and install
The formula builds
pdfwmfrom source against thephpformula, using that formula’sphp-config. The build also usesrustandllvm. It installspdfwm.soand writesext-pdfwm.inifor the detected PHP major and minor version.brew tap dickwu/tapbrew install dickwu/tap/document-embed -
Confirm the module
Use the Homebrew
phpbinary. If your shell still resolvesphpto a versioned formula such asphp@8.3, the command below avoids that, or put the Homebrewphpformula first onPATH.php -mlists the module aspdfwm_php_ext.$(brew --prefix php)/bin/php -m | grep pdfwmWith Homebrew PHP 8.5 the ini file is:
$(brew --prefix)/etc/php/8.5/conf.d/ext-pdfwm.ini -
Point at models and Pdfium
Set
PDFWM_MODEL_DIRbefore embedding or extracting. SetPDFIUM_DYNAMIC_LIB_PATHfor PDF work unless Pdfium is already a system library. The export lines in Runtime are the README examples, not paths the formula creates. -
Restart PHP
Restart long-running PHP processes after install or upgrade so they load the new
ext-pdfwm.ini.
Or build from the source repository
The repository is dickwu/document_embed. A release build produces target/release/libpdfwm.dylib on macOS and target/release/libpdfwm.so on Linux. The smoke check in the README loads a debug build and checks that pdfwm_embed_image_path exists. Replace the path.
cargo build --release
php -d extension=/absolute/path/to/target/debug/libpdfwm.dylib -r 'var_dump(function_exists("pdfwm_embed_image_path"));'
Questions
What does document-embed install?
document-embed installs pdfwm, a Rust-based PHP extension for embedding screenshot-robust TrustMark watermarks into images and image-only PDF output. Business code passes an external string ID, and extraction returns that same ID.
How do I install it?
Run brew tap dickwu/tap, then brew install dickwu/tap/document-embed. The formula builds the extension against Homebrew php and writes an ini file for the detected PHP major and minor version.
Will a cropped screenshot still decode?
A partial screenshot that contains one whole tile is still traceable. The default tile_size is 1280. Set tile_size to 0 to disable tiling and embed a single whole-image watermark, which a cropped screenshot cannot decode.
Does the output PDF stay searchable?
No. PDF embedding rasterizes every page, applies the pixel watermark, and rebuilds an image-only PDF. It does not guarantee preservation of searchable text, links, forms, bookmarks, attachments, layers, or vector structure. File size can increase substantially.
Where do the TrustMark models come from?
TrustMark model files must already exist on disk. The extension never downloads models during a PHP request, and the Homebrew formula does not bundle them. Set PDFWM_MODEL_DIR to the directory containing encoder_Q.onnx and decoder_Q.onnx.
What does PDF embed and extract need besides the extension?
PDF embed and extract need Pdfium unless Pdfium is already available as a system library. Set PDFIUM_DYNAMIC_LIB_PATH to the dynamic library file, or to the directory containing the platform-named Pdfium library.
What does extraction return?
Extraction returns id, confidence, and votes. Confidence is 1.0 when corroborated by min_search_votes windows, and lower for a single decode. Votes are how many windows agreed. PDF extraction additionally votes across pages. Votes alone are not proof of a watermark. Constrain results with id_codec and id_max_digits so a clean page cannot be misread. pdfwm does not expose payload_hex and does not require an application-side payload mapping table.
How long can the ID be?
The default TrustMark version is BCH_5, with 61 protected data bits. The first 2 bits are a codec header. uint_decimal fits 0..576460752303423487 without leading zeroes. decimal_bcd fits up to 13 digits and preserves leading zeroes. base36 fits up to 10 characters in the set [0-9A-Z] and preserves leading zeroes. utf8_raw fits up to 7 UTF-8 bytes. id_codec defaults to auto and tries those four codecs in that order. IDs that do not fit fail before image or PDF processing with PdfwmIdTooLongException.
Is TrustMark DRM?
No. TrustMark is not DRM, encryption, or malicious-removal-proof steganography. It improves leak tracing through screenshots and common image transforms, but it cannot stop a user from taking screenshots, photographing a screen, or attacking the watermark.