summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <mail@daniel-baumann.ch>2022-06-19 07:06:23 +0000
committerDaniel Baumann <mail@daniel-baumann.ch>2022-06-19 07:06:34 +0000
commit5af1e7262d704ec79cc40ecc2f61bcd8d1dd02d7 (patch)
tree5f908593917c99e6f9ea8c65861a51e30dbfa19c
parentReleasing debian version 0.1.2-4. (diff)
downloadpydyf-5af1e7262d704ec79cc40ecc2f61bcd8d1dd02d7.tar.xz
pydyf-5af1e7262d704ec79cc40ecc2f61bcd8d1dd02d7.zip
Merging upstream version 0.2.0.
Signed-off-by: Daniel Baumann <mail@daniel-baumann.ch>
-rw-r--r--.github/FUNDING.yml1
-rw-r--r--.github/workflows/doconfly.yml29
-rw-r--r--.github/workflows/tests.yml35
-rw-r--r--.gitignore4
-rw-r--r--PKG-INFO57
-rw-r--r--README.rst2
-rw-r--r--docs/changelog.rst43
-rw-r--r--docs/conf.py6
-rw-r--r--docs/going_further.rst21
-rwxr-xr-xpydyf/__init__.py18
-rw-r--r--pyproject.toml8
-rw-r--r--setup.py31
-rw-r--r--tests/test_pydyf.py20
13 files changed, 159 insertions, 116 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..709eb98
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+open_collective: courtbouillon
diff --git a/.github/workflows/doconfly.yml b/.github/workflows/doconfly.yml
new file mode 100644
index 0000000..cf2c1ca
--- /dev/null
+++ b/.github/workflows/doconfly.yml
@@ -0,0 +1,29 @@
+name: doconfly
+on:
+ push:
+ branches:
+ - master
+ tags:
+ - "*"
+
+jobs:
+ doconfly:
+ name: doconfly job
+ runs-on: ubuntu-latest
+ env:
+ PORT: ${{ secrets.PORT }}
+ SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
+ TAKOYAKI: ${{ secrets.TAKOYAKI }}
+ USER: ${{ secrets.USER }}
+ DOCUMENTATION_PATH: ${{ secrets.DOCUMENTATION_PATH }}
+ DOCUMENTATION_URL: ${{ secrets.DOCUMENTATION_URL }}
+ steps:
+ - run: |
+ which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )
+ eval $(ssh-agent -s)
+ echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
+ mkdir -p ~/.ssh
+ chmod 700 ~/.ssh
+ ssh-keyscan -p $PORT $TAKOYAKI >> ~/.ssh/known_hosts
+ chmod 644 ~/.ssh/known_hosts
+ ssh $USER@$TAKOYAKI -p $PORT "doconfly/doconfly.sh $GITHUB_REPOSITORY $GITHUB_REF $DOCUMENTATION_PATH $DOCUMENTATION_URL"
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..b69256e
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,35 @@
+name: pydyf's tests
+on: [push, pull_request]
+
+jobs:
+ tests:
+ name: ${{ matrix.os }} - ${{ matrix.python-version }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ python-version: ['3.7', '3.8', '3.9', '3.10', 'pypy-3.8']
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Install Ghostscript (Ubuntu)
+ if: matrix.os == 'ubuntu-latest'
+ run: sudo apt-get update -y && sudo apt-get install ghostscript -y
+ - name: Install Ghostscript (macOS)
+ if: matrix.os == 'macos-latest'
+ run: brew install ghostscript
+ - name: Install Ghostscript (Windows)
+ if: matrix.os == 'windows-latest'
+ run: |
+ C:\msys64\usr\bin\bash -lc 'pacman -S mingw-w64-x86_64-ghostscript --noconfirm'
+ echo "C:\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH
+ rm C:\msys64\mingw64\bin\python.exe
+ - name: Upgrade pip and setuptools
+ run: python -m pip install --upgrade pip setuptools
+ - name: Install tests’ requirements
+ run: python -m pip install .[test]
+ - name: Launch tests
+ run: python -m pytest
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..95a1136
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/docs/_build
+/tests/results
+__pycache__
+.coverage
diff --git a/PKG-INFO b/PKG-INFO
deleted file mode 100644
index d6c6777..0000000
--- a/PKG-INFO
+++ /dev/null
@@ -1,57 +0,0 @@
-Metadata-Version: 2.1
-Name: pydyf
-Version: 0.1.2
-Summary: A low-level PDF generator.
-Keywords: pdf,generator
-Author-email: CourtBouillon <contact@courtbouillon.org>
-Maintainer-email: CourtBouillon <contact@courtbouillon.org>
-Requires-Python: >=3.6
-Description-Content-Type: text/x-rst
-Classifier: Development Status :: 4 - Beta
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: BSD License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Requires-Dist: sphinx ; extra == "doc"
-Requires-Dist: sphinx_rtd_theme ; extra == "doc"
-Requires-Dist: pytest ; extra == "test"
-Requires-Dist: pytest-cov ; extra == "test"
-Requires-Dist: pytest-flake8 ; extra == "test"
-Requires-Dist: pytest-isort ; extra == "test"
-Requires-Dist: coverage[toml] ; extra == "test"
-Requires-Dist: pillow ; extra == "test"
-Project-URL: Changelog, https://github.com/CourtBouillon/pydyf/releases
-Project-URL: Code, https://github.com/CourtBouillon/pydyf
-Project-URL: Documentation, https://doc.courtbouillon.org/pydyf/
-Project-URL: Donation, https://opencollective.com/courtbouillon
-Project-URL: Homepage, https://www.courtbouillon.org/pydyf
-Project-URL: Issues, https://github.com/CourtBouillon/pydyf/issues
-Provides-Extra: doc
-Provides-Extra: test
-
-pydyf is a low-level PDF generator written in Python and based on PDF
-specification 1.7.
-
-* Free software: BSD license
-* For Python 3.6+, tested on CPython and PyPy
-* Documentation: https://doc.courtbouillon.org/pydyf
-* Changelog: https://github.com/CourtBouillon/pydyf/releases
-* Code, issues, tests: https://github.com/CourtBouillon/pydyf
-* Code of conduct: https://www.courtbouillon.org/code-of-conduct
-* Professional support: https://www.courtbouillon.org
-* Donation: https://opencollective.com/courtbouillon
-
-Copyrights are retained by their contributors, no copyright assignment is
-required to contribute to pydyf. Unless explicitly stated otherwise, any
-contribution intentionally submitted for inclusion is licensed under the BSD
-3-clause license, without any additional terms or conditions. For full
-authorship information, see the version control history.
-
diff --git a/README.rst b/README.rst
index 6c37a4a..f76eccb 100644
--- a/README.rst
+++ b/README.rst
@@ -2,7 +2,7 @@ pydyf is a low-level PDF generator written in Python and based on PDF
specification 1.7.
* Free software: BSD license
-* For Python 3.6+, tested on CPython and PyPy
+* For Python 3.7+, tested on CPython and PyPy
* Documentation: https://doc.courtbouillon.org/pydyf
* Changelog: https://github.com/CourtBouillon/pydyf/releases
* Code, issues, tests: https://github.com/CourtBouillon/pydyf
diff --git a/docs/changelog.rst b/docs/changelog.rst
index acbf3c3..5243edc 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -2,6 +2,49 @@ Changelog
=========
+Version 0.2.0
+-------------
+
+Released on 2022-05-23.
+
+Dependencies:
+
+* Python 3.7+ is now needed, Python 3.6 is not supported anymore
+
+ New features:
+
+* `d0be36b <https://github.com/CourtBouillon/pydyf/commit/d0be36b>`_:
+ Allow to set PDF version
+* `879261c <https://github.com/CourtBouillon/pydyf/commit/879261c>`_:
+ Allow to set PDF identifier
+
+Contributors:
+
+* Guillaume Ayoub
+
+Backers and sponsors:
+
+* Grip Angebotssoftware
+* Manuel Barkhau
+* Crisp BV
+* SimonSoft
+* Menutech
+* Spacinov
+* KontextWork
+* René Fritz
+* Kobalt
+* NCC Group
+* Des images et des mots
+* Nathalie Gutton
+* Andreas Zettl
+* Tom Pohl
+* Moritz Mahringer
+* Florian Demmer
+* Yanal-Yvez Fargialla
+* Gábor
+* Piotr Horzycki
+
+
Version 0.1.2
-------------
diff --git a/docs/conf.py b/docs/conf.py
index 585afd0..a08c175 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,13 +1,7 @@
# pydyf documentation build configuration file.
-import sys
-from pathlib import Path
-
import pydyf
-# Add current path for css_diagram_role
-sys.path.append(str(Path(__file__).parent))
-
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
diff --git a/docs/going_further.rst b/docs/going_further.rst
index ce9a8f3..c66f6c2 100644
--- a/docs/going_further.rst
+++ b/docs/going_further.rst
@@ -5,12 +5,11 @@ Going Further
Why pydyf?
-------------
-pydyf has been created to replace Cairo PDF generation in WeasyPrint_.
+pydyf has been created to replace Cairo_ PDF generation in WeasyPrint_.
-Indeed, there are some bugs in WeasyPrint caused by Cairo_ and Cairo has some
-difficulties to make releases.
-Also there are features which will be easier to implement while having more
-control on the PDF generation.
+Indeed, there were some bugs in WeasyPrint caused by Cairo, and new versions of
+Cairo can take a long time to be released. There are also many features that
+are easier to implement with more control on the PDF generation.
So we created pydyf.
@@ -20,13 +19,13 @@ So we created pydyf.
Why Python?
-----------
-Python is a really good language to design a small, OS-agnostic parser. As it
-is object-oriented, it gives the possibility to follow the specification with
-high-level classes and a small amount of very simple code.
+Python is a really good language to design a small, OS-agnostic library. As it
+is object-oriented, it gives the possibility to follow the PDF specification
+with high-level classes and a small amount of very simple code.
And of course, WeasyPrint is written in Python too, giving an obvious reason
for this choice.
-Speed is not pydyf’s main goal. Code simplicity, maintainability and
-flexibility are more important goals for this library, as they give the
-ability to stay really close to the specification and to fix bugs easily.
+Speed is not pydyf’s main goal. Code simplicity, maintainability and
+flexibility are more important goals for this library, as they give the ability
+to stay really close to the specification and to fix bugs easily.
diff --git a/pydyf/__init__.py b/pydyf/__init__.py
index 05dccf6..a627a13 100755
--- a/pydyf/__init__.py
+++ b/pydyf/__init__.py
@@ -6,8 +6,9 @@ A low-level PDF generator.
import re
import zlib
from codecs import BOM_UTF16_BE
+from hashlib import md5
-VERSION = __version__ = '0.1.2'
+VERSION = __version__ = '0.2.0'
def _to_bytes(item):
@@ -425,7 +426,7 @@ class PDF:
})
self.add_object(self.pages)
- #: PDF :class:`Dictionary` containing the PDF’s metadata.
+ #: PDF :class:`Dictionary` containing the PDF’s metadata.
self.info = Dictionary({})
self.add_object(self.info)
@@ -469,15 +470,17 @@ class PDF:
self.current_position += len(content) + 1
output.write(content + b'\n')
- def write(self, output):
+ def write(self, output, version=b'1.7', identifier=None):
"""Write PDF to output.
:param output: Output stream.
:type output: binary :term:`file object`
+ :param bytes version: PDF version.
+ :param bytes identifier: PDF file identifier.
"""
# Write header
- self.write_line(b'%PDF-1.7', output)
+ self.write_line(b'%PDF-' + version, output)
self.write_line(b'%\xf0\x9f\x96\xa4', output)
# Write all non-free PDF objects
@@ -502,6 +505,13 @@ class PDF:
self.write_line(f'/Size {len(self.objects)}'.encode(), output)
self.write_line(b'/Root ' + self.catalog.reference, output)
self.write_line(b'/Info ' + self.info.reference, output)
+ if identifier is not None:
+ data = b''.join(
+ obj.data for obj in self.objects if obj.free != 'f')
+ data_hash = md5(data).hexdigest().encode()
+ self.write_line(
+ b'/ID [' + String(identifier).data + b' ' +
+ String(data_hash).data + b']', output)
self.write_line(b'>>', output)
self.write_line(b'startxref', output)
self.write_line(f'{self.xref_position}'.encode(), output)
diff --git a/pyproject.toml b/pyproject.toml
index 5a88369..a89cea0 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -8,7 +8,7 @@ description = 'A low-level PDF generator.'
keywords = ['pdf', 'generator']
authors = [{name = 'CourtBouillon', email = 'contact@courtbouillon.org'}]
maintainers = [{name = 'CourtBouillon', email = 'contact@courtbouillon.org'}]
-requires-python = '>=3.6'
+requires-python = '>=3.7'
readme = {file = 'README.rst', content-type = 'text/x-rst'}
license = {file = 'LICENSE'}
classifiers = [
@@ -19,10 +19,10 @@ classifiers = [
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3 :: Only',
- 'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
+ 'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
]
@@ -38,13 +38,13 @@ Donation = 'https://opencollective.com/courtbouillon'
[project.optional-dependencies]
doc = ['sphinx', 'sphinx_rtd_theme']
-test = ['pytest', 'pytest-cov', 'pytest-flake8', 'pytest-isort', 'coverage[toml]', 'pillow']
+test = ['pytest', 'pytest-xdist', 'pytest-flake8', 'pytest-isort', 'pytest-cov', 'coverage[toml]', 'pillow']
[tool.flit.sdist]
exclude = ['.*']
[tool.pytest.ini_options]
-addopts = '--isort --flake8 --cov --no-cov-on-fail'
+addopts = '--isort --flake8 --numprocesses=auto'
[tool.coverage.run]
branch = true
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 40b63c0..0000000
--- a/setup.py
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env python
-# setup.py generated by flit for tools that don't yet use PEP 517
-
-from distutils.core import setup
-
-packages = \
-['pydyf']
-
-package_data = \
-{'': ['*']}
-
-extras_require = \
-{'doc': ['sphinx', 'sphinx_rtd_theme'],
- 'test': ['pytest',
- 'pytest-cov',
- 'pytest-flake8',
- 'pytest-isort',
- 'coverage[toml]',
- 'pillow']}
-
-setup(name='pydyf',
- version='0.1.2',
- description='A low-level PDF generator.',
- author=None,
- author_email='CourtBouillon <contact@courtbouillon.org>',
- url=None,
- packages=packages,
- package_data=package_data,
- extras_require=extras_require,
- python_requires='>=3.6',
- )
diff --git a/tests/test_pydyf.py b/tests/test_pydyf.py
index 1e758a5..83c260c 100644
--- a/tests/test_pydyf.py
+++ b/tests/test_pydyf.py
@@ -1,3 +1,5 @@
+import io
+
import pydyf
from . import assert_pixels
@@ -283,10 +285,10 @@ def test_transform():
document = pydyf.PDF()
draw = pydyf.Stream()
+ draw.transform(1, 0, 0, 1, 1, 1)
draw.move_to(2, 2)
draw.set_line_width(2)
draw.line_to(2, 5)
- draw.transform(1, 0, 0, 1, 1, 1)
draw.stroke()
document.add_object(draw)
@@ -694,7 +696,7 @@ def test_text():
KKKKKKKKKK
KKKKKKKKKK
KKKKKKKKKK
- __________
+ zzzzzzzzzz
__________
__________
__________
@@ -702,6 +704,20 @@ def test_text():
''')
+def test_identifier():
+ document = pydyf.PDF()
+ pdf = io.BytesIO()
+ document.write(pdf, identifier=b'abc')
+ assert b'abc' in pdf.getvalue()
+
+
+def test_version():
+ document = pydyf.PDF()
+ pdf = io.BytesIO()
+ document.write(pdf, version=b'2.0')
+ assert b'2.0' in pdf.getvalue()
+
+
def test_string_encoding():
assert pydyf.String('abc').data == b'(abc)'
assert pydyf.String('déf').data == b'<feff006400e90066>'