@shuji-bonji/pdf-writer-mcp 0.5.0 → 0.6.0
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/CHANGELOG.md +123 -17
- package/README.ja.md +13 -4
- package/README.md +13 -4
- package/dist/constants.d.ts +23 -0
- package/dist/constants.js +36 -0
- package/dist/constants.js.map +1 -1
- package/dist/services/annotation.d.ts +11 -3
- package/dist/services/annotation.js +4 -3
- package/dist/services/annotation.js.map +1 -1
- package/dist/services/attachment.d.ts +52 -0
- package/dist/services/attachment.js +145 -0
- package/dist/services/attachment.js.map +1 -0
- package/dist/services/editor.d.ts +6 -1
- package/dist/services/editor.js +217 -4
- package/dist/services/editor.js.map +1 -1
- package/dist/services/form.d.ts +75 -0
- package/dist/services/form.js +253 -0
- package/dist/services/form.js.map +1 -0
- package/dist/services/output.d.ts +6 -2
- package/dist/services/output.js +6 -2
- package/dist/services/output.js.map +1 -1
- package/dist/services/page-number.d.ts +43 -0
- package/dist/services/page-number.js +79 -0
- package/dist/services/page-number.js.map +1 -0
- package/dist/services/struct-append.d.ts +41 -0
- package/dist/services/struct-append.js +194 -0
- package/dist/services/struct-append.js.map +1 -0
- package/dist/services/watermark.d.ts +52 -0
- package/dist/services/watermark.js +75 -0
- package/dist/services/watermark.js.map +1 -0
- package/dist/tools/definitions.d.ts +240 -0
- package/dist/tools/definitions.js +182 -0
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/handlers.d.ts +6 -1
- package/dist/tools/handlers.js +27 -2
- package/dist/tools/handlers.js.map +1 -1
- package/dist/types/index.d.ts +139 -0
- package/dist/utils/page-spec.js +4 -1
- package/dist/utils/page-spec.js.map +1 -1
- package/dist/utils/validation.d.ts +6 -1
- package/dist/utils/validation.js +130 -2
- package/dist/utils/validation.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,129 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.6.0] - 2026-07-16
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`fill_form`** — fill AcroForm fields: text, checkbox, dropdown, optionlist,
|
|
10
|
+
radio. Values are validated against the field's type and, for choice fields,
|
|
11
|
+
against its options; both errors name what the document actually offers.
|
|
12
|
+
Naming a field that does not exist lists the real field names, which is how a
|
|
13
|
+
caller discovers the form without a separate listing tool.
|
|
14
|
+
|
|
15
|
+
- Values are rendered with an **embedded font**, so Japanese works. pdf-lib
|
|
16
|
+
would otherwise regenerate every appearance with Helvetica on `save()` and
|
|
17
|
+
fail on `WinAnsi cannot encode "山"`; the font goes through the same
|
|
18
|
+
font-manager as the create tools (ADR-7/8).
|
|
19
|
+
- `flatten: true` fills and flattens in one pass.
|
|
20
|
+
- Filling does **not** touch the structure tree, so a tagged PDF keeps whatever
|
|
21
|
+
conformance it came in with.
|
|
22
|
+
|
|
23
|
+
- **`flatten_form`** — flatten an AcroForm so the filled values become static
|
|
24
|
+
content, for fixing values before distribution.
|
|
25
|
+
|
|
26
|
+
- **Refuses tagged PDFs by default.** Flattening removes the Widget
|
|
27
|
+
annotations that the `Form` structure elements point to and bakes their
|
|
28
|
+
appearance in as untagged content. Measured, not assumed: veraPDF reports
|
|
29
|
+
`7.1-3 Content shall be marked as Artifact or tagged as real content`.
|
|
30
|
+
`allowBreakingTags: true` overrides and reports a warning.
|
|
31
|
+
- Drops the now-empty `/AcroForm` and prunes the dangling references that
|
|
32
|
+
pdf-lib's `flatten()` leaves behind in `/Annots` and `/Kids` (poppler
|
|
33
|
+
otherwise reports `Invalid XRef entry`).
|
|
34
|
+
|
|
35
|
+
- **`add_watermark`** — overlay a diagonal watermark ("社外秘" / "DRAFT" /
|
|
36
|
+
"COPY") at the centre of each page.
|
|
37
|
+
|
|
38
|
+
- `text`, `fontSize` (60), `color` (`#808080`), `opacity` (0.15), `angle` (45),
|
|
39
|
+
`behind` (true), `pages`.
|
|
40
|
+
- **Behind the body content by default.** pdf-lib can only append to a content
|
|
41
|
+
stream, so the watermark is drawn and then moved to the front of the
|
|
42
|
+
`/Contents` array; each stream is self-contained in `q`/`Q`, so reordering is
|
|
43
|
+
safe.
|
|
44
|
+
- **Becomes an artifact on tagged PDFs** (PDF/UA-1 7.1-3). Verified: veraPDF
|
|
45
|
+
`ua1` 106/106.
|
|
46
|
+
|
|
47
|
+
- **`stamp_page_numbers`** — stamp page numbers onto an existing PDF.
|
|
48
|
+
|
|
49
|
+
- `format` expands `{n}` and `{total}` (`{n} / {total}`, `- {n} -`, `{n} ページ`).
|
|
50
|
+
- `position` (six corners), `margin`, `fontSize`, `color`, `pages`, `startAt` —
|
|
51
|
+
`pages: "2-"` with `startAt: 1` numbers everything but the cover from 1.
|
|
52
|
+
- **Stamps become artifacts on tagged PDFs** (PDF/UA-1 7.1-3). Page numbers
|
|
53
|
+
carry no meaning for a screen reader, and content that is neither tagged nor
|
|
54
|
+
marked as an artifact breaks conformance. Verified: veraPDF `ua1` 106/106.
|
|
55
|
+
- Rotated pages (`/Rotate`) are compensated for, so "bottom-right" lands where
|
|
56
|
+
the reader sees the bottom right.
|
|
57
|
+
- The first editing tool that needs a font: it goes through the same
|
|
58
|
+
font-manager as the create tools, so harfbuzz subsetting (ADR-7/8) and the
|
|
59
|
+
missing-glyph check apply. Japanese formats need `fontPath` or
|
|
60
|
+
`PDF_WRITER_FONT`.
|
|
61
|
+
|
|
62
|
+
### Changed
|
|
63
|
+
|
|
64
|
+
- **biome adopted** for linting and formatting, matching the rest of the family
|
|
65
|
+
(same `biome.json`, same scripts). `npm run check` now runs in CI and in the
|
|
66
|
+
publish workflow. Existing sources were reformatted; the suite is clean.
|
|
67
|
+
- The version is pinned to an exact `2.5.4` rather than a caret range. Biome's
|
|
68
|
+
formatting output changes between minor releases, so a range lets a local
|
|
69
|
+
`npm install` drift ahead of CI and produce spurious diffs.
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
- `parsePageSpec` reported an open-ended chunk past the end (`"2-"` on a 1-page
|
|
74
|
+
document) as *reversed* rather than *out of range* — the open end collapses to
|
|
75
|
+
`pageCount`, which made `from > to` trigger first.
|
|
76
|
+
- `handleCreateTablePdf` duplicated the Latin-1 check as a regex containing
|
|
77
|
+
control characters; it now reuses the existing `hasNonLatin1` helper.
|
|
78
|
+
- `src/config.ts` imports `node:module` rather than the bare `module` specifier.
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
- **`attach_file`** — embed a file in a PDF (Tier B, first tool). Registers it in
|
|
82
|
+
`/Names /EmbeddedFiles`, references it from the catalog `/AF`, and sets
|
|
83
|
+
`/AFRelationship`. This is the shape PDF/A-3 (ISO 19005-3) requires, and the
|
|
84
|
+
one used to bundle a human-readable invoice with its machine-readable
|
|
85
|
+
counterpart (CSV/XML) in a single file — the 電子帳簿保存法 use case.
|
|
86
|
+
|
|
87
|
+
- `relationship`: `Data` (machine-readable counterpart), `Source` (the data the
|
|
88
|
+
document came from), `Alternative`, `Supplement`, `Unspecified` (default).
|
|
89
|
+
Omitting it produces a warning — PDF/A-3 §6.8 wants a meaningful value, and
|
|
90
|
+
`Unspecified` says nothing about why the file is there.
|
|
91
|
+
- `mimeType` is inferred from the extension when omitted (`.csv` → `text/csv`),
|
|
92
|
+
falling back to `application/octet-stream`.
|
|
93
|
+
- `name` renames the attachment inside the PDF; duplicates are rejected because
|
|
94
|
+
name-tree keys must be unique.
|
|
95
|
+
- Attaching to a tagged PDF leaves it conformant (verified: veraPDF `ua1`,
|
|
96
|
+
106/106).
|
|
97
|
+
|
|
98
|
+
Descriptions and names round-trip in Japanese, and attached bytes come back
|
|
99
|
+
byte-identical.
|
|
100
|
+
|
|
101
|
+
## [0.5.1] - 2026-07-16
|
|
102
|
+
|
|
103
|
+
### Added
|
|
104
|
+
|
|
105
|
+
- **`add_annotation` keeps tagged PDFs conformant.** When the target document is
|
|
106
|
+
tagged, the annotation is now nested in an `Annot` structure element and the
|
|
107
|
+
page gets `/Tabs /S` — PDF/UA-1 7.18.1-1 and 7.18.3-1. Verified: adding an
|
|
108
|
+
annotation to a tagged document still passes veraPDF `--flavour ua1` (106/106).
|
|
109
|
+
|
|
110
|
+
This closes the gap 0.4.0 left behind: `add_annotation` shipped before tagging
|
|
111
|
+
existed, and veraPDF flagged it once 0.5.0 made tagged output possible.
|
|
112
|
+
|
|
113
|
+
- **`alt` option on `add_annotation`** — the alternate text for the `Annot`
|
|
114
|
+
element. Omitting it on a tagged document produces a `warnings` entry rather
|
|
115
|
+
than silently emitting an annotation assistive technology cannot describe.
|
|
116
|
+
|
|
117
|
+
### Notes
|
|
118
|
+
|
|
119
|
+
`services/struct-append.ts` is a new counterpart to `struct-tree.ts`: the latter
|
|
120
|
+
*builds* a tree from scratch (create tools), the former *appends* to an existing
|
|
121
|
+
one (edit tools). Appending means reading `/ParentTreeNextKey`, inserting into
|
|
122
|
+
the number tree while keeping keys ascending, and writing `/StructParent` back
|
|
123
|
+
onto the annotation — the mechanics a future `ensure_tagged` (Tier C) will need.
|
|
124
|
+
|
|
125
|
+
Untagged documents are left untouched: no structure tree is invented just because
|
|
126
|
+
an annotation was added.
|
|
127
|
+
|
|
5
128
|
## [0.5.0] - 2026-07-16
|
|
6
129
|
|
|
7
130
|
### Added
|
|
@@ -47,23 +170,6 @@ All notable changes to this project will be documented in this file.
|
|
|
47
170
|
- XMP metadata is written as UTF-8 bytes. `context.stream(string)` writes one
|
|
48
171
|
byte per character, which mangled Japanese titles.
|
|
49
172
|
|
|
50
|
-
## [Unreleased]
|
|
51
|
-
|
|
52
|
-
### Changed
|
|
53
|
-
|
|
54
|
-
- **biome adopted** for linting and formatting, matching the rest of the family
|
|
55
|
-
(same `biome.json`, same scripts). `npm run check` now runs in CI and in the
|
|
56
|
-
publish workflow. Existing sources were reformatted; the suite is clean.
|
|
57
|
-
- The version is pinned to an exact `2.5.4` rather than a caret range. Biome's
|
|
58
|
-
formatting output changes between minor releases, so a range lets a local
|
|
59
|
-
`npm install` drift ahead of CI and produce spurious diffs.
|
|
60
|
-
|
|
61
|
-
### Fixed
|
|
62
|
-
|
|
63
|
-
- `handleCreateTablePdf` duplicated the Latin-1 check as a regex containing
|
|
64
|
-
control characters; it now reuses the existing `hasNonLatin1` helper.
|
|
65
|
-
- `src/config.ts` imports `node:module` rather than the bare `module` specifier.
|
|
66
|
-
|
|
67
173
|
## [0.4.0] - 2026-07-16
|
|
68
174
|
|
|
69
175
|
### Added
|
package/README.ja.md
CHANGED
|
@@ -48,7 +48,12 @@ PDF/UA はタイトルを要求するため、`tagged: true` では `title` が
|
|
|
48
48
|
| `reorder_pages` | 全ページの順列による並べ替え |
|
|
49
49
|
| `rotate_pages` | 時計回りに回転(90/180/270)。既存の回転に加算 |
|
|
50
50
|
| `add_bookmarks` | しおり(アウトライン)の設定。`children` で入れ子にでき、既存のしおりは置換 |
|
|
51
|
-
| `add_annotation` | 付箋(`text`)/ ハイライト(`highlight`)/ 矩形(`square
|
|
51
|
+
| `add_annotation` | 付箋(`text`)/ ハイライト(`highlight`)/ 矩形(`square`)の注釈を追加。タグ付き PDF では `Annot` 構造要素に内包され PDF/UA 準拠を維持する(`alt` で説明を渡す) |
|
|
52
|
+
| `attach_file` | ファイルを埋め込む(`/Names /EmbeddedFiles` + catalog `/AF` + `/AFRelationship`)。PDF/A-3 が要求する形式で、機械可読データを文書に同梱する |
|
|
53
|
+
| `stamp_page_numbers` | ページ番号を刻む(`{n}` / `{total}`、6 箇所の配置、`pages`、`startAt`)。タグ付き PDF では Artifact になるため準拠を維持する |
|
|
54
|
+
| `fill_form` | AcroForm に値を記入する。日本語は埋め込みフォントで描画。同時にフラット化も可能 |
|
|
55
|
+
| `flatten_form` | フォームを静的な内容に焼き込む。タグ付き PDF は既定で拒否(PDF/UA が壊れるため) |
|
|
56
|
+
| `add_watermark` | 斜めの透かしを重ねる("社外秘" / "DRAFT")。既定で本文の背面。タグ付き PDF では Artifact になる |
|
|
52
57
|
|
|
53
58
|
共通オプション: `outputPath` / `returnBase64` / `allowBreakingSignatures`。
|
|
54
59
|
|
|
@@ -63,7 +68,7 @@ PDF/UA はタイトルを要求するため、`tagged: true` では `title` が
|
|
|
63
68
|
"mcpServers": {
|
|
64
69
|
"pdf-writer": {
|
|
65
70
|
"command": "npx",
|
|
66
|
-
"args": ["-y", "@shuji-bonji/pdf-writer-mcp"],
|
|
71
|
+
"args": ["-y", "@shuji-bonji/pdf-writer-mcp@latest"],
|
|
67
72
|
"env": {
|
|
68
73
|
"PDF_WRITER_FONT": "/absolute/path/to/NotoSansJP-Regular.otf"
|
|
69
74
|
}
|
|
@@ -74,6 +79,8 @@ PDF/UA はタイトルを要求するため、`tagged: true` では `title` が
|
|
|
74
79
|
|
|
75
80
|
`PDF_WRITER_FONT` を設定しておくと、各ツールで `fontPath` を省略しても日本語が出せます。
|
|
76
81
|
|
|
82
|
+
> **`@latest` を付ける(またはバージョンを固定する)ことを推奨します。** バージョン指定なしの `npx -y <pkg>` は、**最初にキャッシュした版を使い続けます**(`-y` はインストール確認を省くだけで、更新確認はしません)。数か月前の版が動き続けることもあります。`@latest` にすると起動のたびにレジストリを確認します。再現性を優先するなら `@0.5.0` のように固定してください。キャッシュを消すには `rm -rf ~/.npm/_npx`。
|
|
83
|
+
|
|
77
84
|
## フォント
|
|
78
85
|
|
|
79
86
|
標準 PDF フォント(Helvetica)は **ASCII のみ** です。日本語など非ラテン文字を描画するには、`fontPath` または `PDF_WRITER_FONT` で埋め込み可能な**単一フェイス**のフォント(`.ttf` / `.otf`)を指定してください。
|
|
@@ -143,8 +150,10 @@ TEST_FONT_PATH=/path/to/NotoSansJP-Regular.otf npm test
|
|
|
143
150
|
- [x] 編集系 Tier A 第1波 — メタデータ・ページ操作(v0.2.0)
|
|
144
151
|
- [x] 編集系 Tier A 第2波 — しおり・注釈(v0.4.0)
|
|
145
152
|
- [x] タグ付き PDF / PDF/UA-1 — veraPDF で準拠を確認(v0.5.0)
|
|
146
|
-
- [
|
|
147
|
-
- [
|
|
153
|
+
- [x] タグ付き出力での注釈の `Annot` タグ内包(v0.5.1)
|
|
154
|
+
- [x] 編集系 Tier B — 添付ファイル(v0.6.0)
|
|
155
|
+
- [ ] 編集系 Tier B — フォーム記入 / フラット化、透かし、ページ番号スタンプ
|
|
156
|
+
- [ ] 画像の代替テキスト(`Figure` + `/Alt`)
|
|
148
157
|
- [ ] `.ttc` からのフェイス自動抽出
|
|
149
158
|
- [ ] 見出し用と本文用のフォント分け(太字フェイス埋め込み)
|
|
150
159
|
- [ ] 画像埋め込み、ヘッダー / フッター
|
package/README.md
CHANGED
|
@@ -48,7 +48,12 @@ PDF/UA mandates a document title, so `tagged: true` requires `title`. `lang` (BC
|
|
|
48
48
|
| `reorder_pages` | Reorder by an explicit permutation of all pages |
|
|
49
49
|
| `rotate_pages` | Rotate clockwise (90/180/270), accumulating over existing rotation |
|
|
50
50
|
| `add_bookmarks` | Set the outline (bookmarks); nestable via `children`, replaces any existing outline |
|
|
51
|
-
| `add_annotation` | Add a sticky note (`text`), `highlight`, or `square` annotation to a page |
|
|
51
|
+
| `add_annotation` | Add a sticky note (`text`), `highlight`, or `square` annotation to a page. On tagged PDFs the annotation is nested in an `Annot` element and stays PDF/UA conformant — pass `alt` to describe it |
|
|
52
|
+
| `attach_file` | Embed a file (`/Names /EmbeddedFiles` + catalog `/AF` + `/AFRelationship`) — the PDF/A-3 shape for bundling machine-readable data with a document |
|
|
53
|
+
| `stamp_page_numbers` | Stamp page numbers (`{n}` / `{total}`, six positions, `pages`, `startAt`). Becomes an artifact on tagged PDFs, so conformance holds |
|
|
54
|
+
| `fill_form` | Fill AcroForm fields. Japanese values via an embedded font; can flatten in the same pass |
|
|
55
|
+
| `flatten_form` | Flatten a form into static content. Refuses tagged PDFs by default (breaks PDF/UA) |
|
|
56
|
+
| `add_watermark` | Overlay a diagonal watermark ("社外秘" / "DRAFT"). Behind the body content by default; artifact on tagged PDFs |
|
|
52
57
|
|
|
53
58
|
Shared options: `outputPath`, `returnBase64`, `allowBreakingSignatures`.
|
|
54
59
|
|
|
@@ -63,7 +68,7 @@ Page specs use `"1,3-5,8-"` (1-based; `-3` means up to page 3, `8-` means page 8
|
|
|
63
68
|
"mcpServers": {
|
|
64
69
|
"pdf-writer": {
|
|
65
70
|
"command": "npx",
|
|
66
|
-
"args": ["-y", "@shuji-bonji/pdf-writer-mcp"],
|
|
71
|
+
"args": ["-y", "@shuji-bonji/pdf-writer-mcp@latest"],
|
|
67
72
|
"env": {
|
|
68
73
|
"PDF_WRITER_FONT": "/absolute/path/to/NotoSansJP-Regular.otf"
|
|
69
74
|
}
|
|
@@ -74,6 +79,8 @@ Page specs use `"1,3-5,8-"` (1-based; `-3` means up to page 3, `8-` means page 8
|
|
|
74
79
|
|
|
75
80
|
`PDF_WRITER_FONT` lets every tool omit `fontPath` and still render CJK text.
|
|
76
81
|
|
|
82
|
+
> **Use `@latest` (or pin a version).** `npx -y <pkg>` without a version keeps running whatever it cached the first time — `-y` only skips the install prompt, it does not check for updates. A bare specifier will happily run a months-old release. `@latest` makes npx check the registry on each start; pin `@0.5.0` instead if you want reproducibility. To clear a stale cache: `rm -rf ~/.npm/_npx`.
|
|
83
|
+
|
|
77
84
|
## Fonts
|
|
78
85
|
|
|
79
86
|
The standard PDF font (Helvetica) covers **ASCII only**. To render Japanese or any non-Latin text, point `fontPath` or `PDF_WRITER_FONT` at an embeddable **single-face** font (`.ttf` / `.otf`).
|
|
@@ -143,8 +150,10 @@ TEST_FONT_PATH=/path/to/NotoSansJP-Regular.otf npm test
|
|
|
143
150
|
- [x] Editing Tier A wave 1 — metadata and page operations (v0.2.0)
|
|
144
151
|
- [x] Editing Tier A wave 2 — bookmarks and annotations (v0.4.0)
|
|
145
152
|
- [x] Tagged PDF / PDF/UA-1 — verified by veraPDF (v0.5.0)
|
|
146
|
-
- [
|
|
147
|
-
- [
|
|
153
|
+
- [x] Annotations nested in `Annot` tags on tagged output (v0.5.1)
|
|
154
|
+
- [x] Editing Tier B — file attachments (v0.6.0)
|
|
155
|
+
- [ ] Editing Tier B — form filling/flattening, watermarks, page-number stamping
|
|
156
|
+
- [ ] Images with alt text (`Figure` + `/Alt`)
|
|
148
157
|
- [ ] Automatic face extraction from `.ttc`
|
|
149
158
|
- [ ] Separate faces for headings and body (bold face embedding)
|
|
150
159
|
- [ ] Image embedding, headers/footers
|
package/dist/constants.d.ts
CHANGED
|
@@ -36,7 +36,30 @@ export declare const LIMITS: {
|
|
|
36
36
|
/** しおりの最大総数・最大ネスト深さ */
|
|
37
37
|
readonly BOOKMARK_MAX_TOTAL: 2000;
|
|
38
38
|
readonly BOOKMARK_MAX_DEPTH: 8;
|
|
39
|
+
/** 埋め込みファイルの最大サイズ(バイト)。PDF 全体がメモリに載るため上限を設ける */
|
|
40
|
+
readonly ATTACHMENT_MAX_BYTES: number;
|
|
39
41
|
};
|
|
42
|
+
/** stamp_page_numbers の配置 */
|
|
43
|
+
export declare const STAMP_POSITIONS: readonly ['bottom-left', 'bottom-center', 'bottom-right', 'top-left', 'top-center', 'top-right'];
|
|
44
|
+
/** stamp_page_numbers の既定値 */
|
|
45
|
+
export declare const STAMP_DEFAULTS: {
|
|
46
|
+
readonly format: '{n}';
|
|
47
|
+
readonly position: 'bottom-center';
|
|
48
|
+
readonly margin: 24;
|
|
49
|
+
readonly fontSize: 9;
|
|
50
|
+
readonly color: '#666666';
|
|
51
|
+
readonly startAt: 1;
|
|
52
|
+
};
|
|
53
|
+
/** add_watermark の既定値 */
|
|
54
|
+
export declare const WATERMARK_DEFAULTS: {
|
|
55
|
+
readonly fontSize: 60;
|
|
56
|
+
readonly color: '#808080';
|
|
57
|
+
readonly opacity: 0.15;
|
|
58
|
+
readonly angle: 45;
|
|
59
|
+
readonly behind: true;
|
|
60
|
+
};
|
|
61
|
+
/** attach_file が受け付ける AFRelationship(PDF/A-3 §6.8) */
|
|
62
|
+
export declare const ATTACHMENT_RELATIONSHIPS: readonly ['Source', 'Data', 'Alternative', 'Supplement', 'Unspecified'];
|
|
40
63
|
/** add_annotation が受け付ける注釈種別 */
|
|
41
64
|
export declare const ANNOTATION_TYPES: readonly ['text', 'highlight', 'square'];
|
|
42
65
|
/** text 注釈のアイコン名(ISO 32000-1 Table 172 の一般的な値) */
|
package/dist/constants.js
CHANGED
|
@@ -35,7 +35,43 @@ export const LIMITS = {
|
|
|
35
35
|
/** しおりの最大総数・最大ネスト深さ */
|
|
36
36
|
BOOKMARK_MAX_TOTAL: 2_000,
|
|
37
37
|
BOOKMARK_MAX_DEPTH: 8,
|
|
38
|
+
/** 埋め込みファイルの最大サイズ(バイト)。PDF 全体がメモリに載るため上限を設ける */
|
|
39
|
+
ATTACHMENT_MAX_BYTES: 100 * 1024 * 1024,
|
|
38
40
|
};
|
|
41
|
+
/** stamp_page_numbers の配置 */
|
|
42
|
+
export const STAMP_POSITIONS = [
|
|
43
|
+
'bottom-left',
|
|
44
|
+
'bottom-center',
|
|
45
|
+
'bottom-right',
|
|
46
|
+
'top-left',
|
|
47
|
+
'top-center',
|
|
48
|
+
'top-right',
|
|
49
|
+
];
|
|
50
|
+
/** stamp_page_numbers の既定値 */
|
|
51
|
+
export const STAMP_DEFAULTS = {
|
|
52
|
+
format: '{n}',
|
|
53
|
+
position: 'bottom-center',
|
|
54
|
+
margin: 24,
|
|
55
|
+
fontSize: 9,
|
|
56
|
+
color: '#666666',
|
|
57
|
+
startAt: 1,
|
|
58
|
+
};
|
|
59
|
+
/** add_watermark の既定値 */
|
|
60
|
+
export const WATERMARK_DEFAULTS = {
|
|
61
|
+
fontSize: 60,
|
|
62
|
+
color: '#808080',
|
|
63
|
+
opacity: 0.15,
|
|
64
|
+
angle: 45,
|
|
65
|
+
behind: true,
|
|
66
|
+
};
|
|
67
|
+
/** attach_file が受け付ける AFRelationship(PDF/A-3 §6.8) */
|
|
68
|
+
export const ATTACHMENT_RELATIONSHIPS = [
|
|
69
|
+
'Source',
|
|
70
|
+
'Data',
|
|
71
|
+
'Alternative',
|
|
72
|
+
'Supplement',
|
|
73
|
+
'Unspecified',
|
|
74
|
+
];
|
|
39
75
|
/** add_annotation が受け付ける注釈種別 */
|
|
40
76
|
export const ANNOTATION_TYPES = ['text', 'highlight', 'square'];
|
|
41
77
|
/** text 注釈のアイコン名(ISO 32000-1 Table 172 の一般的な値) */
|
package/dist/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;IACpB,EAAE,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;IACrB,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;IAClB,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC;CACV,CAAC;AAIX;;GAEG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,qBAAqB;IACrB,aAAa,EAAE,CAAC;IAChB,aAAa,EAAE,EAAE;IACjB,sCAAsC;IACtC,UAAU,EAAE,CAAC;IACb,UAAU,EAAE,GAAG;IACf,6CAA6C;IAC7C,eAAe,EAAE,OAAO;IACxB,+BAA+B;IAC/B,cAAc,EAAE,EAAE;IAClB,cAAc,EAAE,KAAK;IACrB,4BAA4B;IAC5B,gBAAgB,EAAE,EAAE;IACpB,wBAAwB;IACxB,eAAe,EAAE,GAAG;IACpB,uBAAuB;IACvB,kBAAkB,EAAE,KAAK;IACzB,kBAAkB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;IACpB,EAAE,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;IACrB,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;IAClB,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC;CACV,CAAC;AAIX;;GAEG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,qBAAqB;IACrB,aAAa,EAAE,CAAC;IAChB,aAAa,EAAE,EAAE;IACjB,sCAAsC;IACtC,UAAU,EAAE,CAAC;IACb,UAAU,EAAE,GAAG;IACf,6CAA6C;IAC7C,eAAe,EAAE,OAAO;IACxB,+BAA+B;IAC/B,cAAc,EAAE,EAAE;IAClB,cAAc,EAAE,KAAK;IACrB,4BAA4B;IAC5B,gBAAgB,EAAE,EAAE;IACpB,wBAAwB;IACxB,eAAe,EAAE,GAAG;IACpB,uBAAuB;IACvB,kBAAkB,EAAE,KAAK;IACzB,kBAAkB,EAAE,CAAC;IACrB,gDAAgD;IAChD,oBAAoB,EAAE,GAAG,GAAG,IAAI,GAAG,IAAI;CAC/B,CAAC;AAEX,6BAA6B;AAC7B,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,aAAa;IACb,eAAe;IACf,cAAc;IACd,UAAU;IACV,YAAY;IACZ,WAAW;CACH,CAAC;AAEX,8BAA8B;AAC9B,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,MAAM,EAAE,KAAK;IACb,QAAQ,EAAE,eAAe;IACzB,MAAM,EAAE,EAAE;IACV,QAAQ,EAAE,CAAC;IACX,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,CAAC;CACF,CAAC;AAEX,yBAAyB;AACzB,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,QAAQ,EAAE,EAAE;IACZ,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,IAAI;IACb,KAAK,EAAE,EAAE;IACT,MAAM,EAAE,IAAI;CACJ,CAAC;AAEX,sDAAsD;AACtD,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,QAAQ;IACR,MAAM;IACN,aAAa;IACb,YAAY;IACZ,aAAa;CACL,CAAC;AAEX,gCAAgC;AAChC,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAU,CAAC;AAEzE,kDAAkD;AAClD,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,MAAM;IACN,SAAS;IACT,KAAK;IACL,MAAM;IACN,cAAc;IACd,WAAW;IACX,QAAQ;CACA,CAAC;AAEX,qCAAqC;AACrC,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAU,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,eAAe,CAAC;AAExD;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,mCAAmC;IACnC,GAAG,EAAE,MAAM;CACH,CAAC"}
|
|
@@ -9,9 +9,17 @@
|
|
|
9
9
|
*
|
|
10
10
|
* 座標系は PDF 準拠(左下原点・pt)。生成系レンダラの top 基準とは異なる点に注意。
|
|
11
11
|
*/
|
|
12
|
-
import { type PDFDocument, type RGB } from 'pdf-lib';
|
|
12
|
+
import { type PDFDocument, type PDFPage, type PDFRef, type RGB } from 'pdf-lib';
|
|
13
13
|
import type { AddAnnotationArgs } from '../types/index.js';
|
|
14
14
|
/** #rrggbb / #rgb を pdf-lib の RGB へ */
|
|
15
15
|
export declare function parseHexColor(hex: string): RGB;
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
export interface AddedAnnotation {
|
|
17
|
+
/** 追加後のそのページの注釈数 */
|
|
18
|
+
count: number;
|
|
19
|
+
/** 追加した注釈への参照(構造木へ結び付けるのに使う) */
|
|
20
|
+
ref: PDFRef;
|
|
21
|
+
/** 対象ページ */
|
|
22
|
+
page: PDFPage;
|
|
23
|
+
}
|
|
24
|
+
/** 注釈をページに追加する */
|
|
25
|
+
export declare function addAnnotation(doc: PDFDocument, args: AddAnnotationArgs): AddedAnnotation;
|
|
@@ -25,7 +25,7 @@ export function parseHexColor(hex) {
|
|
|
25
25
|
function colorArray(doc, color) {
|
|
26
26
|
return doc.context.obj([color.red, color.green, color.blue]);
|
|
27
27
|
}
|
|
28
|
-
/**
|
|
28
|
+
/** 注釈をページに追加する */
|
|
29
29
|
export function addAnnotation(doc, args) {
|
|
30
30
|
const pages = doc.getPages();
|
|
31
31
|
const pageIndex = args.page - 1;
|
|
@@ -74,8 +74,9 @@ export function addAnnotation(doc, args) {
|
|
|
74
74
|
annots = context.obj([]);
|
|
75
75
|
page.node.set(PDFName.of('Annots'), annots);
|
|
76
76
|
}
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
const ref = context.register(dict);
|
|
78
|
+
annots.push(ref);
|
|
79
|
+
return { count: annots.size(), ref, page };
|
|
79
80
|
}
|
|
80
81
|
function defaultColor(type) {
|
|
81
82
|
switch (type) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"annotation.js","sourceRoot":"","sources":["../../src/services/annotation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,QAAQ,EAGR,YAAY,EACZ,OAAO,EACP,SAAS,
|
|
1
|
+
{"version":3,"file":"annotation.js","sourceRoot":"","sources":["../../src/services/annotation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedded files (attachments)
|
|
3
|
+
*
|
|
4
|
+
* PDF に別ファイルを埋め込む(ISO 32000-1 §7.11.4 / §14.13)。
|
|
5
|
+
* pdf-lib の `doc.attach()` が /Names /EmbeddedFiles・catalog /AF・/UF・/Params まで
|
|
6
|
+
* 書いてくれるため、本モジュールは
|
|
7
|
+
* - 入力の検証(存在・サイズ・重複名)
|
|
8
|
+
* - MIME 型の推定
|
|
9
|
+
* - AFRelationship の既定値と用途の説明
|
|
10
|
+
* - 既存の添付一覧の読み取り
|
|
11
|
+
* を担う。
|
|
12
|
+
*
|
|
13
|
+
* PDF/A-3(ISO 19005-3)と電子帳簿保存法の文脈:
|
|
14
|
+
* PDF/A-3 は任意形式のファイルを埋め込める唯一の PDF/A 部で、
|
|
15
|
+
* 「人が読む請求書(PDF)+機械可読データ(CSV/XML)」を 1 ファイルに束ねる用途に使う。
|
|
16
|
+
* その際 /AFRelationship は必須で、機械可読データなら Data、
|
|
17
|
+
* 元データなら Source を指定する(§6.8)。
|
|
18
|
+
*/
|
|
19
|
+
import { type PDFDocument } from 'pdf-lib';
|
|
20
|
+
import type { AttachmentRelationship } from '../types/index.js';
|
|
21
|
+
export declare function guessMimeType(fileName: string): string;
|
|
22
|
+
export interface EmbeddedFileInfo {
|
|
23
|
+
name: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
relationship?: string;
|
|
26
|
+
mimeType?: string;
|
|
27
|
+
}
|
|
28
|
+
/** 既に埋め込まれているファイルの一覧を返す */
|
|
29
|
+
export declare function listEmbeddedFiles(doc: PDFDocument): EmbeddedFileInfo[];
|
|
30
|
+
export interface AttachFileOptions {
|
|
31
|
+
/** 埋め込むファイルのパス */
|
|
32
|
+
filePath: string;
|
|
33
|
+
/** PDF 内での表示名。省略時は元のファイル名 */
|
|
34
|
+
name?: string;
|
|
35
|
+
/** 説明(/Desc) */
|
|
36
|
+
description?: string;
|
|
37
|
+
/** MIME 型。省略時は拡張子から推定 */
|
|
38
|
+
mimeType?: string;
|
|
39
|
+
/** PDF/A-3 §6.8 の関係。既定 Unspecified */
|
|
40
|
+
relationship?: AttachmentRelationship;
|
|
41
|
+
}
|
|
42
|
+
export interface AttachedFile {
|
|
43
|
+
name: string;
|
|
44
|
+
bytes: number;
|
|
45
|
+
mimeType: string;
|
|
46
|
+
relationship: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* ファイルを PDF に埋め込む。
|
|
50
|
+
* 同名の添付が既にある場合はエラー(名前ツリーのキーは一意であるべき)。
|
|
51
|
+
*/
|
|
52
|
+
export declare function attachFile(doc: PDFDocument, options: AttachFileOptions): Promise<AttachedFile>;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedded files (attachments)
|
|
3
|
+
*
|
|
4
|
+
* PDF に別ファイルを埋め込む(ISO 32000-1 §7.11.4 / §14.13)。
|
|
5
|
+
* pdf-lib の `doc.attach()` が /Names /EmbeddedFiles・catalog /AF・/UF・/Params まで
|
|
6
|
+
* 書いてくれるため、本モジュールは
|
|
7
|
+
* - 入力の検証(存在・サイズ・重複名)
|
|
8
|
+
* - MIME 型の推定
|
|
9
|
+
* - AFRelationship の既定値と用途の説明
|
|
10
|
+
* - 既存の添付一覧の読み取り
|
|
11
|
+
* を担う。
|
|
12
|
+
*
|
|
13
|
+
* PDF/A-3(ISO 19005-3)と電子帳簿保存法の文脈:
|
|
14
|
+
* PDF/A-3 は任意形式のファイルを埋め込める唯一の PDF/A 部で、
|
|
15
|
+
* 「人が読む請求書(PDF)+機械可読データ(CSV/XML)」を 1 ファイルに束ねる用途に使う。
|
|
16
|
+
* その際 /AFRelationship は必須で、機械可読データなら Data、
|
|
17
|
+
* 元データなら Source を指定する(§6.8)。
|
|
18
|
+
*/
|
|
19
|
+
import { readFile } from 'node:fs/promises';
|
|
20
|
+
import { basename, extname, resolve } from 'node:path';
|
|
21
|
+
import { AFRelationship, PDFArray, PDFDict, PDFName } from 'pdf-lib';
|
|
22
|
+
import { LIMITS } from '../constants.js';
|
|
23
|
+
/** 拡張子 → MIME 型(電帳法・請求まわりで実際に使うものに絞る) */
|
|
24
|
+
const MIME_TYPES = {
|
|
25
|
+
'.csv': 'text/csv',
|
|
26
|
+
'.xml': 'application/xml',
|
|
27
|
+
'.json': 'application/json',
|
|
28
|
+
'.txt': 'text/plain',
|
|
29
|
+
'.pdf': 'application/pdf',
|
|
30
|
+
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
31
|
+
'.xls': 'application/vnd.ms-excel',
|
|
32
|
+
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
33
|
+
'.doc': 'application/msword',
|
|
34
|
+
'.zip': 'application/zip',
|
|
35
|
+
'.png': 'image/png',
|
|
36
|
+
'.jpg': 'image/jpeg',
|
|
37
|
+
'.jpeg': 'image/jpeg',
|
|
38
|
+
'.gif': 'image/gif',
|
|
39
|
+
'.html': 'text/html',
|
|
40
|
+
};
|
|
41
|
+
const DEFAULT_MIME = 'application/octet-stream';
|
|
42
|
+
export function guessMimeType(fileName) {
|
|
43
|
+
return MIME_TYPES[extname(fileName).toLowerCase()] ?? DEFAULT_MIME;
|
|
44
|
+
}
|
|
45
|
+
/** pdf-lib の AFRelationship へ変換する */
|
|
46
|
+
function toAFRelationship(rel) {
|
|
47
|
+
switch (rel) {
|
|
48
|
+
case 'Source':
|
|
49
|
+
return AFRelationship.Source;
|
|
50
|
+
case 'Data':
|
|
51
|
+
return AFRelationship.Data;
|
|
52
|
+
case 'Alternative':
|
|
53
|
+
return AFRelationship.Alternative;
|
|
54
|
+
case 'Supplement':
|
|
55
|
+
return AFRelationship.Supplement;
|
|
56
|
+
default:
|
|
57
|
+
return AFRelationship.Unspecified;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** 既に埋め込まれているファイルの一覧を返す */
|
|
61
|
+
export function listEmbeddedFiles(doc) {
|
|
62
|
+
const names = doc.catalog.lookup(PDFName.of('Names'));
|
|
63
|
+
if (!(names instanceof PDFDict))
|
|
64
|
+
return [];
|
|
65
|
+
const ef = names.lookup(PDFName.of('EmbeddedFiles'));
|
|
66
|
+
if (!(ef instanceof PDFDict))
|
|
67
|
+
return [];
|
|
68
|
+
const arr = ef.lookup(PDFName.of('Names'));
|
|
69
|
+
if (!(arr instanceof PDFArray))
|
|
70
|
+
return [];
|
|
71
|
+
const out = [];
|
|
72
|
+
// 名前ツリーの /Names は [name1, spec1, name2, spec2, ...]
|
|
73
|
+
for (let i = 0; i + 1 < arr.size(); i += 2) {
|
|
74
|
+
const nameObj = arr.lookup(i);
|
|
75
|
+
const spec = arr.lookup(i + 1);
|
|
76
|
+
const name = decodeName(nameObj);
|
|
77
|
+
if (name === null)
|
|
78
|
+
continue;
|
|
79
|
+
const info = { name };
|
|
80
|
+
if (spec instanceof PDFDict) {
|
|
81
|
+
const desc = spec.lookup(PDFName.of('Desc'));
|
|
82
|
+
const decoded = decodeName(desc);
|
|
83
|
+
if (decoded)
|
|
84
|
+
info.description = decoded;
|
|
85
|
+
const rel = spec.lookup(PDFName.of('AFRelationship'));
|
|
86
|
+
if (rel instanceof PDFName)
|
|
87
|
+
info.relationship = rel.decodeText();
|
|
88
|
+
const efDict = spec.lookup(PDFName.of('EF'));
|
|
89
|
+
if (efDict instanceof PDFDict) {
|
|
90
|
+
const stream = efDict.get(PDFName.of('F'));
|
|
91
|
+
const resolved = stream ? doc.context.lookup(stream) : undefined;
|
|
92
|
+
const subtype = resolved && 'dict' in resolved
|
|
93
|
+
? resolved.dict.get(PDFName.of('Subtype'))
|
|
94
|
+
: undefined;
|
|
95
|
+
if (subtype instanceof PDFName)
|
|
96
|
+
info.mimeType = subtype.decodeText();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
out.push(info);
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
/** PDFString / PDFHexString のいずれでもテキストとして読む */
|
|
104
|
+
function decodeName(value) {
|
|
105
|
+
if (value && typeof value === 'object' && 'decodeText' in value) {
|
|
106
|
+
return value.decodeText();
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* ファイルを PDF に埋め込む。
|
|
112
|
+
* 同名の添付が既にある場合はエラー(名前ツリーのキーは一意であるべき)。
|
|
113
|
+
*/
|
|
114
|
+
export async function attachFile(doc, options) {
|
|
115
|
+
const abs = resolve(options.filePath);
|
|
116
|
+
let bytes;
|
|
117
|
+
try {
|
|
118
|
+
bytes = await readFile(abs);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
throw new Error(`Cannot read the file to attach: ${abs}`);
|
|
122
|
+
}
|
|
123
|
+
if (bytes.length > LIMITS.ATTACHMENT_MAX_BYTES) {
|
|
124
|
+
throw new Error(`Attachment is too large (${bytes.length} bytes, max ${LIMITS.ATTACHMENT_MAX_BYTES})`);
|
|
125
|
+
}
|
|
126
|
+
const name = options.name ?? basename(abs);
|
|
127
|
+
if (name.trim() === '') {
|
|
128
|
+
throw new Error('name must not be empty');
|
|
129
|
+
}
|
|
130
|
+
const existing = listEmbeddedFiles(doc);
|
|
131
|
+
if (existing.some((f) => f.name === name)) {
|
|
132
|
+
throw new Error(`An attachment named "${name}" already exists in this PDF. Pass a different "name".`);
|
|
133
|
+
}
|
|
134
|
+
const mimeType = options.mimeType ?? guessMimeType(name);
|
|
135
|
+
const relationship = options.relationship ?? 'Unspecified';
|
|
136
|
+
const stat = { creationDate: new Date(), modificationDate: new Date() };
|
|
137
|
+
await doc.attach(bytes, name, {
|
|
138
|
+
mimeType,
|
|
139
|
+
description: options.description,
|
|
140
|
+
afRelationship: toAFRelationship(relationship),
|
|
141
|
+
...stat,
|
|
142
|
+
});
|
|
143
|
+
return { name, bytes: bytes.length, mimeType, relationship };
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=attachment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachment.js","sourceRoot":"","sources":["../../src/services/attachment.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* allowBreakingSignatures: true があるときのみ続行する。
|
|
13
13
|
* 署名を保持する増分更新(incremental_save)は Tier C の課題。
|
|
14
14
|
*/
|
|
15
|
-
import type { AddAnnotationArgs, AddBookmarksArgs, CommonEditOptions, EditResult, SetMetadataArgs, SplitResult } from '../types/index.js';
|
|
15
|
+
import type { AddAnnotationArgs, AddBookmarksArgs, AddWatermarkArgs, AttachFileArgs, AttachResult, CommonEditOptions, EditResult, FillFormArgs, FlattenFormArgs, FormResult, SetMetadataArgs, SplitResult, StampPageNumbersArgs, StampResult, WatermarkResult } from '../types/index.js';
|
|
16
16
|
/** 入力バイト列に電子署名(/ByteRange)が含まれるかの軽量検査 */
|
|
17
17
|
export declare function containsSignature(bytes: Uint8Array): boolean;
|
|
18
18
|
export declare function setMetadata(args: SetMetadataArgs): Promise<EditResult>;
|
|
@@ -23,4 +23,9 @@ export declare function reorderPages(inputPath: string, order: number[], opts: C
|
|
|
23
23
|
export declare function rotatePages(inputPath: string, rotation: number, pages: string | undefined, opts: CommonEditOptions): Promise<EditResult>;
|
|
24
24
|
export declare function addBookmarks(args: AddBookmarksArgs): Promise<EditResult>;
|
|
25
25
|
export declare function addAnnotation(args: AddAnnotationArgs): Promise<EditResult>;
|
|
26
|
+
export declare function attachFileToPdf(args: AttachFileArgs): Promise<AttachResult>;
|
|
27
|
+
export declare function stampPageNumbers(args: StampPageNumbersArgs): Promise<StampResult>;
|
|
28
|
+
export declare function addWatermark(args: AddWatermarkArgs): Promise<WatermarkResult>;
|
|
29
|
+
export declare function fillForm(args: FillFormArgs): Promise<FormResult>;
|
|
30
|
+
export declare function flattenForm(args: FlattenFormArgs): Promise<FormResult>;
|
|
26
31
|
export declare function splitPdf(inputPath: string, ranges: string[], outputDir: string, prefix: string | undefined, opts: CommonEditOptions): Promise<SplitResult>;
|