sphinx-typst-render 0.2.2__tar.gz → 0.2.4__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.
- {sphinx_typst_render-0.2.2 → sphinx_typst_render-0.2.4}/PKG-INFO +23 -1
- {sphinx_typst_render-0.2.2 → sphinx_typst_render-0.2.4}/README.md +22 -0
- {sphinx_typst_render-0.2.2 → sphinx_typst_render-0.2.4}/pyproject.toml +1 -1
- {sphinx_typst_render-0.2.2 → sphinx_typst_render-0.2.4}/pyproject.toml.orig +1 -1
- {sphinx_typst_render-0.2.2 → sphinx_typst_render-0.2.4}/src/sphinx_typst_render/__init__.py +12 -2
- {sphinx_typst_render-0.2.2 → sphinx_typst_render-0.2.4}/src/sphinx_typst_render/_compile.py +50 -1
- {sphinx_typst_render-0.2.2 → sphinx_typst_render-0.2.4}/src/sphinx_typst_render/_directive.py +4 -0
- {sphinx_typst_render-0.2.2 → sphinx_typst_render-0.2.4}/LICENSE +0 -0
- {sphinx_typst_render-0.2.2 → sphinx_typst_render-0.2.4}/src/sphinx_typst_render/_downloads.py +0 -0
- {sphinx_typst_render-0.2.2 → sphinx_typst_render-0.2.4}/src/sphinx_typst_render/static/typst-render.css +0 -0
- {sphinx_typst_render-0.2.2 → sphinx_typst_render-0.2.4}/src/sphinx_typst_render/static/typst-render.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sphinx-typst-render
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Render Typst sources into downloadable PDFs and inline previews in Sphinx and Jupyter Book.
|
|
5
5
|
Keywords: jupyter-book,sphinx,typst,pdf,forms,documentation
|
|
6
6
|
Author: NB-TUDelft, K.Zabłocki (Zamkorus)
|
|
@@ -88,6 +88,7 @@ Pass `:inline:` to also show a preview image and a download link in the body.
|
|
|
88
88
|
| `:class:` | none | Extra CSS classes on the wrapper. |
|
|
89
89
|
| `:inline:` | off | Also show a preview and link in the page body. |
|
|
90
90
|
| `:fillable:` | off | Add interactive form fields. See below. |
|
|
91
|
+
| `:max-length:` | from config | Characters a text field accepts. `0` strips the limit. |
|
|
91
92
|
|
|
92
93
|
### Configuration
|
|
93
94
|
|
|
@@ -98,6 +99,7 @@ Pass `:inline:` to also show a preview image and a download link in the body.
|
|
|
98
99
|
| `typst_render_stage_field_library` | `True` | Stage `capture_field.typ` into the source root. |
|
|
99
100
|
| `typst_render_source_label` | `"Source"` | Heading above the page's own downloads. |
|
|
100
101
|
| `typst_render_downloads_label` | `"Worksheets"` | Heading above the rendered PDFs. |
|
|
102
|
+
| `typst_render_field_max_length` | `10000` | Characters a text field accepts. `0` strips the limit. |
|
|
101
103
|
|
|
102
104
|
## The download menu
|
|
103
105
|
|
|
@@ -178,6 +180,26 @@ yourself.
|
|
|
178
180
|
Available helpers are `text_field`, `textarea_field`, `checkbox_field`, and
|
|
179
181
|
`radio_field`, plus the lower level `capture_field`.
|
|
180
182
|
|
|
183
|
+
## Field length
|
|
184
|
+
|
|
185
|
+
ReportLab caps every text field at 100 characters by default, which stops a
|
|
186
|
+
worksheet answer mid-sentence. This package overrides that with an explicit,
|
|
187
|
+
generous cap:
|
|
188
|
+
|
|
189
|
+
```yaml
|
|
190
|
+
sphinx:
|
|
191
|
+
config:
|
|
192
|
+
typst_render_field_max_length: 10000 # 0 strips the limit entirely
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The value is written explicitly rather than omitted because **some readers
|
|
196
|
+
treat a missing `/MaxLen` as zero** and then refuse all input. Stripping it is
|
|
197
|
+
still available with `0`, for projects that have checked their readers cope.
|
|
198
|
+
Either way it is a config change, not a new release of this package. A single
|
|
199
|
+
block can override it with `:max-length:`.
|
|
200
|
+
|
|
201
|
+
Only text fields are affected. A cap on a checkbox would be meaningless.
|
|
202
|
+
|
|
181
203
|
## Caching
|
|
182
204
|
|
|
183
205
|
Output is written beside the source and skipped when nothing relevant changed.
|
|
@@ -57,6 +57,7 @@ Pass `:inline:` to also show a preview image and a download link in the body.
|
|
|
57
57
|
| `:class:` | none | Extra CSS classes on the wrapper. |
|
|
58
58
|
| `:inline:` | off | Also show a preview and link in the page body. |
|
|
59
59
|
| `:fillable:` | off | Add interactive form fields. See below. |
|
|
60
|
+
| `:max-length:` | from config | Characters a text field accepts. `0` strips the limit. |
|
|
60
61
|
|
|
61
62
|
### Configuration
|
|
62
63
|
|
|
@@ -67,6 +68,7 @@ Pass `:inline:` to also show a preview image and a download link in the body.
|
|
|
67
68
|
| `typst_render_stage_field_library` | `True` | Stage `capture_field.typ` into the source root. |
|
|
68
69
|
| `typst_render_source_label` | `"Source"` | Heading above the page's own downloads. |
|
|
69
70
|
| `typst_render_downloads_label` | `"Worksheets"` | Heading above the rendered PDFs. |
|
|
71
|
+
| `typst_render_field_max_length` | `10000` | Characters a text field accepts. `0` strips the limit. |
|
|
70
72
|
|
|
71
73
|
## The download menu
|
|
72
74
|
|
|
@@ -147,6 +149,26 @@ yourself.
|
|
|
147
149
|
Available helpers are `text_field`, `textarea_field`, `checkbox_field`, and
|
|
148
150
|
`radio_field`, plus the lower level `capture_field`.
|
|
149
151
|
|
|
152
|
+
## Field length
|
|
153
|
+
|
|
154
|
+
ReportLab caps every text field at 100 characters by default, which stops a
|
|
155
|
+
worksheet answer mid-sentence. This package overrides that with an explicit,
|
|
156
|
+
generous cap:
|
|
157
|
+
|
|
158
|
+
```yaml
|
|
159
|
+
sphinx:
|
|
160
|
+
config:
|
|
161
|
+
typst_render_field_max_length: 10000 # 0 strips the limit entirely
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The value is written explicitly rather than omitted because **some readers
|
|
165
|
+
treat a missing `/MaxLen` as zero** and then refuse all input. Stripping it is
|
|
166
|
+
still available with `0`, for projects that have checked their readers cope.
|
|
167
|
+
Either way it is a config change, not a new release of this package. A single
|
|
168
|
+
block can override it with `:max-length:`.
|
|
169
|
+
|
|
170
|
+
Only text fields are affected. A cap on a checkbox would be meaningless.
|
|
171
|
+
|
|
150
172
|
## Caching
|
|
151
173
|
|
|
152
174
|
Output is written beside the source and skipped when nothing relevant changed.
|
|
@@ -5,7 +5,13 @@ from __future__ import annotations
|
|
|
5
5
|
import json
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
|
-
from ._compile import
|
|
8
|
+
from ._compile import (
|
|
9
|
+
DEFAULT_MAX_LENGTH,
|
|
10
|
+
RenderRequest,
|
|
11
|
+
RenderResult,
|
|
12
|
+
render,
|
|
13
|
+
stage_field_library,
|
|
14
|
+
)
|
|
9
15
|
from ._directive import TypstDirective
|
|
10
16
|
from ._downloads import (
|
|
11
17
|
add_download_buttons,
|
|
@@ -15,6 +21,7 @@ from ._downloads import (
|
|
|
15
21
|
)
|
|
16
22
|
|
|
17
23
|
__all__ = [
|
|
24
|
+
"DEFAULT_MAX_LENGTH",
|
|
18
25
|
"RenderRequest",
|
|
19
26
|
"RenderResult",
|
|
20
27
|
"TypstDirective",
|
|
@@ -22,7 +29,7 @@ __all__ = [
|
|
|
22
29
|
"setup",
|
|
23
30
|
"stage_field_library",
|
|
24
31
|
]
|
|
25
|
-
__version__ = "0.2.
|
|
32
|
+
__version__ = "0.2.4"
|
|
26
33
|
|
|
27
34
|
STATIC_DIR = Path(__file__).parent / "static"
|
|
28
35
|
|
|
@@ -66,6 +73,9 @@ def setup(app):
|
|
|
66
73
|
app.add_config_value("typst_render_preview", "svg", "env")
|
|
67
74
|
app.add_config_value("typst_render_ppi", 144.0, "env")
|
|
68
75
|
app.add_config_value("typst_render_stage_field_library", True, "env")
|
|
76
|
+
# Characters a generated text field accepts. Set to 0 to strip the limit
|
|
77
|
+
# entirely, which some readers mis-handle as zero, so it is opt in.
|
|
78
|
+
app.add_config_value("typst_render_field_max_length", DEFAULT_MAX_LENGTH, "env")
|
|
69
79
|
# Headings inserted into the theme's download menu. The first names what
|
|
70
80
|
# the page itself is, which differs per project: a manual, a chapter, a
|
|
71
81
|
# page. An empty string leaves that group unlabelled.
|
|
@@ -17,7 +17,12 @@ OUT_DIRNAME = "_typst"
|
|
|
17
17
|
LIB_FILENAME = "capture_field.typ"
|
|
18
18
|
|
|
19
19
|
#: Bumped whenever the output layout changes, to invalidate stale caches.
|
|
20
|
-
_CACHE_VERSION =
|
|
20
|
+
_CACHE_VERSION = 3
|
|
21
|
+
|
|
22
|
+
#: Characters a generated text field accepts unless configured otherwise.
|
|
23
|
+
#: Large enough that no worksheet answer reaches it, and written explicitly
|
|
24
|
+
#: because some readers treat a missing /MaxLen as zero and refuse all input.
|
|
25
|
+
DEFAULT_MAX_LENGTH = 10000
|
|
21
26
|
|
|
22
27
|
|
|
23
28
|
@dataclass(frozen=True)
|
|
@@ -30,6 +35,9 @@ class RenderRequest:
|
|
|
30
35
|
fillable: bool = False
|
|
31
36
|
ppi: float | None = None
|
|
32
37
|
preview_page: int = 1
|
|
38
|
+
#: Characters a text field accepts. 0 removes the limit entirely, which
|
|
39
|
+
#: some readers mis-handle as zero. See _apply_field_lengths.
|
|
40
|
+
max_length: int = DEFAULT_MAX_LENGTH
|
|
33
41
|
|
|
34
42
|
|
|
35
43
|
@dataclass(frozen=True)
|
|
@@ -80,6 +88,7 @@ def _options(request: RenderRequest) -> dict[str, object]:
|
|
|
80
88
|
"fillable": request.fillable,
|
|
81
89
|
"ppi": request.ppi,
|
|
82
90
|
"page": request.preview_page,
|
|
91
|
+
"max_length": request.max_length,
|
|
83
92
|
}
|
|
84
93
|
|
|
85
94
|
|
|
@@ -115,6 +124,45 @@ def _fingerprint(request: RenderRequest) -> str:
|
|
|
115
124
|
return digest.hexdigest()
|
|
116
125
|
|
|
117
126
|
|
|
127
|
+
def _apply_field_lengths(writer, max_length: int) -> None:
|
|
128
|
+
"""Set, or remove, the character cap on every text field.
|
|
129
|
+
|
|
130
|
+
Three behaviours are in play here. ReportLab's ``AcroForm.textfield()``
|
|
131
|
+
defaults to ``maxlen=100`` and typst-fillable does not override it, so a
|
|
132
|
+
field silently refuses input after 100 characters. Removing ``/MaxLen``
|
|
133
|
+
altogether is not safe either, because some readers treat a missing cap as
|
|
134
|
+
zero and then refuse all input. So the default is to write an explicit,
|
|
135
|
+
generous value, and ``max_length = 0`` drops the key for anyone who has
|
|
136
|
+
checked that their readers cope.
|
|
137
|
+
|
|
138
|
+
Only text fields are touched. A cap on a checkbox would be meaningless.
|
|
139
|
+
"""
|
|
140
|
+
from pypdf.generic import NameObject, NumberObject
|
|
141
|
+
|
|
142
|
+
seen: set[int] = set()
|
|
143
|
+
|
|
144
|
+
def apply(obj) -> None:
|
|
145
|
+
if obj is None or id(obj) in seen:
|
|
146
|
+
return
|
|
147
|
+
seen.add(id(obj))
|
|
148
|
+
if obj.get("/FT") == "/Tx":
|
|
149
|
+
if max_length > 0:
|
|
150
|
+
obj[NameObject("/MaxLen")] = NumberObject(max_length)
|
|
151
|
+
elif "/MaxLen" in obj:
|
|
152
|
+
del obj["/MaxLen"]
|
|
153
|
+
for kid in obj.get("/Kids", []) or []:
|
|
154
|
+
apply(kid.get_object())
|
|
155
|
+
|
|
156
|
+
for page in writer.pages:
|
|
157
|
+
for annot in page.get("/Annots", []) or []:
|
|
158
|
+
apply(annot.get_object())
|
|
159
|
+
|
|
160
|
+
acroform = writer._root_object.get("/AcroForm")
|
|
161
|
+
if acroform:
|
|
162
|
+
for field in acroform.get("/Fields", []) or []:
|
|
163
|
+
apply(field.get_object())
|
|
164
|
+
|
|
165
|
+
|
|
118
166
|
def _add_form_fields(base: bytes, request: RenderRequest) -> bytes:
|
|
119
167
|
"""Overlay interactive AcroForm fields onto an already compiled PDF.
|
|
120
168
|
|
|
@@ -149,6 +197,7 @@ def _add_form_fields(base: bytes, request: RenderRequest) -> bytes:
|
|
|
149
197
|
for index, page in enumerate(reader.pages):
|
|
150
198
|
if index < len(writer.pages):
|
|
151
199
|
writer.pages[index].merge_page(page, over=False)
|
|
200
|
+
_apply_field_lengths(writer, request.max_length)
|
|
152
201
|
# Ask the reader to build field appearances, so a blank field is visible.
|
|
153
202
|
writer.set_need_appearances_writer(True)
|
|
154
203
|
|
{sphinx_typst_render-0.2.2 → sphinx_typst_render-0.2.4}/src/sphinx_typst_render/_directive.py
RENAMED
|
@@ -50,6 +50,7 @@ class TypstDirective(SphinxDirective):
|
|
|
50
50
|
"height": directives.length_or_unitless,
|
|
51
51
|
"ppi": directives.positive_int,
|
|
52
52
|
"page": directives.positive_int,
|
|
53
|
+
"max-length": directives.nonnegative_int,
|
|
53
54
|
"class": directives.class_option,
|
|
54
55
|
}
|
|
55
56
|
|
|
@@ -79,6 +80,9 @@ class TypstDirective(SphinxDirective):
|
|
|
79
80
|
fillable="fillable" in self.options,
|
|
80
81
|
ppi=self.options.get("ppi", config.typst_render_ppi),
|
|
81
82
|
preview_page=self.options.get("page", 1),
|
|
83
|
+
max_length=self.options.get(
|
|
84
|
+
"max-length", config.typst_render_field_max_length
|
|
85
|
+
),
|
|
82
86
|
)
|
|
83
87
|
|
|
84
88
|
# An inline preview has to be an image Sphinx can see, which means it
|
|
File without changes
|
{sphinx_typst_render-0.2.2 → sphinx_typst_render-0.2.4}/src/sphinx_typst_render/_downloads.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|