feat(playground): improve jsx authoring starters

This commit is contained in:
hand-dot
2026-05-11 14:37:59 +09:00
parent 27c8d21227
commit 71a705db0f
20 changed files with 628 additions and 170 deletions

12
PLAN.md
View File

@@ -1,6 +1,6 @@
# JSX / md2pdf ロードマップ
最終更新: 2026-05-09
最終更新: 2026-05-11
## 目的
@@ -44,6 +44,13 @@ rich content の応用範囲を磨く。
- `Document` root、margin-aware な `Header` / `Footer`、page 全体座標の `Static` は入った。Templates starter / docs
を通じて repeated layer、margin area、`Absolute` origin が直感とズレないかを確認し、必要なら wording やサンプルを直す。
- `Header` / `Footer` の実用パターンとして、page number、repeated title、watermark / stamp などを starter に残す。
- React に慣れた人と AI authoring を主な利用者として意識する。`Text` / MVT は JSX render 時に高さを測れるため、
starter / docs では「通常は `height` を書かない」体験に寄せる。固定 field、画像、absolute overlay など
明示寸法が意味を持つ場所だけ `height` を書く。
- POC playground (`react-pdfme-jsx`) の実用サンプルを参考に、invoice、form、report、research paper、Japanese business
document、header/footer/page number、watermark / badge などの starter を増やす。ただし POC 専用 API
(`autoHeight`, `staticHeader`, `For` など) はそのまま持ち込まず、現行の `Document` / `Header` / `Footer` /
通常の JS 配列処理へ寄せる。
- 編集の正は generated `Template + inputs`。JSX source は初期生成 / regeneration 用の metadata として扱う。
Designer で編集した template を JSX に自動逆変換することは当面目指さない。
- JSX playground は source authoring と Viewer preview に寄せる。Form 入力確認は生成方法に依存しない共通 project 導線として
@@ -63,7 +70,8 @@ rich content の応用範囲を磨く。
- Form 入力や dynamic layout reflow で `Text` / MVT が runtime に `overflow: "expand"` した場合、
JSX の親 `Box` / container は自動では広がらない。生成後の `Box` は rectangle schema であり、
子 schema の実描画高さと親 container を結び直す contract がまだないため。親子 container dynamic
layout は将来の設計課題として扱う。
layout は実装したい重要課題として扱う。まずは JSX から生成された visual `Box` と子 text/MVT の関係を
metadata として template に残すか、単一 text/MVT 子の decoration に畳むかを比較する。
- `Absolute``Page`, top `Static`, `Box` 内の小さな escape hatch として扱う。`Stack` / `Row`
直下対応、anchor / top-right / bottom-right shorthand、z-index 的な描画順制御は必要性が出てから検討する。

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -359,6 +359,23 @@ describe('@pdfme/jsx renderToTemplate', () => {
);
});
it('advances Stack children by auto-measured Text height', async () => {
const result = await renderToTemplate(
<Page margin={0}>
<Stack gap={2}>
<Text width={24} lineHeight={1.2}>
This text wraps and does not specify height.
</Text>
<Text>After measured text</Text>
</Stack>
</Page>,
);
const [measured, after] = result.template.schemas[0] ?? [];
expect(measured?.height).toBeGreaterThan(0);
expect(after?.position.y).toBeCloseTo((measured?.height ?? 0) + 2);
});
it('distributes Row width across flex children', async () => {
const result = await renderToTemplate(
<Page size={{ width: 100, height: 100 }} margin={0}>
@@ -555,6 +572,31 @@ describe('@pdfme/jsx renderToTemplate', () => {
});
});
it('grows Box background around auto-measured child content', async () => {
const result = await renderToTemplate(
<Page margin={0}>
<Stack>
<Box background="#eeeeee" padding={{ x: 3, y: 2 }}>
<Text width={24} lineHeight={1.2}>
Auto measured text inside a visual Box.
</Text>
</Box>
<Text>After box</Text>
</Stack>
</Page>,
);
const [box, text, after] = result.template.schemas[0] ?? [];
expect(text?.height).toBeGreaterThan(0);
expect(box).toMatchObject({
type: 'rectangle',
color: '#eeeeee',
position: { x: 0, y: 0 },
height: (text?.height ?? 0) + 4,
});
expect(after?.position.y).toBeCloseTo(box?.height ?? 0);
});
it('does not render a rectangle schema for a Box without visual styles', async () => {
const result = await renderToTemplate(
<Page margin={0}>

View File

@@ -18,7 +18,7 @@
"y": 0
},
"width": 174,
"height": 7,
"height": 3.273984,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -43,7 +43,7 @@
"y": 279
},
"width": 174,
"height": 6,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -72,7 +72,7 @@
"y": 18
},
"width": 174,
"height": 12,
"height": 9.003456,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -91,13 +91,13 @@
{
"name": "text_4",
"type": "text",
"content": "Switch the preview to Form and edit the fields directly.",
"content": "Open this generated template in Form/Viewer to edit the fields.",
"position": {
"x": 18,
"y": 37
"y": 34.003456
},
"width": 174,
"height": 8,
"height": 3.683232,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -118,10 +118,10 @@
"type": "rectangle",
"position": {
"x": 18,
"y": 52
"y": 44.686688000000004
},
"width": 84,
"height": 24,
"height": 21.864736,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -136,10 +136,10 @@
"content": "Customer name",
"position": {
"x": 22,
"y": 56
"y": 48.686688000000004
},
"width": 76,
"height": 5,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -161,7 +161,7 @@
"content": "Mina Carter",
"position": {
"x": 22,
"y": 63
"y": 53.551424000000004
},
"width": 76,
"height": 9,
@@ -198,10 +198,10 @@
"type": "rectangle",
"position": {
"x": 108,
"y": 52
"y": 44.686688000000004
},
"width": 84,
"height": 24,
"height": 21.864736,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -216,10 +216,10 @@
"content": "Email",
"position": {
"x": 112,
"y": 56
"y": 48.686688000000004
},
"width": 76,
"height": 5,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -241,7 +241,7 @@
"content": "mina@example.com",
"position": {
"x": 112,
"y": 63
"y": 53.551424000000004
},
"width": 76,
"height": 9,
@@ -278,10 +278,10 @@
"type": "rectangle",
"position": {
"x": 18,
"y": 83
"y": 73.551424
},
"width": 174,
"height": 34.093024,
"height": 31.95776,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -296,10 +296,10 @@
"content": "Message",
"position": {
"x": 22,
"y": 87
"y": 77.551424
},
"width": 166,
"height": 5,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -321,7 +321,7 @@
"content": "{\"firstName\":\"Mina\",\"plan\":\"Growth\",\"status\":\"Ready for review\"}",
"position": {
"x": 22,
"y": 94
"y": 82.41615999999999
},
"width": 166,
"height": 19.093024,
@@ -365,10 +365,10 @@
"type": "rectangle",
"position": {
"x": 18,
"y": 124.093024
"y": 112.509184
},
"width": 56,
"height": 43,
"height": 40.864736,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -383,10 +383,10 @@
"content": "Logo upload",
"position": {
"x": 22,
"y": 128.093024
"y": 116.509184
},
"width": 48,
"height": 5,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -408,7 +408,7 @@
"content": "",
"position": {
"x": 22,
"y": 135.093024
"y": 121.37392
},
"width": 48,
"height": 28,
@@ -421,10 +421,10 @@
"type": "rectangle",
"position": {
"x": 80,
"y": 124.093024
"y": 112.509184
},
"width": 112,
"height": 38,
"height": 17.2588832,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -436,13 +436,13 @@
{
"name": "text_9",
"type": "text",
"content": "This preset keeps the generated template editable. The Form preview writes changed input values back into the playground state, so Generate PDF uses the latest edits.",
"content": "Editable fields usually keep explicit heights for predictable input boxes. Read-only text can usually omit height and let JSX measure it.",
"position": {
"x": 84,
"y": 128.093024
"y": 116.509184
},
"width": 104,
"height": 30,
"height": 9.2588832,
"rotate": 0,
"opacity": 1,
"readOnly": true,

View File

@@ -15,10 +15,10 @@
"content": "@pdfme/jsx beta",
"position": {
"x": 16,
"y": 3
"y": 0
},
"width": 80,
"height": 6,
"height": 3.273984,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -37,13 +37,13 @@
{
"name": "text_2",
"type": "text",
"content": "Header / Footer / Absolute",
"content": "Header and Footer repeat on every page",
"position": {
"x": 96,
"y": 3
"y": 0
},
"width": 80,
"height": 6,
"height": 3.273984,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -79,10 +79,10 @@
"content": "Generated from JSX",
"position": {
"x": 16,
"y": 281.8
"y": 279.3
},
"width": 80,
"height": 5,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -104,10 +104,10 @@
"content": "Page {currentPage} of {totalPages}",
"position": {
"x": 96,
"y": 281.8
"y": 279.3
},
"width": 54,
"height": 5,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -175,10 +175,10 @@
"content": "Invoice",
"position": {
"x": 16,
"y": 19
"y": 19.659807999999998
},
"width": 92,
"height": 12,
"height": 9.821952,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -200,10 +200,10 @@
"content": "A compact authoring example using Stack, Row, Table and visual schemas.",
"position": {
"x": 16,
"y": 33
"y": 31.48176
},
"width": 92,
"height": 6,
"height": 6.8584320000000005,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -241,7 +241,7 @@
"y": 47
},
"width": 82,
"height": 30,
"height": 26.800336,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -259,7 +259,7 @@
"y": 51
},
"width": 74,
"height": 5,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -281,10 +281,10 @@
"content": "Kumo Coffee\nAki Tanaka\naki@example.com",
"position": {
"x": 20,
"y": 58
"y": 55.864736
},
"width": 74,
"height": 15,
"height": 13.9356,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -314,7 +314,7 @@
"y": 47
},
"width": 90,
"height": 39,
"height": 26.864736,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -332,7 +332,7 @@
"y": 51
},
"width": 82,
"height": 5,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -351,13 +351,13 @@
{
"name": "list_1",
"type": "list",
"content": "[\"Layout primitives create regular pdfme schemas.\",\"\\tNested rows and boxes stay readable.\",\"Download the generated template JSON.\"]",
"content": "[\"Text and MVT can omit height; JSX measures them while rendering.\",\"\\tBoxes without height grow around measured children.\",\"Download the generated template JSON.\"]",
"position": {
"x": 108,
"y": 58
"y": 55.864736
},
"width": 82,
"height": 24,
"height": 14,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -381,7 +381,7 @@
"content": "[[\"Design system setup\",\"1\",\"$800\"],[\"PDF template automation\",\"2\",\"$1,200\"],[\"QA and playground review\",\"1\",\"$350\"]]",
"position": {
"x": 16,
"y": 93
"y": 80.864736
},
"width": 178,
"height": 36,
@@ -458,10 +458,10 @@
"type": "rectangle",
"position": {
"x": 16,
"y": 136
"y": 123.864736
},
"width": 130,
"height": 28,
"height": 12.4198784,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -476,10 +476,10 @@
"content": "**Note:** read-only Text can use inline-markdown. Editable Text intentionally cannot.",
"position": {
"x": 20,
"y": 140
"y": 127.864736
},
"width": 122,
"height": 20,
"height": 4.4198784,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -500,7 +500,7 @@
"type": "ellipse",
"position": {
"x": 152,
"y": 136
"y": 123.864736
},
"width": 22,
"height": 22,
@@ -517,7 +517,7 @@
"type": "rectangle",
"position": {
"x": 176,
"y": 136
"y": 123.864736
},
"width": 18,
"height": 22,
@@ -540,7 +540,7 @@
"y": 18
},
"width": 178,
"height": 10,
"height": 7.366464,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -562,10 +562,10 @@
"content": "PageBreak creates another schemas array in the generated template. This page shows that JSX is only an authoring layer: the output remains a normal pdfme Template.",
"position": {
"x": 16,
"y": 34
"y": 31.366464
},
"width": 178,
"height": 22,
"height": 9.2588832,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -587,10 +587,10 @@
"type": "rectangle",
"position": {
"x": 16,
"y": 62
"y": 46.6253472
},
"width": 178,
"height": 34,
"height": 14.9723632,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -605,10 +605,10 @@
"content": "Try changing numbers, colors, Stack gaps, Row widths, or Table rows. The Viewer updates after a short debounce.",
"position": {
"x": 21,
"y": 67
"y": 51.6253472
},
"width": 168,
"height": 24,
"height": 4.9723632,
"rotate": 0,
"opacity": 1,
"readOnly": true,

