Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-pikepdf for openSUSE:Factory 
checked in at 2024-01-25 18:42:05
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pikepdf (Old)
 and      /work/SRC/openSUSE:Factory/.python-pikepdf.new.1815 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pikepdf"

Thu Jan 25 18:42:05 2024 rev:22 rq:1141499 version:8.11.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pikepdf/python-pikepdf.changes    
2023-12-28 23:03:47.335541986 +0100
+++ /work/SRC/openSUSE:Factory/.python-pikepdf.new.1815/python-pikepdf.changes  
2024-01-25 18:42:42.869887824 +0100
@@ -1,0 +2,20 @@
+Mon Jan 22 22:41:11 UTC 2024 - Dirk Müller <dmuel...@suse.com>
+
+- update to 8.11.2:
+  * Fixed handling of XMP metadata when metadata contains objects
+    in a default namespace.
+  * Replaced all relative imports with absolute imports.
+  * Rebuilt with QPDF 11.7.0.
+  * Added support for setting page boxes to a rectangle directly,
+    e.g. ``page.mediabox = rectangle`` - previously rectangle had
+    to manually converted to an array.
+  * Fixed rendering of PDF and individual pages in Jupyter/IPython.
+    Newer versions of these tools are now pickier about what types
+    of data they render, and don't render PDFs directly; we now
+    provide SVG which works well. Requires installation of MuPDF
+    as before.
+  * Fixed rendering of inline images in Jupyter/IPython, which was
+    not implemented.
+  * Fixed build process to use new artifacts v4 actions on GitHub.
+
+-------------------------------------------------------------------

Old:
----
  pikepdf-8.10.1.tar.gz

New:
----
  pikepdf-8.11.2.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-pikepdf.spec ++++++
--- /var/tmp/diff_new_pack.J4VsTy/_old  2024-01-25 18:42:43.573912942 +0100
+++ /var/tmp/diff_new_pack.J4VsTy/_new  2024-01-25 18:42:43.573912942 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-pikepdf
 #
-# Copyright (c) 2023 SUSE LLC
+# Copyright (c) 2024 SUSE LLC
 # Copyright (c) 2020-2021, Martin Hauke <mar...@gmx.de>
 #
 # All modifications and additions to the file contributed by third parties
@@ -19,7 +19,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-pikepdf
-Version:        8.10.1
+Version:        8.11.2
 Release:        0
 Summary:        Read and write PDFs with Python, powered by qpdf
 License:        MPL-2.0

++++++ pikepdf-8.10.1.tar.gz -> pikepdf-8.11.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/PKG-INFO new/pikepdf-8.11.2/PKG-INFO
--- old/pikepdf-8.10.1/PKG-INFO 2023-12-17 10:33:05.458814400 +0100
+++ new/pikepdf-8.11.2/PKG-INFO 2024-01-01 05:15:45.578745000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: pikepdf
-Version: 8.10.1
+Version: 8.11.2
 Summary: Read and write PDFs with Python, powered by qpdf
 Author-email: "James R. Barlow" <ja...@purplerock.ca>
 License: MPL-2.0
@@ -23,7 +23,8 @@
 License-File: LICENSE.txt
 Requires-Dist: Pillow>=10.0.1
 Requires-Dist: Deprecated
-Requires-Dist: lxml>=4.8
+Requires-Dist: lxml>=4.8; python_version >= "3.10"
+Requires-Dist: lxml<5,>=4.8; python_version < "3.10"
 Requires-Dist: packaging
 Provides-Extra: dev
 Requires-Dist: pre-commit; extra == "dev"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/docs/binary-wheels.csv 
new/pikepdf-8.11.2/docs/binary-wheels.csv
--- old/pikepdf-8.10.1/docs/binary-wheels.csv   2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/docs/binary-wheels.csv   2024-01-01 05:14:08.000000000 
+0100
@@ -1,8 +1,8 @@
 ,3.8,3.9,3.10,3.11,3.12,PyPy 3.9, PyPy 3.10
-:fa:`apple` macOS Intel,✅,✅,✅,✅,✅,⏳,❌
+:fa:`apple` macOS Intel,✅,✅,✅,✅,✅,✅,⏳
 :fa:`apple` macOS Apple Silicon,❌,✅,✅,✅,✅,⏳,⏳
 :fa:`windows` Windows x64,✅,✅,✅,✅,✅,✅,⏳
 :fa:`linux` manylinux2014 x64,✅,✅,✅,✅,✅,✅,⏳
 :fa:`linux` manylinux2014 aarch64 (ARM64),✅,✅,✅,✅,✅,⏳,⏳
 :fa:`linux` musllinux x64,❌,✅,✅,✅,✅,⏳,⏳
-:fa:`linux` musllinux aarch64 (ARM64),❌,✅,✅,✅,✅,⏳,⏳
\ No newline at end of file
+:fa:`linux` musllinux aarch64 (ARM64),❌,⏳,⏳,⏳,⏳,⏳,⏳
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/docs/conf.py 
new/pikepdf-8.11.2/docs/conf.py
--- old/pikepdf-8.10.1/docs/conf.py     2023-12-17 10:31:00.000000000 +0100
+++ new/pikepdf-8.11.2/docs/conf.py     2024-01-01 05:14:08.000000000 +0100
@@ -92,7 +92,7 @@
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 
-release = "8.10.1"
+release = "8.11.2"
 version = '.'.join(release.split('.')[:2])
 
 # The language for content autogenerated by Sphinx. Refer to documentation
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/docs/index.rst 
new/pikepdf-8.11.2/docs/index.rst
--- old/pikepdf-8.10.1/docs/index.rst   2023-12-17 10:31:00.000000000 +0100
+++ new/pikepdf-8.11.2/docs/index.rst   2024-01-01 05:14:08.000000000 +0100
@@ -171,6 +171,7 @@
     :name: reference_toc
 
     references/arch
