folio-pdf 0.0.2__py3-none-win_amd64.whl
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.
- folio_pdf/__init__.py +285 -0
- folio_pdf/color.py +85 -0
- folio_pdf/core.py +126 -0
- folio_pdf/document.py +1130 -0
- folio_pdf/elements/__init__.py +46 -0
- folio_pdf/elements/area_break.py +21 -0
- folio_pdf/elements/barcode/__init__.py +9 -0
- folio_pdf/elements/barcode/barcode.py +128 -0
- folio_pdf/elements/barcode/barcode_element.py +95 -0
- folio_pdf/elements/columns.py +96 -0
- folio_pdf/elements/div.py +461 -0
- folio_pdf/elements/flex/__init__.py +9 -0
- folio_pdf/elements/flex/flex.py +159 -0
- folio_pdf/elements/flex/flex_item.py +141 -0
- folio_pdf/elements/float.py +56 -0
- folio_pdf/elements/grid.py +489 -0
- folio_pdf/elements/heading.py +139 -0
- folio_pdf/elements/image/__init__.py +9 -0
- folio_pdf/elements/image/image.py +186 -0
- folio_pdf/elements/image/image_element.py +130 -0
- folio_pdf/elements/line_separator.py +26 -0
- folio_pdf/elements/link.py +158 -0
- folio_pdf/elements/list.py +192 -0
- folio_pdf/elements/paragraph.py +334 -0
- folio_pdf/elements/svg/__init__.py +9 -0
- folio_pdf/elements/svg/svg.py +88 -0
- folio_pdf/elements/svg/svg_element.py +88 -0
- folio_pdf/elements/tabbed_line.py +187 -0
- folio_pdf/elements/table/__init__.py +10 -0
- folio_pdf/elements/table/table.py +183 -0
- folio_pdf/elements/table/table_cell.py +219 -0
- folio_pdf/elements/table/table_row.py +109 -0
- folio_pdf/enums.py +218 -0
- folio_pdf/exceptions.py +92 -0
- folio_pdf/folio_pdf.pyi +42 -0
- folio_pdf/font.py +81 -0
- folio_pdf/form_field.py +207 -0
- folio_pdf/form_filler.py +103 -0
- folio_pdf/forms.py +414 -0
- folio_pdf/libs/folio-windows-x86_64.dll +0 -0
- folio_pdf/libs/folio.h +781 -0
- folio_pdf/merger.py +308 -0
- folio_pdf/ocsp_client.py +33 -0
- folio_pdf/outline.py +65 -0
- folio_pdf/page.py +944 -0
- folio_pdf/page_importer.py +66 -0
- folio_pdf/py.typed +0 -0
- folio_pdf/reader.py +223 -0
- folio_pdf/redactor_options.py +109 -0
- folio_pdf/run_list.py +231 -0
- folio_pdf/signer.py +73 -0
- folio_pdf/signer_options.py +128 -0
- folio_pdf/tsa_client.py +42 -0
- folio_pdf/write_options.py +166 -0
- folio_pdf-0.0.2.dist-info/METADATA +81 -0
- folio_pdf-0.0.2.dist-info/RECORD +57 -0
- folio_pdf-0.0.2.dist-info/WHEEL +4 -0
folio_pdf/__init__.py
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright 2026 Gbenga Adeyi and Folio PDF Authors
|
|
3
|
+
SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import ctypes as ct
|
|
7
|
+
from io import BytesIO
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from folio_pdf.color import Color
|
|
11
|
+
from folio_pdf.document import Document
|
|
12
|
+
from folio_pdf.font import Font
|
|
13
|
+
from folio_pdf.merger import PDFMerger
|
|
14
|
+
from folio_pdf.outline import Outline
|
|
15
|
+
from folio_pdf.page import Page
|
|
16
|
+
from folio_pdf.reader import PDFReader
|
|
17
|
+
from folio_pdf.redactor_options import RedactorOptions
|
|
18
|
+
from folio_pdf.signer_options import SignerOptions
|
|
19
|
+
|
|
20
|
+
from .core import _read_from_obj_buffer, lib
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"Document",
|
|
24
|
+
"Page",
|
|
25
|
+
"Font",
|
|
26
|
+
"PDFMerger",
|
|
27
|
+
"Outline",
|
|
28
|
+
"PDFReader",
|
|
29
|
+
"RedactorOptions",
|
|
30
|
+
"SignerOptions",
|
|
31
|
+
"Color",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
lib.folio_html_to_pdf.argtypes = [ct.c_char_p, ct.c_char_p]
|
|
36
|
+
lib.folio_html_to_pdf.restype = ct.c_int32
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def html_to_pdf(html: str, destination: str | Path) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Converts an HTML string to a PDF file and writes it to the given path.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
html: the HTML content to render
|
|
45
|
+
destination: the destination file path for the generated PDF
|
|
46
|
+
"""
|
|
47
|
+
_destination = destination
|
|
48
|
+
if isinstance(_destination, Path):
|
|
49
|
+
_destination = _destination.as_posix()
|
|
50
|
+
if not _destination.endswith("pdf"):
|
|
51
|
+
_destination += ".pdf"
|
|
52
|
+
# TODO: Handle result code
|
|
53
|
+
lib.folio_html_to_pdf(
|
|
54
|
+
ct.c_char_p(html.encode()), ct.c_char_p(_destination.encode())
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
lib.folio_html_to_buffer.argtypes = [ct.c_char_p, ct.c_double, ct.c_double]
|
|
59
|
+
lib.folio_html_to_buffer.restype = ct.c_int64
|
|
60
|
+
|
|
61
|
+
lib.folio_buffer_data.restype = ct.c_void_p
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def html_to_buffer(html: str, page_width: float, page_height: float) -> BytesIO:
|
|
65
|
+
"""
|
|
66
|
+
Converts an HTML string to a PDF with custom page dimensions and returns
|
|
67
|
+
the raw bytes in an in memory buffer.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
html: the HTML content to render
|
|
71
|
+
page_width: page width in points
|
|
72
|
+
page_height: page height in points
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
the generated PDF in a buffer
|
|
76
|
+
|
|
77
|
+
Example:
|
|
78
|
+
```python
|
|
79
|
+
from folio_pdf import html_to_buffer
|
|
80
|
+
from folio_pdf.enums import PageSizes
|
|
81
|
+
|
|
82
|
+
# Using the dimensions of an A4 page size
|
|
83
|
+
width, height = PageSizes.A4.dimensions
|
|
84
|
+
result = html_to_buffer(
|
|
85
|
+
html='<h1>Folio PDF is awesome!</h1>',
|
|
86
|
+
page_width=width,
|
|
87
|
+
page_height=height)
|
|
88
|
+
```
|
|
89
|
+
"""
|
|
90
|
+
buf = lib.folio_html_to_buffer(
|
|
91
|
+
ct.c_char_p(html.encode()),
|
|
92
|
+
ct.c_double(page_width),
|
|
93
|
+
ct.c_double(page_height),
|
|
94
|
+
)
|
|
95
|
+
data = _read_from_obj_buffer(buf)
|
|
96
|
+
return BytesIO(data)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
lib.folio_html_convert.argtypes = [ct.c_char_p, ct.c_double, ct.c_double]
|
|
100
|
+
lib.folio_html_convert.restype = ct.c_uint64
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def html_convert(html: str, page_width: float, page_height: float) -> Document:
|
|
104
|
+
"""
|
|
105
|
+
Converts an HTML string to a `Document` with custom page dimensions.
|
|
106
|
+
|
|
107
|
+
The caller is responsible for closing the returned document.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
html: the HTML content to render
|
|
111
|
+
page_width: page width in points
|
|
112
|
+
page_height: page height in points
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
a new `Document` representing the converted HTML
|
|
116
|
+
|
|
117
|
+
Example:
|
|
118
|
+
```python
|
|
119
|
+
from folio_pdf import html_convert
|
|
120
|
+
from folio_pdf.enums import PageSizes
|
|
121
|
+
|
|
122
|
+
# Using the dimensions of a Letter page size
|
|
123
|
+
width, height = PageSizes.LETTER.dimensions
|
|
124
|
+
doc = html_convert(
|
|
125
|
+
html='<h1>Folio PDF is awesome!</h1>',
|
|
126
|
+
page_width=width,
|
|
127
|
+
page_height=height)
|
|
128
|
+
```
|
|
129
|
+
"""
|
|
130
|
+
doc_handle = lib.folio_html_convert(
|
|
131
|
+
ct.c_char_p(html.encode()),
|
|
132
|
+
ct.c_double(page_width),
|
|
133
|
+
ct.c_double(page_height),
|
|
134
|
+
)
|
|
135
|
+
return Document._new_from_handle(doc_handle)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
lib.folio_html_parse_css_length.argtypes = [
|
|
139
|
+
ct.c_char_p,
|
|
140
|
+
ct.c_double,
|
|
141
|
+
ct.c_double,
|
|
142
|
+
]
|
|
143
|
+
lib.folio_html_parse_css_length.restype = ct.c_double
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def parse_css_length(s: str, font_size: float, relative_to: float) -> float:
|
|
147
|
+
"""Parses a CSS length string and returns its value in points.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
s: the CSS length expression (e.g., `"1in"`, `"16px"`, `"50%"`, `"2em"`)
|
|
151
|
+
font_size: the current font size in points (used for `em`/`rem`)
|
|
152
|
+
relative_to: the reference length in points (used for `%`)
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
the parsed length in points
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
Example:
|
|
159
|
+
```python
|
|
160
|
+
pts = parse_css_length("1in", 12, 0) # 72.0
|
|
161
|
+
em = parse_css_length("2em", 16,0) # 32.0
|
|
162
|
+
pct = parse_css_length("50%", 12, 100) # 50.0
|
|
163
|
+
```
|
|
164
|
+
"""
|
|
165
|
+
return lib.folio_html_parse_css_length(
|
|
166
|
+
ct.c_char_p(s.encode()),
|
|
167
|
+
ct.c_double(font_size),
|
|
168
|
+
ct.c_double(relative_to),
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
lib.folio_sign_pdf.argtypes = [
|
|
173
|
+
ct.c_void_p,
|
|
174
|
+
ct.c_int32,
|
|
175
|
+
ct.c_int64,
|
|
176
|
+
]
|
|
177
|
+
lib.folio_sign_pdf.restype = ct.c_int64
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def sign_pdf(pdf_data: bytes, opts: SignerOptions):
|
|
181
|
+
buf = lib.folio_sign_pdf(
|
|
182
|
+
ct.c_char_p(pdf_data), ct.c_int32(len(pdf_data)), opts._handle
|
|
183
|
+
)
|
|
184
|
+
return _read_from_obj_buffer(buf)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def redact_text(reader: PDFReader, targets: list[str], opts: RedactorOptions) -> bytes:
|
|
188
|
+
"""
|
|
189
|
+
Redacts every occurrence of each target string in the given PDF reader.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
reader: source PDF reader
|
|
193
|
+
targets: literal strings to match and redact
|
|
194
|
+
opts: redaction appearance options
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
the redacted PDF bytes
|
|
198
|
+
"""
|
|
199
|
+
CharPArray = ct.c_char_p * len(targets)
|
|
200
|
+
|
|
201
|
+
lib.folio_redact_text.argtypes = [
|
|
202
|
+
ct.c_uint64,
|
|
203
|
+
CharPArray,
|
|
204
|
+
ct.c_int32,
|
|
205
|
+
ct.c_uint64,
|
|
206
|
+
]
|
|
207
|
+
lib.folio_redact_text.restype = ct.c_uint64
|
|
208
|
+
|
|
209
|
+
buf = lib.folio_redact_text(
|
|
210
|
+
reader._handle, CharPArray(targets), ct.c_int32(len(targets)), opts._handle
|
|
211
|
+
)
|
|
212
|
+
return _read_from_obj_buffer(buf)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
lib.folio_redact_pattern.argtypes = [ct.c_uint64, ct.c_char_p, ct.c_uint64]
|
|
216
|
+
lib.folio_redact_pattern.restype = ct.c_uint64
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def redact_pattern(reader: PDFReader, pattern: str, opts: RedactorOptions):
|
|
220
|
+
"""
|
|
221
|
+
Redacts every match of the given regular expression pattern.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
reader: source PDF reader
|
|
225
|
+
pattern: a regular expression used to locate redaction targets
|
|
226
|
+
opts: redaction appearance options
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
the redacted PDF bytes
|
|
230
|
+
"""
|
|
231
|
+
buf = lib.folio_redact_pattern(
|
|
232
|
+
reader._handle, ct.c_char_p(pattern.encode()), opts._handle
|
|
233
|
+
)
|
|
234
|
+
return _read_from_obj_buffer(buf)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def redact_regions(
|
|
238
|
+
reader: PDFReader,
|
|
239
|
+
pages: list[int],
|
|
240
|
+
x1s: list[float],
|
|
241
|
+
y1s: list[float],
|
|
242
|
+
x2s: list[float],
|
|
243
|
+
y2s: list[float],
|
|
244
|
+
opts: RedactorOptions,
|
|
245
|
+
): # TODO: Make this more python friendly
|
|
246
|
+
"""
|
|
247
|
+
Redacts the explicit rectangular regions supplied by the caller.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
reader: source PDF reader
|
|
251
|
+
regions: list of page regions to black out
|
|
252
|
+
opts: redaction appearance options
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
the redacted PDF bytes
|
|
256
|
+
"""
|
|
257
|
+
assert len(x1s) == len(y1s) == len(x2s) == len(y2s), (
|
|
258
|
+
"The lists x1s,y1s,x2s & y2s must all be of the same length"
|
|
259
|
+
)
|
|
260
|
+
Int32Array = ct.c_int32 * len(pages)
|
|
261
|
+
DoubleArray = ct.c_double * len(x1s)
|
|
262
|
+
|
|
263
|
+
lib.folio_redact_regions.argtypes = [
|
|
264
|
+
ct.c_uint64,
|
|
265
|
+
Int32Array,
|
|
266
|
+
DoubleArray,
|
|
267
|
+
DoubleArray,
|
|
268
|
+
DoubleArray,
|
|
269
|
+
DoubleArray,
|
|
270
|
+
ct.c_int32,
|
|
271
|
+
ct.c_uint64,
|
|
272
|
+
]
|
|
273
|
+
lib.folio_redact_regions.restype = ct.c_uint64
|
|
274
|
+
|
|
275
|
+
buf = lib.folio_redact_regions(
|
|
276
|
+
reader._handle,
|
|
277
|
+
Int32Array(pages),
|
|
278
|
+
DoubleArray(x1s),
|
|
279
|
+
DoubleArray(y1s),
|
|
280
|
+
DoubleArray(x2s),
|
|
281
|
+
DoubleArray(y2s),
|
|
282
|
+
ct.c_int32(len(x1s)),
|
|
283
|
+
opts._handle,
|
|
284
|
+
)
|
|
285
|
+
return _read_from_obj_buffer(buf)
|
folio_pdf/color.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from folio_pdf.enums import Colors
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class Color:
|
|
8
|
+
"""
|
|
9
|
+
Represents an RGB color with each channel in the range `0.0 - 1.0`.
|
|
10
|
+
|
|
11
|
+
Common colors are available via the `Colors` enum that can be used to call
|
|
12
|
+
the `from_named` class method. New colors can be created from
|
|
13
|
+
normalized floats from instantiating the color class, 8-bit integers via `from_rgb`
|
|
14
|
+
class method, or a CSS hex string via `from_hex` class method.
|
|
15
|
+
|
|
16
|
+
Examples:
|
|
17
|
+
```python
|
|
18
|
+
from folio_pdf import Color
|
|
19
|
+
from folio_pdf.enums import Colors
|
|
20
|
+
|
|
21
|
+
red = Color.from_named(Colors.RED)
|
|
22
|
+
blue = Color.from_named('blue')
|
|
23
|
+
green = Color.from_rgb(0,255,0)
|
|
24
|
+
black = Color.from_hex("#000000") # with `#` prefix
|
|
25
|
+
white = Color.from_hex("ffffff")
|
|
26
|
+
```
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
r: float
|
|
30
|
+
g: float
|
|
31
|
+
b: float
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def from_named(cls, color: str | Colors):
|
|
35
|
+
_color = color
|
|
36
|
+
if isinstance(_color, str):
|
|
37
|
+
_color = Colors(_color.lower())
|
|
38
|
+
match _color:
|
|
39
|
+
case Colors.BLACK:
|
|
40
|
+
return cls(0, 0, 0)
|
|
41
|
+
case Colors.WHITE:
|
|
42
|
+
return cls(1, 1, 1)
|
|
43
|
+
case Colors.RED:
|
|
44
|
+
return cls(1, 0, 0)
|
|
45
|
+
case Colors.GREEN:
|
|
46
|
+
return cls(0, 1, 0)
|
|
47
|
+
case Colors.BLUE:
|
|
48
|
+
return cls(0, 0, 1)
|
|
49
|
+
case Colors.GRAY:
|
|
50
|
+
return cls(0.5, 0.5, 0.5)
|
|
51
|
+
case Colors.LIGHT_GRAY:
|
|
52
|
+
return cls(0.75, 0.75, 0.75)
|
|
53
|
+
case Colors.DARK_GRAY:
|
|
54
|
+
return cls(0.25, 0.25, 0.25)
|
|
55
|
+
case Colors.NAVY:
|
|
56
|
+
return cls(0, 0, 0.5)
|
|
57
|
+
case Colors.TEAL:
|
|
58
|
+
return cls(0, 0.5, 0.5)
|
|
59
|
+
case Colors.ORANGE:
|
|
60
|
+
return cls(1, 0.65, 0)
|
|
61
|
+
case Colors.PURPLE:
|
|
62
|
+
return cls(0.5, 0, 0.5)
|
|
63
|
+
case Colors.YELLOW:
|
|
64
|
+
return cls(1, 1, 0)
|
|
65
|
+
case Colors.CYAN:
|
|
66
|
+
return cls(0, 1, 1)
|
|
67
|
+
case Colors.MAGENTA:
|
|
68
|
+
return cls(1, 0, 1)
|
|
69
|
+
case Colors.BROWN:
|
|
70
|
+
return cls(0.6, 0.3, 0)
|
|
71
|
+
case Colors.PINK:
|
|
72
|
+
return cls(1, 0.75, 0.8)
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def from_rgb(cls, r: float, g: float, b: float):
|
|
76
|
+
return cls(r / 255, g / 255, b / 255)
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def from_hex(cls, hex: str):
|
|
80
|
+
_hex = hex[1:] if hex.startswith("#") else hex
|
|
81
|
+
return cls.from_rgb(
|
|
82
|
+
int(_hex[:2], 16),
|
|
83
|
+
int(_hex[2:4], 16),
|
|
84
|
+
int(_hex[4:], 16),
|
|
85
|
+
)
|
folio_pdf/core.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright 2026 Gbenga Adeyi and Folio PDF Authors
|
|
3
|
+
SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import ctypes as ct
|
|
7
|
+
import platform
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Callable
|
|
11
|
+
|
|
12
|
+
from folio_pdf.enums import ErrorCodes
|
|
13
|
+
|
|
14
|
+
THIS_FILE_PATH = Path(__file__).parent
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _load_lib() -> ct.CDLL:
|
|
18
|
+
os_name = platform.system()
|
|
19
|
+
arch = platform.machine().lower()
|
|
20
|
+
|
|
21
|
+
arch_aliases = {
|
|
22
|
+
"amd64": "x86_64",
|
|
23
|
+
"x86_64": "x86_64",
|
|
24
|
+
"arm64": "aarch64",
|
|
25
|
+
"aarch64": "aarch64",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
arch = arch_aliases.get(arch, arch)
|
|
29
|
+
|
|
30
|
+
extensions = {
|
|
31
|
+
"Windows": "dll",
|
|
32
|
+
"Linux": "so",
|
|
33
|
+
"Darwin": "dylib",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
prefixes = {
|
|
37
|
+
"Windows": "",
|
|
38
|
+
"Linux": "lib",
|
|
39
|
+
"Darwin": "lib",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if os_name not in extensions:
|
|
43
|
+
raise RuntimeError(f"Unsupported OS: {os_name} Arch: {arch}")
|
|
44
|
+
|
|
45
|
+
filename = (
|
|
46
|
+
f"{prefixes[os_name]}folio-{os_name.lower()}-{arch}.{extensions[os_name]}"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
lib_path = THIS_FILE_PATH / "libs" / filename
|
|
50
|
+
|
|
51
|
+
if not lib_path.exists():
|
|
52
|
+
raise RuntimeError(f"folio shared library not found at: {lib_path}")
|
|
53
|
+
|
|
54
|
+
return ct.CDLL(str(lib_path))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
lib = _load_lib()
|
|
58
|
+
|
|
59
|
+
# Core
|
|
60
|
+
lib.folio_version.argtypes = []
|
|
61
|
+
lib.folio_version.restype = ct.c_char_p
|
|
62
|
+
|
|
63
|
+
lib.folio_last_error.argtypes = []
|
|
64
|
+
lib.folio_last_error.restype = ct.c_char_p
|
|
65
|
+
|
|
66
|
+
# Buffer
|
|
67
|
+
lib.folio_buffer_data.argtypes = [ct.c_int64]
|
|
68
|
+
lib.folio_buffer_data.restype = ct.c_void_p
|
|
69
|
+
|
|
70
|
+
lib.folio_buffer_len.argtypes = [ct.c_int64]
|
|
71
|
+
lib.folio_buffer_len.restype = ct.c_int32
|
|
72
|
+
|
|
73
|
+
lib.folio_buffer_free.argtypes = [ct.c_int64]
|
|
74
|
+
lib.folio_buffer_free.restype = None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _with_error_handling(exception: type[Exception]):
|
|
78
|
+
def decorator(func: Callable):
|
|
79
|
+
def wrapper(self, *args, **kwargs):
|
|
80
|
+
res_code = func(self, *args, **kwargs)
|
|
81
|
+
err = ErrorCodes(res_code)
|
|
82
|
+
if err != ErrorCodes.OK:
|
|
83
|
+
msg_bytes = lib.folio_last_error()
|
|
84
|
+
raise exception(str(msg_bytes))
|
|
85
|
+
return self
|
|
86
|
+
|
|
87
|
+
return wrapper
|
|
88
|
+
|
|
89
|
+
return decorator
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _read_from_obj_buffer(buf: int):
|
|
93
|
+
size = lib.folio_buffer_len(buf)
|
|
94
|
+
ptr = lib.folio_buffer_data(buf)
|
|
95
|
+
data = ct.string_at(ptr, size)
|
|
96
|
+
lib.folio_buffer_free(buf)
|
|
97
|
+
return data
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class AbstractFolioObject(ABC):
|
|
101
|
+
_requires_close: bool
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
@abstractmethod
|
|
105
|
+
def _handle(self) -> ct.c_uint64:
|
|
106
|
+
"""Returns the native object handle"""
|
|
107
|
+
...
|
|
108
|
+
|
|
109
|
+
def close(self):
|
|
110
|
+
err_msg = (
|
|
111
|
+
"close method has not been implemented for folio object"
|
|
112
|
+
if self._requires_close
|
|
113
|
+
else "folio object does not require close"
|
|
114
|
+
)
|
|
115
|
+
raise NotImplementedError(err_msg)
|
|
116
|
+
|
|
117
|
+
@staticmethod
|
|
118
|
+
def _read_from_obj_buffer(buf: int):
|
|
119
|
+
return _read_from_obj_buffer(buf)
|
|
120
|
+
|
|
121
|
+
def __enter__(self):
|
|
122
|
+
return self
|
|
123
|
+
|
|
124
|
+
def __exit__(self, *_args, **_kwargs):
|
|
125
|
+
if self._requires_close:
|
|
126
|
+
self.close()
|