View File

@@ -18,7 +18,7 @@
"y": 277
},
"width": 174,
"height": 6,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -47,7 +47,7 @@
"y": 20
},
"width": 174,
"height": 12,
"height": 9.003456,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -69,10 +69,10 @@
"content": "JSX から日本語を含むテンプレートを作成する例です。",
"position": {
"x": 18,
"y": 39
"y": 36.003456
},
"width": 174,
"height": 8,
"height": 3.683232,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -93,10 +93,10 @@
"type": "rectangle",
"position": {
"x": 18,
"y": 54
"y": 46.686688000000004
},
"width": 174,
"height": 46,
"height": 26.165296,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -111,10 +111,10 @@
"content": "pdfme の JSX authoring は、通常の pdfme Template と inputs を生成するための薄いレイヤーです。日本語を扱う場合は、Viewer や generator の options.font に NotoSansJP などのフォントを登録してください。",
"position": {
"x": 23,
"y": 59
"y": 51.686688000000004
},
"width": 164,
"height": 36,
"height": 16.165296,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -137,7 +137,7 @@
"content": "[[\"フォント\",\"NotoSansJP\"],[\"出力\",\"Template + inputs\"],[\"プレビュー\",\"Viewer / Form\"]]",
"position": {
"x": 18,
"y": 107
"y": 79.851984
},
"width": 174,
"height": 36,