+    references/build
     references/contributing
     references/debugging
     references/resources
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/docs/references/build.rst 
new/pikepdf-8.11.2/docs/references/build.rst
--- old/pikepdf-8.10.1/docs/references/build.rst        1970-01-01 
01:00:00.000000000 +0100
+++ new/pikepdf-8.11.2/docs/references/build.rst        2024-01-01 
05:14:08.000000000 +0100
@@ -0,0 +1,69 @@
+Build process notes
+===================
+
+This section contains notes on complexities in the GitHub Actions
+build-deploy workflow.
+
+macOS crypto provider
+---------------------
+
+Users reported trouble with open legacy encrypted files on macOS
+specifically, e.g. https://github.com/pikepdf/pikepdf/issues/520
+
+It appears this is because we were using Homebrew's qpdf which is
+currently linked against Homebrew's openssl. The error came from
+qpdf specifically not finding openssl's legacy crypto provider. How
+exactly that comes about is unclear - it may be that delocate-wheel
+is inconsistent, it may be Homebrew has disabled the legacy
+provider, etc. Since we use gnutls for crypto and build libqpdf on
+the fly for Linux wheels, might as well do the same for macOS
+to address this.
+
+So now we build and link against our own libqpdf, which in turn is
+linked against gnutls, which does not seem to have the same issues.
+All of our Linux and macOS builds are doing the same thing now,
+rather than being split on crypto provider.
+
+Some ugly complications emerged:
+
+1. QPDF now uses some C++17 features like std::get that are not
+available on macOS 10.9, the default, which seems to support only a
+subset of C++17. Because of that we need to explicitly set
+MACOSX_DEPLOYMENT_TARGET="11.0"
+when building QPDF.
+
+2. x86_64. For reasons unclear, when MACOSX_DEPLOYMENT_TARGET="11.0" is set
+and pikepdf builds, the generated wheel is built without issue,
+but installing on a macos-12-x86_64 runner gives this error:
+
+.. code-block::
+
+    ERROR: pikepdf-8.11.0-cp38-cp38-macosx_11_0_x86_64.whl is not a supported 
wheel on this platform.
+
+(Same for MACOSX_DEPLOYMENT_TARGET="12.0").
+
+Thus, we have to pick a different value for this environment variable
+for the pikepdf. Clearly something is wrong here, but this unintuitive
+configuration is what works. When MACOSX_DEPLOYMENT_TARGET is not set
+pybind11's Pybind11Extension class examines other settings and should
+set -mmacosx-version-min=10.14 for when cxx_std=17.
+
+Adding to confusion, the build logs from setuptools show that are
+targeting 10.9, and the wheels are generated for 10.9.
+
+There may be dragons here, still.
+
+3. arm64. MACOSX_DEPLOYMENT_TARGET="11.0" is the minimum version for arm64.
+We set this to ensure that pikepdf builds for this target, and then
+because of the aforementioned Pybind11Extension check, pikepdf's
+deployment target would be set to 10.14. To override this, we set
+MACOSX_DEPLOYMENT_TARGET="11.0" for each builder, as well as when
+building libqpdf.
+
+4. Environment differences between GitHub runners and Cirrus CI runners
+mean we need to use sudo for Cirrus CI, hence maybe_sudo.
+
+Taking a quick peek at numpy, it may be easier to build universal2 wheels
+and use QEMU or Cirrus CI to confirm that they work. That would be another
+big build overhaul, so let's wait for GitHub Actions to release arm64
+runners instead.
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/docs/releasenotes/version8.rst 
new/pikepdf-8.11.2/docs/releasenotes/version8.rst
--- old/pikepdf-8.10.1/docs/releasenotes/version8.rst   2023-12-17 
10:31:00.000000000 +0100
+++ new/pikepdf-8.11.2/docs/releasenotes/version8.rst   2024-01-01 
05:14:08.000000000 +0100
@@ -1,3 +1,33 @@
+v8.11.2
+=======
+
+- Fixed handling of XMP metadata when metadata contains objects in a default
+  namespace.
+
+v8.11.1
+=======
+
+- macOS wheels are now linked against the GnuTLS crypto library instead of
+  OpenSSL. Hopefully this will alleviate situations where the legacy crypto
+  provider could not be loaded. :issue:`520`
+- Replaced all relative imports with absolute imports.
+- Excluded lxml 5.x for Python 3.8 and 3.9, since this project is not producing
+  wheels for 3.8 and 3.9 for the latest versions.
+
+v8.11.0
+=======
+
+- Rebuilt with QPDF 11.7.0.
+- Added support for setting page boxes to a rectangle directly, e.g.
+  ``page.mediabox = rectangle`` - previously rectangle had to
+  manually converted to an array.
+- Fixed rendering of PDF and individual pages in Jupyter/IPython. Newer 
versions
+  of these tools are now pickier about what types of data they render, and 
don't
+  render PDFs directly; we now provide SVG which works well. Requires 
installation
+  of MuPDF as before.
+- Fixed rendering of inline images in Jupyter/IPython, which was not 
implemented.
+- Fixed build process to use new artifacts v4 actions on GitHub.
+
 v8.10.1
 =======
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/docs/topics/attachments.rst 
new/pikepdf-8.11.2/docs/topics/attachments.rst
--- old/pikepdf-8.10.1/docs/topics/attachments.rst      2023-12-17 
10:31:00.000000000 +0100
+++ new/pikepdf-8.11.2/docs/topics/attachments.rst      2024-01-01 
05:14:08.000000000 +0100
@@ -22,7 +22,7 @@
     >>> pdf.attachments['README.md'] = filespec
 
     >>> pdf.attachments
-    <pikepdf._core.Attachments with 1 attached files>
+    <pikepdf._core.Attachments: ['README.md']>
 
 This creates an attached file named ``README.md``, which holds the data in 
``filespec``.
 Now we can retrieve the data.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/pyproject.toml 
new/pikepdf-8.11.2/pyproject.toml
--- old/pikepdf-8.10.1/pyproject.toml   2023-12-17 10:31:00.000000000 +0100
+++ new/pikepdf-8.11.2/pyproject.toml   2024-01-01 05:14:08.000000000 +0100
@@ -7,7 +7,7 @@
 
 [project]
 name = "pikepdf"
-version = "8.10.1"
+version = "8.11.2"
 description = "Read and write PDFs with Python, powered by qpdf"
 readme = "README.md"
 requires-python = ">=3.8"
@@ -26,7 +26,13 @@
   "Topic :: Multimedia :: Graphics",
   "Topic :: Software Development :: Libraries",
 ]
-dependencies = ["Pillow>=10.0.1", "Deprecated", "lxml>=4.8", "packaging"]
+dependencies = [
+  "Pillow>=10.0.1",
+  "Deprecated",
+  "lxml>=4.8; python_version >= '3.10'",
+  "lxml>=4.8,<5; python_version < '3.10'", # lxml doesn't make 5.0 wheels for 
python < 3.10
+  "packaging",
+]
 
 [project.urls]
 documentation = "https://pikepdf.readthedocs.io/";
@@ -41,7 +47,7 @@
   "sphinx-design",
   "sphinx-issues",
   "sphinx-rtd-theme",
