We’re happy to announce the release of GroupDocs.Parser for Python v26.10, available as of November 2026. This release upgrades the underlying engine to version 26.8 and moves from .NET Core 3.1 to .NET 10.0.14, fixing two denial-of-service vulnerabilities in System.Security.Cryptography.Pkcs. It introduces major new features including annotation extraction, HTML table parsing, OCR for Arabic and Korean, a Python logger, external-resource handling, preview callbacks as plain functions, and a groupdocs-parser command-line tool. The wheel is now a pure-Python package targeting Python 3.5 – 3.14, with updated platform tags (manylinux_2_27_x86_64, macosx_13_0_*) and removal of the 32-bit Windows wheel.

What’s new in this release

  • [Feature] Add support for extracting annotations with get_annotations() and TextOptions.include_annotations (PARSERPYTHONNET-23)
  • [Feature] Add the groupdocs-parser command-line tool (PARSERPYTHONNET-24)
  • [Feature] Add support for a Python logger object implementing ILogger (PARSERPYTHONNET-25)
  • [Feature] Add support for handling the loading of external resources from Python (PARSERPYTHONNET-26)
  • [Feature] Add OCR for Arabic and Korean text (PARSERPYTHONNET-27)
  • [Feature] Add support for extracting tables from HTML documents (PARSERPYTHONNET-28)
  • [Feature] Add support for applying the license from the GROUPDOCS_LIC_PATH environment variable (PARSERPYTHONNET-36)
  • [Enhancement] Update GroupDocs.Parser to 26.8 and the embedded runtime to .NET 10.0.14 (PARSERPYTHONNET-29)
  • [Enhancement] Ship a pure-Python package for Python 3.5 – 3.14 in place of the compiled extension (PARSERPYTHONNET-30)
  • [Enhancement] Pass preview callbacks to PreviewOptions as plain Python functions (PARSERPYTHONNET-31)
  • [Enhancement] Reject assignments to unknown attributes of API objects (PARSERPYTHONNET-32)
  • [Enhancement] Update the platform tags to glibc 2.27 and macOS 13, and remove the 32-bit Windows wheel (PARSERPYTHONNET-33)
  • [Enhancement] Raise GroupDocsParserException and its subclasses instead of RuntimeError (PARSERPYTHONNET-34)
  • [Enhancement] Update System.Security.Cryptography.Pkcs to 10.0.14, which fixes two denial-of-service vulnerabilities (PARSERPYTHONNET-37)
  • [Fix] Some Word and RTF documents fail with The document appears to be corrupted or a missing SkiaSharp 3.119 assembly (PARSERPYTHONNET-35)
  • [Fix] Memory grows with each document opened from a BytesIO (PARSERPYTHONNET-38)

Public API changes

  • Engine errors raise GroupDocsParserException, InvalidPasswordException, CorruptedDocumentException or UnsupportedDocumentFormatException from groupdocs.parser.exceptions, where 25.12 raised RuntimeError for all of them. The classes existed in 25.12, but they did not derive from Exception and were never raised. except Exception catches the errors of both releases.
  • Assigning an unknown attribute raises AttributeError.
  • Lists the engine returns support len() and indexing, and an empty one is false.

Removed

  • groupdocs.parser.options.ICreatePageStream
  • groupdocs.parser.options.IReleasePageStream
  • groupdocs.parser.options.IPreviewPageRender

Pass functions to PreviewOptions and PreviewOptions.preview_page_render instead.

New classes and members

  • groupdocs.parser: Resources.
  • groupdocs.parser.data: AnnotationItem.
  • groupdocs.parser.options: CreatePageStream, ReleasePageStream, PreviewPageRender, Language, PreviewFormats.
  • Parser: get_annotations(), get_structure(), dispose().
  • Features: annotations.
  • TextOptions: include_annotations.
  • OcrOptions: language.
  • ILogger: error().
  • TocItem: extract_text().
  • PageTableArea: get() and table[row, column].
  • DocumentData, Template: get().
  • TemplateCollection: append().
  • Resources: storage_path, writable.

New features

Extract annotations

Parser.get_annotations() returns the comments and notes of a PDF, Word or PowerPoint document as AnnotationItem objects with the value, type, page index, timestamp, author name and initials; get_annotations(page_index) reads one page. TextOptions.include_annotations adds the annotation text to the extracted text, and parser.features.annotations tells whether a format supports annotations.

from groupdocs.parser import Parser
from groupdocs.parser.options import TextOptions

with Parser("reviewed.docx") as parser:
    for note in parser.get_annotations() or []:
        print(note.page_index + 1, note.annotation_type, note.author_name, note.value.strip())

    options = TextOptions()
    options.include_annotations = True
    print(parser.get_text(options))

Command line

The wheel installs the groupdocs-parser console script (also python -m groupdocs.parser) with the commands text, info, metadata, images, annotations and list-formats. text takes --page N (counted from 1), --format plain|markdown|html, --ocr, --include-annotations and -o FILE; info, metadata and annotations take --json; images takes --out DIR; every command takes --password. --license and --version come before the command. The exit code is 0 on success, 2 for a user error such as a missing file, and 1 for an engine error or a format that does not support the operation.

groupdocs-parser text report.pdf --page 2 -o page2.txt
groupdocs-parser text report.docx --format markdown
groupdocs-parser info report.pdf --json
groupdocs-parser annotations reviewed.docx --json
groupdocs-parser --license license.lic text protected.pdf --password secret

Python logger

