Adding upstream version 0.2.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
d372080b5f
commit
fb81c4a897
13 changed files with 159 additions and 116 deletions
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
open_collective: courtbouillon
|
29
.github/workflows/doconfly.yml
vendored
Normal file
29
.github/workflows/doconfly.yml
vendored
Normal file
|
@ -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"
|
35
.github/workflows/tests.yml
vendored
Normal file
35
.github/workflows/tests.yml
vendored
Normal file
|
@ -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
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
/docs/_build
|
||||
/tests/results
|
||||
__pycache__
|
||||
.coverage
|
57
PKG-INFO
57
PKG-INFO
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
-------------
|
||||
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
31
setup.py
31
setup.py
|
@ -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',
|
||||
)
|
|
@ -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>'
|
||||
|
|
Loading…
Add table
Reference in a new issue