-  "tomli;python_version < '3.11'",
+  "tomli; python_version < '3.11'",
 ]
 mypy = ["lxml-stubs", "types-Pillow", "types-requests", "types-setuptools"]
 test = [
@@ -114,15 +120,21 @@
 
 [tool.cibuildwheel.environment]
 QPDF_MIN_VERSION = "11.5.0"
-QPDF_VERSION = "11.6.4"
+QPDF_VERSION = "11.7.0"
 QPDF_PATTERN = 
"https://github.com/qpdf/qpdf/releases/download/vVERSION/qpdf-VERSION.tar.gz";
 
 [tool.cibuildwheel.linux]
 before-all = [
-  "yum install -y wget libxml2-devel libxslt-devel libjpeg-turbo-devel 
zlib-devel",
+  """yum install -y \
+    libjpeg-turbo-devel \
+    libxml2-devel \
+    libxslt-devel \
+    wget \
+    zlib-devel \
+  """,
   "sh -c 'yum install -y libxmp || true'",
-  "bash build-scripts/linux-download-qpdf.bash $QPDF_VERSION",
-  "bash build-scripts/linux-build-wheel-deps.bash",
+  "bash build-scripts/posix-download-qpdf.bash $QPDF_VERSION",
+  "bash build-scripts/posix-build-wheel-deps.bash",
 ]
 environment-pass = ["CI"]
 manylinux-x86_64-image = "manylinux2014"
@@ -133,17 +145,36 @@
 [[tool.cibuildwheel.overrides]]
 select = "*-musllinux*"
 before-all = [
-  "apk add py3-lxml py3-pillow python3 py3-setuptools py3-pybind11-dev 
python3-dev py3-wheel exempi-dev wget cmake make zlib-dev jpeg-dev",
-  "bash build-scripts/linux-download-qpdf.bash $QPDF_VERSION",
-  "bash build-scripts/linux-build-wheel-deps.bash",
+  """apk add \
+    cmake \
+    exempi-dev \
+    jpeg-dev \
+    make \
+    mupdf-tools \
+    py3-lxml \
+    py3-pillow \
+    py3-pybind11-dev \
+    py3-setuptools \
+    py3-wheel \
+    python3 \
+    python3-dev \
+    wget \
+    zlib-dev \
+  """,
+  "bash build-scripts/posix-download-qpdf.bash $QPDF_VERSION",
+  "bash build-scripts/posix-build-wheel-deps.bash",
 ]
 
 [tool.cibuildwheel.macos]
-# Use workaround until https://github.com/actions/setup-python/issues/577 is 
fixed
 before-all = [
   "sh -c 'brew update || true'",
-  "sh -c 'brew install qpdf little-cms2 || true'",
-]
+  "sh -c 'brew install gnutls little-cms2 mupdf wget || true'",
+  "bash build-scripts/posix-download-qpdf.bash $QPDF_VERSION",
+  "bash build-scripts/posix-build-wheel-deps.bash",
+] # Use 'brew whatever || true' workaround until 
https://github.com/actions/setup-python/issues/577 is fixed
+before-build = [
+  'if [ "$(uname -m)" == "arm64" ]; then export 
MACOSX_DEPLOYMENT_TARGET="11.0"; fi',
+] # Ensure each build is targeting 11.0 when we are on arm64. See 
docs/references/build.rst
 
 [tool.cibuildwheel.windows]
 
@@ -193,9 +224,15 @@
   "W",    # pycodestyle
   "F",    # pyflakes
   "I001", # isort
+  "TID",  # flake8-tidy-imports
   "UP",   # pyupgrade
+  "YTT",  # flake8-2020
 ]
-ignore = ["D105"] # docstring in magic method
+ignore = ["D105", "B028"]
+
+[tool.ruff.lint.flake8-tidy-imports]
+ban-relative-imports = "all"
+banned-module-level-imports = ["numpy"]
 
 [tool.ruff.lint.isort]
 known-first-party = ["pikepdf", "pikepdf._core"]
@@ -220,4 +257,4 @@
 "examples/*.py" = ["D103"]
 
 # [tool.ruff.format]
-# quote-style = "single"
+# quote-style = "preserve"  # 2023-12-28 - ruff feature not ready yet
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/core/object.cpp 
new/pikepdf-8.11.2/src/core/object.cpp
--- old/pikepdf-8.10.1/src/core/object.cpp      2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/src/core/object.cpp      2024-01-01 05:14:08.000000000 
+0100
@@ -132,7 +132,19 @@
     case qpdf_object_type_e::ot_array: {
         // Call operator==() on each element of the arrays, meaning this
         // recurses into this function
-        return (self.getArrayAsVector() == other.getArrayAsVector());
+        if (self.getArrayNItems() != other.getArrayNItems())
+            return false;
+        {
+            auto self_aitems  = self.aitems();
+            auto other_aitems = other.aitems();
+            auto iter_self    = self_aitems.begin();
+            auto iter_other   = other_aitems.begin();
+            for (; iter_self != self_aitems.end(); ++iter_self, ++iter_other) {
+                if (!objecthandle_equal(*iter_self, *iter_other))
+                    return false;
+            }
+        }
+        return true;
     }
     case qpdf_object_type_e::ot_dictionary: {
         // Call operator==() on each element of the arrays, meaning this
@@ -193,9 +205,11 @@
     if (!haystack.isArray())
         throw std::logic_error("pikepdf.Object is not an Array"); // 
LCOV_EXCL_LINE
 
-    auto vec    = haystack.getArrayAsVector();
-    auto result = std::find(std::begin(vec), std::end(vec), needle);
-    return (result != std::end(vec));
+    for (auto &item : haystack.aitems()) {
+        if (objecthandle_equal(item, needle))
+            return true;
+    }
+    return false;
 }
 
 QPDFObjectHandle object_get_key(QPDFObjectHandle h, std::string const &key)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/core/object_convert.cpp 
new/pikepdf-8.11.2/src/core/object_convert.cpp
--- old/pikepdf-8.10.1/src/core/object_convert.cpp      2023-12-17 
10:31:00.000000000 +0100
+++ new/pikepdf-8.11.2/src/core/object_convert.cpp      2024-01-01 
05:14:08.000000000 +0100
@@ -87,6 +87,10 @@
             "Can't convert ObjectHelper (or subclass) to Object implicitly. "
             "Use .obj to get access the underlying object.");
     }
+    if (py::isinstance<QPDFObjectHandle::Rectangle>(handle)) {
+        auto rect = handle.cast<QPDFObjectHandle::Rectangle>();
+        return QPDFObjectHandle::newFromRectangle(rect);
+    }
 
     // Special-case booleans since pybind11 coerces nonzero integers to boolean
     if (py::isinstance<py::bool_>(handle)) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/core/object_repr.cpp 
