Skip to content

Commit 746209e

Browse files
authored
feat: scripts example and small fixes (#3353)
1 parent 7b37bc8 commit 746209e

13 files changed

Lines changed: 397 additions & 45 deletions

File tree

packages/examples/vite/README.md

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,32 @@
66
77
## Catalog
88

9-
| Example | Description | PDF | Source |
10-
|---------|-------------|-----|--------|
11-
| Duplicated Images | Same image rendered multiple times (deduplication) | [PDF](./src/examples/duplicated-images/output.pdf) | [Source](./src/examples/duplicated-images/index.tsx) |
12-
| Ellipsis | Text truncation with ellipsis at various widths | [PDF](./src/examples/ellipsis/output.pdf) | [Source](./src/examples/ellipsis/index.tsx) |
13-
| Emoji | Twemoji rendering inline with text | [PDF](./src/examples/emoji/output.pdf) | [Source](./src/examples/emoji/index.tsx) |
14-
| Font Family Fallback | Font fallbacks with mixed families and scripts | [PDF](./src/examples/font-family-fallback/output.pdf) | [Source](./src/examples/font-family-fallback/index.tsx) |
15-
| Font Weight | Roboto typeface at different weights | [PDF](./src/examples/font-weight/output.pdf) | [Source](./src/examples/font-weight/index.tsx) |
16-
| Forms | Interactive form components (inputs, checkboxes, selects) | [PDF](./src/examples/forms/output.pdf) | [Source](./src/examples/forms/index.tsx) |
17-
| Go To | Internal document navigation with anchor links | [PDF](./src/examples/go-to/output.pdf) | [Source](./src/examples/go-to/index.tsx) |
18-
| Image Background | ImageBackground component with overlaid content | [PDF](./src/examples/image-background/output.pdf) | [Source](./src/examples/image-background/index.tsx) |
19-
| JPEG Orientation | All 8 EXIF orientation values handled correctly | [PDF](./src/examples/jpg-orientation/output.pdf) | [Source](./src/examples/jpg-orientation/index.tsx) |
20-
| Knobs | Slider controls using absolute positioning | [PDF](./src/examples/knobs/output.pdf) | [Source](./src/examples/knobs/index.tsx) |
21-
| Link | Various link types (text, styled, view, hitSlop) | [PDF](./src/examples/link/output.pdf) | [Source](./src/examples/link/index.tsx) |
22-
| Math (LaTeX) | LaTeX math expressions via @react-pdf/math || [Source](./src/examples/math/index.tsx) |
23-
| Media Queries | Responsive layout adapting to page width | [PDF](./src/examples/media-queries/output.pdf) | [Source](./src/examples/media-queries/index.tsx) |
24-
| Min Presence Ahead | Controlling page breaks with minPresenceAhead | [PDF](./src/examples/min-presence-ahead/output.pdf) | [Source](./src/examples/min-presence-ahead/index.tsx) |
25-
| Multiline Text | Inline styling and text wrapping | [PDF](./src/examples/multiline-text/output.pdf) | [Source](./src/examples/multiline-text/index.tsx) |
26-
| Object Fit | Image contain, cover, and none modes | [PDF](./src/examples/object-fit/output.pdf) | [Source](./src/examples/object-fit/index.tsx) |
27-
| Page Wrap | Multi-page document with headers and page numbers | [PDF](./src/examples/page-wrap/output.pdf) | [Source](./src/examples/page-wrap/index.tsx) |
28-
| Responsive Images | srcSet-based image selection by page width | [PDF](./src/examples/responsive-images/output.pdf) | [Source](./src/examples/responsive-images/index.tsx) |
29-
| Resume | Multi-section resume with media queries | [PDF](./src/examples/resume/output.pdf) | [Source](./src/examples/resume/index.tsx) |
30-
| Soft Hyphens | Automatic word breaking with Unicode soft hyphens | [PDF](./src/examples/soft-hyphens/output.pdf) | [Source](./src/examples/soft-hyphens/index.tsx) |
31-
| SVG | Paths, gradients, patterns, and complex illustrations | [PDF](./src/examples/svg/output.pdf) | [Source](./src/examples/svg/index.tsx) |
32-
| SVG Transform | Random translate and rotate transforms on rectangles | [PDF](./src/examples/svg-transform/output.pdf) | [Source](./src/examples/svg-transform/index.tsx) |
33-
| Transform Origin | All supported transformOrigin values | [PDF](./src/examples/transform-origin/output.pdf) | [Source](./src/examples/transform-origin/index.tsx) |
9+
| Example | Description | PDF | Source |
10+
| -------------------- | --------------------------------------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------- |
11+
| Duplicated Images | Same image rendered multiple times (deduplication) | [PDF](./src/examples/duplicated-images/output.pdf) | [Source](./src/examples/duplicated-images/index.tsx) |
12+
| Ellipsis | Text truncation with ellipsis at various widths | [PDF](./src/examples/ellipsis/output.pdf) | [Source](./src/examples/ellipsis/index.tsx) |
13+
| Emoji | Twemoji rendering inline with text | [PDF](./src/examples/emoji/output.pdf) | [Source](./src/examples/emoji/index.tsx) |
14+
| Font Family Fallback | Font fallbacks with mixed families and scripts | [PDF](./src/examples/font-family-fallback/output.pdf) | [Source](./src/examples/font-family-fallback/index.tsx) |
15+
| Font Weight | Roboto typeface at different weights | [PDF](./src/examples/font-weight/output.pdf) | [Source](./src/examples/font-weight/index.tsx) |
16+
| Forms | Interactive form components (inputs, checkboxes, selects) | [PDF](./src/examples/forms/output.pdf) | [Source](./src/examples/forms/index.tsx) |
17+
| Go To | Internal document navigation with anchor links | [PDF](./src/examples/go-to/output.pdf) | [Source](./src/examples/go-to/index.tsx) |
18+
| Image Background | ImageBackground component with overlaid content | [PDF](./src/examples/image-background/output.pdf) | [Source](./src/examples/image-background/index.tsx) |
19+
| JPEG Orientation | All 8 EXIF orientation values handled correctly | [PDF](./src/examples/jpg-orientation/output.pdf) | [Source](./src/examples/jpg-orientation/index.tsx) |
20+
| Knobs | Slider controls using absolute positioning | [PDF](./src/examples/knobs/output.pdf) | [Source](./src/examples/knobs/index.tsx) |
21+
| Link | Various link types (text, styled, view, hitSlop) | [PDF](./src/examples/link/output.pdf) | [Source](./src/examples/link/index.tsx) |
22+
| Math (LaTeX) | LaTeX math expressions via @react-pdf/math || [Source](./src/examples/math/index.tsx) |
23+
| Media Queries | Responsive layout adapting to page width | [PDF](./src/examples/media-queries/output.pdf) | [Source](./src/examples/media-queries/index.tsx) |
24+
| Min Presence Ahead | Controlling page breaks with minPresenceAhead | [PDF](./src/examples/min-presence-ahead/output.pdf) | [Source](./src/examples/min-presence-ahead/index.tsx) |
25+
| Multiline Text | Inline styling and text wrapping | [PDF](./src/examples/multiline-text/output.pdf) | [Source](./src/examples/multiline-text/index.tsx) |
26+
| Object Fit | Image contain, cover, and none modes | [PDF](./src/examples/object-fit/output.pdf) | [Source](./src/examples/object-fit/index.tsx) |
27+
| Page Wrap | Multi-page document with headers and page numbers | [PDF](./src/examples/page-wrap/output.pdf) | [Source](./src/examples/page-wrap/index.tsx) |
28+
| Responsive Images | srcSet-based image selection by page width | [PDF](./src/examples/responsive-images/output.pdf) | [Source](./src/examples/responsive-images/index.tsx) |
29+
| Resume | Multi-section resume with media queries | [PDF](./src/examples/resume/output.pdf) | [Source](./src/examples/resume/index.tsx) |
30+
| World Scripts | Complex script rendering — Bengali, Tamil, Thai, Arabic, Devanagari, and accented Latin | [PDF](./src/examples/scripts/output.pdf) | [Source](./src/examples/scripts/index.tsx) |
31+
| Soft Hyphens | Automatic word breaking with Unicode soft hyphens | [PDF](./src/examples/soft-hyphens/output.pdf) | [Source](./src/examples/soft-hyphens/index.tsx) |
32+
| SVG | Paths, gradients, patterns, and complex illustrations | [PDF](./src/examples/svg/output.pdf) | [Source](./src/examples/svg/index.tsx) |
33+
| SVG Transform | Random translate and rotate transforms on rectangles | [PDF](./src/examples/svg-transform/output.pdf) | [Source](./src/examples/svg-transform/index.tsx) |
34+
| Transform Origin | All supported transformOrigin values | [PDF](./src/examples/transform-origin/output.pdf) | [Source](./src/examples/transform-origin/index.tsx) |
3435

3536
## Running locally
3637

packages/examples/vite/generate-pdfs.mjs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@
99
import { build } from 'esbuild';
1010
import { mkdirSync, unlinkSync } from 'fs';
1111
import { join, resolve, dirname } from 'path';
12-
import { fileURLToPath } from 'url';
13-
import { createRequire } from 'module';
12+
import { fileURLToPath, pathToFileURL } from 'url';
1413

1514
const __dirname = dirname(fileURLToPath(import.meta.url));
16-
const require = createRequire(import.meta.url);
1715

1816
const EXAMPLES_DIR = join(__dirname, 'src', 'examples');
1917
const OUTPUT_DIR = join(__dirname, 'output');
@@ -41,6 +39,7 @@ const exampleNames = [
4139
'forms',
4240
'responsive-images',
4341
'resume',
42+
'scripts',
4443
'soft-hyphens',
4544
'svg',
4645
'svg-transform',
@@ -73,7 +72,7 @@ mkdirSync(OUTPUT_DIR, { recursive: true });
7372

7473
async function generatePdf(name) {
7574
const entryPoint = join(EXAMPLES_DIR, name, 'index.tsx');
76-
const bundlePath = join(OUTPUT_DIR, `_bundle_${name}.cjs`);
75+
const bundlePath = join(OUTPUT_DIR, `_bundle_${name}.mjs`);
7776
const pdfPath = join(OUTPUT_DIR, `${name}.pdf`);
7877

7978
try {
@@ -82,7 +81,7 @@ async function generatePdf(name) {
8281
entryPoints: [entryPoint],
8382
bundle: true,
8483
platform: 'node',
85-
format: 'cjs',
84+
format: 'esm',
8685
outfile: bundlePath,
8786
external: [
8887
'@react-pdf/renderer',
@@ -97,7 +96,7 @@ async function generatePdf(name) {
9796
});
9897

9998
// Load the bundle
100-
const mod = require(bundlePath);
99+
const mod = await import(pathToFileURL(bundlePath).href);
101100
const example = mod.default || mod;
102101
const DocumentComponent = example.Document;
103102

@@ -107,8 +106,8 @@ async function generatePdf(name) {
107106
}
108107

109108
// Render to PDF
110-
const React = require('react');
111-
const { renderToFile } = require('@react-pdf/renderer');
109+
const React = await import('react');
110+
const { renderToFile } = await import('@react-pdf/renderer');
112111
const element = React.createElement(DocumentComponent);
113112

114113
await renderToFile(element, pdfPath);

packages/examples/vite/src/examples/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import scripts from './scripts';
12
import duplicatedImages from './duplicated-images';
23
import ellipsis from './ellipsis';
34
import emoji from './emoji';
@@ -25,6 +26,7 @@ import passwordProtection from './password-protection';
2526
import softHyphens from './soft-hyphens';
2627

2728
const EXAMPLES = [
29+
scripts,
2830
duplicatedImages,
2931
ellipsis,
3032
emoji,
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
import React from 'react';
2+
import {
3+
Document,
4+
Page,
5+
View,
6+
Text,
7+
Font,
8+
StyleSheet,
9+
} from '@react-pdf/renderer';
10+
11+
import RobotoFont from '../../../public/Roboto-Regular.ttf';
12+
13+
Font.register({
14+
family: 'Roboto',
15+
fonts: [{ src: RobotoFont }],
16+
});
17+
18+
Font.register({
19+
family: 'NotoSansBengali',
20+
src: 'https://fonts.gstatic.com/s/notosansbengali/v20/Cn-SJsCGWQxOjaGwMQ6fIiMywrNJIky6nvd8BjzVMvJx2mcSPVFpVEqE-6KmsolKudCk8izI0lc.ttf',
21+
});
22+
23+
Font.register({
24+
family: 'NotoSansTamil',
25+
src: 'https://fonts.gstatic.com/s/notosanstamil/v27/ieVc2YdFI3GCY6SyQy1KfStzYKZgzN1z4LKDbeZce-0429tBManUktuex7vGor0RqKDt_EvT.ttf',
26+
});
27+
28+
Font.register({
29+
family: 'Sarabun',
30+
src: 'https://fonts.gstatic.com/s/sarabun/v17/DtVjJx26TKEr37c9WBI.ttf',
31+
});
32+
33+
Font.register({
34+
family: 'NotoSansArabic',
35+
src: 'https://fonts.gstatic.com/s/notosansarabic/v18/nwpxtLGrOAZMl5nJ_wfgRg3DrWFZWsnVBJ_sS6tlqHHFlhQ5l3sQWIHPqzCfyGyvu3CBFQLaig.ttf',
36+
});
37+
38+
Font.register({
39+
family: 'NotoSansDevanagari',
40+
src: 'https://fonts.gstatic.com/s/notosansdevanagari/v30/TuGoUUFzXI5FBtUq5a8bjKYTZjtRU6Sgv3NaV_SNmI0b8QQCQmHn6B2OHjbL_08AlXQly-A.ttf',
41+
});
42+
43+
const styles = StyleSheet.create({
44+
page: {
45+
padding: 40,
46+
backgroundColor: '#fafafa',
47+
},
48+
title: {
49+
fontSize: 18,
50+
fontWeight: 'bold',
51+
color: '#1a1a1a',
52+
},
53+
subtitle: {
54+
fontSize: 9,
55+
color: '#888',
56+
marginBottom: 20,
57+
},
58+
card: {
59+
backgroundColor: '#ffffff',
60+
borderRadius: 5,
61+
padding: 16,
62+
borderWidth: 1,
63+
borderColor: '#e8e8e8',
64+
marginBottom: 8,
65+
},
66+
row: {
67+
flexDirection: 'row',
68+
gap: 8,
69+
marginBottom: 8,
70+
},
71+
halfCard: {
72+
flex: 1,
73+
backgroundColor: '#ffffff',
74+
borderRadius: 5,
75+
padding: 12,
76+
borderWidth: 1,
77+
borderColor: '#e8e8e8',
78+
},
79+
label: {
80+
fontSize: 8,
81+
color: '#999',
82+
textTransform: 'uppercase',
83+
letterSpacing: 0.5,
84+
marginBottom: 10,
85+
},
86+
scriptText: {
87+
fontSize: 24,
88+
color: '#1a1a1a',
89+
marginBottom: 4,
90+
},
91+
scriptTextSmall: {
92+
fontSize: 14,
93+
color: '#333',
94+
lineHeight: 1.6,
95+
},
96+
footer: {
97+
marginTop: 'auto',
98+
paddingTop: 12,
99+
borderTopWidth: 1,
100+
borderTopColor: '#e8e8e8',
101+
},
102+
footerText: {
103+
fontSize: 7,
104+
color: '#aaa',
105+
},
106+
});
107+
108+
const Scripts = () => (
109+
<Document>
110+
<Page style={styles.page}>
111+
<Text style={styles.title}>World Scripts</Text>
112+
<Text style={styles.subtitle}>
113+
Complex script rendering with Unicode decomposition and shaping
114+
</Text>
115+
116+
<View style={styles.row}>
117+
<View style={styles.halfCard}>
118+
<Text style={styles.label}>Bengali</Text>
119+
<Text style={[styles.scriptText, { fontFamily: 'NotoSansBengali' }]}>
120+
বাংলা
121+
</Text>
122+
<Text
123+
style={[styles.scriptTextSmall, { fontFamily: 'NotoSansBengali' }]}
124+
>
125+
আমার সোনার বাংলা আমি তোমায় ভালোবাসি
126+
</Text>
127+
</View>
128+
<View style={styles.halfCard}>
129+
<Text style={styles.label}>Tamil</Text>
130+
<Text style={[styles.scriptText, { fontFamily: 'NotoSansTamil' }]}>
131+
தமிழ்
132+
</Text>
133+
<Text
134+
style={[styles.scriptTextSmall, { fontFamily: 'NotoSansTamil' }]}
135+
>
136+
யாதும் ஊரே யாவரும் கேளிர்
137+
</Text>
138+
</View>
139+
</View>
140+
141+
<View style={styles.row}>
142+
<View style={styles.halfCard}>
143+
<Text style={styles.label}>Thai</Text>
144+
<Text style={[styles.scriptText, { fontFamily: 'Sarabun' }]}>
145+
ภาษาไทย
146+
</Text>
147+
<Text style={[styles.scriptTextSmall, { fontFamily: 'Sarabun' }]}>
148+
สวัสดีครับ ยินดีต้อนรับ
149+
</Text>
150+
</View>
151+
<View style={styles.halfCard}>
152+
<Text style={styles.label}>Arabic</Text>
153+
<Text style={[styles.scriptText, { fontFamily: 'NotoSansArabic' }]}>
154+
العربية
155+
</Text>
156+
<Text
157+
style={[styles.scriptTextSmall, { fontFamily: 'NotoSansArabic' }]}
158+
>
159+
مرحبا بالعالم
160+
</Text>
161+
</View>
162+
</View>
163+
164+
<View style={styles.row}>
165+
<View style={styles.halfCard}>
166+
<Text style={styles.label}>Devanagari</Text>
167+
<Text
168+
style={[styles.scriptText, { fontFamily: 'NotoSansDevanagari' }]}
169+
>
170+
हिन्दी
171+
</Text>
172+
<Text
173+
style={[
174+
styles.scriptTextSmall,
175+
{ fontFamily: 'NotoSansDevanagari' },
176+
]}
177+
>
178+
वसुधैव कुटुम्बकम्
179+
</Text>
180+
</View>
181+
<View style={styles.halfCard}>
182+
<Text style={styles.label}>Latin — Accented</Text>
183+
<Text style={[styles.scriptText, { fontFamily: 'Roboto' }]}>
184+
café résumé
185+
</Text>
186+
<Text style={[styles.scriptTextSmall, { fontFamily: 'Roboto' }]}>
187+
naïve François Zürich Ångström
188+
</Text>
189+
</View>
190+
</View>
191+
192+
<View style={styles.card}>
193+
<Text style={styles.label}>Mixed Scripts</Text>
194+
<Text
195+
style={[styles.scriptTextSmall, { fontFamily: 'NotoSansBengali' }]}
196+
>
197+
মুফতি ইকবাল হোসেন নাটোরি
198+
</Text>
199+
<Text style={[styles.scriptTextSmall, { fontFamily: 'Sarabun' }]}>
200+
กำกกกำกกก
201+
</Text>
202+
<Text style={[styles.scriptTextSmall, { fontFamily: 'NotoSansTamil' }]}>
203+
சொத்து
204+
</Text>
205+
</View>
206+
207+
<View style={styles.footer}>
208+
<Text style={styles.footerText}>
209+
Fonts loaded from Google Fonts — requires Unicode decomposition for
210+
correct glyph shaping
211+
</Text>
212+
</View>
213+
</Page>
214+
</Document>
215+
);
216+
217+
export default {
218+
id: 'scripts',
219+
name: 'World Scripts',
220+
description:
221+
'Complex script rendering — Bengali, Tamil, Thai, Arabic, Devanagari, and accented Latin',
222+
Document: Scripts,
223+
};
24.9 KB
Binary file not shown.

packages/textkit/src/run/append.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import scale from './scale';
22
import getFont from './getFont';
33
import isNumber from '../utils/isNumber';
4-
import apendStringIndices from '../string-indices/append';
4+
import appendStringIndices from '../string-indices/append';
55
import appendGlyphIndices from '../glyph-indices/append';
66
import glyphFromCodePoint from '../glyph/fromCodePoint';
77
import { Glyph, Run } from '../types';
@@ -18,7 +18,7 @@ const appendGlyph = (glyph: Glyph, run: Run): Run => {
1818
const stringLength = run.stringIndices?.length || 0;
1919

2020
const glyphs = run.glyphs.concat(glyph);
21-
const stringIndices = apendStringIndices(glyphLength, run.stringIndices);
21+
const stringIndices = appendStringIndices(glyphLength, run.stringIndices);
2222
const glyphIndices = appendGlyphIndices(stringLength, run.glyphIndices);
2323

2424
const end = run.end + glyphLength;
164 KB
Binary file not shown.
79.6 KB
Binary file not shown.

0 commit comments

Comments
 (0)