Skip to content

Commit 444740b

Browse files
authored
Merge pull request #2 from MendyLanda/font-feature-settings
2 parents 997c60c + 0bfb69c commit 444740b

File tree

11 files changed

+145
-8
lines changed

11 files changed

+145
-8
lines changed

.changeset/six-paws-tie.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@react-pdf/textkit": minor
3+
"@react-pdf/layout": minor
4+
"@react-pdf/types": minor
5+
---
6+
7+
Add support for fontFeatureSettings to customize ligatures, tabular number display, and other font features
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/* eslint react/prop-types: 0 */
2+
/* eslint react/jsx-sort-props: 0 */
3+
4+
import { Document, Font, Page, StyleSheet, Text } from '@react-pdf/renderer';
5+
import React from 'react';
6+
7+
import RobotoFont from '../../../public/Roboto-Regular.ttf';
8+
import RubikFont from '../../../public/Rubik-Regular.ttf';
9+
10+
const styles = StyleSheet.create({
11+
body: {
12+
paddingTop: 35,
13+
paddingBottom: 45,
14+
paddingHorizontal: 35,
15+
position: 'relative',
16+
fontSize: 14,
17+
},
18+
headline: {
19+
fontFamily: 'Roboto',
20+
fontSize: 24,
21+
paddingVertical: 12,
22+
},
23+
rubik: {
24+
fontFamily: 'Rubik',
25+
},
26+
roboto: {
27+
fontFamily: 'Roboto',
28+
},
29+
tabular: {
30+
fontFeatureSettings: ['tnum'],
31+
},
32+
smallCapitals: {
33+
fontFeatureSettings: ['smcp'],
34+
},
35+
disableCommonLigatures: {
36+
fontFeatureSettings: { liga: 0 },
37+
},
38+
});
39+
40+
Font.register({
41+
family: 'Rubik',
42+
fonts: [{ src: RubikFont, fontWeight: 400 }],
43+
});
44+
Font.register({
45+
family: 'Roboto',
46+
fonts: [{ src: RobotoFont, fontWeight: 400 }],
47+
});
48+
49+
const MyDoc = () => {
50+
const longNumberExample = "012'345'678'901";
51+
const commonLigaturesExample = 'A firefighter from Sheffield';
52+
return (
53+
<Page style={styles.body}>
54+
<Text style={styles.headline}>Rubik</Text>
55+
<Text style={styles.rubik}>{longNumberExample} – Default features</Text>
56+
<Text style={[styles.rubik, styles.tabular]}>
57+
{longNumberExample} – Tabular numbers
58+
</Text>
59+
<Text style={styles.headline}>Roboto</Text>
60+
<Text style={styles.roboto}>
61+
{commonLigaturesExample} – Default features
62+
</Text>
63+
<Text style={[styles.roboto, styles.disableCommonLigatures]}>
64+
{commonLigaturesExample} – Common ligatures off
65+
</Text>
66+
<Text style={[styles.roboto, styles.smallCapitals]}>
67+
{commonLigaturesExample} – Small capitals
68+
</Text>
69+
</Page>
70+
);
71+
};
72+
73+
const FontFeatureSettings = () => {
74+
return (
75+
<Document>
76+
<MyDoc />
77+
</Document>
78+
);
79+
};
80+
81+
export default {
82+
id: 'font-feature-settings',
83+
name: 'Font Feature Settings',
84+
description: '',
85+
Document: FontFeatureSettings,
86+
};

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import duplicatedImages from './duplicated-images';
22
import ellipsis from './ellipsis';
33
import emoji from './emoji';
44
import fontFamilyFallback from './font-family-fallback';
5+
import fontFeatureSettings from './font-feature-settings';
56
import fontWeight from './font-weight';
7+
import forms from './forms';
68
import fractals from './fractals';
79
import goTo from './go-to';
810
import imageStressTest from './image-stress-test';
@@ -18,14 +20,14 @@ import resume from './resume';
1820
import svg from './svg';
1921
import svgTransform from './svg-transform';
2022
import transformOrigin from './transform-origin';
21-
import forms from './forms';
2223

