Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/olive-walls-breathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@react-pdf/pdfkit": minor
---

refactor: align mulptiles filesWith upstream
23 changes: 19 additions & 4 deletions packages/pdfkit/src/mixins/annotations.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import PDFAnnotationReference from '../structure_annotation';

export default {
annotate(x, y, w, h, options) {
options.Type = 'Annot';
Expand All @@ -19,6 +21,9 @@ export default {
options.Dest = new String(options.Dest);
}

const structParent = options.structParent;
delete options.structParent;

// Capitalize keys
for (let key in options) {
const val = options[key];
Expand All @@ -27,6 +32,12 @@ export default {

const ref = this.ref(options);
this.page.annotations.push(ref);

if (structParent && typeof structParent.add === 'function') {
const annotRef = new PDFAnnotationReference(ref);
structParent.add(annotRef);
}

ref.end();
return this;
},
Expand All @@ -47,7 +58,7 @@ export default {
options.Subtype = 'Link';
options.A = this.ref({
S: 'GoTo',
D: new String(name)
D: new String(name),
});
options.A.end();
return this.annotate(x, y, w, h, options);
Expand All @@ -62,7 +73,7 @@ export default {
if (url >= 0 && url < pages.Kids.length) {
options.A = this.ref({
S: 'GoTo',
D: [pages.Kids[url], 'XYZ', null, null, null]
D: [pages.Kids[url], 'XYZ', null, null, null],
});
options.A.end();
} else {
Expand All @@ -72,11 +83,15 @@ export default {
// Link to an external url
options.A = this.ref({
S: 'URI',
URI: new String(url)
URI: new String(url),
});
options.A.end();
}

if (options.structParent && !options.Contents) {
options.Contents = new String('');
}

return this.annotate(x, y, w, h, options);
},

Expand Down Expand Up @@ -164,5 +179,5 @@ export default {
y2 = m1 * x2 + m3 * y2 + m5;

return [x1, y1, x2, y2];
}
},
};
151 changes: 138 additions & 13 deletions packages/pdfkit/src/mixins/fonts.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,57 @@
import PDFFontFactory from '../font_factory';
import { CM_TO_IN, IN_TO_PT, MM_TO_CM, PC_TO_PT, PX_TO_IN } from '../utils';

const isEqualFont = (font1, font2) => {
// compare font checksum
if (
font1.font._tables?.head?.checkSumAdjustment !==
font2.font._tables?.head?.checkSumAdjustment
) {
return false;
}

// compare font name table
if (
JSON.stringify(font1.font._tables?.name?.records) !==
JSON.stringify(font2.font._tables?.name?.records)
) {
return false;
}

return true;
};

export default {
initFonts() {
initFonts(
defaultFont = 'Helvetica',
defaultFontFamily = null,
defaultFontSize = 12,
) {
// Lookup table for embedded fonts
this._fontFamilies = {};
this._fontCount = 0;

// Font state
this._fontSize = 12;
// Useful to export the font builder so that someone can create a snapshot of the current state
// (e.g. Reverting back to the previous font)
this._fontSource = defaultFont;
this._fontFamily = defaultFontFamily;
this._fontSize = defaultFontSize;
this._font = null;

// rem size is fixed per document as the document is the root element
this._remSize = defaultFontSize;

this._registeredFonts = {};

// Set the default font
return this.font('Helvetica');
if (defaultFont) {
this.font(defaultFont, defaultFontFamily);
}
},

font(src, family, size) {
let cacheKey;
let font;
let cacheKey, font;
if (typeof family === 'number') {
size = family;
family = null;
Expand All @@ -35,6 +68,8 @@ export default {
}
}

this._fontSource = src;
this._fontFamily = family;
if (size != null) {
this.fontSize(size);
}
Expand All @@ -51,7 +86,10 @@ export default {

// check for existing font familes with the same name already in the PDF
// useful if the font was passed as a buffer
if ((font = this._fontFamilies[this._font.name])) {
if (
(font = this._fontFamilies[this._font.name]) &&
isEqualFont(this._font, font)
) {
this._font = font;
return this;
}
Expand All @@ -61,31 +99,118 @@ export default {
this._fontFamilies[cacheKey] = this._font;
}

if (this._font.name) {
if (this._font.name && !this._fontFamilies[this._font.name]) {
this._fontFamilies[this._font.name] = this._font;
}

// if the font wasn't registered under any key (e.g. loaded via raw buffer
// with no cacheKey and a postscript name that collides with an
// already-registered font), register it under its id so it always gets
// finalized and doesn't leave a dangling references
if (
!cacheKey &&
(!this._font.name || this._fontFamilies[this._font.name] !== this._font)
) {
this._fontFamilies[this._font.id] = this._font;
}

return this;
},

fontSize(_fontSize) {
this._fontSize = _fontSize;
this._fontSize = this.sizeToPoint(_fontSize);
return this;
},

currentLineHeight(includeGap) {
if (includeGap == null) {
includeGap = false;
}
return this._font.lineHeight(this._fontSize, includeGap);
},

registerFont(name, src, family) {
this._registeredFonts[name] = {
src,
family
family,
};

return this;
}
},

/**
* Convert a {@link Size} into a point measurement
*
* @param {Size | boolean | undefined} size - The size to convert
* @param {Size | boolean | undefined} defaultValue - The default value when undefined
* @param {PDFPage} page - The page used for computing font sizes
* @param {number} [percentageWidth] - The value to use for computing size based on `%`
*
* @returns number
*/
sizeToPoint(
size,
defaultValue = 0,
page = this.page,
percentageWidth = undefined,
) {
if (!percentageWidth) percentageWidth = this._fontSize;
if (typeof defaultValue !== 'number')
defaultValue = this.sizeToPoint(defaultValue);
if (size === undefined) return defaultValue;
if (typeof size === 'number') return size;
if (typeof size === 'boolean') return Number(size);

const match = String(size).match(
/((\d+)?(\.\d+)?)(em|in|px|cm|mm|pc|ex|ch|rem|vw|vh|vmin|vmax|%|pt)?/,
);
if (!match) throw new Error(`Unsupported size '${size}'`);
let multiplier;
switch (match[4]) {
case 'em':
multiplier = this._fontSize;
break;
case 'in':
multiplier = IN_TO_PT;
break;
case 'px':
multiplier = PX_TO_IN * IN_TO_PT;
break;
case 'cm':
multiplier = CM_TO_IN * IN_TO_PT;
break;
case 'mm':
multiplier = MM_TO_CM * CM_TO_IN * IN_TO_PT;
break;
case 'pc':
multiplier = PC_TO_PT;
break;
case 'ex':
multiplier = this.currentLineHeight();
break;
case 'ch':
multiplier = this.widthOfString('0');
break;
case 'rem':
multiplier = this._remSize;
break;
case 'vw':
multiplier = page.width / 100;
break;
case 'vh':
multiplier = page.height / 100;
break;
case 'vmin':
multiplier = Math.min(page.width, page.height) / 100;
break;
case 'vmax':
multiplier = Math.max(page.width, page.height) / 100;
break;
case '%':
multiplier = percentageWidth / 100;
break;
case 'pt':
default:
multiplier = 1;
}

return multiplier * Number(match[1]);
},
};
Loading
Loading