new/pikepdf-8.11.2/src/core/object_repr.cpp
--- old/pikepdf-8.10.1/src/core/object_repr.cpp 2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/src/core/object_repr.cpp 2024-01-01 05:14:08.000000000 
+0100
@@ -218,7 +218,7 @@
         {
             bool first_item = true;
             ss << " ";
-            for (auto item : h.getArrayAsVector()) {
+            for (auto &item : h.aitems()) {
                 if (!first_item)
                     ss << ", ";
                 first_item = false;
@@ -240,9 +240,7 @@
         {
             bool first_item = true;
             ss << "\n";
-            for (auto item : h.getDictAsMap()) {
-                auto &key = item.first;
-                auto &obj = item.second;
+            for (auto &[key, obj] : h.ditems()) {
                 if (!first_item)
                     ss << ",\n";
                 first_item = false;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/core/pikepdf.cpp 
new/pikepdf-8.11.2/src/core/pikepdf.cpp
--- old/pikepdf-8.10.1/src/core/pikepdf.cpp     2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/src/core/pikepdf.cpp     2024-01-01 05:14:08.000000000 
+0100
@@ -59,8 +59,8 @@
         match_replace{"QPDF", "pikepdf.Pdf"},
     };
 
-    for (auto mr : replacements) {
-        msg = std::regex_replace(msg, mr.first, mr.second);
+    for (auto [regex, replacement] : replacements) {
+        msg = std::regex_replace(msg, regex, replacement);
     }
     return msg;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf/__init__.py 
new/pikepdf-8.11.2/src/pikepdf/__init__.py
--- old/pikepdf-8.10.1/src/pikepdf/__init__.py  2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/src/pikepdf/__init__.py  2024-01-01 05:14:08.000000000 
+0100
@@ -7,15 +7,15 @@
 
 from __future__ import annotations
 
-from ._version import __version__
+from pikepdf._version import __version__
 
 try:
-    from . import _core
+    from pikepdf import _core
 except ImportError as _e:  # pragma: no cover
     _msg = "pikepdf's extension library failed to import"
     raise ImportError(_msg) from _e
 
-from ._core import (
+from pikepdf._core import (
     AccessMode,
     Annotation,
     AttachedFileSpec,
@@ -41,8 +41,8 @@
     TokenFilter,
     TokenType,
 )
-from ._exceptions import DependencyError
-from .objects import (
+from pikepdf._exceptions import DependencyError
+from pikepdf.objects import (
     Array,
     Dictionary,
     Name,
@@ -52,7 +52,7 @@
     Stream,
     String,
 )
-from .models import (
+from pikepdf.models import (
     Encryption,
     Outline,
     OutlineItem,
@@ -73,8 +73,8 @@
 # While _cpphelpers is intended to be called from our C++ code only, explicitly
 # importing helps introspection tools like PyInstaller figure out that the 
module
 # is necessary.
-from . import _cpphelpers, _methods, codec  # noqa: F401, F841
-from . import settings
+from pikepdf import _cpphelpers, _methods, codec  # noqa: F401, F841
+from pikepdf import settings
 
 __libqpdf_version__: str = _core.qpdf_version()
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf/_core.pyi 
new/pikepdf-8.11.2/src/pikepdf/_core.pyi
--- old/pikepdf-8.10.1/src/pikepdf/_core.pyi    2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/src/pikepdf/_core.pyi    2024-01-01 05:14:08.000000000 
+0100
@@ -1238,7 +1238,7 @@
         If the /ArtBox is not defined, the /CropBox is returned.
         """
     @artbox.setter
-    def artbox(self, val: Array) -> None: ...
+    def artbox(self, val: Array | Rectangle) -> None: ...
     @property
     def bleedbox(self) -> Array:
         """Return page's effective /BleedBox, in PDF units.
@@ -1250,7 +1250,7 @@
         If the /BleedBox is not defined, the /CropBox is returned.
         """
     @bleedbox.setter
-    def bleedbox(self, val: Array) -> None: ...
+    def bleedbox(self, val: Array | Rectangle) -> None: ...
     @property
     def cropbox(self) -> Array:
         """Return page's effective /CropBox, in PDF units.
@@ -1264,7 +1264,7 @@
         If the /CropBox is not defined, the /MediaBox is returned.
         """
     @cropbox.setter
-    def cropbox(self, val: Array) -> None: ...
+    def cropbox(self, val: Array | Rectangle) -> None: ...
     @property
     def mediabox(self) -> Array:
         """Return page's /MediaBox, in PDF units.
@@ -1274,7 +1274,7 @@
         the page is to be printed."
         """
     @mediabox.setter
-    def mediabox(self, val: Array) -> None: ...
+    def mediabox(self, val: Array | Rectangle) -> None: ...
     @property
     def obj(self) -> Dictionary: ...
     @property
@@ -1291,7 +1291,7 @@
         /CropBox is not defined, /MediaBox is returned).
         """
     @trimbox.setter
-    def trimbox(self, val: Array) -> None: ...
+    def trimbox(self, val: Array | Rectangle) -> None: ...
     @property
     def resources(self) -> Dictionary:
         """Return this page's resources dictionary.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf/_methods.py 
new/pikepdf-8.11.2/src/pikepdf/_methods.py
--- old/pikepdf-8.10.1/src/pikepdf/_methods.py  2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/src/pikepdf/_methods.py  2024-01-01 05:14:08.000000000 
+0100
@@ -16,7 +16,7 @@
 import mimetypes
 import shutil
 from collections.abc import KeysView, MutableMapping
-from contextlib import ExitStack
+from contextlib import ExitStack, suppress
 from decimal import Decimal
 from io import BytesIO, RawIOBase
 from pathlib import Path
@@ -74,7 +74,7 @@
         tmp_in.flush()
 
         proc = run(
-            ['mudraw', '-F', fmt, '-o', '-', tmp_in.name],
+            ['mutool', 'draw', '-F', fmt, '-o', '-', tmp_in.name],
             capture_output=True,
             check=True,
         )
@@ -157,14 +157,20 @@
 
 @augments(Pdf)
 class Extend_Pdf:
-    def _repr_mimebundle_(
-        self, include=None, exclude=None
-    ):  # pylint: disable=unused-argument
+    def _quick_save(self):
         bio = BytesIO()
         self.save(bio)
         bio.seek(0)
+        return bio
 
-        data = {'application/pdf': bio.read()}
+    def _repr_mimebundle_(
+        self, include=None, exclude=None
+    ):  # pylint: disable=unused-argument
+        pdf_data = self._quick_save().read()
+        data = {
+            'application/pdf': pdf_data,
+            'image/svg+xml': _mudraw(pdf_data, 'svg').decode('utf-8'),
+        }
         return data
 
     @property
@@ -430,19 +436,15 @@
 
 
 def check_is_box(obj) -> None:
-    try:
+    with suppress(AttributeError):
         if obj.is_rectangle:
             return
-    except AttributeError:
-        pass
-
     try:
         pdfobj = Array(obj)
         if pdfobj.is_rectangle:
             return
     except Exception as e:
         raise ValueError("object is not a rectangle") from e
-
     raise ValueError("object is not a rectangle")
 
 
@@ -661,7 +663,7 @@
 
     def _repr_mimebundle_(self, include=None, exclude=None):
         data = {}
-        bundle = {'application/pdf', 'image/png'}
+        bundle = {'application/pdf', 'image/svg+xml'}
         if include:
             bundle = {k for k in bundle if k in include}
         if exclude:
@@ -669,11 +671,9 @@
         pagedata = _single_page_pdf(self)
         if 'application/pdf' in bundle:
             data['application/pdf'] = pagedata
-        if 'image/png' in bundle:
-            try:
-                data['image/png'] = _mudraw(pagedata, 'png')
-            except (FileNotFoundError, RuntimeError):
-                pass
+        if 'image/svg+xml' in bundle:
+            with suppress(FileNotFoundError, RuntimeError):
+                data['image/svg+xml'] = _mudraw(pagedata, 
'svg').decode('utf-8')
         return data
 
 
@@ -717,8 +717,6 @@
         yield from self._get_all_filespecs()
 
     def __repr__(self):
-        if len(self) == 0:
-            return "<pikepdf._core.Attachments no attached files>"
         return f"<pikepdf._core.Attachments: {list(self)}>"
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf/_version.py 
new/pikepdf-8.11.2/src/pikepdf/_version.py
--- old/pikepdf-8.10.1/src/pikepdf/_version.py  2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/src/pikepdf/_version.py  2024-01-01 05:14:08.000000000 
+0100
@@ -3,4 +3,4 @@
 
 from __future__ import annotations
 
-__version__ = "8.10.1"
+__version__ = "8.11.2"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf/_xml.py 
new/pikepdf-8.11.2/src/pikepdf/_xml.py
--- old/pikepdf-8.10.1/src/pikepdf/_xml.py      2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/src/pikepdf/_xml.py      2024-01-01 05:14:08.000000000 
+0100
@@ -6,7 +6,7 @@
 from typing import IO, Any, AnyStr
 
 from lxml.etree import XMLParser as _UnsafeXMLParser
-from lxml.etree import _ElementTree
+from lxml.etree import _Element, _ElementTree
 from lxml.etree import parse as _parse
 
 
@@ -25,4 +25,4 @@
     return _parse(source, parser=parser)
 
 
-__all__ = ['parse_xml']
+__all__ = ['parse_xml', '_ElementTree', '_Element']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf/canvas.py 
new/pikepdf-8.11.2/src/pikepdf/canvas.py
--- old/pikepdf-8.10.1/src/pikepdf/canvas.py    2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/src/pikepdf/canvas.py    2024-01-01 05:14:08.000000000 
+0100
@@ -65,6 +65,9 @@
         the data, a reference should be set in at least one page's /Resources 
dictionary
         to retain the font in the output PDF and ensure it is usable on that 
page.
 
+        The returned Dictionary should be created as an indirect object, using
+        ``pdf.make_indirect()``.
+
         Returns a Dictionary suitable for insertion into a /Resources /Font 
dictionary.
         """
 
@@ -78,10 +81,12 @@
 
     def register(self, pdf: Pdf) -> Dictionary:
         """Register the font."""
-        return Dictionary(
-            BaseFont=Name.Helvetica,
-            Type=Name.Font,
-            Subtype=Name.Type1,
+        return pdf.make_indirect(
+            Dictionary(
+                BaseFont=Name.Helvetica,
+                Type=Name.Font,
+                Subtype=Name.Type1,
+            )
         )
 
 
@@ -435,6 +440,9 @@
         self.do.push()
         return result
 
+    def _repr_mimebundle_(self, include=None, exclude=None):
+        return self.to_pdf()._repr_mimebundle_(include, exclude)
+
 
 class Text:
     """Text object for rendering text on a pikepdf canvas."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf/codec.py 
new/pikepdf-8.11.2/src/pikepdf/codec.py
--- old/pikepdf-8.10.1/src/pikepdf/codec.py     2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/src/pikepdf/codec.py     2024-01-01 05:14:08.000000000 
+0100
@@ -8,7 +8,7 @@
 import codecs
 from typing import Any, Container
 
-from ._core import pdf_doc_to_utf8, utf8_to_pdf_doc
+from pikepdf._core import pdf_doc_to_utf8, utf8_to_pdf_doc
 
 # pylint: disable=redefined-builtin
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf/models/__init__.py 
new/pikepdf-8.11.2/src/pikepdf/models/__init__.py
--- old/pikepdf-8.10.1/src/pikepdf/models/__init__.py   2023-12-17 
10:31:00.000000000 +0100
+++ new/pikepdf-8.11.2/src/pikepdf/models/__init__.py   2024-01-01 
05:14:08.000000000 +0100
@@ -5,18 +5,18 @@
 
 from __future__ import annotations
 
-from ._content_stream import (
+from pikepdf.models._content_stream import (
     ContentStreamInstructions,
     PdfParsingError,
     UnparseableContentStreamInstructions,
     parse_content_stream,
     unparse_content_stream,
 )
-from .encryption import Encryption, EncryptionInfo, Permissions
-from .image import PdfImage, PdfInlineImage, UnsupportedImageTypeError
-from .matrix import PdfMatrix
-from .metadata import PdfMetadata
-from .outlines import (
+from pikepdf.models.encryption import Encryption, EncryptionInfo, Permissions
+from pikepdf.models.image import PdfImage, PdfInlineImage, 
UnsupportedImageTypeError
+from pikepdf.models.matrix import PdfMatrix
+from pikepdf.models.metadata import PdfMetadata
+from pikepdf.models.outlines import (
     Outline,
     OutlineItem,
     OutlineStructureError,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf/models/encryption.py 
new/pikepdf-8.11.2/src/pikepdf/models/encryption.py
--- old/pikepdf-8.10.1/src/pikepdf/models/encryption.py 2023-12-17 
10:31:00.000000000 +0100
+++ new/pikepdf-8.11.2/src/pikepdf/models/encryption.py 2024-01-01 
05:14:08.000000000 +0100
@@ -137,6 +137,14 @@
         """
         return len(self._encdict['encryption_key']) * 8
 
+    def __repr__(self):
+        return (
+            f'<{self.__class__.__name__}: {self.R=}, {self.V=}, {self.P=} '
+            f'{self.stream_method=}, {self.string_method=}, '
+            f'{self.file_method=}, {self.user_password=}, '
+            f'{self.encryption_key=}, {self.bits=}>'
+        )
+
 
 class Encryption(NamedTuple):
     """Specify the encryption settings to apply when a PDF is saved."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf/models/image.py 
new/pikepdf-8.11.2/src/pikepdf/models/image.py
--- old/pikepdf-8.10.1/src/pikepdf/models/image.py      2023-12-17 
10:31:00.000000000 +0100
+++ new/pikepdf-8.11.2/src/pikepdf/models/image.py      2024-01-01 
05:14:08.000000000 +0100
@@ -354,6 +354,13 @@
     def as_pil_image(self) -> Image.Image:
         """Convert this PDF image to a Python PIL (Pillow) image."""
 
+    def _repr_png_(self) -> bytes:
+        """Display hook for IPython/Jupyter."""
+        b = BytesIO()
+        with self.as_pil_image() as im:
+            im.save(b, 'PNG')
+            return b.getvalue()
+
 
 class PdfImage(PdfImageBase):
     """Support class to provide a consistent API for manipulating PDF images.
@@ -810,13 +817,6 @@
             f'size={self.width}x{self.height} at {hex(id(self))}>'
         )
 
-    def _repr_png_(self) -> bytes:
-        """Display hook for IPython/Jupyter."""
-        b = BytesIO()
-        with self.as_pil_image() as im:
-            im.save(b, 'PNG')
-            return b.getvalue()
-
 
 class PdfJpxImage(PdfImage):
     """Support class for JPEG 2000 images. Implements the same API as 
:class:`PdfImage`.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf/models/metadata.py 
new/pikepdf-8.11.2/src/pikepdf/models/metadata.py
--- old/pikepdf-8.10.1/src/pikepdf/models/metadata.py   2023-12-17 
10:31:00.000000000 +0100
+++ new/pikepdf-8.11.2/src/pikepdf/models/metadata.py   2024-01-01 
05:14:08.000000000 +0100
@@ -12,14 +12,14 @@
 from datetime import datetime, timezone
 from functools import wraps
 from io import BytesIO
-from typing import TYPE_CHECKING, Any, Callable, NamedTuple, Set
+from typing import TYPE_CHECKING, Any, Callable, Iterator, NamedTuple, Set
 from warnings import warn
 
 from lxml import etree
 from lxml.etree import QName, XMLSyntaxError
 
 from pikepdf._version import __version__ as pikepdf_version
-from pikepdf._xml import parse_xml
+from pikepdf._xml import _Element, parse_xml
 from pikepdf.objects import Name, Stream, String
 
 if sys.version_info < (3, 9):  # pragma: no cover
@@ -383,11 +383,11 @@
     ):
         """Construct PdfMetadata. Use Pdf.open_metadata() instead."""
         self._pdf = pdf
-        self._xmp = None
         self.mark = pikepdf_mark
         self.sync_docinfo = sync_docinfo
         self._updating = False
         self.overwrite_invalid_xml = overwrite_invalid_xml
+        self._xmp = None
 
     def load_from_docinfo(
         self, docinfo, delete_missing: bool = False, raise_failure: bool = 
False
@@ -600,12 +600,17 @@
         try:
             prefix, tag = name.split(':', maxsplit=1)
         except ValueError:
-            # If missing the namespace, put it in the top level namespace
-            # To do this completely correct we actually need to figure out
-            # the namespace based on context defined by parent tags. That
-            #   https://www.w3.org/2001/tag/doc/qnameids.html
-            prefix, tag = 'x', name
-        uri = cls.NS[prefix]
+            # If missing the namespace, it belongs in the default namespace.
+            # A tag such <xyz xmlns="http://example.com";> defines a default
+            # namespace of http://example.com for all enclosed tags that don't
+            # override the namespace with a colon prefix.
+            # XMP does not usually use the default namespace, so we can
+            # assume it's just blank. In practice a document that depends on
+            # defining a default namespace over some part of its content
+            # could introduce a collision.
+            # See: https://www.w3.org/TR/REC-xml-names/#dt-defaultNS
+            prefix, tag = '', name
+        uri = cls.NS.get(prefix, None)
         return str(QName(uri, tag))
 
     def _prefix_from_uri(self, uriname):
@@ -617,7 +622,7 @@
         uri = uripart.replace('{', '')
         return self.REVERSE_NS[uri] + ':' + tag
 
-    def _get_subelements(self, node):
+    def _get_subelements(self, node: _Element) -> Any:
         """Gather the sub-elements attached to a node.
 
         Gather rdf:Bag and and rdf:Seq into set and list respectively. For
@@ -641,7 +646,8 @@
             return result
         return ''
 
-    def _get_rdf_root(self):
+    def _get_rdf_root(self) -> _Element:
+        assert self._xmp is not None
         rdf = self._xmp.find('.//rdf:RDF', self.NS)
         if rdf is None:
             rdf = self._xmp.getroot()
@@ -649,7 +655,9 @@
                 raise ValueError("Metadata seems to be XML but not XMP")
         return rdf
 
-    def _get_elements(self, name: str | QName = ''):
+    def _get_elements(
+        self, name: str | QName = ''
+    ) -> Iterator[tuple[_Element, str | bytes | None, Any, _Element]]:
         """Get elements from XMP.
 
         Core routine to find elements matching name within the XMP and yield
@@ -689,7 +697,7 @@
                 values = self._get_subelements(node)
                 yield (node, None, values, rdfdesc)
 
-    def _get_element_values(self, name=''):
+    def _get_element_values(self, name: str | QName = '') -> Iterator[Any]:
         yield from (v[2] for v in self._get_elements(name))
 
     @ensure_loaded
@@ -839,7 +847,7 @@
                 if (
                     len(node.attrib) == 1
                     and len(node) == 0
-                    and QName(XMP_NS_RDF, 'about') in node.attrib
+                    and QName(XMP_NS_RDF, 'about') in node.attrib.keys()
                 ):
                     # The only thing left on this node is rdf:about="", so 
remove it
                     parent.remove(node)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf/models/outlines.py 
new/pikepdf-8.11.2/src/pikepdf/models/outlines.py
--- old/pikepdf-8.10.1/src/pikepdf/models/outlines.py   2023-12-17 
10:31:00.000000000 +0100
+++ new/pikepdf-8.11.2/src/pikepdf/models/outlines.py   2024-01-01 
05:14:08.000000000 +0100
@@ -299,6 +299,15 @@
     def __repr__(self):
         return f'<pikepdf.{self.__class__.__name__}: {len(self.root)} items>'
 
+    def _repr_pretty_(self, p, cycle):
+        if cycle:
+            p.text("...")
+        else:
+            with p.group(2, "pikepdf.models.outlines.Outline<\n", "\n>"):
+                for _, item in enumerate(self.root):
+                    p.breakable()
+                    p.pretty(str(item))
+
     def __enter__(self):
         self._updating = True
         return self
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf/objects.py 
new/pikepdf-8.11.2/src/pikepdf/objects.py
--- old/pikepdf-8.10.1/src/pikepdf/objects.py   2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/src/pikepdf/objects.py   2024-01-01 05:14:08.000000000 
+0100
@@ -22,8 +22,8 @@
 from secrets import token_urlsafe
 from typing import TYPE_CHECKING, Any, Iterable, Mapping, cast
 
-from . import _core
-from ._core import Matrix, Object, ObjectType, Rectangle
+from pikepdf import _core
+from pikepdf._core import Matrix, Object, ObjectType, Rectangle
 
 if TYPE_CHECKING:  # pragma: no cover
     from pikepdf import Pdf
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf/settings.py 
new/pikepdf-8.11.2/src/pikepdf/settings.py
--- old/pikepdf-8.10.1/src/pikepdf/settings.py  2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/src/pikepdf/settings.py  2024-01-01 05:14:08.000000000 
+0100
@@ -5,7 +5,7 @@
 
 from __future__ import annotations
 
-from ._core import (
+from pikepdf._core import (
     get_decimal_precision,
     set_decimal_precision,
     set_flate_compression_level,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf.egg-info/PKG-INFO 
new/pikepdf-8.11.2/src/pikepdf.egg-info/PKG-INFO
--- old/pikepdf-8.10.1/src/pikepdf.egg-info/PKG-INFO    2023-12-17 
10:33:05.000000000 +0100
+++ new/pikepdf-8.11.2/src/pikepdf.egg-info/PKG-INFO    2024-01-01 
05:15:45.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: pikepdf
-Version: 8.10.1
+Version: 8.11.2
 Summary: Read and write PDFs with Python, powered by qpdf
 Author-email: "James R. Barlow" <ja...@purplerock.ca>
 License: MPL-2.0
@@ -23,7 +23,8 @@
 License-File: LICENSE.txt
 Requires-Dist: Pillow>=10.0.1
 Requires-Dist: Deprecated
-Requires-Dist: lxml>=4.8
+Requires-Dist: lxml>=4.8; python_version >= "3.10"
+Requires-Dist: lxml<5,>=4.8; python_version < "3.10"
 Requires-Dist: packaging
 Provides-Extra: dev
 Requires-Dist: pre-commit; extra == "dev"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf.egg-info/SOURCES.txt 
new/pikepdf-8.11.2/src/pikepdf.egg-info/SOURCES.txt
--- old/pikepdf-8.10.1/src/pikepdf.egg-info/SOURCES.txt 2023-12-17 
10:33:05.000000000 +0100
+++ new/pikepdf-8.11.2/src/pikepdf.egg-info/SOURCES.txt 2024-01-01 
05:15:45.000000000 +0100
@@ -28,6 +28,7 @@
 docs/images/save-pike.jpg
 docs/images/sushi.jpg
 docs/references/arch.rst
+docs/references/build.rst
 docs/references/contributing.rst
 docs/references/debugging.rst
 docs/references/resources.rst
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/src/pikepdf.egg-info/requires.txt 
new/pikepdf-8.11.2/src/pikepdf.egg-info/requires.txt
--- old/pikepdf-8.10.1/src/pikepdf.egg-info/requires.txt        2023-12-17 
10:33:05.000000000 +0100
+++ new/pikepdf-8.11.2/src/pikepdf.egg-info/requires.txt        2024-01-01 
05:15:45.000000000 +0100
@@ -1,8 +1,13 @@
 Pillow>=10.0.1
 Deprecated
-lxml>=4.8
 packaging
 
+[:python_version < "3.10"]
+lxml<5,>=4.8
+
+[:python_version >= "3.10"]
+lxml>=4.8
+
 [dev]
 pre-commit
 typer[all]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/tests/conftest.py 
new/pikepdf-8.11.2/tests/conftest.py
--- old/pikepdf-8.10.1/tests/conftest.py        2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/tests/conftest.py        2024-01-01 05:14:08.000000000 
+0100
@@ -5,6 +5,7 @@
 
 import os
 import platform
+import shutil
 import sys
 from pathlib import Path
 
@@ -53,6 +54,9 @@
 skip_if_ci = pytest.mark.skipif(
     os.environ.get('CI', '') == 'true', reason="test too slow for CI"
 )
+fails_if_no_mutool = pytest.mark.xfail(
+    shutil.which("mutool") is None, reason="test fails without mutool"
+)
 
 
 def needs_libqpdf_v(version: str, *, reason=None):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/tests/test_ipython.py 
new/pikepdf-8.11.2/tests/test_ipython.py
--- old/pikepdf-8.10.1/tests/test_ipython.py    2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/tests/test_ipython.py    2024-01-01 05:14:08.000000000 
+0100
@@ -11,7 +11,7 @@
 from io import BytesIO
 
 import pytest
-from PIL import Image
+from conftest import fails_if_no_mutool
 
 import pikepdf
 
@@ -53,20 +53,17 @@
         'image/png' not in mimebundle
     ), "Generated image/png when mudraw() was rigged to fail"
 
-    def return_simple_png(prog_args, *args, **kwargs):
-        im = Image.new('1', (1, 1))
-        bio = BytesIO()
-        im.save(bio, format='PNG')
-        bio.seek(0)
+    def return_simple_svg(prog_args, *args, **kwargs):
+        bio = BytesIO(b'<svg xmlns="http://www.w3.org/2000/svg";></svg>')
         return subprocess.CompletedProcess(prog_args, 0, stdout=bio.read(), 
stderr=b'')
 
-    monkeypatch.setattr(pikepdf._methods, 'run', return_simple_png)
+    monkeypatch.setattr(pikepdf._methods, 'run', return_simple_svg)
     mimebundle = page0._repr_mimebundle_(
-        include=['image/png'], exclude=['application/pdf']
+        include=['image/svg+xml'], exclude=['application/pdf']
     )
     assert (
-        'image/png' in mimebundle
-    ), "Did not generate image/png when mudraw() was rigged to succeed"
+        'image/svg+xml' in mimebundle
+    ), "Did not generate image/svg+xml when mudraw() was rigged to succeed"
 
 
 def test_display_image(pal):
@@ -76,6 +73,7 @@
     assert result[1:4] == b'PNG'
 
 
+@fails_if_no_mutool
 def test_display_pdf(pal):
     mimebundle = pal._repr_mimebundle_(
         include=['application/pdf'], exclude=['text/css']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/tests/test_metadata.py 
new/pikepdf-8.11.2/tests/test_metadata.py
--- old/pikepdf-8.10.1/tests/test_metadata.py   2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/tests/test_metadata.py   2024-01-01 05:14:08.000000000 
+0100
@@ -733,7 +733,7 @@
 
 
 def test_qname_no_namespace():
-    assert PdfMetadata._qname('abc') == '{adobe:ns:meta/}abc'
+    assert PdfMetadata._qname('abc') == 'abc'
 
 
 def test_register_xmlns():
@@ -741,3 +741,73 @@
     assert (
         PdfMetadata._qname('pikepdf:foo') == 
'{http://github.com/pikepdf/pikepdf/}foo'
     )
+
+
+def test_undocumented_pdfx_identifier(trivial):
+    trivial.Root.Metadata = Stream(
+        trivial,
+        """\
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.0-c316 
44.253921, Sun Oct 01 2006 17:14:39">
+   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";>
+      <rdf:Description xmlns:dc="http://purl.org/dc/elements/1.1/"; 
rdf:about="">
+         <dc:format>application/pdf</dc:format>
+         <dc:title>
+            <rdf:Alt>
+               <rdf:li xml:lang="x-default">A</rdf:li>
+            </rdf:Alt>
+         </dc:title>
+         <dc:creator>
+            <rdf:Seq>
+               <rdf:li>B</rdf:li>
+               <rdf:li>C</rdf:li>
+            </rdf:Seq>
+         </dc:creator>
+         <dc:description>
+            <rdf:Alt>
+               <rdf:li xml:lang="x-default">D</rdf:li>
+            </rdf:Alt>
+         </dc:description>
+         <dc:publisher>
+            <rdf:Bag>
+               <rdf:li>E</rdf:li>
+            </rdf:Bag>
+         </dc:publisher>
+      </rdf:Description>
+      <rdf:Description 
xmlns:prism="http://prismstandard.org/namespaces/basic/3.0/"; rdf:about="">
+         <prism:aggregationType>journal</prism:aggregationType>
+         <prism:publicationName>Journal</prism:publicationName>
+         <prism:copyright>© 2023</prism:copyright>
+         <prism:issn>1234-5678</prism:issn>
+         <prism:volume>1</prism:volume>
+         <prism:number>1</prism:number>
+         <prism:coverDisplayDate>March-April 2023</prism:coverDisplayDate>
+         <prism:pageRange>1-2</prism:pageRange>
+         <prism:startingPage>1</prism:startingPage>
+         <prism:endingPage>2</prism:endingPage>
+      </rdf:Description>
+      <rdf:Description xmlns:pdfx="http://ns.adobe.com/pdfx/1.3/"; rdf:about="">
+         
<TGji2z9aLyPf_nweGmtj-nLNNnt2Nlwj7yteGngiQzd2Sy937zt-Jo9eQmdmKn9yJmMmTma/>
+         <pdfx:robots>noindex</pdfx:robots>
+      </rdf:Description>
+      <rdf:Description xmlns:xap="http://ns.adobe.com/xap/1.0/"; rdf:about="">
+         <xap:CreatorTool>NA</xap:CreatorTool>
+         <xap:CreateDate>2023-03-07T23:47:54+05:30</xap:CreateDate>
+         <xap:ModifyDate>2023-03-07T23:48:24+05:30</xap:ModifyDate>
+         <xap:MetadataDate>2023-03-07T23:48:24+05:30</xap:MetadataDate>
+      </rdf:Description>
+      <rdf:Description xmlns:xapRights="http://ns.adobe.com/xap/1.0/rights/"; 
rdf:about="">
+         <xapRights:Marked>True</xapRights:Marked>
+      </rdf:Description>
+      <rdf:Description xmlns:pdf="http://ns.adobe.com/pdf/1.3/"; rdf:about="">
+         <pdf:Producer>Acrobat Distiller 8.1.0 (Windows)</pdf:Producer>
+      </rdf:Description>
+      <rdf:Description xmlns:xapMM="http://ns.adobe.com/xap/1.0/mm/"; 
rdf:about="">
+         
<xapMM:DocumentID>uuid:81b6d753-a810-4613-b207-111111111111</xapMM:DocumentID>
+         
<xapMM:InstanceID>uuid:0d50ecbf-b7d1-4b76-b4a7-222222222222</xapMM:InstanceID>
+      </rdf:Description>
+   </rdf:RDF>
+</x:xmpmeta>
+""".encode(),
+    )
+    with trivial.open_metadata() as m:
+        list(m.items())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/tests/test_page.py 
new/pikepdf-8.11.2/tests/test_page.py
--- old/pikepdf-8.10.1/tests/test_page.py       2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/tests/test_page.py       2024-01-01 05:14:08.000000000 
+0100
@@ -55,9 +55,13 @@
     inset = 20
     page.trimbox = [inset, inset, page.mediabox[2] - inset, page.mediabox[3] - 
inset]
     inset = 30
-    page.cropbox = [inset, inset, page.mediabox[2] - inset, page.mediabox[3] - 
inset]
+    page.cropbox = Array(
+        [inset, inset, page.mediabox[2] - inset, page.mediabox[3] - inset]
+    )
     inset = 40
-    page.artbox = [inset, inset, page.mediabox[2] - inset, page.mediabox[3] - 
inset]
+    page.artbox = Rectangle(
+        inset, inset, page.mediabox[2] - inset, page.mediabox[3] - inset
+    )
 
     assert page.mediabox != page.bleedbox
     assert page.mediabox != page.artbox
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pikepdf-8.10.1/tests/test_repr.py 
new/pikepdf-8.11.2/tests/test_repr.py
--- old/pikepdf-8.10.1/tests/test_repr.py       2023-12-17 10:31:00.000000000 
+0100
+++ new/pikepdf-8.11.2/tests/test_repr.py       2024-01-01 05:14:08.000000000 
+0100
@@ -24,7 +24,6 @@
             '/Array': Array([1, 2, 3.14]),
             '/Operator': Operator('q'),
             '/Dictionary': Dictionary({'/Color': 'Red'}),
-            '/None': None,
         }
     )
     short_pi = '3.14'
@@ -37,7 +36,6 @@
                 "/Color": "Red"
             },
             "/Integer": 42,
-            "/None": None,
             "/Operator": pikepdf.Operator("q"),
             "/Real": Decimal('42.42'),
             "/String": "hi"

Reply via email to