caerp-sign-pdf 2024.1.3__tar.gz → 2024.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. caerp_sign_pdf-2024.2.0/CURRENT_VERSION +1 -0
  2. {caerp_sign_pdf-2024.1.3 → caerp_sign_pdf-2024.2.0}/PKG-INFO +4 -1
  3. caerp_sign_pdf-2024.2.0/requirements.txt +1 -0
  4. {caerp_sign_pdf-2024.1.3 → caerp_sign_pdf-2024.2.0}/src/caerp_sign_pdf/public.py +59 -31
  5. {caerp_sign_pdf-2024.1.3 → caerp_sign_pdf-2024.2.0}/src/caerp_sign_pdf.egg-info/PKG-INFO +4 -1
  6. caerp_sign_pdf-2024.2.0/src/caerp_sign_pdf.egg-info/requires.txt +1 -0
  7. caerp_sign_pdf-2024.1.3/CURRENT_VERSION +0 -1
  8. caerp_sign_pdf-2024.1.3/requirements.txt +0 -1
  9. caerp_sign_pdf-2024.1.3/src/caerp_sign_pdf.egg-info/requires.txt +0 -1
  10. {caerp_sign_pdf-2024.1.3 → caerp_sign_pdf-2024.2.0}/LICENSE.txt +0 -0
  11. {caerp_sign_pdf-2024.1.3 → caerp_sign_pdf-2024.2.0}/MANIFEST.in +0 -0
  12. {caerp_sign_pdf-2024.1.3 → caerp_sign_pdf-2024.2.0}/README.rst +0 -0
  13. {caerp_sign_pdf-2024.1.3 → caerp_sign_pdf-2024.2.0}/setup.cfg +0 -0
  14. {caerp_sign_pdf-2024.1.3 → caerp_sign_pdf-2024.2.0}/setup.py +0 -0
  15. {caerp_sign_pdf-2024.1.3 → caerp_sign_pdf-2024.2.0}/src/caerp_sign_pdf/__init__.py +0 -0
  16. {caerp_sign_pdf-2024.1.3 → caerp_sign_pdf-2024.2.0}/src/caerp_sign_pdf/models.py +0 -0
  17. {caerp_sign_pdf-2024.1.3 → caerp_sign_pdf-2024.2.0}/src/caerp_sign_pdf.egg-info/SOURCES.txt +0 -0
  18. {caerp_sign_pdf-2024.1.3 → caerp_sign_pdf-2024.2.0}/src/caerp_sign_pdf.egg-info/dependency_links.txt +0 -0
  19. {caerp_sign_pdf-2024.1.3 → caerp_sign_pdf-2024.2.0}/src/caerp_sign_pdf.egg-info/not-zip-safe +0 -0
  20. {caerp_sign_pdf-2024.1.3 → caerp_sign_pdf-2024.2.0}/src/caerp_sign_pdf.egg-info/top_level.txt +0 -0
@@ -0,0 +1 @@
1
+ 2024.2.0
@@ -1,12 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: caerp_sign_pdf
3
- Version: 2024.1.3
3
+ Version: 2024.2.0
4
4
  Summary: caerp_sign_pdf
5
5
  Home-page: https://framagit.org/caerp/caerp_sign_pdf
6
6
  Author: Kilya
7
7
  Author-email: contact@kilya.net
8
8
  License: GPLv3
9
9
  Keywords: web wsgi bfg pylons pyramid caerp
10
+ Platform: UNKNOWN
10
11
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
11
12
  Classifier: Programming Language :: Python
12
13
  Classifier: Framework :: Pyramid
@@ -69,3 +70,5 @@ Sous linux la signature d'un document PDF peut être vérifiée facilement en li
69
70
 
70
71
  pdfsig <monfichierpdf.pdf>
71
72
 
73
+
74
+
@@ -0,0 +1 @@
1
+ pyHanko[pkcs11,image-support,opentype,xmp]==0.25.1
@@ -1,13 +1,13 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import logging
3
3
  import hashlib
4
+ import subprocess
4
5
 