A plain Python object with trace(message), warning(message) and error(message, exception) methods can be passed to ParserSettings as the logger, and the engine calls it with what it meets while it extracts, such as a damaged file. A function log(level, message, exception) works too.

from groupdocs.parser import Parser
from groupdocs.parser.options import ParserSettings


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)


with Parser("document.pdf", ParserSettings(PrintLogger())) as parser:
    print(parser.get_document_info().page_count)

Control the loading of external resources

The engine asks an external-resource handler before it loads each picture an HTML or MHTML document references. The handler is a function, an object with an on_loading(args) method, or a subclass of ExternalResourceHandler; setting args.skipped = True skips the resource. ParserSettings takes it as external_resource_handler.

from groupdocs.parser import Parser
from groupdocs.parser.options import ParserSettings


def skip_remote(args):
    args.skipped = args.uri.startswith("http")


with Parser("page.html", ParserSettings(external_resource_handler=skip_remote)) as parser:
    print(len(parser.get_images() or []))

OCR for Arabic and Korean

OcrOptions.language selects the alphabet OCR recognizes, English by default. Language.ARA and Language.KOR are new in this engine. The recognition models are part of the engine: the first OCR call unpacks them, about 80 MB, into an aspose_data folder in the working directory, with no download. To choose the folder, set Resources.storage_path (from groupdocs.parser import Resources) before the first OCR call.

from groupdocs.parser import Parser
from groupdocs.parser.options import Language, OcrOptions, TextOptions

ocr_options = OcrOptions()
ocr_options.language = Language.KOR
with Parser("scan.jpg") as parser:
    print(parser.get_text(TextOptions(False, True, ocr_options)))

Tables from HTML documents

get_tables() now finds the tables of HTML documents, as it does for PDF, Word and spreadsheet documents; 25.12 found none. A cell is table[row, column], or table.get(row, column).

from groupdocs.parser import Parser

with Parser("report.html") as parser:
    for table in parser.get_tables() or []:
        for row in range(table.row_count):
            print([table[row, column].text.rstrip("\x07") for column in range(table.column_count)])

License from the environment

Set GROUPDOCS_LIC_PATH to the path of a license file, or to a URL that serves one, and the license is applied at the first Parser call, with no license code; 25.12 did not read the variable. If that license cannot be applied (a missing file, an expired subscription, a license for another product), every Parser call raises LicenseError, naming the variable and the engine’s reason, until License().set_license() applies a license. Set GROUPDOCS_LIC_STRICT=0 to get one LicenseWarning and evaluation mode instead.

from groupdocs.parser import LicenseError, Parser

try:
    with Parser("report.pdf") as parser:
        print(parser.get_text())
except LicenseError as e:
    print(e)  # GROUPDOCS_LIC_PATH=... could not be applied: <reason>

Preview callbacks as plain functions

PreviewOptions(create_page_stream, release_page_stream) takes ordinary functions, and so does PreviewOptions.preview_page_render. create_page_stream(page_number) returns a writable stream, such as a file opened with "wb" or an io.BytesIO; pages are numbered from 1. The engine closes a file it was given, and calls release_page_stream(page_number) once the page is written. In 25.12 these were the ICreatePageStream, IReleasePageStream and IPreviewPageRender classes.

from groupdocs.parser import Parser
from groupdocs.parser.options import PreviewOptions


def create_page_stream(page_number):
    return open("page-%d.png" % page_number, "wb")


def release_page_stream(page_number):
    print("page", page_number, "written")


options = PreviewOptions(create_page_stream, release_page_stream)
options.preview_page_render = lambda info: print("rendering page", info.page_number)
with Parser("document.pdf") as parser:
    parser.generate_preview(options)

Misspelled attributes raise AttributeError

Assigning a property that an API object does not have raises AttributeError with a “Did you mean” hint. 25.12 accepted the assignment, stored a Python-only attribute and left the engine setting unchanged.

from groupdocs.parser.options import TextOptions

options = TextOptions()
try:
    options.include_annotation = True
except AttributeError as error:
    print(error)

One wheel for Python 3.5 – 3.14

25.12 was a compiled extension, with one binary module per Python version and a .NET Core 3.1 runtime. 26.10 is one py3-none-<platform> wheel per platform, plain Python over an embedded .NET 10 runtime, and it declares python_requires=">=3.5,<3.15". Python 3.14 is new: 25.12 declared <3.14. Most 25.12 code runs unchanged, and all 129 Docs examples that ran on 25.12 run on 26.10. get_text() and get_formatted_text() still return a str, which now also answers read_to_end().

Updated platform tags and 64-bit Windows wheel

The Linux wheel is tagged manylinux_2_27_x86_64, the oldest glibc the embedded .NET 10 runtime starts on, and the macOS wheels macosx_13_0_x86_64 and macosx_13_0_arm64. There is no 32-bit Windows wheel any more. Installing requires pip 20.3 or newer.

Code example

Extract annotations from a Word document and include them in the extracted text:

from groupdocs.parser import Parser
from groupdocs.parser.options import TextOptions

with Parser("reviewed.docx") as parser:
    for note in parser.get_annotations() or []:
        print(note.page_index + 1, note.annotation_type, note.author_name, note.value.strip())

    options = TextOptions()
    options.include_annotations = True
    print(parser.get_text(options))

How to get the update

PyPI

Install or upgrade using PyPI:

groupdocs-parser-net 26.10.0 on PyPI

pip install groupdocs-parser-net==26.10.0

Direct download

Download platform-specific wheels from the GroupDocs.Parser for Python via .NET 26.10 release page:

Resources