2324
const EXAMPLES = [
2425
duplicatedImages,
2526
ellipsis,
2627
emoji,
2728
fontFamilyFallback,
2829
fontWeight,
30+
fontFeatureSettings,
2931
fractals,
3032
goTo,
3133
JpgOrientation,

packages/examples/vite/src/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import './index.css';
22

3+
import { PDFViewer } from '@rpdf/renderer';
34
import React, { useEffect, useState } from 'react';
45
import { createRoot } from 'react-dom/client';
5-
import { PDFViewer } from '@rpdf/renderer';
66

77
import EXAMPLES from './examples';
88

@@ -25,13 +25,13 @@ const ExamplesPage = () => {
2525
const { Document } = EXAMPLES[index];
2626

2727
return (
28-
<main className="w-screen h-screen flex">
28+
<main className="flex w-screen h-screen">
2929
<nav className="bg-slate-100 w-60">
3030
<ul>
3131
{EXAMPLES.map((example) => (
3232
<li
3333
key={example.id}
34-
className="hover:bg-slate-200 w-full px-4 py-1 cursor-pointer transition-all border-b border-slate-300 flex"
34+
className="flex w-full px-4 py-1 transition-all border-b cursor-pointer hover:bg-slate-200 border-slate-300"
3535
>
3636
<a href={`#${example.id}`} className="flex-1">
3737
{example.name}
@@ -41,7 +41,7 @@ const ExamplesPage = () => {
4141
</ul>
4242
</nav>
4343

44-
<div key={hash} className="h-full flex-1">
44+
<div key={hash} className="flex-1 h-full">
4545
<PDFViewer showToolbar={false} className="size-full">
4646
<Document />
4747
</PDFViewer>

packages/layout/src/steps/resolveInheritance.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const BASE_INHERITABLE_PROPERTIES = [
1212
'fontSize',
1313
'fontStyle',
1414
'fontWeight',
15+
'fontFeatureSettings',
1516
'letterSpacing',
1617
'opacity',
1718
'textDecoration',

packages/layout/src/svg/inheritProps.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const BASE_SVG_INHERITED_PROPS = [
2323
'fontSize',
2424
'fontStyle',
2525
'fontWeight',
26+
'fontFeatureSettings',
2627
'letterSpacing',
2728
'opacity',
2829
'textDecoration',

packages/layout/src/text/getAttributedString.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const getFragments = (
4747
fontWeight,
4848
fontStyle,
4949
fontSize = 18,
50+
fontFeatureSettings,
5051
textAlign,
5152
lineHeight,
5253
textDecoration,
@@ -100,6 +101,7 @@ const getFragments = (
100101
// @ts-expect-error allow this props access
101102
link: parentLink || instance.props?.src || instance.props?.href,
102103
align: textAlign || (direction === 'rtl' ? 'right' : 'left'),
104+
features: fontFeatureSettings,
103105
};
104106

105107
for (let i = 0; i < instance.children.length; i += 1) {

packages/layout/tests/steps/resolveInhritance.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,8 @@ describe('layout resolveInheritance', () => {
175175
test('Should inherit textAlign value', shouldInherit('textAlign'));
176176
test('Should inherit visibility value', shouldInherit('visibility'));
177177
test('Should inherit wordSpacing value', shouldInherit('wordSpacing'));
178+
test(
179+
'Should inherit fontFeatureSettings value',
180+
shouldInherit('fontFeatureSettings'),
181+
);
178182
});

packages/stylesheet/src/types.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,12 +321,44 @@ export type TextTransform =
321321

322322
export type VerticalAlign = 'sub' | 'super';
323323

324+
export type FontFeatureSetting =
325+
| 'liga'
326+
| 'dlig'
327+
| 'onum'
328+
| 'lnum'
329+
| 'tnum'
330+
| 'zero'
331+
| 'frac'
332+
| 'sups'
333+
| 'subs'
334+
| 'smcp'
335+
| 'c2sc'
336+
| 'case'
337+
| 'hlig'
338+
| 'calt'
339+
| 'swsh'
340+
| 'hist'
341+
| 'ss**'
342+
| 'kern'
343+
| 'locl'
344+
| 'rlig'
345+
| 'medi'
346+
| 'init'
347+
| 'isol'
348+
| 'fina'
349+
| 'mark'
350+
| 'mkmk';
351+
export type FontFeatureSettings =
352+
| FontFeatureSetting[]
353+
| Record<FontFeatureSetting, number>;
354+
324355
export type TextStyle = {
325356
direction?: 'ltr' | 'rtl';
326357
fontSize?: number | string;
327358
fontFamily?: string | string[];
328359
fontStyle?: FontStyle;
329360
fontWeight?: FontWeight;
361+
fontFeatureSettings?: FontFeatureSettings;
330362
letterSpacing?: number | string;
331363
lineHeight?: number | string;
332364
maxLines?: number;
@@ -347,6 +379,7 @@ export type TextSafeStyle = TextExpandedStyle & {
347379
fontWeight?: number;
348380
letterSpacing?: number;
349381
lineHeight?: number;
382+
fontFeatureSettings?: FontFeatureSettings;
350383
};
351384

352385
// Margins

packages/textkit/src/layout/generateGlyphs.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const layoutRun = (string: string) => {
4242
*/
4343
return (run: Run) => {
4444
const { start, end, attributes = {} } = run;
45-
const { font } = attributes;
45+
const { font, features } = attributes;
4646

4747
if (!font) return { ...run, glyphs: [], glyphIndices: [], positions: [] };
4848

@@ -53,7 +53,7 @@ const layoutRun = (string: string) => {
5353
// passing LTR To force fontkit to not reverse the string
5454
const glyphRun = font[0].layout(
5555
runString,
56-
undefined,
56+
features,
5757
undefined,
5858
undefined,
5959
'ltr',

0 commit comments

Comments
 (0)