Skip to content

Commit 2c3c887

Browse files
authored
feat: support multiple line-height units (#2952)
1 parent 9075655 commit 2c3c887

5 files changed

Lines changed: 84 additions & 8 deletions

File tree

.changeset/tame-yaks-speak.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@react-pdf/layout": patch
3+
"@react-pdf/stylesheet": patch
4+
---
5+
6+
feat: support multiple line-height units

packages/layout/src/text/getAttributedString.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const getFragments = (fontStore, instance, parentLink, level = 0) => {
6363
color,
6464
opacity,
6565
fontSize,
66+
lineHeight,
6667
direction,
6768
verticalAlign,
6869
backgroundColor,
@@ -81,7 +82,6 @@ const getFragments = (fontStore, instance, parentLink, level = 0) => {
8182
strikeColor: textDecorationColor || color,
8283
underlineColor: textDecorationColor || color,
8384
link: parentLink || instance.props?.src || instance.props?.href,
84-
lineHeight: lineHeight ? lineHeight * fontSize : null,
8585
align: textAlign || (direction === 'rtl' ? 'right' : 'left'),
8686
};
8787

packages/stylesheet/src/transform/index.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,23 @@ import transformUnits from './units';
22
import transformColor from './colors';
33
import processTransform from './transform';
44
import processFontWeight from './fontWeight';
5+
import processLineHeight from './lineHeight';
56
import processObjectPosition from './objectPosition';
67
import processTransformOrigin from './transformOrigin';
78
import castFloat from '../utils/castFloat';
89

910
const handlers = {
1011
transform: processTransform,
1112
fontWeight: processFontWeight,
13+
lineHeight: processLineHeight,
1214
objectPositionX: processObjectPosition,
1315
objectPositionY: processObjectPosition,
1416
transformOriginX: processTransformOrigin,
1517
transformOriginY: processTransformOrigin,
1618
};
1719

18-
const transformStyle = (key, value, container) => {
19-
const result = handlers[key] ? handlers[key](value) : value;
20+
const transformStyle = (key, value, styles, container) => {
21+
const result = handlers[key] ? handlers[key](value, styles) : value;
2022

2123
return transformColor(transformUnits(container, castFloat(result)));
2224
};
@@ -33,16 +35,16 @@ const transformStyle = (key, value, container) => {
3335
* @param {Object} container
3436
* @returns {Transform} transform function
3537
*/
36-
const transform = (container) => (style) => {
37-
if (!style) return style;
38+
const transform = (container) => (styles) => {
39+
if (!styles) return styles;
3840

39-
const propsArray = Object.keys(style);
41+
const propsArray = Object.keys(styles);
4042
const resolvedStyle = {};
4143

4244
for (let i = 0; i < propsArray.length; i += 1) {
4345
const key = propsArray[i];
44-
const value = style[key];
45-
const transformed = transformStyle(key, value, container);
46+
const value = styles[key];
47+
const transformed = transformStyle(key, value, styles, container);
4648

4749
resolvedStyle[key] = transformed;
4850
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/* eslint-disable no-restricted-globals */
2+
3+
import { matchPercent } from '@react-pdf/fns';
4+
5+
const processLineHeight = (value, styles) => {
6+
if (value === '') return value;
7+
8+
const { fontSize = 18 } = styles;
9+
10+
// Percent values: use this number multiplied by the element's font size
11+
const { percent } = matchPercent(value) || {};
12+
if (percent) return percent * fontSize;
13+
14+
// Unitless values: use this number multiplied by the element's font size
15+
return isNaN(value) ? value : value * fontSize;
16+
};
17+
18+
export default processLineHeight;

packages/stylesheet/tests/transform.test.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,56 @@ describe('stylesheet transform', () => {
105105
});
106106
});
107107

108+
describe('transform lineHeight', () => {
109+
test('should transform unitless number amount', () => {
110+
const styles = transformStyles({ lineHeight: 2 });
111+
112+
expect(styles.lineHeight).toBe(18 * 2);
113+
});
114+
115+
test('should transform unitless number amount with font-size', () => {
116+
const styles = transformStyles({ lineHeight: 2, fontSize: 10 });
117+
118+
expect(styles.lineHeight).toBe(10 * 2);
119+
});
120+
121+
test('should transform unitless string amount', () => {
122+
const styles = transformStyles({ lineHeight: '2' });
123+
124+
expect(styles.lineHeight).toBe(18 * 2);
125+
});
126+
127+
test('should transform unitless string amount with font-size', () => {
128+
const styles = transformStyles({ lineHeight: '2', fontSize: 10 });
129+
130+
expect(styles.lineHeight).toBe(10 * 2);
131+
});
132+
133+
test('should transform percentage amount', () => {
134+
const styles = transformStyles({ lineHeight: '200%' });
135+
136+
expect(styles.lineHeight).toBe(18 * 2);
137+
});
138+
139+
test('should transform percentage amount with font-size', () => {
140+
const styles = transformStyles({ lineHeight: '200%', fontSize: 10 });
141+
142+
expect(styles.lineHeight).toBe(10 * 2);
143+
});
144+
145+
test('should transform height px dimensions', () => {
146+
const styles = transformStyles({ lineHeight: '20px' });
147+
148+
expect(styles.lineHeight).toBe(20);
149+
});
150+
151+
test('should transform width mm dimensions', () => {
152+
const styles = transformStyles({ lineHeight: '20mm' });
153+
154+
expect(styles.lineHeight).toBeCloseTo(56.69, 1);
155+
});
156+
});
157+
108158
describe('transform colors', () => {
109159
test('should keep hex values as they are', () => {
110160
const styles = transformStyles({ color: '#0000FF' });

0 commit comments

Comments
 (0)