Skip to content

Commit

Permalink
Fix text height rendering for PDF generation (#196)
Browse files Browse the repository at this point in the history
* Update PDF generation to render text in correct position

* MISC - UI logic updated to align fotn rendering with generated PDFs

* MISC - variables names updated

* MISC - heightOfFontAtSize method updated to include acent workaround for specific fonts

* MISC - ascent value modifier removed

* MISC - buffer package added to UI, fallback font safe guarding added to generator file

* MISC - fallback font added to fontkit.create() if no custom fonts present

* Test snapshots and pdf files updated to account for new changes to the generator

* MISC - generator file structure updated based on feedback

* MISC - generator file updated and console log removed

* MISC - TextSchema updated to make use of fallback font

* MISC - Test PDF files updated to only change ones that contain TextSchema

---------

Co-authored-by: Jack Barton <jack@pennyblack.io>
  • Loading branch information
steffancarrington and jbarton123 committed Jul 27, 2023
1 parent 040e52b commit 1bed97e
Show file tree
Hide file tree
Showing 49 changed files with 169 additions and 78 deletions.
1 change: 1 addition & 0 deletions packages/common/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const DEFAULT_CHARACTER_SPACING = 0;
export const DEFAULT_FONT_COLOR = '#000';
export const DEFAULT_TOLERANCE = 1;
export const DEFAULT_FONT_SIZE_ADJUSTMENT = 0.25;
export const DEFAULT_PT_TO_PX_RATIO = 1.333;
export const DEFAULT_PT_TO_MM_RATIO = 0.3528;

export const BLANK_PDF =
Expand Down
14 changes: 14 additions & 0 deletions packages/common/src/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
GenerateProps as GeneratePropsSchema,
UIProps as UIPropsSchema,
} from './schema.js';
import type { Font as FontkitFont } from 'fontkit';

