GroupDocs.Watermark for Python via .NET 26.10 Release Notes

This release upgrades the GroupDocs.Watermark engine from 26.6 to 26.9. The embedded .NET runtime moves from 10.0.0 to 10.0.12 and the wheel tags now target manylinux_2_27_x86_64, macosx_12_0_x86_64 and macosx_12_0_arm64. New features let WatermarkerSettings.logger accept a Python object or function and raise an AttributeError when assigning a non‑existent attribute. Preview callbacks now receive the actual page stream, fixing empty or truncated io.BytesIO previews. Support for Python 3.5 – 3.7 and guardrails against installing on unsupported Linux/macOS versions are also added.

Breaking change: assigning an attribute that a class does not have now raises AttributeError (e.g., watermark.opactiy = 0.5 used to be stored on the Python object and change nothing; now it raises). No classes or members were renamed or moved, but the wheel platform tags changed, so pip ≥ 20.3 is required to select the correct binary – see Requirements for details.

Full List of Changes in This Release

KeyCategorySummary
WATERMARKPYTHON‑65FeatureAccept a Python object or function as WatermarkerSettings.logger
WATERMARKPYTHON‑66EnhancementUpdate GroupDocs.Watermark engine from 26.6 to 26.9
WATERMARKPYTHON‑67EnhancementUpdate the embedded .NET runtime from 10.0.0 to 10.0.12
WATERMARKPYTHON‑68EnhancementRaise AttributeError when code assigns an attribute a class does not have
WATERMARKPYTHON‑69FixCannot use the package on Python 3.5 – 3.7
WATERMARKPYTHON‑70Fixpip installs the package on Linux and macOS versions that cannot run it
WATERMARKPYTHON‑71FixPreview release callbacks receive None instead of the page stream
WATERMARKPYTHON‑72FixPage previews written to io.BytesIO are empty
WATERMARKPYTHON‑73FixSpreadsheet page previews written through a release callback are truncated

Major Features

Custom logger for WatermarkerSettings

WatermarkerSettings.logger now accepts a Python object or a plain function. In 26.6 assigning any value raised

TypeError: _marshal_callback_or_handle: cannot wrap ... as GroupDocs.Watermark.Options.ILogger

The logger can be an object that implements trace(message), warning(message) and error(message, exception), or a callable fn(level, message, exception). The engine logs a trace when it opens a file and an error (with the .NET exception text) when it cannot load one; failures inside the logger are ignored.

from groupdocs.watermark import Watermarker, WatermarkerSettings

class PrintLogger:
    def trace(self, message):
        print("trace:", message)

    def warning(self, message):
        print("warning:", message)

    def error(self, message, exception):
        print("error:", message, exception)

settings = WatermarkerSettings()
settings.logger = PrintLogger()  # object with the required methods
# or: settings.logger = lambda level, msg, exc: print(level, msg, exc)

with Watermarker("document.pdf", settings) as watermarker:
    print(watermarker.get_document_info().page_count)

Strict attribute assignment

Assigning a misspelled or .NET‑style property on a watermark wrapper now raises an AttributeError instead of silently creating a Python‑only attribute. This prevents code from thinking it has changed the document when it has not.

from groupdocs.watermark.watermarks import TextWatermark, Font

watermark = TextWatermark("CONFIDENTIAL", Font("Arial", 36))
try:
    watermark.opacity_level = 0.5  # misspelled: the property is `opacity`
except AttributeError as error:
    print(error)

The raised error is:

AttributeError: 'TextWatermark' object has no attribute 'opacity_level'; assigning it would not reach the .NET object. Did you mean: 'opacity'?

Existing correct property assignments continue to work unchanged.

API Changes

Upgrading requires using the new wheel (pip ≥ 20.3) and handling the new AttributeError for misspelled attributes; other API signatures remain unchanged.

New classes and members

  • Color: a constructor; it had none.
  • get() on 4 indexed classes — ReadOnlyListBase, SpreadsheetHeaderFooterCollection, SpreadsheetHeaderFooterSectionCollection, WordProcessingHeaderFooterCollection — as an alternative to x[key]; their item property is removed.

