@cteno/sdk-linux-x64 0.0.0-stage → 2.10.13
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.
- package/LICENSE.daemon +31 -0
- package/README.md +4 -2
- package/agents/flow-analyzer/AGENT.md +71 -0
- package/bin/cteno-host-cli +4 -0
- package/bin/cteno-wiki +0 -0
- package/bin/rg +0 -0
- package/bin/secret-exec +0 -0
- package/manifest.json +12 -0
- package/package.json +33 -4
- package/plugins/agent-secrets/.cteno-plugin/plugin.json +133 -0
- package/plugins/agent-secrets/bin/linux-x64/fnox +4 -0
- package/plugins/agent-secrets/bin/linux-x64/secret-exec +0 -0
- package/plugins/agent-secrets/skills/secret/SKILL.md +201 -0
- package/plugins/cteno.browser/.cteno-plugin/plugin.json +65 -0
- package/plugins/cteno.browser/skills/browser/SKILL.md +28 -0
- package/plugins/cteno.inbox/.cteno-plugin/plugin.json +128 -0
- package/plugins/cteno.inbox/bin/all/cteno-connector-credential +31 -0
- package/plugins/cteno.inbox/bin/all/cteno-telegram-connector +33 -0
- package/plugins/cteno.inbox/bin/linux-x64/cteno-inbox +0 -0
- package/plugins/cteno.inbox/skills/cteno-inbox/SKILL.md +374 -0
- package/plugins/cteno.inbox/tools/inbox_focus/TOOL.md +44 -0
- package/plugins/cteno.memory/.cteno-plugin/plugin.json +176 -0
- package/plugins/cteno.memory/agents/llm-wiki/AGENT.md +96 -0
- package/plugins/cteno.memory/bin/linux-x64/cteno-wiki +0 -0
- package/plugins/cteno.memory/skills/codebase-wiki/SKILL.md +52 -0
- package/plugins/cteno.memory/skills/cteno.memory.native/SKILL.md +110 -0
- package/plugins/cteno.memory/tools/memory/TOOL.md +492 -0
- package/plugins/cteno.memory/ui/index.html +822 -0
- package/plugins/cteno.models/.cteno-plugin/plugin.json +191 -0
- package/plugins/cteno.websearch/.cteno-plugin/plugin.json +45 -0
- package/plugins/jev/.cteno-plugin/plugin.json +305 -0
- package/plugins/jev/skills/jev-browser/SKILL.md +66 -0
- package/plugins/jev/skills/jev-browser/jev_browser.py +335 -0
- package/skills/agent-create/SKILL.md +86 -0
- package/skills/cteno-host-cli/SKILL.md +248 -0
- package/skills/docx/LICENSE +23 -0
- package/skills/docx/NOTICE.md +8 -0
- package/skills/docx/SKILL.md +69 -0
- package/skills/docx/references/cjk_typography.md +357 -0
- package/skills/docx/references/design_principles.md +819 -0
- package/skills/docx/references/revisions-and-comments.md +88 -0
- package/skills/docx/references/track_changes_guide.md +200 -0
- package/skills/docx/references/typography_guide.md +294 -0
- package/skills/docx/scripts/cjk_fonts.py +244 -0
- package/skills/docx/scripts/docx_comments.py +289 -0
- package/skills/docx/scripts/docx_common.py +92 -0
- package/skills/docx/scripts/docx_create.py +259 -0
- package/skills/docx/scripts/docx_edit.py +250 -0
- package/skills/docx/scripts/docx_read.py +149 -0
- package/skills/docx/scripts/docx_revisions.py +146 -0
- package/skills/docx/scripts/docx_template.py +70 -0
- package/skills/docx/scripts/docx_validate.py +156 -0
- package/skills/docx/scripts/render.py +250 -0
- package/skills/docx/tests/test_docx_skill.py +525 -0
- package/skills/home-devices/SKILL.md +121 -0
- package/skills/mcp/SKILL.md +72 -0
- package/skills/mcp/scripts/mcp_setup.py +439 -0
- package/skills/pdf/LICENSE +23 -0
- package/skills/pdf/NOTICE.md +8 -0
- package/skills/pdf/SKILL.md +63 -0
- package/skills/pdf/references/design-tokens.md +381 -0
- package/skills/pdf/references/forms.md +99 -0
- package/skills/pdf/references/nano-pdf-editing.md +40 -0
- package/skills/pdf/references/ocr-extraction.md +165 -0
- package/skills/pdf/scripts/_raster.py +76 -0
- package/skills/pdf/scripts/extract_marker.py +88 -0
- package/skills/pdf/scripts/extract_pymupdf.py +101 -0
- package/skills/pdf/scripts/pdf_create.py +191 -0
- package/skills/pdf/scripts/pdf_fill_form.py +97 -0
- package/skills/pdf/scripts/pdf_form_layout.py +168 -0
- package/skills/pdf/scripts/pdf_make_form.py +145 -0
- package/skills/pdf/scripts/pdf_merge.py +50 -0
- package/skills/pdf/scripts/pdf_meta.py +115 -0
- package/skills/pdf/scripts/pdf_page_image.py +99 -0
- package/skills/pdf/scripts/pdf_read.py +153 -0
- package/skills/pdf/scripts/pdf_secure.py +71 -0
- package/skills/pdf/scripts/pdf_split.py +84 -0
- package/skills/pdf/scripts/pdf_stamp.py +143 -0
- package/skills/pdf/scripts/pdf_watermark.py +51 -0
- package/skills/pdf/scripts/render.py +250 -0
- package/skills/pdf/tests/test_pdf_skill.py +415 -0
- package/skills/pptx/LICENSE +23 -0
- package/skills/pptx/NOTICE.md +8 -0
- package/skills/pptx/SKILL.md +62 -0
- package/skills/pptx/references/design-system.md +392 -0
- package/skills/pptx/references/pptxgenjs.md +420 -0
- package/skills/pptx/references/slide-types.md +413 -0
- package/skills/pptx/scripts/cjk_fonts.py +244 -0
- package/skills/pptx/scripts/pptx_create.py +240 -0
- package/skills/pptx/scripts/pptx_edit.py +436 -0
- package/skills/pptx/scripts/pptx_from_template.py +88 -0
- package/skills/pptx/scripts/pptx_read.py +131 -0
- package/skills/pptx/scripts/pptx_render.py +96 -0
- package/skills/pptx/scripts/render.py +250 -0
- package/skills/pptx/tests/test_powerpoint_skill.py +477 -0
- package/skills/skill-create/SKILL.md +114 -0
- package/skills/video-generation/SKILL.md +27 -0
- package/skills/xlsx/LICENSE +23 -0
- package/skills/xlsx/NOTICE.md +8 -0
- package/skills/xlsx/SKILL.md +63 -0
- package/skills/xlsx/references/format.md +71 -0
- package/skills/xlsx/references/restructuring.md +71 -0
- package/skills/xlsx/scripts/csv_to_xlsx.py +110 -0
- package/skills/xlsx/scripts/formula_check.py +428 -0
- package/skills/xlsx/scripts/render.py +250 -0
- package/skills/xlsx/scripts/xlsx_create.py +265 -0
- package/skills/xlsx/scripts/xlsx_edit.py +269 -0
- package/skills/xlsx/scripts/xlsx_read.py +166 -0
- package/skills/xlsx/scripts/xlsx_recalc.py +119 -0
- package/skills/xlsx/scripts/xlsx_restructure.py +343 -0
- package/skills/xlsx/scripts/xlsx_to_csv.py +75 -0
- package/skills/xlsx/tests/test_xlsx_skill.py +544 -0
- package/tools/create_goal/TOOL.md +36 -0
- package/tools/image_generation/TOOL.md +81 -0
- package/tools/skill/TOOL.md +117 -0
- package/tools/update_profile_notes/TOOL.md +39 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
# Design System
|
|
2
|
+
|
|
3
|
+
Design guidance for PDFs you create (HTML/CSS or reportlab). It answers "what should it look like and why".
|
|
4
|
+
|
|
5
|
+
> Adapted from MiniMax's minimax-pdf design system (MIT). Mentions of `tokens`, cover patterns and block types describe design decisions; implement them in your HTML/CSS or reportlab code — the original generator scripts are not shipped.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## The one rule
|
|
10
|
+
|
|
11
|
+
Every design decision must be **rooted in the document's content and purpose**.
|
|
12
|
+
Dark teal + cream is not "professional". Serif + beige is not "elegant".
|
|
13
|
+
A color chosen because it fits the content will always outperform a color chosen
|
|
14
|
+
because it seems safe.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Palette logic
|
|
19
|
+
|
|
20
|
+
Derive the palette from a short description of the content:
|
|
21
|
+
|
|
22
|
+
### Mood → base palette
|
|
23
|
+
|
|
24
|
+
| Content signal | Mood | Background | Accent | Text |
|
|
25
|
+
|---|---|---|---|---|
|
|
26
|
+
| Research, science, analysis | Authoritative | `#0F1F2E` deep ink | `#00B4A6` teal | `#F0EDE6` warm white |
|
|
27
|
+
| Business, strategy, finance | Confident | `#1C1C2B` near-black | `#E8A020` amber | `#F5F2EC` cream |
|
|
28
|
+
| Creative, portfolio, design | Expressive | `#1A0A2E` deep violet | `#FF6B6B` coral | `#FAF5FF` lavender white |
|
|
29
|
+
| Education, academic paper | Scholarly | `#FAFAF7` warm white | `#2C4A7C` navy | `#1A1A2E` dark |
|
|
30
|
+
| Healthcare, wellness | Calm | `#F5F9F8` pale mint | `#2D8B72` forest | `#1E3830` deep green |
|
|
31
|
+
| Resume / personal | Clean | `#FFFFFF` white | pick from content | `#111111` near-black |
|
|
32
|
+
| General / unknown | Neutral | `#F8F6F1` warm off-white | `#3D3D3D` dark gray | `#1A1A1A` black |
|
|
33
|
+
| Formal publications, annual reports | Magazine | `#F2F0EC` warm linen | `#1C3557` deep navy | `#0D1A2B` near-black |
|
|
34
|
+
| Premium/dark reports, tech reviews | Darkroom | `#151C27` deep navy | `#4A6FA5` steel blue | `#F0EDE6` warm white |
|
|
35
|
+
| Technical docs, developer reports | Terminal | `#0D1117` near-black | `#39D353` neon green | `#E6EDF3` cool white |
|
|
36
|
+
| Portfolios, creative, photography | Poster | `#FFFFFF` white | `#0A0A0A` near-black | `#0A0A0A` near-black |
|
|
37
|
+
|
|
38
|
+
### Accent selection rules
|
|
39
|
+
|
|
40
|
+
- **One accent color only.** Using two accents splits visual energy.
|
|
41
|
+
- Accent appears on: cover geometric elements, section rules, callout left borders,
|
|
42
|
+
table header background, page header rule. Nowhere else.
|
|
43
|
+
- Accent must contrast with the cover background by at least 4.5:1 (WCAG AA).
|
|
44
|
+
- Do not default to blue. Blue is the most overused accent in AI-generated documents.
|
|
45
|
+
|
|
46
|
+
### Color pairing anti-patterns (never use these)
|
|
47
|
+
|
|
48
|
+
| ❌ Avoid | Why |
|
|
49
|
+
|---|---|
|
|
50
|
+
| Purple gradient on white | The default AI aesthetic — immediately signals "generated" |
|
|
51
|
+
| Navy + gold | Overused corporate cliché |
|
|
52
|
+
| All-black background | Prints badly, feels aggressive |
|
|
53
|
+
| More than 3 colors in the system | Visual noise |
|
|
54
|
+
| Accent on body text | Destroys readability |
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Typography system
|
|
59
|
+
|
|
60
|
+
### Font pairing logic
|
|
61
|
+
|
|
62
|
+
Two typefaces maximum. Always.
|
|
63
|
+
|
|
64
|
+
| Role | Criteria | Good choices (system-safe) |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| Display (cover title, H1) | Distinctive, strong contrast, high weight | Times New Roman, Georgia (serif) |
|
|
67
|
+
| Text (body, captions, UI) | Highly readable at 10–11pt | Helvetica, Arial (sans) |
|
|
68
|
+
|
|
69
|
+
Cover fonts are loaded live via `@import url(...)` in the cover HTML — Playwright
|
|
70
|
+
fetches them at render time, no local caching. Body pages always use system fonts
|
|
71
|
+
(Times-Bold / Helvetica) via ReportLab — consistent and offline-safe.
|
|
72
|
+
|
|
73
|
+
Pairs by mood (cover HTML only — body always uses system fonts):
|
|
74
|
+
- Authoritative: `Playfair Display` / `IBM Plex Sans`
|
|
75
|
+
- Confident: `Syne` / `Nunito Sans`
|
|
76
|
+
- Expressive: `Fraunces` / `Inter`
|
|
77
|
+
- Scholarly: `EB Garamond` / `Source Sans 3`
|
|
78
|
+
- Clean: `DM Serif Display` / `DM Sans`
|
|
79
|
+
- Restrained: `Cormorant Garamond` / `Jost`
|
|
80
|
+
- Bold: `Barlow Condensed` / `Barlow`
|
|
81
|
+
- Dynamic: `Montserrat` / `Montserrat`
|
|
82
|
+
- Classical: `Cormorant` / `Crimson Pro`
|
|
83
|
+
- Editorial: `Bebas Neue` / `Libre Franklin`
|
|
84
|
+
- Body fallback (always): `Times-Bold` / `Helvetica` (ReportLab system fonts)
|
|
85
|
+
|
|
86
|
+
### Type scale
|
|
87
|
+
|
|
88
|
+
All sizes in points. Use this scale for headings and body.
|
|
89
|
+
|
|
90
|
+
| Token | Size | Leading | Usage |
|
|
91
|
+
|---|---|---|---|
|
|
92
|
+
| `display` | 54pt | 1.0 | Cover title |
|
|
93
|
+
| `h1` | 22pt | 1.3 | Section headings |
|
|
94
|
+
| `h2` | 15pt | 1.4 | Subsection headings |
|
|
95
|
+
| `h3` | 11.5pt | 1.5 | Sub-subsection |
|
|
96
|
+
| `body` | 10.5pt | 1.6 | Main prose |
|
|
97
|
+
| `caption` | 8.5pt | 1.4 | Figure/table captions |
|
|
98
|
+
| `meta` | 8pt | 1.3 | Header/footer text |
|
|
99
|
+
|
|
100
|
+
### Spacing system
|
|
101
|
+
|
|
102
|
+
Margins and rhythm are what separate "looks designed" from "looks printed".
|
|
103
|
+
|
|
104
|
+
| Token | Value | Notes |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| `margin_outer` | 2.8cm | Left/right page margin |
|
|
107
|
+
| `margin_top` | 2.8cm | Top page margin |
|
|
108
|
+
| `margin_bottom` | 2.5cm | Bottom page margin |
|
|
109
|
+
| `section_gap` | 26pt | Space before H1 |
|
|
110
|
+
| `para_gap` | 8pt | Space after paragraph |
|
|
111
|
+
| `line_gap` | 17pt | Leading for body text |
|
|
112
|
+
|
|
113
|
+
Never use ReportLab's default margins (too tight). Always set explicitly.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Cover design
|
|
118
|
+
|
|
119
|
+
The cover is the most important page. It determines whether a reader trusts the document.
|
|
120
|
+
|
|
121
|
+
### Thirteen cover patterns
|
|
122
|
+
|
|
123
|
+
Pick one cover pattern that fits the content.
|
|
124
|
+
|
|
125
|
+
**1. `fullbleed`** — used for: `report`, `general`
|
|
126
|
+
- Deep background fills 100% of page
|
|
127
|
+
- Title: large, left-aligned, upper 60% of page
|
|
128
|
+
- Accent: thin horizontal rule + top-right corner strip
|
|
129
|
+
- Dot-grid background texture (subtle, 8–10% opacity)
|
|
130
|
+
- Footer band: author + date metadata
|
|
131
|
+
- Fonts: Playfair Display / IBM Plex Sans
|
|
132
|
+
|
|
133
|
+
**2. `split`** — used for: `proposal`
|
|
134
|
+
- Left 42% panel: solid cover color, title + author
|
|
135
|
+
- Right 58%: off-white, dot-grid decoration
|
|
136
|
+
- Hard vertical dividing line in accent color
|
|
137
|
+
- No gradients — pure flat geometry
|
|
138
|
+
- Fonts: Syne / Nunito Sans
|
|
139
|
+
|
|
140
|
+
**3. `typographic`** — used for: `resume`, `academic`
|
|
141
|
+
- White/off-white background
|
|
142
|
+
- Name or title as oversized display type (60–80pt), left-aligned
|
|
143
|
+
- First word in accent color, remainder in dark
|
|
144
|
+
- Thin rule below title block
|
|
145
|
+
- Fonts: DM Serif Display / DM Sans (resume) · EB Garamond / Source Sans 3 (academic)
|
|
146
|
+
|
|
147
|
+
**4. `atmospheric`** — used for: `portfolio`
|
|
148
|
+
- Near-black background
|
|
149
|
+
- Soft radial glow in accent color (upper-right quadrant)
|
|
150
|
+
- Title centered-left, 2 lines max
|
|
151
|
+
- Short rule in accent below title
|
|
152
|
+
- Dot-grid texture at low opacity
|
|
153
|
+
- Fonts: Fraunces / Inter
|
|
154
|
+
|
|
155
|
+
**5. `minimal`** — used for: `minimal`
|
|
156
|
+
- Near-white background, 8px left accent bar is the only color
|
|
157
|
+
- Title in very large, light-weight display type (300 weight)
|
|
158
|
+
- Hairline rule, author + date as single muted line
|
|
159
|
+
- Nothing else — the bar does all the visual work
|
|
160
|
+
- Fonts: Cormorant Garamond / Jost
|
|
161
|
+
|
|
162
|
+
**6. `stripe`** — used for: `stripe`
|
|
163
|
+
- Page cut into three horizontal bands: accent / dark / light
|
|
164
|
+
- Top band: category label; middle: oversized title in white; bottom: metadata
|
|
165
|
+
- Hard edges, no gradients, no textures — newspaper / brand poster aesthetic
|
|
166
|
+
- Fonts: Barlow Condensed / Barlow
|
|
167
|
+
|
|
168
|
+
**7. `diagonal`** — used for: `diagonal`
|
|
169
|
+
- SVG polygon cuts page diagonally: dark upper-left, light lower-right
|
|
170
|
+
- Accent-colored edge line traces the diagonal cut
|
|
171
|
+
- Title on dark area, metadata on light area
|
|
172
|
+
- Fonts: Montserrat / Montserrat
|
|
173
|
+
|
|
174
|
+
**8. `frame`** — used for: `frame`
|
|
175
|
+
- White/cream background with an inset rectangular border (1.2px, 28px from edges)
|
|
176
|
+
- Accent strips inside top + bottom of frame; small accent corner squares
|
|
177
|
+
- Title centered in the frame space, centered alignment, classical weight
|
|
178
|
+
- Formal, timeless — annual reports, legal documents, academic papers
|
|
179
|
+
- Fonts: Cormorant / Crimson Pro
|
|
180
|
+
|
|
181
|
+
**9. `editorial`** — used for: `editorial`
|
|
182
|
+
- Ghost first-letter of title fills upper-right at 5% opacity — visual texture
|
|
183
|
+
- 5px accent top bar; full-width uppercase title in condensed weight
|
|
184
|
+
- Title all-caps, very large (80px), flush-left
|
|
185
|
+
- Footer rule + author/date metadata
|
|
186
|
+
- Fonts: Bebas Neue / Libre Franklin
|
|
187
|
+
|
|
188
|
+
**10. `magazine`** — used for: `magazine`
|
|
189
|
+
- Warm cream/linen background; fully centered, vertical stack layout
|
|
190
|
+
- Org/company name in small spaced caps + 2px accent rule beneath (top anchor)
|
|
191
|
+
- Large bold serif title (52px) centered; short accent rule under title
|
|
192
|
+
- Italic subtitle; optional `cover_image` URL renders as centered hero thumbnail
|
|
193
|
+
- Optional `abstract` field: justified text block with bold "Abstract:" label
|
|
194
|
+
- Author name in accent color (large, bold); date beneath
|
|
195
|
+
- Fonts: Playfair Display / EB Garamond
|
|
196
|
+
|
|
197
|
+
**11. `darkroom`** — used for: `darkroom`
|
|
198
|
+
- Same centered stack layout as `magazine` but deep navy background, white text
|
|
199
|
+
- Org name + rules in semi-transparent white; accent rules desaturated
|
|
200
|
+
- Hero image (if provided) gets `grayscale(20%) brightness(0.9)` filter
|
|
201
|
+
- Fonts: Playfair Display / EB Garamond
|
|
202
|
+
|
|
203
|
+
**12. `terminal`** — used for: `terminal`
|
|
204
|
+
- Near-black background; neon green accent; Space Mono monospace throughout
|
|
205
|
+
- Grid overlay: faint horizontal + vertical lines at 48px intervals (7% opacity)
|
|
206
|
+
- Status label top-left: green dot + `SYSTEM_REPORT // <date>`
|
|
207
|
+
- Title inside a bracket frame (border-left + border-top + pseudo-element corner)
|
|
208
|
+
- Subtitle prefixed with `>` in accent color
|
|
209
|
+
- Abstract text left; author block right; status bar at bottom (UTF-8 / Ln 1)
|
|
210
|
+
- Fonts: Space Mono / Space Mono
|
|
211
|
+
|
|
212
|
+
**13. `poster`** — used for: `poster`
|
|
213
|
+
- White background; thick 52px left sidebar in accent (typically near-black)
|
|
214
|
+
- Title: 96px, 900-weight, all-caps, condensed — the dominant visual element
|
|
215
|
+
- Subtitle in typewriter font below title; thin 2px rule as separator
|
|
216
|
+
- Author + meta in Courier Prime monospace beneath rule
|
|
217
|
+
- Optional `cover_image` rendered as 260×340 grayscale thumbnail, right-aligned
|
|
218
|
+
- Accent square icon block (lower-right) with white horizontal lines
|
|
219
|
+
- Fonts: Barlow Condensed / Courier Prime
|
|
220
|
+
|
|
221
|
+
### Optional token: `cover_image`
|
|
222
|
+
|
|
223
|
+
Patterns `magazine`, `darkroom`, and `poster` accept an optional `cover_image`
|
|
224
|
+
token containing an absolute URL or `file://` path to an image.
|
|
225
|
+
The image renders via `<img src="...">` — Playwright fetches it at render time.
|
|
226
|
+
If omitted, the image area is simply skipped (layout adjusts gracefully).
|
|
227
|
+
|
|
228
|
+
### Cover CSS requirements (critical for Playwright rendering)
|
|
229
|
+
|
|
230
|
+
These three rules must appear in every cover HTML file or the output will have
|
|
231
|
+
white borders / incorrect dimensions:
|
|
232
|
+
|
|
233
|
+
```css
|
|
234
|
+
body { margin: 0; padding: 0; }
|
|
235
|
+
html, body { width: 794px; height: 1123px; overflow: hidden; }
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
No `@page` rules needed — Playwright handles page size via the `pdf()` call.
|
|
239
|
+
Do NOT use CSS `background-image` for textures — use inline SVG or `<canvas>`.
|
|
240
|
+
Always use `position: absolute` + `z-index` for layered elements.
|
|
241
|
+
|
|
242
|
+
### What always kills a cover
|
|
243
|
+
|
|
244
|
+
- Centered title on white background with a thin horizontal line underneath
|
|
245
|
+
- Gradient from one color to another (reads as PowerPoint, not print design)
|
|
246
|
+
- Drop shadows on text
|
|
247
|
+
- More than one accent color
|
|
248
|
+
- Emoji or icon fonts (fail silently on headless Chromium)
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Inner page rules
|
|
253
|
+
|
|
254
|
+
### What "restraint" means in practice
|
|
255
|
+
|
|
256
|
+
Every design decision should remove something, not add something.
|
|
257
|
+
The page is done when there is nothing left to remove.
|
|
258
|
+
|
|
259
|
+
- Accent color appears on section rules only — not on headings, not on bullets
|
|
260
|
+
- No card components (bordered boxes with colored headers)
|
|
261
|
+
- No rounded corners on anything except callout boxes (4px max)
|
|
262
|
+
- No shadows anywhere
|
|
263
|
+
- Tables: header row in accent, alternating row tint, no grid lines except outer box
|
|
264
|
+
- Callout boxes: left border in accent (4px), very light tint background, no icon
|
|
265
|
+
|
|
266
|
+
### Page header / footer
|
|
267
|
+
|
|
268
|
+
Header: document title (left, 7.5pt, muted) + accent rule (1.5pt, full width below)
|
|
269
|
+
Footer: author name (left, 7.5pt, muted) + page number (right, 7.5pt, muted) + light rule above
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Quality bar
|
|
274
|
+
|
|
275
|
+
A PDF passes if a designer would not be embarrassed to hand it to a client.
|
|
276
|
+
Concretely:
|
|
277
|
+
|
|
278
|
+
- Cover has a clear visual identity that is not "generic AI output"
|
|
279
|
+
- Body text is readable at arm's length without squinting
|
|
280
|
+
- Every page looks like it belongs to the same document
|
|
281
|
+
- No element bleeds off the edge or overlaps another
|
|
282
|
+
- Page numbers are present and correct
|
|
283
|
+
- The accent color appears fewer than 8 times per page on average
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Block type reference
|
|
288
|
+
|
|
289
|
+
All body blocks use the same token system — colors and fonts come from the chosen palette, never ad-hoc.
|
|
290
|
+
|
|
291
|
+
| Block | Rendering | Design notes |
|
|
292
|
+
|---|---|---|
|
|
293
|
+
| `h1` | 22pt heading + full-width accent rule below | KeepTogether with rule — heading never orphaned |
|
|
294
|
+
| `h2` | 15pt heading, dark text | No rule, no accent — visual hierarchy through size only |
|
|
295
|
+
| `h3` | 11.5pt bold, dark text | **No accent color** — accent on body headings violates the one-accent-location rule |
|
|
296
|
+
| `body` | 10.5pt justified, 17pt leading | Supports `<b>` `<i>` `<font>` markup |
|
|
297
|
+
| `bullet` | Body size with `•` prefix, 14pt indent | Use for unordered lists |
|
|
298
|
+
| `numbered` | Body size with `N.` prefix, hanging indent | Counter auto-resets on any non-numbered block — no manual numbering needed |
|
|
299
|
+
| `callout` | Accent left-border (4px) + light tint background | Max one callout per section — overuse kills impact |
|
|
300
|
+
| `table` | Accent header row, alternating row tint, outer box only | Supports `col_widths` (fractions, e.g. `[0.3, 0.5, 0.2]`) for custom column widths |
|
|
301
|
+
| `image` | Scaled to column width, preserving aspect ratio | Use `path` or `src`; always provide a `caption` |
|
|
302
|
+
| `figure` | Same as image, but caption auto-prefixed "Figure N:" | Figure counter increments across all `figure`, `chart`, `flowchart` blocks |
|
|
303
|
+
| `code` | Courier 8.5pt, accent left-border, light tint background | Supports optional `language` label (rendered above block) |
|
|
304
|
+
| `math` | Formula centered, optional right-aligned equation label | LaTeX syntax; matplotlib mathtext renderer |
|
|
305
|
+
| `chart` | Bar / line / pie chart rendered via matplotlib | Color palette derived from document accent; figure auto-numbered |
|
|
306
|
+
| `flowchart` | Process diagram with labeled arrows | Supports 4 node shapes; back-edges drawn as curved arcs |
|
|
307
|
+
| `bibliography` | Numbered reference list with hanging indent | Heading rendered as h2 + accent rule; items as `[N] text` |
|
|
308
|
+
| `divider` | Accent-colored 1.2pt rule with padding | Use sparingly — only for major thematic breaks |
|
|
309
|
+
| `caption` | 8.5pt muted text, centered | Appears below images/tables via field or explicit block |
|
|
310
|
+
| `pagebreak` | Force page break | — |
|
|
311
|
+
| `spacer` | Vertical whitespace | `pt` field (default 12) |
|
|
312
|
+
|
|
313
|
+
### Math formula guidance
|
|
314
|
+
|
|
315
|
+
**Input syntax:** standard LaTeX math notation — `\frac{}{}`, `\int`, `\sum`, `\alpha`, `^`, `_`, etc.
|
|
316
|
+
**Rendering engine:** matplotlib mathtext — pure Python, no LaTeX compiler, no browser required.
|
|
317
|
+
|
|
318
|
+
| Syntax example | Rendered as |
|
|
319
|
+
|---|---|
|
|
320
|
+
| `E = mc^2` | Inline expression |
|
|
321
|
+
| `\frac{\sqrt{\pi}}{2}` | Fraction |
|
|
322
|
+
| `\int_0^\infty e^{-x^2} dx` | Integral |
|
|
323
|
+
| `\sum_{i=1}^{n} x_i` | Summation |
|
|
324
|
+
| `\alpha + \beta = \gamma` | Greek letters |
|
|
325
|
+
|
|
326
|
+
**Limitations:** matplotlib mathtext covers most common expressions but not advanced LaTeX environments (`align`, `cases`, `matrix`). Split complex multi-line proofs into multiple `math` blocks.
|
|
327
|
+
|
|
328
|
+
**Fallback:** if matplotlib is not installed, renders as `expression` in code style.
|
|
329
|
+
|
|
330
|
+
**Equation labels:** `"label": "(1)"` — rendered right-aligned beside the formula.
|
|
331
|
+
|
|
332
|
+
### Chart guidance
|
|
333
|
+
|
|
334
|
+
**Rendered entirely in Python** — no external chart services, image files, or internet required.
|
|
335
|
+
|
|
336
|
+
| chart_type | Use case | Required fields |
|
|
337
|
+
|---|---|---|
|
|
338
|
+
| `bar` | Comparing discrete categories | `labels`, `datasets` |
|
|
339
|
+
| `line` | Trends over time or ordered categories | `labels`, `datasets` |
|
|
340
|
+
| `pie` | Part-to-whole composition | `labels`, `datasets[0].values` |
|
|
341
|
+
|
|
342
|
+
- Colors are derived from the document accent for visual consistency — do not set custom colors.
|
|
343
|
+
- Multi-series: add multiple objects to `datasets`, each with a `label` and `values` array.
|
|
344
|
+
- Figure auto-numbering: set `"figure": true` (default) or `"figure": false` to suppress.
|
|
345
|
+
|
|
346
|
+
### Flowchart guidance
|
|
347
|
+
|
|
348
|
+
**Node shapes:**
|
|
349
|
+
|
|
350
|
+
| shape | Use for |
|
|
351
|
+
|---|---|
|
|
352
|
+
| `rect` (default) | Process step |
|
|
353
|
+
| `diamond` | Decision / condition |
|
|
354
|
+
| `oval` or `terminal` | Start / End |
|
|
355
|
+
| `parallelogram` | Input / Output |
|
|
356
|
+
|
|
357
|
+
- Nodes are placed in input order (top to bottom). This controls the layout.
|
|
358
|
+
- Forward edges draw straight arrows; back-edges (to earlier nodes) draw curved arcs.
|
|
359
|
+
- Keep labels short (3–5 words max) — the diagram is A4-column-width at 78% scale.
|
|
360
|
+
- Figure auto-numbering applies same as chart.
|
|
361
|
+
|
|
362
|
+
### Bibliography guidance
|
|
363
|
+
|
|
364
|
+
- `id` field is the reference label — use numbers ("1", "2") or alphanumeric ("Smith23").
|
|
365
|
+
- Text should be in a consistent citation style (APA, Chicago, etc.) — the renderer does not enforce style.
|
|
366
|
+
- The `title` field defaults to "References". Set `"title": ""` to suppress the heading.
|
|
367
|
+
- A `bibliography` block always starts with a new section heading + accent rule.
|
|
368
|
+
|
|
369
|
+
### Image / figure guidance
|
|
370
|
+
|
|
371
|
+
- Preferred formats: PNG, JPEG
|
|
372
|
+
- Scaled down if wider than the text column; never scaled up
|
|
373
|
+
- `figure` blocks auto-number; `image` blocks do not — use `figure` for numbered figures
|
|
374
|
+
- If the file does not exist at render time, a `[Image not found]` placeholder is substituted
|
|
375
|
+
|
|
376
|
+
### Code block guidance
|
|
377
|
+
|
|
378
|
+
- Preserves whitespace exactly — do not indent code in the JSON value
|
|
379
|
+
- Optional `language` field renders a small language label above the block (e.g., `"language": "python"`)
|
|
380
|
+
- No syntax highlighting (by design) — consistent with restraint principle
|
|
381
|
+
- Keep lines under ~90 characters for A4 column width
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Building Fillable Forms: spec format and workflow
|
|
2
|
+
|
|
3
|
+
The same JSON spec drives both `pdf_form_layout.py` (design lint) and
|
|
4
|
+
`pdf_make_form.py` (AcroForm build). Coordinates are PDF points, origin
|
|
5
|
+
at the bottom-left of the page (1 pt = 1/72 inch; A4 is 595.27 x 841.89,
|
|
6
|
+
letter is 612 x 792).
|
|
7
|
+
|
|
8
|
+
## Spec shape
|
|
9
|
+
|
|
10
|
+
```json
|
|
11
|
+
{
|
|
12
|
+
"title": "Example Intake Form",
|
|
13
|
+
"author": "example-author",
|
|
14
|
+
"page_size": "A4",
|
|
15
|
+
"page_count": 1,
|
|
16
|
+
"fields": [
|
|
17
|
+
{"name": "surname", "type": "text", "page": 1,
|
|
18
|
+
"label": "Surname", "label_box": [72, 700, 150, 714],
|
|
19
|
+
"entry_box": [160, 696, 400, 716],
|
|
20
|
+
"value": "", "tooltip": "Family name"},
|
|
21
|
+
|
|
22
|
+
{"name": "agree", "type": "checkbox", "page": 1,
|
|
23
|
+
"label": "I agree", "label_box": [72, 660, 150, 674],
|
|
24
|
+
"entry_box": [160, 658, 176, 674], "checked": false},
|
|
25
|
+
|
|
26
|
+
{"name": "color", "type": "radio", "page": 1,
|
|
27
|
+
"label": "Color", "label_box": [72, 620, 150, 634],
|
|
28
|
+
"entry_box": [160, 616, 400, 636],
|
|
29
|
+
"options": ["red", "blue"], "value": "blue"},
|
|
30
|
+
|
|
31
|
+
{"name": "size", "type": "dropdown", "page": 1,
|
|
32
|
+
"label": "Size", "label_box": [72, 580, 150, 594],
|
|
33
|
+
"entry_box": [160, 576, 300, 596],
|
|
34
|
+
"options": ["small", "large"], "value": "small"}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- `page_size`: `"A4"`, `"letter"`, or `[width, height]` in points.
|
|
40
|
+
- `page_count`: optional; extended automatically to the highest field page.
|
|
41
|
+
- Boxes are `[x0, y0, x1, y1]` with `x0 < x1`, `y0 < y1`.
|
|
42
|
+
- `label` is drawn as static text near `label_box`; omit it (and
|
|
43
|
+
`label_box`) for unlabeled fields.
|
|
44
|
+
- `radio`: the buttons are laid out left-to-right inside `entry_box`,
|
|
45
|
+
one slot per option, each with a small static caption. `value`
|
|
46
|
+
pre-selects an option by its export name.
|
|
47
|
+
- `dropdown` maps to an AcroForm choice (combo) field.
|
|
48
|
+
|
|
49
|
+
## Field types → what pdf_read.py --fields reports
|
|
50
|
+
|
|
51
|
+
| Spec type | /FT | value format after fill |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| text | /Tx (`text`) | the string |
|
|
54
|
+
| checkbox | /Btn (`button`) | `/Yes` or `/Off` |
|
|
55
|
+
| radio | /Btn (`button`) | `/<export>`, e.g. `/red` |
|
|
56
|
+
| dropdown | /Ch (`choice`) | the option string |
|
|
57
|
+
|
|
58
|
+
When filling with `pdf_fill_form.py`, checkboxes accept `true`/`false`;
|
|
59
|
+
radio values need the leading slash (`"/red"`); dropdown values are the
|
|
60
|
+
plain option string.
|
|
61
|
+
|
|
62
|
+
## Layout lint rules (pdf_form_layout.py)
|
|
63
|
+
|
|
64
|
+
Per field, on its declared page:
|
|
65
|
+
|
|
66
|
+
- boxes must be well-formed and inside the page bounds;
|
|
67
|
+
- entry boxes must be at least 8x8 pt (12 pt tall for text/dropdown);
|
|
68
|
+
- no two entry boxes on the same page may overlap (the second and later
|
|
69
|
+
fields of an overlapping cluster are flagged);
|
|
70
|
+
- a label must sit within 150 pt of its entry box and must not overlap it.
|
|
71
|
+
|
|
72
|
+
Exit code 0 = clean, 1 = at least one problem; the JSON report lists
|
|
73
|
+
per-field `problems`. Lint the spec BEFORE building — fixing numbers in
|
|
74
|
+
JSON is cheaper than debugging a rendered PDF.
|
|
75
|
+
|
|
76
|
+
## Visual review loop
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
python3 scripts/pdf_form_layout.py spec.json --render-overlay overlay.png [--pdf built.pdf]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Red rectangles = entry boxes (with field names), blue = label boxes.
|
|
83
|
+
Without `--pdf` the overlay is drawn on a blank page (PIL-only, always
|
|
84
|
+
works); with `--pdf` the real page is rasterized underneath
|
|
85
|
+
(needs pypdfium2 or pdftoppm — otherwise the report says
|
|
86
|
+
`"rendered": false` with install hints). Feed the PNG to `vision_analyze`
|
|
87
|
+
and ask specifically about collisions, alignment, and stray labels.
|
|
88
|
+
|
|
89
|
+
## Radio-group quirks (reportlab + pypdf)
|
|
90
|
+
|
|
91
|
+
- reportlab requires at least two `radio()` calls per group; a
|
|
92
|
+
single-option radio group produces a broken field.
|
|
93
|
+
- Pre-selecting is done at build time via `"value"`; changing selection
|
|
94
|
+
later via `pdf_fill_form.py` needs the slashed export name (`"/red"`).
|
|
95
|
+
- Some viewers render reportlab radio appearances inconsistently after a
|
|
96
|
+
pypdf fill; verify with `--fields` (data truth) plus a rendered page
|
|
97
|
+
image (visual truth) rather than either alone.
|
|
98
|
+
- Flattening radio groups is the least reliable flatten case — check the
|
|
99
|
+
output image before shipping.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Natural-language PDF text editing with nano-pdf (merged from the nano-pdf skill)
|
|
2
|
+
# nano-pdf
|
|
3
|
+
|
|
4
|
+
Edit PDFs using natural-language instructions. Point it at a page and describe what to change. For structural PDF work (merge, split, forms, watermarks, creation), see the `pdf` skill; for text extraction from scans, see `ocr-and-documents`.
|
|
5
|
+
|
|
6
|
+
## Prerequisites
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
# Install with uv (recommended — already available in Hermes)
|
|
10
|
+
uv pip install nano-pdf
|
|
11
|
+
|
|
12
|
+
# Or with pip
|
|
13
|
+
pip install nano-pdf
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
nano-pdf edit <file.pdf> <page_number> "<instruction>"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Examples
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Change a title on page 1
|
|
26
|
+
nano-pdf edit deck.pdf 1 "Change the title to 'Q3 Results' and fix the typo in the subtitle"
|
|
27
|
+
|
|
28
|
+
# Update a date on a specific page
|
|
29
|
+
nano-pdf edit report.pdf 3 "Update the date from January to February 2026"
|
|
30
|
+
|
|
31
|
+
# Fix content
|
|
32
|
+
nano-pdf edit contract.pdf 2 "Change the client name from 'Acme Corp' to 'Acme Industries'"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Notes
|
|
36
|
+
|
|
37
|
+
- Page numbers may be 0-based or 1-based depending on version — if the edit hits the wrong page, retry with ±1
|
|
38
|
+
- Always verify the output PDF after editing (use `read_file` to check file size, or open it)
|
|
39
|
+
- The tool uses an LLM under the hood — requires an API key (check `nano-pdf --help` for config)
|
|
40
|
+
- Works well for text changes; complex layout modifications may need a different approach
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# OCR & Document Text Extraction (merged from the ocr-and-documents skill)
|
|
2
|
+
|
|
3
|
+
Scripts referenced below live in this skill's scripts/ directory.
|
|
4
|
+
# PDF & Document Extraction
|
|
5
|
+
|
|
6
|
+
For DOCX: see the `docx` skill (create/edit) or use `python-docx` for structured reads.
|
|
7
|
+
For PPTX: see the `powerpoint` skill (full create/read/edit support).
|
|
8
|
+
For PDF manipulation (merge, split, forms, watermarks, creation): see the `pdf` skill.
|
|
9
|
+
This skill covers **text extraction from PDFs and scanned documents**.
|
|
10
|
+
|
|
11
|
+
> **Coming from a `read_file` EXTRACTION COVERAGE WARNING?** `read_file` auto-converts local PDFs but reads the text layer only; the warning footer lists the pages that yielded no text (scanned images). For a handful of pages, render + vision is fastest: `pdftoppm -jpeg -r 150 -f N -l N file.pdf $TMPDIR/page` then `vision_analyze` each image. For bulk OCR of many pages, use marker-pdf below (Step 2).
|
|
12
|
+
|
|
13
|
+
## Step 1: Remote URL Available?
|
|
14
|
+
|
|
15
|
+
If the document has a URL, **always try `web_extract` first**:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
web_extract(urls=["https://arxiv.org/pdf/2402.03300"])
|
|
19
|
+
web_extract(urls=["https://example.com/report.pdf"])
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
This handles PDF-to-markdown conversion via Firecrawl with no local dependencies.
|
|
23
|
+
|
|
24
|
+
Only use local extraction when: the file is local, web_extract fails, or you need batch processing.
|
|
25
|
+
|
|
26
|
+
## Step 2: Choose Local Extractor
|
|
27
|
+
|
|
28
|
+
| Feature | pymupdf (~25MB) | marker-pdf (~3-5GB) |
|
|
29
|
+
|---------|-----------------|---------------------|
|
|
30
|
+
| **Text-based PDF** | ✅ | ✅ |
|
|
31
|
+
| **Scanned PDF (OCR)** | ❌ | ✅ (90+ languages) |
|
|
32
|
+
| **Tables** | ✅ (basic) | ✅ (high accuracy) |
|
|
33
|
+
| **Equations / LaTeX** | ❌ | ✅ |
|
|
34
|
+
| **Code blocks** | ❌ | ✅ |
|
|
35
|
+
| **Forms** | ❌ | ✅ |
|
|
36
|
+
| **Headers/footers removal** | ❌ | ✅ |
|
|
37
|
+
| **Reading order detection** | ❌ | ✅ |
|
|
38
|
+
| **Images extraction** | ✅ (embedded) | ✅ (with context) |
|
|
39
|
+
| **Images → text (OCR)** | ❌ | ✅ |
|
|
40
|
+
| **EPUB** | ✅ | ✅ |
|
|
41
|
+
| **Markdown output** | ✅ (via pymupdf4llm) | ✅ (native, higher quality) |
|
|
42
|
+
| **Install size** | ~25MB | ~3-5GB (PyTorch + models) |
|
|
43
|
+
| **Speed** | Instant | ~1-14s/page (CPU), ~0.2s/page (GPU) |
|
|
44
|
+
|
|
45
|
+
**Decision**: Use pymupdf unless you need OCR, equations, forms, or complex layout analysis.
|
|
46
|
+
|
|
47
|
+
If the user needs marker capabilities but the system lacks ~5GB free disk:
|
|
48
|
+
> "This document needs OCR/advanced extraction (marker-pdf), which requires ~5GB for PyTorch and models. Your system has [X]GB free. Options: free up space, provide a URL so I can use web_extract, or I can try pymupdf which works for text-based PDFs but not scanned documents or equations."
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## pymupdf (lightweight)
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install pymupdf pymupdf4llm
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Via helper script**:
|
|
59
|
+
```bash
|
|
60
|
+
python scripts/extract_pymupdf.py document.pdf # Plain text
|
|
61
|
+
python scripts/extract_pymupdf.py document.pdf --markdown # Markdown
|
|
62
|
+
python scripts/extract_pymupdf.py document.pdf --tables # Tables
|
|
63
|
+
python scripts/extract_pymupdf.py document.pdf --images out/ # Extract images
|
|
64
|
+
python scripts/extract_pymupdf.py document.pdf --metadata # Title, author, pages
|
|
65
|
+
python scripts/extract_pymupdf.py document.pdf --pages 0-4 # Specific pages
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Inline**:
|
|
69
|
+
```bash
|
|
70
|
+
python -c "
|
|
71
|
+
import pymupdf
|
|
72
|
+
doc = pymupdf.open('document.pdf')
|
|
73
|
+
for page in doc:
|
|
74
|
+
print(page.get_text())
|
|
75
|
+
"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## marker-pdf (high-quality OCR)
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# Check disk space first
|
|
84
|
+
python scripts/extract_marker.py --check
|
|
85
|
+
|
|
86
|
+
pip install marker-pdf
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Via helper script**:
|
|
90
|
+
```bash
|
|
91
|
+
python scripts/extract_marker.py document.pdf # Markdown
|
|
92
|
+
python scripts/extract_marker.py document.pdf --json # JSON with metadata
|
|
93
|
+
python scripts/extract_marker.py document.pdf --output_dir out/ # Save images
|
|
94
|
+
python scripts/extract_marker.py scanned.pdf # Scanned PDF (OCR)
|
|
95
|
+
python scripts/extract_marker.py document.pdf --use_llm # LLM-boosted accuracy
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**CLI** (installed with marker-pdf):
|
|
99
|
+
```bash
|
|
100
|
+
marker_single document.pdf --output_dir ./output
|
|
101
|
+
marker /path/to/folder --workers 4 # Batch
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Arxiv Papers
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
# Abstract only (fast)
|
|
110
|
+
web_extract(urls=["https://arxiv.org/abs/2402.03300"])
|
|
111
|
+
|
|
112
|
+
# Full paper
|
|
113
|
+
web_extract(urls=["https://arxiv.org/pdf/2402.03300"])
|
|
114
|
+
|
|
115
|
+
# Search
|
|
116
|
+
web_search(query="arxiv GRPO reinforcement learning 2026")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Split, Merge & Search
|
|
120
|
+
|
|
121
|
+
pymupdf handles these natively — use `execute_code` or inline Python:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
# Split: extract pages 1-5 to a new PDF
|
|
125
|
+
import pymupdf
|
|
126
|
+
doc = pymupdf.open("report.pdf")
|
|
127
|
+
new = pymupdf.open()
|
|
128
|
+
for i in range(5):
|
|
129
|
+
new.insert_pdf(doc, from_page=i, to_page=i)
|
|
130
|
+
new.save("pages_1-5.pdf")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
# Merge multiple PDFs
|
|
135
|
+
import pymupdf
|
|
136
|
+
result = pymupdf.open()
|
|
137
|
+
for path in ["a.pdf", "b.pdf", "c.pdf"]:
|
|
138
|
+
result.insert_pdf(pymupdf.open(path))
|
|
139
|
+
result.save("merged.pdf")
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
# Search for text across all pages
|
|
144
|
+
import pymupdf
|
|
145
|
+
doc = pymupdf.open("report.pdf")
|
|
146
|
+
for i, page in enumerate(doc):
|
|
147
|
+
results = page.search_for("revenue")
|
|
148
|
+
if results:
|
|
149
|
+
print(f"Page {i+1}: {len(results)} match(es)")
|
|
150
|
+
print(page.get_text("text"))
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
No extra dependencies needed — pymupdf covers split, merge, search, and text extraction in one package.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Notes
|
|
158
|
+
|
|
159
|
+
- `web_extract` is always first choice for URLs
|
|
160
|
+
- pymupdf is the safe default — instant, no models, works everywhere
|
|
161
|
+
- marker-pdf is for OCR, scanned docs, equations, complex layouts — install only when needed
|
|
162
|
+
- Both helper scripts accept `--help` for full usage
|
|
163
|
+
- marker-pdf downloads ~2.5GB of models to `~/.cache/huggingface/` on first use
|
|
164
|
+
- For Word docs: `pip install python-docx` (better than OCR — parses actual structure)
|
|
165
|
+
- For PowerPoint: see the `powerpoint` skill (uses python-pptx)
|