const blob2Base64Pdf = (blob: Blob) => {
return new Promise<string>((resolve, reject) => {
Expand Down Expand Up @@ -73,6 +74,19 @@ export const getDefaultFont = (): Font => ({
[DEFAULT_FONT_NAME]: { data: b64toUint8Array(DEFAULT_FONT_VALUE), fallback: true },
});

export const heightOfFontAtSize = (font: FontkitFont, size: number) => {
let { ascent, descent, bbox } = font;

const scale = 1000 / font.unitsPerEm;
const yTop = (ascent || bbox.maxY) * scale;
const yBottom = (descent || bbox.minY) * scale;

let height = yTop - yBottom;
height -= Math.abs(descent * scale) || 0;

return (height / 1000) * size;
};

const uniq = <T>(array: Array<T>) => Array.from(new Set(array));

const getFontNamesInSchemas = (schemas: { [key: string]: Schema }[]) =>
Expand Down
4 changes: 4 additions & 0 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
DEFAULT_TOLERANCE,
DEFAULT_FONT_SIZE_ADJUSTMENT,
DEFAULT_PT_TO_MM_RATIO,
DEFAULT_PT_TO_PX_RATIO,
BLANK_PDF,
DEFAULT_FONT_VALUE,
} from './constants.js';
Expand Down Expand Up @@ -42,6 +43,7 @@ import {
b64toUint8Array,
getFallbackFontName,
getDefaultFont,
heightOfFontAtSize,
checkFont,
checkInputs,
checkUIOptions,
Expand All @@ -64,6 +66,7 @@ export {
DEFAULT_TOLERANCE,
DEFAULT_FONT_SIZE_ADJUSTMENT,
DEFAULT_PT_TO_MM_RATIO,
DEFAULT_PT_TO_PX_RATIO,
BLANK_PDF,
DEFAULT_FONT_VALUE,
schemaTypes,
Expand All @@ -74,6 +77,7 @@ export {
b64toUint8Array,
getFallbackFontName,
getDefaultFont,
heightOfFontAtSize,
checkFont,
checkInputs,
checkUIOptions,
Expand Down
Binary file modified packages/generator/__tests__/assets/pdfs/assert/Aone72312宛名.pdf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified packages/generator/__tests__/assets/pdfs/assert/background.pdf
Binary file not shown.
Binary file modified packages/generator/__tests__/assets/pdfs/assert/canvasPdf.pdf
Binary file not shown.
Binary file modified packages/generator/__tests__/assets/pdfs/assert/connpass名札.pdf
Binary file not shown.
Binary file modified packages/generator/__tests__/assets/pdfs/assert/fontColor.pdf
Binary file not shown.
Binary file modified packages/generator/__tests__/assets/pdfs/assert/fontSubset.pdf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified packages/generator/__tests__/assets/pdfs/assert/test.pdf
Binary file not shown.
Binary file modified packages/generator/__tests__/assets/pdfs/assert/z97mmx210mm.pdf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified packages/generator/__tests__/assets/pdfs/assert/履歴書.pdf
Binary file not shown.
Binary file modified packages/generator/__tests__/assets/pdfs/assert/書類送付状.pdf
Binary file not shown.
Binary file not shown.
Binary file modified packages/generator/__tests__/assets/pdfs/assert/納品書.pdf
Binary file not shown.
Binary file modified packages/generator/__tests__/assets/pdfs/assert/表彰状.pdf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified packages/generator/__tests__/assets/pdfs/assert/見積書.pdf
Binary file not shown.
Binary file not shown.
Binary file modified packages/generator/__tests__/assets/pdfs/assert/請求書.pdf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified packages/generator/__tests__/assets/pdfs/assert/領収書.pdf
Binary file not shown.
Binary file modified packages/generator/__tests__/assets/pdfs/assert/領収書x4.pdf
Binary file not shown.
3 changes: 1 addition & 2 deletions packages/generator/src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const preprocessing = async (arg: { inputs: SchemaInputs[]; template: Template;
const fallbackFontName = getFallbackFontName(font);

const pdfDoc = await PDFDocument.create();
// @ts-ignore
pdfDoc.registerFontkit(fontkit);

const pdfFontObj = await embedAndGetFontObj({ pdfDoc, font });
Expand All @@ -47,8 +48,6 @@ const generate = async (props: GenerateProps) => {
const { font = getDefaultFont() } = options;
const { schemas } = template;



const preRes = await preprocessing({ inputs, template, font });
const { pdfDoc, pdfFontObj, fallbackFontName, embeddedPages, embedPdfBoxes } = preRes;

Expand Down
47 changes: 34 additions & 13 deletions packages/generator/src/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,18 @@ import {
BasePdf,
BarCodeType,
Alignment,
DEFAULT_FONT_NAME,
DEFAULT_FONT_SIZE,
DEFAULT_ALIGNMENT,
DEFAULT_LINE_HEIGHT,
DEFAULT_CHARACTER_SPACING,
DEFAULT_FONT_COLOR,
calculateDynamicFontSize,
heightOfFontAtSize,
getDefaultFont
} from '@pdfme/common';
import { Buffer } from 'buffer';
import * as fontkit from 'fontkit';

export interface InputImageCache {
[key: string]: PDFImage | undefined;
Expand Down Expand Up @@ -251,7 +256,8 @@ const getSplittedLines = (inputLine: string, isOverEval: IsOverEval): string[] =
const splittedLine = inputLine.substring(0, splitPos);
const rest = inputLine.substring(splitPos).trimStart();

if (rest.length === 0) { // end recursion if there is no rest
if (rest.length === 0) {
// end recursion if there is no rest
return [splittedLine];
}

Expand All @@ -278,11 +284,23 @@ const drawInputByTextSchema = async (arg: {
const { font, pdfFontObj, fallbackFontName } = fontSetting;

const pdfFontValue = pdfFontObj[templateSchema.fontName ? templateSchema.fontName : fallbackFontName];
const fallbackFont = getDefaultFont();
let schemaFontData = fallbackFont[DEFAULT_FONT_NAME].data;

if (templateSchema.fontName) {
schemaFontData = font[templateSchema.fontName].data;
}

const fontkitFont = fontkit.create(Buffer.from(schemaFontData as ArrayBuffer));

drawBackgroundColor({ templateSchema, page, pageHeight });

const { width, rotate } = getSchemaSizeAndRotate(templateSchema);
const { size, color, alignment, lineHeight, characterSpacing } = await getFontProp({ input, font, schema: templateSchema });
const { width, height, rotate } = getSchemaSizeAndRotate(templateSchema);
const { size, color, alignment, lineHeight, characterSpacing } = await getFontProp({
input,
font,
schema: templateSchema,
});

page.pushOperators(setCharacterSpacing(characterSpacing));

Expand All @@ -294,16 +312,19 @@ const drawInputByTextSchema = async (arg: {
pdfFontValue.widthOfTextAtSize(testString, size) + (testString.length - 1) * characterSpacing;
return width <= testStringWidth;
};
const splitedLines = getSplittedLines(inputLine, isOverEval);
const drawLine = (splitedLine: string, splitedLineIndex: number) => {
const textWidth =
pdfFontValue.widthOfTextAtSize(splitedLine, size) +
(splitedLine.length - 1) * characterSpacing;
page.drawText(splitedLine, {
const splitLines = getSplittedLines(inputLine, isOverEval);

const drawLine = (line: string, lineIndex: number) => {
const textWidth = pdfFontValue.widthOfTextAtSize(line, size) + (line.length - 1) * characterSpacing;
const textHeight = heightOfFontAtSize(fontkitFont, size);

page.drawText(line, {
x: calcX(templateSchema.position.x, alignment, width, textWidth),
y:
calcY(templateSchema.position.y, pageHeight, size) -
lineHeight * size * (inputLineIndex + splitedLineIndex + beforeLineOver) -
calcY(templateSchema.position.y, pageHeight, height) +
height -
textHeight -
lineHeight * size * (inputLineIndex + lineIndex + beforeLineOver) -
(lineHeight === 0 ? 0 : ((lineHeight - 1) * size) / 2),
rotate,
size,
Expand All @@ -313,10 +334,10 @@ const drawInputByTextSchema = async (arg: {
font: pdfFontValue,
wordBreaks: [''],
});
if (splitedLines.length === splitedLineIndex + 1) beforeLineOver += splitedLineIndex;
if (splitLines.length === lineIndex + 1) beforeLineOver += lineIndex;
};

splitedLines.forEach(drawLine);
splitLines.forEach(drawLine);
});
};

Expand Down
1 change: 0 additions & 1 deletion packages/generator/type.d.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -370,18 +370,22 @@ exports[`Designer snapshot 1`] = `
title="field1"
>
<div
style="padding: 0px; resize: none; position: absolute; font-family: inherit; height: 56.692913386499995px; width: 377.95275591px; text-align: left; font-size: 30pt; letter-spacing: 0pt; line-height: 1em; white-space: pre-line; word-break: break-word; color: rgb(0, 0, 0);"
style="position: absolute; top: 0px; padding: 0px; height: 56.692913386499995px; width: 377.95275591px; resize: none; font-family: inherit; color: rgb(0, 0, 0); font-size: 30pt; letter-spacing: 0pt; line-height: 1em; text-align: left; white-space: pre-line; word-break: break-word;"
>
<span
style="letter-spacing: inherit;"
<div
style="margin-top: 0px; padding-top: 3.436640624999999px;"
>
b
</span>
<span
style="letter-spacing: 0;"
>
b
</span>
<span
style="letter-spacing: inherit;"
>
b
</span>
<span
style="letter-spacing: 0;"
>
b
</span>
</div>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ exports[`Preview(as Form) snapshot 1`] = `
>
<textarea
placeholder="bb"
style="padding: 0px; resize: none; position: absolute; font-family: inherit; height: 56.692913386499995px; width: 377.95275591px; text-align: left; font-size: 30pt; letter-spacing: 0pt; line-height: 1em; white-space: pre-line; word-break: break-word; color: rgb(0, 0, 0);"
style="position: absolute; top: 0px; padding: 3.436640624999999px 0px 0px 0px; height: 56.692913386499995px; width: 377.95275591px; resize: none; font-family: inherit; color: rgb(0, 0, 0); font-size: 30pt; letter-spacing: 0pt; line-height: 1em; text-align: left; white-space: pre-line; word-break: break-word; margin-top: 0px;"
tabindex="100"
>
field1
Expand Down Expand Up @@ -227,38 +227,42 @@ exports[`Preview(as Viewer) snapshot 1`] = `
title="field1"
>
<div
style="padding: 0px; resize: none; position: absolute; font-family: inherit; height: 56.692913386499995px; width: 377.95275591px; text-align: left; font-size: 30pt; letter-spacing: 0pt; line-height: 1em; white-space: pre-line; word-break: break-word; color: rgb(0, 0, 0);"
style="position: absolute; top: 0px; padding: 0px; height: 56.692913386499995px; width: 377.95275591px; resize: none; font-family: inherit; color: rgb(0, 0, 0); font-size: 30pt; letter-spacing: 0pt; line-height: 1em; text-align: left; white-space: pre-line; word-break: break-word;"
>
<span
style="letter-spacing: inherit;"
<div
style="margin-top: 0px; padding-top: 3.436640624999999px;"
>
f
</span>
<span
style="letter-spacing: inherit;"
>
i
</span>
<span
style="letter-spacing: inherit;"
>
e
</span>
<span
style="letter-spacing: inherit;"
>
l
</span>
<span
style="letter-spacing: inherit;"
>
d
</span>
<span
style="letter-spacing: 0;"
>
1
</span>
<span
style="letter-spacing: inherit;"
>
f
</span>
<span
style="letter-spacing: inherit;"
>
i
</span>
<span
style="letter-spacing: inherit;"
>
e
</span>
<span
style="letter-spacing: inherit;"
>
l
</span>
<span
style="letter-spacing: inherit;"
>
d
</span>
<span
style="letter-spacing: 0;"
>
1
</span>
</div>
</div>
</div>
</div>
Expand Down

0 comments on commit 1bed97e

Please sign in to comment.