We’re happy to announce the release of GroupDocs.Watermark for Python v26.10, available as of October 2026. This release upgrades the underlying .NET engine from 26.6 to 26.9 and the embedded .NET runtime from 10.0.0 to 10.0.12. It introduces support for custom loggers, enforces stricter attribute assignment, and fixes critical issues with preview callbacks and page stream handling. Additionally, compatibility is extended to Python 3.5–3.14, and platform support is clarified via strict wheel tags.

What’s new in this release

  • [Feature] Accept a Python object or function as WatermarkerSettings.logger (WATERMARKPYTHON-65)
  • [Enhancement] Update GroupDocs.Watermark engine from 26.6 to 26.9 (WATERMARKPYTHON-66)
  • [Enhancement] Update the embedded .NET runtime from 10.0.0 to 10.0.12 (WATERMARKPYTHON-67)
  • [Enhancement] Raise AttributeError when code assigns an attribute a class does not have (WATERMARKPYTHON-68)
  • [Fix] Cannot use the package on Python 3.5 – 3.7 (WATERMARKPYTHON-69)
  • [Fix] pip installs the package on Linux and macOS versions that cannot run it (WATERMARKPYTHON-70)
  • [Fix] Preview release callbacks receive None instead of the page stream (WATERMARKPYTHON-71)
  • [Fix] Page previews written to io.BytesIO are empty (WATERMARKPYTHON-72)
  • [Fix] Spreadsheet page previews written through a release callback are truncated (WATERMARKPYTHON-73)

Public 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.

New 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 import Watermarker
from groupdocs.watermark.watermarks import TextWatermark, Font

with Watermarker("document.pdf") as wm:
    watermark = TextWatermark("CONFIDENTIAL", Font("Arial", 36))
    # The following line raises an error because the property name is wrong
    watermark.opacity_level = 0.5

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.

Code example

See the Custom logger for WatermarkerSettings and Strict attribute assignment sections above.

How to get the update

PyPI

Direct download

Resources