Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/late-cats-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@react-pdf/textkit": major
"@react-pdf/layout": patch
"@react-pdf/render": patch
---

feat(textkit): expect font array
9 changes: 2 additions & 7 deletions packages/layout/src/svg/layoutText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import layoutEngine, {
textDecoration,
fromFragments,
Fragment,
Font,
} from '@react-pdf/textkit';

import transformText from '../text/transformText';
Expand Down Expand Up @@ -64,14 +63,11 @@ const getFragments = (fontStore: FontStore, instance) => {
// Fallback font
fontFamilies.push('Helvetica');

// TODO: Fix multiple fonts passed
const font = fontFamilies.map((fontFamilyName) => {
if (typeof fontFamilyName !== 'string') return fontFamilyName;

const opts = { fontFamily: fontFamilyName, fontWeight, fontStyle };
const obj = fontStore.getFont(opts);
return obj ? obj.data : fontFamilyName;
}) as Font[];
return obj?.data;
});

const attributes = {
font,
Expand All @@ -98,7 +94,6 @@ const getFragments = (fontStore: FontStore, instance) => {
if (isTextInstance(child)) {
fragments.push({
string: transformText(child.value, textTransform),
// @ts-expect-error custom font substitution engine deals with multiple fonts. unify with textkit
attributes,
});
} else if (child) {
Expand Down
11 changes: 3 additions & 8 deletions packages/layout/src/text/getAttributedString.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as P from '@react-pdf/primitives';
import { Font, Fragment, fromFragments } from '@react-pdf/textkit';
import { Fragment, fromFragments } from '@react-pdf/textkit';
import FontStore from '@react-pdf/font';

import { embedEmojis } from './emoji';
Expand Down Expand Up @@ -65,14 +65,11 @@ const getFragments = (
// Fallback font
fontFamilies.push('Helvetica');

// TODO: Fix multiple fonts passed
const font = fontFamilies.map((fontFamilyName) => {
if (typeof fontFamilyName !== 'string') return fontFamilyName;

const opts = { fontFamily: fontFamilyName, fontWeight, fontStyle };
const obj = fontStore.getFont(opts);
return obj ? obj.data : fontFamilyName;
}) as Font[];
return obj?.data;
});

// Don't pass main background color to textkit. Will be rendered by the render package instead
const backgroundColor = level === 0 ? null : instance.style.backgroundColor;
Expand Down Expand Up @@ -111,7 +108,6 @@ const getFragments = (
if (isImage(child)) {
fragments.push({
string: String.fromCharCode(0xfffc),
// @ts-expect-error custom font substitution engine deals with multiple fonts. unify with textkit
attributes: {
...attributes,
attachment: {
Expand All @@ -124,7 +120,6 @@ const getFragments = (
} else if (isTextInstance(child)) {
fragments.push({
string: transformText(child.value, textTransform),
// @ts-expect-error custom font substitution engine deals with multiple fonts. unify with textkit
attributes,
});
} else if (child) {
Expand Down
2 changes: 1 addition & 1 deletion packages/layout/src/text/ignoreChars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const buildSubsetForFont = (font: Font) =>

const ignoreChars = (fragments: Fragment[]): Fragment[] =>
fragments.map((fragment) => {
const charSubset = buildSubsetForFont(fragment.attributes.font);
const charSubset = buildSubsetForFont(fragment.attributes.font[0]);
const subsetRegex = new RegExp(charSubset.join('|'));

return {
Expand Down
5 changes: 3 additions & 2 deletions packages/render/src/primitives/renderSvgText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const renderRun = (ctx: Context, run: Run) => {
if (!run.positions) return;

const runAdvanceWidth = run.xAdvance;
const { font, fontSize, color, opacity } = run.attributes;
const font = run.attributes.font?.[0];
const { fontSize, color, opacity } = run.attributes;

if (color) ctx.fillColor(color);
ctx.fillOpacity(opacity!);
Expand Down Expand Up @@ -42,7 +43,7 @@ const renderSpan = (

const x = line.box?.x || 0;
const y = line.box?.y || 0;
const font = line.runs[0]?.attributes.font;
const font = line.runs[0]?.attributes.font?.[0];
const scale = line.runs[0]?.attributes?.scale || 1;
const width = line.xAdvance!;

Expand Down
6 changes: 3 additions & 3 deletions packages/render/src/primitives/renderText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const renderAttachments = (ctx: Context, run: Run) => {
if (!run.glyphs) return;
if (!run.positions) return;

const { font } = run.attributes;
const font = run.attributes.font?.[0];
if (!font) return;

ctx.save();
Expand Down Expand Up @@ -63,10 +63,10 @@ const renderRun = (ctx: Context, run: Run) => {
if (!run.glyphs) return;
if (!run.positions) return;

const { font, fontSize, link } = run.attributes;

const font = run.attributes.font?.[0];
if (!font) return;

const { fontSize, link } = run.attributes;
const color = parseColor(run.attributes.color);
const opacity = isNil(run.attributes.opacity)
? color.opacity
Expand Down
16 changes: 10 additions & 6 deletions packages/textkit/src/engines/fontSubstitution/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { last } from '@react-pdf/fns';
import { AttributedString, Run } from '../../types';
import { AttributedString, Font, Run } from '../../types';

const IGNORED_CODE_POINTS = [173];

const getFontSize = (run: Run) => run.attributes.fontSize || 12;

const pickFontFromFontStack = (codePoint, fontStack, lastFont) => {
const pickFontFromFontStack = (
codePoint: number,
fontStack: Font[],
lastFont?: Font,
) => {
const fontStackWithFallback = [...fontStack, lastFont];
for (let i = 0; i < fontStackWithFallback.length; i += 1) {
const font = fontStackWithFallback[i];
Expand All @@ -24,8 +28,8 @@ const pickFontFromFontStack = (codePoint, fontStack, lastFont) => {
const fontSubstitution =
() =>
({ string, runs }: AttributedString) => {
let lastFont = null;
let lastFontSize = null;
let lastFont: Font | null = null;
let lastFontSize: number | null = null;
let lastIndex = 0;
let index = 0;

Expand Down Expand Up @@ -68,7 +72,7 @@ const fontSubstitution =
start: lastIndex,
end: index,
attributes: {
font: lastFont,
font: [lastFont],
scale: lastFontSize / lastFont.unitsPerEm,
},
});
Expand All @@ -90,7 +94,7 @@ const fontSubstitution =
start: lastIndex,
end: string.length,
attributes: {
font: lastFont,
font: [lastFont],
scale: fontSize / lastFont.unitsPerEm,
},
});
Expand Down
2 changes: 1 addition & 1 deletion packages/textkit/src/layout/applyDefaultStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const applyAttributes = (a: Attributes): Attributes => {
direction: a.direction || 'ltr',
features: a.features || [],
fill: a.fill !== false,
font: a.font || null,
font: a.font || [],
fontSize: a.fontSize || 12,
hangingPunctuation: a.hangingPunctuation || false,
hyphenationFactor: a.hyphenationFactor || 0,
Expand Down
2 changes: 1 addition & 1 deletion packages/textkit/src/layout/generateGlyphs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const layoutRun = (string: string) => {
if (typeof font === 'string') throw new Error('Invalid font');

// passing LTR To force fontkit to not reverse the string
const glyphRun = font.layout(
const glyphRun = font[0].layout(
runString,
undefined,
undefined,
Expand Down
7 changes: 6 additions & 1 deletion packages/textkit/src/layout/preprocessRuns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,17 @@ const omitFont = (attributedString: AttributedString): AttributedString => {
return Object.assign({}, attributedString, { runs });
};

type ProcessRunsEngines = Pick<
Engines,
'bidi' | 'scriptItemizer' | 'fontSubstitution'
>;

/**
* Performs font substitution and script itemization on attributed string
*
* @param engines - engines
*/
const preprocessRuns = (engines: Engines) => {
const preprocessRuns = (engines: ProcessRunsEngines) => {
/**
* @param attributedString - Attributed string
* @returns Processed attributed string
Expand Down
2 changes: 1 addition & 1 deletion packages/textkit/src/layout/resolveYOffset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { AttributedString, Run } from '../types';
const resolveRunYOffset = (run: Run) => {
if (!run.positions) return run;

const unitsPerEm = run.attributes?.font?.unitsPerEm || 0;
const unitsPerEm = run.attributes?.font?.[0]?.unitsPerEm || 0;
const yOffset = (run.attributes?.yOffset || 0) * unitsPerEm;
const positions = run.positions.map((p) => Object.assign({}, p, { yOffset }));

Expand Down
2 changes: 1 addition & 1 deletion packages/textkit/src/paragraph/truncate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const getEllipsisCodePoint = (font: Font) => {
*/
const truncate = (paragraph: Paragraph) => {
const runs = last(paragraph)?.runs || [];
const font = last(runs)?.attributes?.font;
const font = last(runs)?.attributes?.font[0];

if (font) {
const index = paragraph.length - 1;
Expand Down
2 changes: 1 addition & 1 deletion packages/textkit/src/run/ascent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import scale from './scale';
const ascent = (run: Run) => {
const { font, attachment } = run.attributes;
const attachmentHeight = attachment?.height || 0;
const fontAscent = typeof font === 'string' ? 0 : font?.ascent || 0;
const fontAscent = typeof font === 'string' ? 0 : font?.[0]?.ascent || 0;

return Math.max(attachmentHeight, fontAscent * scale(run));
};
Expand Down
2 changes: 1 addition & 1 deletion packages/textkit/src/run/descent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import scale from './scale';
*/
const descent = (run: Run) => {
const font = run.attributes?.font;
const fontDescent = typeof font === 'string' ? 0 : font?.descent || 0;
const fontDescent = typeof font === 'string' ? 0 : font?.[0]?.descent || 0;

return scale(run) * fontDescent;
};
Expand Down
2 changes: 1 addition & 1 deletion packages/textkit/src/run/getFont.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Run } from '../types';
* @returns Font
*/
const getFont = (run: Run) => {
return run.attributes?.font || null;
return run.attributes?.font?.[0] || null;
};

export default getFont;
2 changes: 1 addition & 1 deletion packages/textkit/src/run/lineGap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import scale from './scale';
*/
const lineGap = (run: Run) => {
const font = run.attributes?.font;
const lineGap = typeof font === 'string' ? 0 : font?.lineGap || 0;
const lineGap = typeof font === 'string' ? 0 : font?.[0]?.lineGap || 0;
return lineGap * scale(run);
};

Expand Down
2 changes: 1 addition & 1 deletion packages/textkit/src/run/scale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const calculateScale = (run: Run) => {
const attributes = run.attributes || {};
const fontSize = attributes.fontSize || 12;
const font = attributes.font;
const unitsPerEm = typeof font === 'string' ? null : font?.unitsPerEm;
const unitsPerEm = typeof font === 'string' ? null : font?.[0]?.unitsPerEm;

return unitsPerEm ? fontSize / unitsPerEm : 0;
};
Expand Down
2 changes: 1 addition & 1 deletion packages/textkit/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export type Attributes = {
direction?: 'rtl' | 'ltr';
features?: unknown[];
fill?: boolean;
font?: Font;
font?: Font[];
fontSize?: number;
hangingPunctuation?: boolean;
hyphenationFactor?: number;
Expand Down
Loading