GroupDocs.Parser for Python via .NET 26.10 Release Notes
This release upgrades the GroupDocs.Parser engine from 25.10 to 26.8. The embedded .NET runtime moves from .NET Core 3.1 to .NET 10.0.14, and System.Security.Cryptography.Pkcs to 10.0.14, which fixes two denial-of-service vulnerabilities. New features include annotation extraction, tables from HTML documents, OCR for Arabic and Korean, a Python logger and external-resource handler, preview callbacks as plain functions, a groupdocs-parser command line, the license applied from the GROUPDOCS_LIC_PATH environment variable and support for Python 3.14. Some Word and RTF documents that 25.12 could not read now open, and a document opened from a BytesIO no longer leaks memory. One wheel per platform now serves Python 3.5 – 3.14, the platform wheels target manylinux_2_27_x86_64 and macosx_13_0_*, and the 32-bit Windows wheel is removed.
Breaking change: The ICreatePageStream, IReleasePageStream and IPreviewPageRender classes are removed: pass plain Python functions to PreviewOptions and PreviewOptions.preview_page_render instead. Engine errors that 25.12 raised as RuntimeError now raise GroupDocsParserException or one of its subclasses (InvalidPasswordException, CorruptedDocumentException, UnsupportedDocumentFormatException), which do not derive from RuntimeError, so except RuntimeError no longer catches them. Assigning an attribute that a class does not have now raises AttributeError instead of creating a Python-only attribute the engine never sees. The wheel platform tags changed to manylinux_2_27_x86_64, macosx_13_0_x86_64 and macosx_13_0_arm64, and the 32-bit Windows wheel was dropped, so installing requires pip 20.3 or newer. See API Changes for the full list.
Full List of Changes in This Release
| Key | Category | Summary |
|---|---|---|
| PARSERPYTHONNET‑23 | Feature | Add support for extracting annotations with get_annotations() and TextOptions.include_annotations |
| PARSERPYTHONNET‑24 | Feature | Add the groupdocs-parser command-line tool |
| PARSERPYTHONNET‑25 | Feature | Add support for a Python logger object implementing ILogger |
| PARSERPYTHONNET‑26 | Feature | Add support for handling the loading of external resources from Python |
| PARSERPYTHONNET‑27 | Feature | Add OCR for Arabic and Korean text |
| PARSERPYTHONNET‑28 | Feature | Add support for extracting tables from HTML documents |
| PARSERPYTHONNET‑36 | Feature | Add support for applying the license from the GROUPDOCS_LIC_PATH environment variable |
| PARSERPYTHONNET‑29 | Enhancement | Update GroupDocs.Parser to 26.8 and the embedded runtime to .NET 10.0.14 |
| PARSERPYTHONNET‑30 | Enhancement | Ship a pure-Python package for Python 3.5 – 3.14 in place of the compiled extension |
| PARSERPYTHONNET‑31 | Enhancement | Pass preview callbacks to PreviewOptions as plain Python functions |
| PARSERPYTHONNET‑32 | Enhancement | Reject assignments to unknown attributes of API objects |
| PARSERPYTHONNET‑33 | Enhancement | Update the platform tags to glibc 2.27 and macOS 13, and remove the 32-bit Windows wheel |
| PARSERPYTHONNET‑34 | Enhancement | Raise GroupDocsParserException and its subclasses instead of RuntimeError |
| PARSERPYTHONNET‑37 | Enhancement | Update System.Security.Cryptography.Pkcs to 10.0.14, which fixes two denial-of-service vulnerabilities |
| PARSERPYTHONNET‑35 | Fix | Some Word and RTF documents fail with The document appears to be corrupted or a missing SkiaSharp 3.119 assembly |
| PARSERPYTHONNET‑38 | Fix | Memory grows with each document opened from a BytesIO |
Major 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 healthy document may log nothing. 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. 25.12’s manylinux1 and macosx_10_14 / macosx_11_0 tags let pip install wheels on systems where they could not start. OCR on macOS needs 13.3 or newer, which the ONNX Runtime library it uses declares. There is no 32-bit Windows wheel any more. Installing requires pip 20.3 or newer.
API Changes
Upgraders need to replace the preview callback classes with functions, catch GroupDocsParserException where they caught RuntimeError, and fix any misspelled attribute assignment, which now raises AttributeError. Everything else is additive.
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()andtable[row, column].DocumentData,Template:get().TemplateCollection:append().Resources:storage_path, writable.
Removed
groupdocs.parser.options.ICreatePageStreamgroupdocs.parser.options.IReleasePageStreamgroupdocs.parser.options.IPreviewPageRender
Pass functions to PreviewOptions and PreviewOptions.preview_page_render instead (see Preview callbacks as plain functions).
Changed
- Engine errors raise
GroupDocsParserException,InvalidPasswordException,CorruptedDocumentExceptionorUnsupportedDocumentFormatExceptionfromgroupdocs.parser.exceptions, where 25.12 raisedRuntimeErrorfor all of them. The classes existed in 25.12, but they did not derive fromExceptionand were never raised.except Exceptioncatches 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.
Fixed
- Some Word and RTF documents fail with
The document appears to be corruptedor a missingSkiaSharp3.119 assembly (PARSERPYTHONNET‑35). On 25.12,get_text()rejected some valid Word and RTF documents as corrupted, andget_tables()andget_images()on them raisedCould not load file or assembly 'SkiaSharp, Version=3.119.0.0', because the 25.12 wheel shipped SkiaSharp 2.88 while its engine references 3.119. 26.10 ships the SkiaSharp its engine is built against, and those documents return their text, tables and images. - Memory grows with each document opened from a
BytesIO(PARSERPYTHONNET‑38). On 25.12, eachParser(io.BytesIO(data))kept memory after the parser closed: opening a 40 MB document 60 times grew the process from 162 MB to 627 MB, while the same loop with a file path stayed flat. In 26.10 theBytesIOloop stays at about 175 MB.
Known Issues
- On Linux, without ICU the process aborts on the first engine call with
Couldn't find a valid ICU package, and no Python exception is raised. Install ICU (apt-get install libicu-dev, any version) and do not setDOTNET_SYSTEM_GLOBALIZATION_INVARIANT. - On Linux and macOS, presentations need
libgdiplus. Without it, the page preview of every PowerPoint and OpenDocument presentation fails withThe type initializer for 'Gdip' threw an exception, and so does every operation butget_metadata()on ODP and OTP files and on PPT, PPS and POT files that contain a table, andget_hyperlinks()on a PowerPoint file with a table or a hyperlink. Install it withapt-get install libgdiplusorbrew install mono-libgdiplus. - On Linux, without the Liberation fonts
get_structure()reports the merged cells of a Word table as separate rows. Install them withapt-get install fonts-liberation; the Microsoft core fonts are not needed. - OCR on macOS 13.0 – 13.2 raises
GroupDocsParserExceptionnamingMicrosoft.ML.OnnxRuntime.NativeMethods, because the ONNX Runtime library OCR uses needs macOS 13.3. Every other feature works on 13.0. - The text of a table cell from a DOCX or HTML document ends with the control character
\x07. Remove it withcell.text.rstrip("\x07"). - A process forked after its first
Parsercall cannot use Parser in the child: every call there raisesRuntimeError(The .NET runtime cannot be used in a process forked after it started), and the parent keeps working. This coversmultiprocessingwith theforkstart method (the default on Linux before Python 3.14),gunicorn --preloadand Celery’s prefork pool. Usemultiprocessing.get_context("spawn"), start the workers before the first call, or make the first call in each worker. - The first
Parser(...)in a process takes 20 – 30 seconds while the engine loads; documents opened after that take a fraction of a second. ParserSettings()raisesTypeError: __init__() - no overload matched: there is no emptyParserSettings. Pass a logger, an OCR connector or an external-resource handler.- With pip older than 20.3, or on Linux older than glibc 2.27 or macOS older than 13, pip cannot use the 26.10 wheels: an unpinned
pip install groupdocs-parser-netfalls back to 25.12, and a pinned one finds no matching distribution. Upgrade pip withpython -m pip install --upgrade pip.
Requirements
- Python 3.5 – 3.14 (
python_requires = ">=3.5,<3.15") and pip 20.3 or newer, 64-bit - Windows x64, Linux x64 with glibc 2.27 or newer, macOS 13 or newer on Intel or Apple Silicon. OCR on macOS needs 13.3 or newer. 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 set
SYSTEM_VERSION_COMPAT=0when running pip - Windows: no additional packages
- Linux: ICU, fontconfig, the Liberation fonts and libgdiplus —
apt-get install libicu-dev libfontconfig1 fonts-liberation libgdiplus. ICU is required for any document. The Liberation fonts keep merged table cells right inget_structure(). libgdiplus serves presentations: page previews, ODP and OTP files, and PowerPoint files with tables or hyperlinks. The Microsoft core fonts are not needed - macOS:
brew install mono-libgdiplus, for presentations as on Linux - The .NET 10.0.14 runtime is embedded in the wheel; no separate install is needed
Wheel Distribution
The wheels are on PyPI, where pip 20.3 or newer picks the one for your platform, and on releases.groupdocs.com for a machine without PyPI access. Install a downloaded wheel by its file name, from which pip reads the platform:
| Platform | Download | Wheel |
|---|---|---|
| Windows x64 | Windows | groupdocs_parser_net-26.10.0-py3-none-win_amd64.whl |
| Linux x64 | Linux | groupdocs_parser_net-26.10.0-py3-none-manylinux_2_27_x86_64.whl |
| macOS Intel | macOS Intel | groupdocs_parser_net-26.10.0-py3-none-macosx_13_0_x86_64.whl |
| macOS Apple Silicon | macOS Apple Silicon | groupdocs_parser_net-26.10.0-py3-none-macosx_13_0_arm64.whl |
pip install groupdocs-parser-net==26.10.0
Additional Resources
Feedback
Questions and issues are welcome on the GroupDocs.Parser forum.