5
6
  from io import BytesIO
6
7
  from pyhanko.pdf_utils.incremental_writer import IncrementalPdfFileWriter
7
8
  from pyhanko.sign import fields, signers
8
9
  from pyhanko.sign.general import SigningError
9
10
  from pyhanko.stamp import TextStampStyle
10
- from PyPDF4.pdf import PdfFileReader, PdfFileWriter
11
11
  from pyramid.httpexceptions import HTTPUnsupportedMediaType
12
12
 
13
13
  from caerp.views.files.controller import FileData
@@ -27,6 +27,10 @@ nouveau fichier PDF avant de le recharger."
27
27
 
28
28
 
29
29
  class SignPDFService(object):
30
+ """
31
+ The PDF digital signing service
32
+ """
33
+
30
34
  def __init__(self, context, request):
31
35
  self.context = context
32
36
  self.request = request
@@ -58,18 +62,44 @@ class SignPDFService(object):
58
62
  """
59
63
  return hashlib.md5(file_data.getvalue()).hexdigest()
60
64
 
61
- def _get_safe_pdf(self, file_data: BytesIO) -> str:
65
+ def _get_clean_pdf(self, file_data: BytesIO) -> BytesIO:
62
66
  """
63
- Create a new clean PDF buffer from the original
64
- to avoid some format errors
67
+ Create a new clean PDF buffer from the original to avoid format errors
65
68
  """
66
- writer = PdfFileWriter()
67
- reader = PdfFileReader(file_data, strict=False)
68
- num_pages = reader.getNumPages()
69
- for page in range(num_pages):
70
- writer.addPage(reader.getPage(page))
71
- writer.write(file_data)
72
- return file_data
69
+ logger.info(f"Cleaning PDF buffer...")
70
+ file_data.seek(0)
71
+ process = subprocess.Popen(
72
+ ["/usr/bin/pdftocairo", "-pdf", "-", "-"],
73
+ stdin=subprocess.PIPE,
74
+ stdout=subprocess.PIPE,
75
+ )
76
+ output = process.communicate(input=file_data.read())[0]
77
+ result = BytesIO()
78
+ result.write(output)
79
+ result.seek(0)
80
+ return result
81
+
82
+ def _get_pdf_stamp(self, display_stamp: bool) -> TextStampStyle:
83
+ """
84
+ Prepare physical stamp to apply on the PDF
85
+
86
+ :param bool display_stamp: Whether the stamp must be visible or not
87
+
88
+ :return TextStampStyle: The stamp object
89
+ """
90
+ stamp = None
91
+ if display_stamp:
92
+ stamp = TextStampStyle(
93
+ timestamp_format="%d/%m/%Y %H:%M:%S %Z",
94
+ stamp_text="Signé par: %(signer)s\nLe: %(ts)s",
95
+ border_width=2,
96
+ )
97
+ else:
98
+ stamp = TextStampStyle(
99
+ stamp_text="",
100
+ border_width=0,
101
+ )
102
+ return stamp
73
103
 
74
104
  def sign(
75
105
  self, pdf_data: FileData, node_id: int = None, with_stamp: bool = False
@@ -80,7 +110,8 @@ class SignPDFService(object):
80
110
 
81
111
  :param FileData pdf_data: The FileData object describing the PDF file
82
112
  :param int node_id: The related node's id if relevant
83
- :param bool with_stamp: whether we want a stamp on result file or not
113
+ :param bool with_stamp: Whether we want a printed stamp on result file or not
114
+
84
115
  :return bool: either the PDF has been signed or not
85
116
  """
86
117
 
@@ -88,7 +119,7 @@ class SignPDFService(object):
88
119
 
89
120
  # Check mimetype
90
121
  if not pdf_data.mimetype == "application/pdf":
91
- logger.warn("File is not a PDF, signing aborted")
122
+ logger.warning("File is not a PDF, signing aborted")
92
123
  return False
93
124
 
94
125
  # Load sign certificate
@@ -103,26 +134,18 @@ class SignPDFService(object):
103
134
  )