Fixed

  • Cannot use the package on Python 3.5 – 3.7 (WATERMARKPYTHON‑69). Importing groupdocs.watermark on Python 3.5 or 3.6 raised a SyntaxError, and on Python 3.7 the first API call raised AttributeError: module 'typing' has no attribute 'get_origin' because the wheel used newer language features. The 26.10 wheel removes those features, so the package imports and runs on Python 3.5 – 3.14.
  • pip installs the package on Linux and macOS versions that cannot run it (WATERMARKPYTHON‑70). On Linux systems with glibc older than 2.27 or macOS versions earlier than 12 the installed wheel could not start the embedded .NET runtime, causing runtime startup failures. The 26.10 wheels are tagged manylinux_2_27_x86_64, macosx_12_0_x86_64 and macosx_12_0_arm64, so pip now blocks installation on those unsupported platforms.
  • Preview release callbacks receive None instead of the page stream (WATERMARKPYTHON‑71). When using PreviewOptions(create_page_stream, release_page_stream), the release callback was invoked with None instead of the page stream, so cleanup actions never saw the data. In 26.10 the release callback receives the stream returned by the create callback (or can accept only the page number).
  • Page previews written to io.BytesIO are empty (WATERMARKPYTHON‑72). A create callback that returned an io.BytesIO yielded empty pages (all lengths 0) because the engine wrote to a .NET copy of the stream. The 26.10 wheel writes the page bytes back into the original BytesIO, so the stream contains the expected data.
  • Spreadsheet page previews written through a release callback are truncated (WATERMARKPYTHON‑73). Providing a release callback for spreadsheet previews left the stream open and never passed it to Python, resulting in truncated PNG files. The wrapper now closes the engine’s stream before calling the release callback, so the file holds the full image.

Known Issues

  • On Linux or macOS, adding a watermark may raise The type initializer for 'Gdip' threw an exception or DllNotFoundException: libgdiplus; install libgdiplus (sudo apt install libgdiplus or brew install mono-libgdiplus).
  • On Linux, adding a text watermark to a PDF may raise FontNotFoundException; install Microsoft core fonts (sudo apt install ttf-mscorefonts-installer fontconfig && sudo fc-cache -f).
  • Generating a preview for a PowerPoint file on Linux/macOS raises Not implemented.; generate PowerPoint previews only on Windows.
  • If a DllNotFoundException: libSkiaSharp occurs, rename or remove the conflicting system library (sudo mv /usr/local/lib/libSkiaSharp.dylib /usr/local/lib/libSkiaSharp.dylib.bak).
  • The process may abort on first use with Couldn't find a valid ICU package; install libicu-dev and ensure DOTNET_SYSTEM_GLOBALIZATION_INVARIANT is not set.
  • A TypeLoadException can be resolved by reinstalling the package (pip install --force-reinstall groupdocs-watermark-net).

Requirements

  • Python 3.5 – 3.14 (python_requires = ">=3.5,<3.15") and pip 20.3 or newer
  • Linux x64 with glibc 2.27 or newer; macOS 12 or newer, Intel or Apple Silicon. On an Intel Mac, a Python built against a pre-11 macOS SDK reports the system as macOS 10.16 — use pip 24.1 or newer there, or SYSTEM_VERSION_COMPAT=0 pip install groupdocs-watermark-net
  • Windows: no additional packages
  • Linux: ICU, fontconfig, libgdiplus and fonts — apt-get install libicu-dev libfontconfig1 libgdiplus ttf-mscorefonts-installer (on Debian, enable the contrib component first). libgdiplus: text watermarks on Word, Excel, PowerPoint, Visio and image files, text watermarks on PDF files that set sizing_type, and image watermarks, removal and previews on PowerPoint files (the rest of PDF needs none of it). The Microsoft core fonts: text watermarks on PDF files, which look their font up by name (Liberation substitutes are not picked up). PowerPoint previews fail on Linux even with libgdiplus (it does not implement Image.SaveAdd).
  • The .NET 10.0.12 runtime is embedded in the wheel; no separate install is needed

Wheel Distribution

PlatformWheel
Windows x64groupdocs_watermark_net-26.10.0-py3-none-win_amd64.whl
Linux x64groupdocs_watermark_net-26.10.0-py3-none-manylinux_2_27_x86_64.whl
macOS Intelgroupdocs_watermark_net-26.10.0-py3-none-macosx_12_0_x86_64.whl
macOS Apple Silicongroupdocs_watermark_net-26.10.0-py3-none-macosx_12_0_arm64.whl
pip install groupdocs-watermark-net==26.10.0

Additional Resources

Feedback

Questions and issues are welcome on the GroupDocs.Watermark forum.