View File

@@ -15,10 +15,10 @@
"content": "Quarterly product report",
"position": {
"x": 16,
"y": 280.5
"y": 279
},
"width": 80,
"height": 5,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -40,10 +40,10 @@
"content": "Page {currentPage} of {totalPages}",
"position": {
"x": 96,
"y": 280.5
"y": 279
},
"width": 50,
"height": 5,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -72,7 +72,7 @@
"y": 18
},
"width": 110,
"height": 10,
"height": 8.18496,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -94,10 +94,10 @@
"content": "A layout-focused preset for reports and internal briefs.",
"position": {
"x": 16,
"y": 30
"y": 28.18496
},
"width": 110,
"height": 7,
"height": 3.683232,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -119,10 +119,10 @@
"content": "Healthy",
"position": {
"x": 126,
"y": 29
"y": 28.594208000000002
},
"width": 42,
"height": 8,
"height": 3.273984,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -143,10 +143,10 @@
"type": "rectangle",
"position": {
"x": 16,
"y": 44
"y": 38.868192
},
"width": 56,
"height": 24,
"height": 20.2312,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -161,10 +161,10 @@
"content": "Activation",
"position": {
"x": 20,
"y": 48
"y": 42.868192
},
"width": 48,
"height": 5,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -186,10 +186,10 @@
"content": "74%",
"position": {
"x": 20,
"y": 55
"y": 47.732928
},
"width": 48,
"height": 9,
"height": 7.366464,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -210,10 +210,10 @@
"type": "rectangle",
"position": {
"x": 77,
"y": 44
"y": 38.868192
},
"width": 56,
"height": 24,
"height": 20.2312,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -228,10 +228,10 @@
"content": "Retention",
"position": {
"x": 81,
"y": 48
"y": 42.868192
},
"width": 48,
"height": 5,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -253,10 +253,10 @@
"content": "61%",
"position": {
"x": 81,
"y": 55
"y": 47.732928
},
"width": 48,
"height": 9,
"height": 7.366464,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -277,10 +277,10 @@
"type": "rectangle",
"position": {
"x": 138,
"y": 44
"y": 38.868192
},
"width": 56,
"height": 24,
"height": 20.2312,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -295,10 +295,10 @@
"content": "Expansion",
"position": {
"x": 142,
"y": 48
"y": 42.868192
},
"width": 48,
"height": 5,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -320,10 +320,10 @@
"content": "28%",
"position": {
"x": 142,
"y": 55
"y": 47.732928
},
"width": 48,
"height": 9,
"height": 7.366464,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -344,10 +344,10 @@
"type": "rectangle",
"position": {
"x": 16,
"y": 75
"y": 66.099392
},
"width": 178,
"height": 77,
"height": 46.1698592,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -362,10 +362,10 @@
"content": "Notes",
"position": {
"x": 21,
"y": 80
"y": 71.099392
},
"width": 168,
"height": 7,
"height": 4.910976,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -387,10 +387,10 @@
"content": "The JSX authoring layer is useful when a document has repeated visual patterns but still needs to become a normal pdfme template. This example uses boxes, rows, static footer content, and simple visual bars.",
"position": {
"x": 21,
"y": 91
"y": 80.010368
},
"width": 168,
"height": 28,
"height": 9.2588832,
"rotate": 0,
"opacity": 1,
"readOnly": true,
@@ -410,13 +410,13 @@
{
"name": "list_1",
"type": "list",
"content": "[\"Use Row and Stack for predictable layout.\",\"Use Box for padding, borders, and backgrounds.\",\"Use Static or Footer for repeated page content.\"]",
"content": "[\"Use Row and Stack for predictable layout.\",\"Use Box for padding, borders, and backgrounds.\",\"Use Footer for repeated page content.\"]",
"position": {
"x": 21,
"y": 123
"y": 93.26925119999999
},
"width": 168,
"height": 24,
"height": 14,
"rotate": 0,
"opacity": 1,
"readOnly": true,

View File

@@ -0,0 +1,263 @@
{
"basePdf": {
"width": 210,
"height": 297,
"padding": [
18,
20,
18,
20
],
"staticSchema": [
{
"name": "text_1",
"type": "text",
"content": "Research brief — {currentPage} / {totalPages}",
"position": {
"x": 20,
"y": 279
},
"width": 170,
"height": 2.8647359999999997,
"rotate": 0,
"opacity": 1,
"readOnly": true,
"alignment": "right",
"verticalAlignment": "top",
"fontSize": 7,
"fontName": "NotoSansJP",
"lineHeight": 1,
"characterSpacing": 0,
"fontColor": "#64748b",
"backgroundColor": "",
"textFormat": "plain",
"strikethrough": false,
"underline": false
}
]
},
"schemas": [
[
{
"name": "text_2",
"type": "text",
"content": "Practical Notes on JSX Authoring for PDF Templates",
"position": {
"x": 20,
"y": 18
},
"width": 170,
"height": 9.044380799999999,
"rotate": 0,
"opacity": 1,
"readOnly": true,
"alignment": "center",
"verticalAlignment": "top",
"fontSize": 17,
"fontName": "NotoSansJP",
"lineHeight": 1.3,
"characterSpacing": 0,
"fontColor": "#111827",
"backgroundColor": "",
"textFormat": "plain",
"strikethrough": false,
"underline": false
},
{
"name": "box_1",
"type": "rectangle",
"position": {
"x": 20,
"y": 32.0443808
},
"width": 170,
"height": 23.050448000000003,
"rotate": 0,
"opacity": 1,
"readOnly": true,
"color": "#f3f4f6",
"borderColor": "#9ca3af",
"borderWidth": 0.2,
"radius": 0
},
{
"name": "text_3",
"type": "text",
"content": "Abstract — This preset mirrors a paper-style document and intentionally omits height on most Text nodes. JSX measures each block while rendering, and the parent Box grows around the abstract without manual geometry.",
"position": {
"x": 24,
"y": 36.0443808
},
"width": 162,
"height": 15.050448000000001,
"rotate": 0,
"opacity": 1,
"readOnly": true,
"alignment": "left",
"verticalAlignment": "top",
"fontSize": 9,
"fontName": "NotoSansJP",
"lineHeight": 1.5,
"characterSpacing": 0,
"fontColor": "#374151",
"backgroundColor": "",
"textFormat": "plain",
"strikethrough": false,
"underline": false
},
{
"name": "text_4",
"type": "text",
"content": "1. Introduction",
"position": {
"x": 20,
"y": 60.0948288
},
"width": 170,
"height": 5.8522464,
"rotate": 0,
"opacity": 1,
"readOnly": true,
"alignment": "left",
"verticalAlignment": "top",
"fontSize": 11,
"fontName": "NotoSansJP",
"lineHeight": 1.3,
"characterSpacing": 0,
"fontColor": "#111827",
"backgroundColor": "",
"textFormat": "plain",
"strikethrough": false,
"underline": false
},
{
"name": "text_5",
"type": "text",
"content": "Since structured templates are easier to review than absolute-position JSON, JSX can act as a readable authoring surface for humans and AI. The generated output is still an ordinary pdfme Template, so it can be opened in Designer after generation.",
"position": {
"x": 20,
"y": 67.4470752
},
"width": 170,
"height": 15.050448000000001,
"rotate": 0,
"opacity": 1,
"readOnly": true,
"alignment": "left",
"verticalAlignment": "top",
"fontSize": 9,
"fontName": "NotoSansJP",
"lineHeight": 1.5,
"characterSpacing": 0,
"fontColor": "#374151",
"backgroundColor": "",
"textFormat": "plain",
"strikethrough": false,
"underline": false
},
{
"name": "text_6",
"type": "text",
"content": "2. Layout model",
"position": {
"x": 20,
"y": 87.4975232
},
"width": 170,
"height": 5.8522464,
"rotate": 0,
"opacity": 1,
"readOnly": true,
"alignment": "left",
"verticalAlignment": "top",
"fontSize": 11,
"fontName": "NotoSansJP",
"lineHeight": 1.3,
"characterSpacing": 0,
"fontColor": "#111827",
"backgroundColor": "",
"textFormat": "plain",
"strikethrough": false,
"underline": false
},
{
"name": "text_7",
"type": "text",
"content": "Text and MultiVariableText can omit height. During JSX rendering, pdfme measures their content and advances the surrounding Stack or Box. Use explicit height only when you want a fixed field or a fixed visual area.",
"position": {
"x": 20,
"y": 94.8497696
},
"width": 170,
"height": 10.287648,
"rotate": 0,
"opacity": 1,
"readOnly": true,
"alignment": "left",
"verticalAlignment": "top",
"fontSize": 9,
"fontName": "NotoSansJP",
"lineHeight": 1.5,
"characterSpacing": 0,
"fontColor": "#374151",
"backgroundColor": "",
"textFormat": "plain",
"strikethrough": false,
"underline": false
}
],
[
{
"name": "text_8",
"type": "text",
"content": "Appendix",
"position": {
"x": 20,
"y": 18
},
"width": 170,
"height": 5.320224,
"rotate": 0,
"opacity": 1,
"readOnly": true,
"alignment": "left",
"verticalAlignment": "top",
"fontSize": 13,
"fontName": "NotoSansJP",
"lineHeight": 1,
"characterSpacing": 0,
"fontColor": "#111827",
"backgroundColor": "",
"textFormat": "plain",
"strikethrough": false,
"underline": false
},
{
"name": "text_9",
"type": "text",
"content": "Designer edits are applied to the generated Template. The JSX source remains a seed for regeneration, not a lossless representation of later Designer changes.",
"position": {
"x": 20,
"y": 28.320224
},
"width": 170,
"height": 10.287648,
"rotate": 0,
"opacity": 1,
"readOnly": true,
"alignment": "left",
"verticalAlignment": "top",
"fontSize": 9,
"fontName": "NotoSansJP",
"lineHeight": 1.5,
"characterSpacing": 0,
"fontColor": "#374151",
"backgroundColor": "",
"textFormat": "plain",
"strikethrough": false,
"underline": false
}
]
],
"author": "pdfme"
}

View File

@@ -737,6 +737,31 @@
],
"title": "Report page"
},
{
"name": "jsx-research-paper",
"author": "pdfme",
"path": "jsx-research-paper/template.json",
"thumbnailPath": "jsx-research-paper/thumbnail.png",
"pageCount": 2,
"fieldCount": 9,
"schemaTypes": [
"rectangle",
"text"
],
"fontNames": [
"NotoSansJP"
],
"hasCJK": false,
"basePdfKind": "blank",
"description": "A paper-style document that leans on measured text height and PageBreak.",
"sourceKind": "jsx",
"tags": [
"Research",
"Article",
"Multi-page"
],
"title": "Research paper"
},
{
"name": "z-fold-brochure",
"author": "hitomi-t260g",

View File

@@ -737,6 +737,31 @@
],
"title": "Report page"
},
{
"name": "jsx-research-paper",
"author": "pdfme",
"path": "jsx-research-paper/template.json",
"thumbnailPath": "jsx-research-paper/thumbnail.png",
"pageCount": 2,
"fieldCount": 9,
"schemaTypes": [
"rectangle",
"text"
],
"fontNames": [
"NotoSansJP"
],
"hasCJK": false,
"basePdfKind": "blank",
"description": "A paper-style document that leans on measured text height and PageBreak.",
"sourceKind": "jsx",
"tags": [
"Research",
"Article",
"Multi-page"
],
"title": "Research paper"
},
{
"name": "z-fold-brochure",
"author": "hitomi-t260g",

View File

@@ -84,6 +84,12 @@
"sourceKind": "jsx",
"tags": ["CJK", "Table"]
},
"jsx-research-paper": {
"title": "Research paper",
"description": "A paper-style document that leans on measured text height and PageBreak.",
"sourceKind": "jsx",
"tags": ["Research", "Article", "Multi-page"]
},
"jsx-report": {
"title": "Report page",
"description": "A dashboard-style report with cards, progress bars, list content, and page footer.",

View File

@@ -13,23 +13,23 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
source: `return (
<Document size="A4" margin={{ x: 16, y: 18 }} font="NotoSansJP">
<Header>
<Row height={12} alignItems="center" justifyContent="space-between">
<Text width={80} height={6} size={8} color="#64748b">
<Row alignItems="center" justifyContent="space-between">
<Text width={80} size={8} color="#64748b">
@pdfme/jsx beta
</Text>
<Text width={80} height={6} size={8} align="right" color="#64748b">
Header / Footer / Absolute
<Text width={80} size={8} align="right" color="#64748b">
Header and Footer repeat on every page
</Text>
</Row>
</Header>
<Footer>
<Line height={0.3} color="#cbd5e1" />
<Row height={10} alignItems="center" justifyContent="space-between">
<Text width={80} height={5} size={7} color="#64748b">
<Row alignItems="center" justifyContent="space-between">
<Text width={80} size={7} color="#64748b">
Generated from JSX
</Text>
<Text width={54} height={5} size={7} align="right" color="#64748b">
<Text width={54} size={7} align="right" color="#64748b">
{'Page {currentPage} of {totalPages}'}
</Text>
</Row>
@@ -46,10 +46,10 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
<Stack gap={7}>
<Row alignItems="center" justifyContent="space-between">
<Stack width={92} gap={2}>
<Text height={12} size={24} color="#0f172a">
<Text size={24} color="#0f172a">
Invoice
</Text>
<Text height={6} size={9} color="#475569">
<Text size={9} color="#475569">
A compact authoring example using Stack, Row, Table and visual schemas.
</Text>
</Stack>
@@ -61,11 +61,10 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
<Row gap={6}>
<Box width={82} padding={4} borderColor="#e2e8f0" borderWidth={0.4} background="#f8fafc">
<Stack gap={2}>
<Text height={5} size={7} color="#64748b">
<Text size={7} color="#64748b">
Bill to
</Text>
<MultiVariableText
height={15}
size={10}
lineHeight={1.25}
text={'{company}\\n{name}\\n{email}'}
@@ -79,15 +78,14 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
</Box>
<Box flex={1} padding={4} borderColor="#e2e8f0" borderWidth={0.4}>
<Stack gap={2}>
<Text height={5} size={7} color="#64748b">
<Text size={7} color="#64748b">
Summary
</Text>
<List
height={24}
size={8}
items={[
'Layout primitives create regular pdfme schemas.',
{ text: 'Nested rows and boxes stay readable.', level: 1 },
'Text and MVT can omit height; JSX measures them while rendering.',
{ text: 'Boxes without height grow around measured children.', level: 1 },
'Download the generated template JSON.',
]}
/>
@@ -113,7 +111,7 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
<Row gap={6}>
<Box flex={1} padding={4} background="#fefce8" borderColor="#facc15" borderWidth={0.4}>
<Text height={20} size={8} lineHeight={1.35} textFormat="inline-markdown">
<Text size={8} lineHeight={1.35} textFormat="inline-markdown">
**Note:** read-only Text can use inline-markdown. Editable Text intentionally cannot.
</Text>
</Box>
@@ -129,14 +127,14 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
<Page>
<Stack gap={6}>
<Text height={10} size={18} color="#0f172a">
<Text size={18} color="#0f172a">
Second page
</Text>
<Text height={22} size={9} lineHeight={1.35} overflow="expand">
<Text size={9} lineHeight={1.35} overflow="expand">
PageBreak creates another schemas array in the generated template. This page shows that JSX is only an authoring layer: the output remains a normal pdfme Template.
</Text>
<Box padding={5} borderColor="#cbd5e1" borderWidth={0.4} background="#f8fafc">
<Text height={24} size={9} lineHeight={1.35}>
<Text size={9} lineHeight={1.35}>
Try changing numbers, colors, Stack gaps, Row widths, or Table rows. The Viewer updates after a short debounce.
</Text>
</Box>
@@ -152,30 +150,30 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
source: `return (
<Document size="A4" margin={{ x: 18, y: 18 }} font="NotoSansJP">
<Header>
<Text height={7} size={8} color="#64748b">
<Text size={8} color="#64748b">
Editable fields example
</Text>
</Header>
<Footer>
<Text height={6} size={7} align="right" color="#64748b">
<Text size={7} align="right" color="#64748b">
{'Page {currentPage} of {totalPages}'}
</Text>
</Footer>
<Page>
<Stack gap={7}>
<Text height={12} size={22} color="#111827">
<Text size={22} color="#111827">
Customer Intake Form
</Text>
<Text height={8} size={9} color="#6b7280">
Switch the preview to Form and edit the fields directly.
<Text size={9} color="#6b7280">
Open this generated template in Form/Viewer to edit the fields.
</Text>
<Row gap={6}>
<Box flex={1} padding={4} borderColor="#d1d5db" borderWidth={0.4}>
<Stack gap={2}>
<Text height={5} size={7} color="#6b7280">
<Text size={7} color="#6b7280">
Customer name
</Text>
<Text name="customerName" height={9} size={11} padding={1.5} borderColor="#cbd5e1" borderWidth={0.3}>
@@ -185,7 +183,7 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
</Box>
<Box flex={1} padding={4} borderColor="#d1d5db" borderWidth={0.4}>
<Stack gap={2}>
<Text height={5} size={7} color="#6b7280">
<Text size={7} color="#6b7280">
Email
</Text>
<Text name="email" height={9} size={11} padding={1.5} borderColor="#cbd5e1" borderWidth={0.3}>
@@ -197,7 +195,7 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
<Box padding={4} background="#f8fafc" borderColor="#cbd5e1" borderWidth={0.4}>
<Stack gap={2}>
<Text height={5} size={7} color="#64748b">
<Text size={7} color="#64748b">
Message
</Text>
<MultiVariableText
@@ -217,15 +215,15 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
<Row gap={6}>
<Box width={56} padding={4} borderColor="#d1d5db" borderWidth={0.4}>
<Stack gap={2}>
<Text height={5} size={7} color="#6b7280">
<Text size={7} color="#6b7280">
Logo upload
</Text>
<Image name="logo" width={48} height={28} />
</Stack>
</Box>
<Box flex={1} padding={4} background="#ecfeff" borderColor="#06b6d4" borderWidth={0.4}>
<Text height={30} size={9} lineHeight={1.35}>
This preset keeps the generated template editable. The Form preview writes changed input values back into the playground state, so Generate PDF uses the latest edits.
<Text size={9} lineHeight={1.35}>
Editable fields usually keep explicit heights for predictable input boxes. Read-only text can usually omit height and let JSX measure it.
</Text>
</Box>
</Row>
@@ -242,11 +240,11 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
source: `return (
<Document size="A4" margin={{ x: 16, y: 18 }} font="NotoSansJP">
<Footer>
<Row height={8} justifyContent="space-between" alignItems="center">
<Text width={80} height={5} size={7} color="#64748b">
<Row justifyContent="space-between" alignItems="center">
<Text width={80} size={7} color="#64748b">
Quarterly product report
</Text>
<Text width={50} height={5} size={7} align="right" color="#64748b">
<Text width={50} size={7} align="right" color="#64748b">
{'Page {currentPage} of {totalPages}'}
</Text>
</Row>
@@ -256,14 +254,14 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
<Stack gap={7}>
<Row justifyContent="space-between" alignItems="end">
<Stack width={110} gap={2}>
<Text height={10} size={20} color="#0f172a">
<Text size={20} color="#0f172a">
Product Health Report
</Text>
<Text height={7} size={9} color="#64748b">
<Text size={9} color="#64748b">
A layout-focused preset for reports and internal briefs.
</Text>
</Stack>
<Text width={42} height={8} size={8} align="right" color="#16a34a">
<Text width={42} size={8} align="right" color="#16a34a">
Healthy
</Text>
</Row>
@@ -276,8 +274,8 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
].map(([label, value, background]) => (
<Box flex={1} padding={4} background={background} borderColor="#d1d5db" borderWidth={0.3}>
<Stack gap={2}>
<Text height={5} size={7} color="#64748b">{label}</Text>
<Text height={9} size={18} color="#0f172a">{value}</Text>
<Text size={7} color="#64748b">{label}</Text>
<Text size={18} color="#0f172a">{value}</Text>
</Stack>
</Box>
))}
@@ -285,19 +283,18 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
<Box padding={5} borderColor="#cbd5e1" borderWidth={0.4}>
<Stack gap={4}>
<Text height={7} size={12} color="#0f172a">
<Text size={12} color="#0f172a">
Notes
</Text>
<Text height={28} size={9} lineHeight={1.35} overflow="expand">
<Text size={9} lineHeight={1.35} overflow="expand">
The JSX authoring layer is useful when a document has repeated visual patterns but still needs to become a normal pdfme template. This example uses boxes, rows, static footer content, and simple visual bars.
</Text>
<List
height={24}
size={8}
items={[
'Use Row and Stack for predictable layout.',
'Use Box for padding, borders, and backgrounds.',
'Use Static or Footer for repeated page content.',
'Use Footer for repeated page content.',
]}
/>
</Stack>
@@ -314,22 +311,22 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
source: `return (
<Document size="A4" margin={{ x: 18, y: 20 }} font="NotoSansJP">
<Footer>
<Text height={6} size={7} align="right" color="#64748b">
<Text size={7} align="right" color="#64748b">
{'{currentPage} / {totalPages} ページ'}
</Text>
</Footer>
<Page>
<Stack gap={7}>
<Text height={12} size={22} color="#0f172a">
<Text size={22} color="#0f172a">
お知らせ
</Text>
<Text height={8} size={9} color="#64748b">
<Text size={9} color="#64748b">
JSX から日本語を含むテンプレートを作成する例です。
</Text>
<Box padding={5} background="#f8fafc" borderColor="#cbd5e1" borderWidth={0.4}>
<Text height={36} size={10} lineHeight={1.45} overflow="expand">
<Text size={10} lineHeight={1.45} overflow="expand">
pdfme の JSX authoring は、通常の pdfme Template と inputs を生成するための薄いレイヤーです。日本語を扱う場合は、Viewer や generator の options.font に NotoSansJP などのフォントを登録してください。
</Text>
</Box>
@@ -352,6 +349,68 @@ export const jsxPlaygroundPresets: JsxPlaygroundPreset[] = [
</Stack>
</Page>
</Document>
);`,
},
{
id: 'research-paper',
label: 'Research paper',
description: 'A paper-style document that leans on measured text height and PageBreak.',
source: `const sections = [
{
title: '1. Introduction',
body: 'Since structured templates are easier to review than absolute-position JSON, JSX can act as a readable authoring surface for humans and AI. The generated output is still an ordinary pdfme Template, so it can be opened in Designer after generation.',
},
{
title: '2. Layout model',
body: 'Text and MultiVariableText can omit height. During JSX rendering, pdfme measures their content and advances the surrounding Stack or Box. Use explicit height only when you want a fixed field or a fixed visual area.',
},
];
return (
<Document size="A4" margin={{ x: 20, y: 18 }} font="NotoSansJP">
<Footer>
<Text size={7} align="right" color="#64748b">
{'Research brief — {currentPage} / {totalPages}'}
</Text>
</Footer>
<Page>
<Stack gap={5}>
<Text size={17} align="center" lineHeight={1.3} color="#111827">
Practical Notes on JSX Authoring for PDF Templates
</Text>
<Box background="#f3f4f6" borderColor="#9ca3af" borderWidth={0.2} padding={4}>
<Text size={9} lineHeight={1.5} color="#374151">
Abstract — This preset mirrors a paper-style document and intentionally omits height on
most Text nodes. JSX measures each block while rendering, and the parent Box grows around
the abstract without manual geometry.
</Text>
</Box>
{sections.map((section) => (
<Stack gap={1.5}>
<Text size={11} lineHeight={1.3} color="#111827">
{section.title}
</Text>
<Text size={9} lineHeight={1.5} color="#374151">
{section.body}
</Text>
</Stack>
))}
<PageBreak />
<Text size={13} color="#111827">
Appendix
</Text>
<Text size={9} lineHeight={1.5} color="#374151">
Designer edits are applied to the generated Template. The JSX source remains a seed for
regeneration, not a lossless representation of later Designer changes.
</Text>
</Stack>
</Page>
</Document>
);`,
},
];

View File

@@ -37,10 +37,10 @@ const { template, inputs } = await renderToTemplate(
<Document size="A4" margin={{ x: 16, y: 18 }}>
<Page>
<Stack gap={6}>
<Text height={12} size={24}>
<Text size={24}>
Invoice
</Text>
<Text height={8} color="#64748b">
<Text color="#64748b">
Generated from JSX, rendered by pdfme.
</Text>
<Table
@@ -81,6 +81,11 @@ const pdf = await generate({
The layout API intentionally borrows useful ideas from Flexbox without trying to be CSS-compatible.
Use `gap`, `margin`, `alignItems`, `justifyContent`, and `flex` / `flexGrow` for compact templates.
`Text`, `MultiVariableText`, `List`, and `Table` can usually omit `height`. JSX measures their initial
content while rendering and advances the surrounding `Stack`, `Row`, or `Box`. Use explicit `height`
when you want a fixed field, a fixed visual area, or predictable form input box. A `Box` without
`height` grows around its children during JSX rendering.
## Schema Components
`@pdfme/jsx` currently includes the main static and form-oriented schemas:
@@ -98,6 +103,10 @@ Use `gap`, `margin`, `alignItems`, `justifyContent`, and `flex` / `flexGrow` for
If a component has a `name`, it becomes input-backed by default. If it has no `name`, it is rendered as
read-only content. This mirrors the pdfme template data model.
Because the output is a normal pdfme template, Designer edits apply to the generated `Template`. JSX is
the seed authoring surface; it is not a lossless source format after the template has been edited in
Designer.
`Table` uses `columnWeights` for relative column sizing. The values are normalized to pdfme
`headWidthPercentages`, so `columnWeights={[30, 70]}` means a 30/70 split, not `30mm` / `70mm`.
Missing or invalid weights default to `1`, so pass one weight per column when the exact ratio
@@ -125,15 +134,15 @@ For repeated content, prefer `Document` with document-level `Header` / `Footer`:
return (
<Document size="A4" margin={{ x: 16, y: 18 }}>
<Header>
<Text height={6}>Report</Text>
<Text>Report</Text>
</Header>
<Footer>
<Text height={6} align="right">
<Text align="right">
{'Page {currentPage} of {totalPages}'}
</Text>
</Footer>
<Page>
<Text height={12}>Body</Text>
<Text>Body</Text>
</Page>
</Document>
);
@@ -142,6 +151,10 @@ return (
To migrate from the earlier beta static API, move `Header` / `Footer` / `Static` out of `Page` and
place them directly inside `Document`.
`Header` and `Footer` are margin-aware. Their coordinate origin is the page margin area, so they are
usually the right choice for titles, page numbers, and repeated document chrome. `Static` uses the full
page coordinate system and is intended for advanced overlays such as watermarks, crop marks, or stamps.
## Current Limitations
- `@pdfme/jsx` is still beta. Component names and layout details may still be refined.
@@ -153,3 +166,5 @@ place them directly inside `Document`.
fonts, and generator/viewer options.
- `Header`, `Footer`, and `Static` are supported only as direct children of `Document`. They generate
blank `basePdf.staticSchema` and cannot be used with a custom PDF `basePdf`.
- If a form input later expands at runtime, a parent `Box` rectangle is not dynamically resized yet.
Dynamic parent/child container reflow is a future layout feature.

View File

@@ -37,10 +37,10 @@ const { template, inputs } = await renderToTemplate(
<Document size="A4" margin={{ x: 16, y: 18 }}>
<Page>
<Stack gap={6}>
<Text height={12} size={24}>
<Text size={24}>
Invoice
</Text>
<Text height={8} color="#64748b">
<Text color="#64748b">
Generated from JSX, rendered by pdfme.
</Text>
<Table
@@ -80,6 +80,11 @@ const pdf = await generate({
layout API は CSS/Flexbox 互換を目指していません。`gap`, `margin`, `alignItems`,
`justifyContent`, `flex` / `flexGrow` など、template authoring に必要な部分だけを小さく取り込んでいます。
`Text`, `MultiVariableText`, `List`, `Table` は通常 `height` を省略できます。JSX render 時に初期 content
を測定し、周囲の `Stack`, `Row`, `Box` を進めます。固定 field、固定 visual area、Form の入力 box を
明確にしたい場合だけ `height` を指定してください。`height` のない `Box` は、JSX render 時点では子要素の高さに
合わせて伸びます。
## schema component
`@pdfme/jsx` には、主要な static / form 向け schema component が含まれています。
@@ -97,6 +102,10 @@ layout API は CSS/Flexbox 互換を目指していません。`gap`, `margin`,
component に `name` がある場合は input-backed schema になり、`name` がない場合は read-only content として
描画されます。これは pdfme template のデータモデルに合わせた挙動です。
出力は通常の pdfme template なので、Designer で編集した内容は generated `Template` に反映されます。JSX は
初期生成 / 再生成のための authoring surface であり、Designer 編集後の template を lossless に JSX へ戻すものでは
ありません。
`Table``columnWeights` は相対的な列幅の重みです。値は pdfme の `headWidthPercentages` に正規化されるため、
`columnWeights={[30, 70]}``30mm` / `70mm` ではなく 30/70 の比率を意味します。不足している weight や不正な
weight は `1` として扱われるため、正確な比率が必要な場合は列数分の weight を指定してください。以前の beta build
@@ -127,20 +136,24 @@ API が beta の間は、ブラウザ上の sandbox と module 解決を小さ
return (
<Document size="A4" margin={{ x: 16, y: 18 }}>
<Header>
<Text height={6}>Report</Text>
<Text>Report</Text>
</Header>
<Footer>
<Text height={6} align="right">
<Text align="right">
{'Page {currentPage} of {totalPages}'}
</Text>
</Footer>
<Page>
<Text height={12}>Body</Text>
<Text>Body</Text>
</Page>
</Document>
);
```
`Header` / `Footer` は margin-aware です。座標の基準は page margin 領域なので、title、page number、
繰り返し表示する document chrome に向いています。`Static` は page 全体座標を使う低レベル API で、
watermark、crop mark、stamp などの advanced overlay 向けです。
## 現在の制限
- `@pdfme/jsx` は beta です。component 名や layout の細部は今後調整される可能性があります。
@@ -150,3 +163,5 @@ return (
- 出力は `Template + inputs` です。実際の描画は通常通り pdfme plugins, fonts, generator/viewer options に依存します。
- `Header`, `Footer`, `Static``Document` の direct child としてのみ使えます。これらは blank
`basePdf.staticSchema` を生成するため、custom PDF `basePdf` とは併用できません。
- Form 入力によって runtime に text / MVT が expand した場合、親 `Box` の rectangle はまだ自動では
再計算されません。親子 container の dynamic reflow は今後の layout 機能として扱います。