104
135
  raise Exception(ERROR_LOAD_CERTIFICATE_MSG)
105
136
 
106
- # Prepare stamp (if needed)
107
- stamp = None
108
- if with_stamp:
109
- stamp = TextStampStyle(
110
- timestamp_format="%d/%m/%Y %H:%M:%S %Z",
111
- stamp_text="Signé par: %(signer)s\nLe: %(ts)s",
112
- border_width=2,
113
- )
114
- else:
115
- stamp = TextStampStyle(
116
- stamp_text="",
117
- border_width=0,
118
- )
137
+ # Clean PDF
138
+ try:
139
+ pdf_data.data = self._get_clean_pdf(pdf_data.data)
140
+ except Exception as error:
141
+ logger.error(f"Error while cleaning original PDF file : {error}")
142
+ self.sign(pdf_data, node_id, with_stamp, as_image=True)
119
143
 
120
- # Open original PDF
144
+ # Open PDF
121
145
  try:
122
- pdf_data.data = self._get_safe_pdf(pdf_data.data)
123
146
  pdf_writer = IncrementalPdfFileWriter(pdf_data.data, strict=False)
124
147
  except Exception as error:
125
- logger.error(f"Error while opening original PDF file : {error}")
148
+ logger.error(f"Error while opening PDF file : {error}")
126
149
  raise HTTPUnsupportedMediaType(ERROR_PDF_FORMAT_MSG)
127
150
 
128
151
  # Add signature field on the PDF
@@ -134,8 +157,11 @@ class SignPDFService(object):
134
157
  ),
135
158
  )
136
159
  except SigningError:
137
- logger.warn("File is already signed, nothing to do")
160
+ logger.warning("File is already signed, nothing to do")
138
161
  return True
162
+ except Exception as error:
163
+ logger.error(f"Error while adding signature field : {error}")
164
+ raise HTTPUnsupportedMediaType(ERROR_PDF_FORMAT_MSG)
139
165
 
140
166
  # Sign PDF
141
167
  try:
@@ -143,12 +169,14 @@ class SignPDFService(object):
143
169
  pdf_signer = signers.PdfSigner(
144
170
  meta,
145
171
  signer=signer,
146
- stamp_style=stamp,
172
+ stamp_style=self._get_pdf_stamp(with_stamp),
147
173
  )
148
174
  pdf_data.data = pdf_signer.sign_pdf(pdf_writer)
149
175
  except Exception as error:
150
176
  logger.error(f"Error while signing PDF file : {error}")
151
177
  raise HTTPUnsupportedMediaType(ERROR_PDF_FORMAT_MSG)
178
+
179
+ # Success
152
180
  logger.info(
153
181
  "File '{}' signed successfully by {} !".format(
154
182
  pdf_data.name, signer.subject_name
@@ -1,12 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: caerp-sign-pdf
3
- Version: 2024.1.3
3
+ Version: 2024.2.0
4
4
  Summary: caerp_sign_pdf
5
5
  Home-page: https://framagit.org/caerp/caerp_sign_pdf
6
6
  Author: Kilya
7
7
  Author-email: contact@kilya.net
8
8
  License: GPLv3
9
9
  Keywords: web wsgi bfg pylons pyramid caerp
10
+ Platform: UNKNOWN
10
11
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
11
12
  Classifier: Programming Language :: Python
12
13
  Classifier: Framework :: Pyramid
@@ -69,3 +70,5 @@ Sous linux la signature d'un document PDF peut être vérifiée facilement en li
69
70
 
70
71
  pdfsig <monfichierpdf.pdf>
71
72
 
73
+
74
+
@@ -0,0 +1 @@
1
+ pyHanko[image-support,opentype,pkcs11,xmp]==0.25.1
@@ -1 +0,0 @@
1
- 2024.1.3
@@ -1 +0,0 @@
1
- pyHanko[pkcs11,image-support,opentype,xmp]==0.25.0
@@ -1 +0,0 @@
1
- pyHanko[image-support,opentype,pkcs11,xmp]==0.25.0