From 4ed3290cde6178ba4dbebeb085a86a2e83fffd44 Mon Sep 17 00:00:00 2001 From: Nikischin Date: Sun, 2 Jun 2024 13:16:39 +0200 Subject: [PATCH 1/6] Possible fix for dpi (untested) --- packages/layout/src/page/getSize.js | 15 --------------- packages/layout/src/steps/resolveStyles.js | 2 +- packages/stylesheet/src/transform/units.js | 2 +- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/packages/layout/src/page/getSize.js b/packages/layout/src/page/getSize.js index 76ee76aa6..6db1191a5 100644 --- a/packages/layout/src/page/getSize.js +++ b/packages/layout/src/page/getSize.js @@ -70,18 +70,6 @@ const toSizeObject = (v) => ({ width: v[0], height: v[1] }); */ const flipSizeObject = (v) => ({ width: v.height, height: v.width }); -/** - * Adjust page size to passed DPI - * - * @param {{ width: number, height: number }} v size object - * @param {number} dpi DPI - * @returns {{ width: number, height: number }} adjusted size object - */ -const adjustDpi = (v, dpi) => ({ - width: v.width ? v.width * dpi : v.width, - height: v.height ? v.height * dpi : v.height, -}); - /** * Returns size object from a given string * @@ -108,7 +96,6 @@ const getNumberSize = (n) => toSizeObject([n]); */ const getSize = (page) => { const value = page.props?.size || 'A4'; - const dpi = parseFloat(page.props?.dpi || 72); const type = typeof value; @@ -127,8 +114,6 @@ const getSize = (page) => { size = value; } - size = adjustDpi(size, dpi / 72); - return isLandscape(page) ? flipSizeObject(size) : size; }; diff --git a/packages/layout/src/steps/resolveStyles.js b/packages/layout/src/steps/resolveStyles.js index 1c24b3c8d..b94637432 100644 --- a/packages/layout/src/steps/resolveStyles.js +++ b/packages/layout/src/steps/resolveStyles.js @@ -56,7 +56,7 @@ const resolveNodeStyles = (container) => (node) => { * @returns {Object} document page with resolved styles */ export const resolvePageStyles = (page) => { - const dpi = page.props?.dpi || 72; + const dpi = 72; // Removed: page.props?.dpi || 72; const width = page.box?.width || page.style.width; const height = page.box?.height || page.style.height; const orientation = page.props?.orientation || 'portrait'; diff --git a/packages/stylesheet/src/transform/units.js b/packages/stylesheet/src/transform/units.js index c4f18fe96..01ec54b2e 100644 --- a/packages/stylesheet/src/transform/units.js +++ b/packages/stylesheet/src/transform/units.js @@ -22,7 +22,7 @@ const parseValue = (value) => { const transformUnit = (container, value) => { const scalar = parseValue(value); - const dpi = container.dpi || 72; + const dpi = 72; // Removed: container.dpi || 72 const mmFactor = (1 / 25.4) * dpi; const cmFactor = (1 / 2.54) * dpi; From f5b8ecab944359678885c58889945cb35e749f27 Mon Sep 17 00:00:00 2001 From: Nikischin Date: Sun, 2 Jun 2024 14:34:45 +0200 Subject: [PATCH 2/6] Allow to pass pixel sizes --- .changeset/pretty-jars-begin.md | 9 +++++++++ packages/layout/src/page/getSize.js | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 .changeset/pretty-jars-begin.md diff --git a/.changeset/pretty-jars-begin.md b/.changeset/pretty-jars-begin.md new file mode 100644 index 000000000..aa5edf5c6 --- /dev/null +++ b/.changeset/pretty-jars-begin.md @@ -0,0 +1,9 @@ +--- +'@react-pdf/layout': minor +'@react-pdf/pdfkit': minor +'@react-pdf/render': minor +'@react-pdf/stylesheet': minor +'@react-pdf/types': minor +--- + +fix: fix dpi \ No newline at end of file diff --git a/packages/layout/src/page/getSize.js b/packages/layout/src/page/getSize.js index 6db1191a5..4fe676a2d 100644 --- a/packages/layout/src/page/getSize.js +++ b/packages/layout/src/page/getSize.js @@ -70,6 +70,18 @@ const toSizeObject = (v) => ({ width: v[0], height: v[1] }); */ const flipSizeObject = (v) => ({ width: v.height, height: v.width }); +/** + * Adjust page size to passed DPI + * + * @param {{ width: number, height: number }} v size object + * @param {number} dpi DPI + * @returns {{ width: number, height: number }} adjusted size object + */ +const adjustDpi = (v, dpi) => ({ + width: v.width ? v.width * (72 / dpi) : v.width, + height: v.height ? v.height * (72 / dpi) : v.height, +}); + /** * Returns size object from a given string * @@ -96,6 +108,7 @@ const getNumberSize = (n) => toSizeObject([n]); */ const getSize = (page) => { const value = page.props?.size || 'A4'; + const dpi = parseFloat(page.props?.dpi || 72); const type = typeof value; @@ -108,10 +121,13 @@ const getSize = (page) => { size = getStringSize(value); } else if (Array.isArray(value)) { size = toSizeObject(value); + size = adjustDpi(size, dpi); } else if (type === 'number') { size = getNumberSize(value); + size = adjustDpi(size, dpi); } else { size = value; + size = adjustDpi(size, dpi); } return isLandscape(page) ? flipSizeObject(size) : size; From 5b9608ab7abf0f757ade5c69c295267ab8066b00 Mon Sep 17 00:00:00 2001 From: Nikischin Date: Mon, 3 Jun 2024 13:53:58 +0200 Subject: [PATCH 3/6] Allow units for page size --- packages/layout/src/page/getSize.js | 78 ++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 24 deletions(-) diff --git a/packages/layout/src/page/getSize.js b/packages/layout/src/page/getSize.js index 4fe676a2d..39b84a83a 100644 --- a/packages/layout/src/page/getSize.js +++ b/packages/layout/src/page/getSize.js @@ -1,5 +1,6 @@ import isLandscape from './isLandscape'; +// page sizes for 72dpi. 72dpi is used internally by pdfkit const PAGE_SIZES = { '4A0': [4767.87, 6740.79], '2A0': [3370.39, 4767.87], @@ -54,11 +55,56 @@ const PAGE_SIZES = { ID1: [153, 243], }; +/** + * Parses scalar value in value and unit pairs + * + * @param {string} value scalar value + * @returns {Object} parsed value + */ +const parseValue = (value) => { + const match = /^(-?\d*\.?\d+)(in|mm|cm|pt|px)?$/g.exec(value); + + return match + ? { value: parseFloat(match[1]), unit: match[2] || 'pt' } + : { value, unit: undefined }; +}; + +/** + * Transform given scalar value to 72dpi equivalent of size + * + * @param {string} value styles value + * @param {number} inputDpi user defined dpi + * @returns {Object} transformed value + */ +const transformUnit = (value, inputDpi) => { + const scalar = parseValue(value); + + const outputDpi = 72; + const mmFactor = (1 / 25.4) * outputDpi; + const cmFactor = (1 / 2.54) * outputDpi; + + switch (scalar.unit) { + case 'in': + return scalar.value * outputDpi; + case 'mm': + return scalar.value * mmFactor; + case 'cm': + return scalar.value * cmFactor; + default: + return scalar.value * (outputDpi / inputDpi); + } +}; + +const transformUnits = ({ width, height }, dpi) => ({ + width: transformUnit(width, dpi), + height: transformUnit(height, dpi), +}); + /** * Transforms array into size object * - * @param {number[]} v array - * @returns {{ width: number, height: number }} size object with width and height + * @param {number[] | string[]} v array + * @returns {{ width: number | string, height: number | string }} size object with width and height */ const toSizeObject = (v) => ({ width: v[0], height: v[1] }); @@ -70,18 +116,6 @@ const toSizeObject = (v) => ({ width: v[0], height: v[1] }); */ const flipSizeObject = (v) => ({ width: v.height, height: v.width }); -/** - * Adjust page size to passed DPI - * - * @param {{ width: number, height: number }} v size object - * @param {number} dpi DPI - * @returns {{ width: number, height: number }} adjusted size object - */ -const adjustDpi = (v, dpi) => ({ - width: v.width ? v.width * (72 / dpi) : v.width, - height: v.height ? v.height * (72 / dpi) : v.height, -}); - /** * Returns size object from a given string * @@ -95,10 +129,10 @@ const getStringSize = (v) => { /** * Returns size object from a single number * - * @param {number} n page size number - * @returns {{ width: number, height: number }} size object with width and height + * @param {number|string} n page size number + * @returns {{ width: number|string, height: number|string }} size object with width and height */ -const getNumberSize = (n) => toSizeObject([n]); +const getNumberSize = (n) => toSizeObject([n, n]); /** * Return page size in an object { width, height } @@ -116,18 +150,14 @@ const getSize = (page) => { * @type {{ width: number, height: number }} */ let size; - if (type === 'string') { size = getStringSize(value); } else if (Array.isArray(value)) { - size = toSizeObject(value); - size = adjustDpi(size, dpi); + size = transformUnits(toSizeObject(value), dpi); } else if (type === 'number') { - size = getNumberSize(value); - size = adjustDpi(size, dpi); + size = transformUnits(getNumberSize(value), dpi); } else { - size = value; - size = adjustDpi(size, dpi); + size = transformUnits(value, dpi); } return isLandscape(page) ? flipSizeObject(size) : size; From 0554c1df638185a2adaca1e7c983e0a67231ab56 Mon Sep 17 00:00:00 2001 From: Nikischin Date: Sat, 5 Oct 2024 13:01:29 +0200 Subject: [PATCH 4/6] Fix pixel conversion --- .changeset/old-otters-marry.md | 6 ++++++ packages/layout/src/page/getSize.js | 4 +++- packages/layout/src/steps/resolveStyles.js | 2 +- packages/stylesheet/src/transform/units.js | 11 +++++++---- 4 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 .changeset/old-otters-marry.md diff --git a/.changeset/old-otters-marry.md b/.changeset/old-otters-marry.md new file mode 100644 index 000000000..839289156 --- /dev/null +++ b/.changeset/old-otters-marry.md @@ -0,0 +1,6 @@ +--- +"@react-pdf/stylesheet": major +"@react-pdf/layout": major +--- + +Changed unit behavior according to PDF spec. Please note that all unitless values are considered as user unit which is a 72dpi equality of the value. This is according to PDF spec and ensures a consistent layout independent of the dpi setting. diff --git a/packages/layout/src/page/getSize.js b/packages/layout/src/page/getSize.js index 39b84a83a..8e092b2aa 100644 --- a/packages/layout/src/page/getSize.js +++ b/packages/layout/src/page/getSize.js @@ -90,8 +90,10 @@ const transformUnit = (value, inputDpi) => { return scalar.value * mmFactor; case 'cm': return scalar.value * cmFactor; + case 'px': + return Math.round(scalar.value * (outputDpi / inputDpi)); default: - return scalar.value * (outputDpi / inputDpi); + return scalar.value; } }; diff --git a/packages/layout/src/steps/resolveStyles.js b/packages/layout/src/steps/resolveStyles.js index b94637432..1c24b3c8d 100644 --- a/packages/layout/src/steps/resolveStyles.js +++ b/packages/layout/src/steps/resolveStyles.js @@ -56,7 +56,7 @@ const resolveNodeStyles = (container) => (node) => { * @returns {Object} document page with resolved styles */ export const resolvePageStyles = (page) => { - const dpi = 72; // Removed: page.props?.dpi || 72; + const dpi = page.props?.dpi || 72; const width = page.box?.width || page.style.width; const height = page.box?.height || page.style.height; const orientation = page.props?.orientation || 'portrait'; diff --git a/packages/stylesheet/src/transform/units.js b/packages/stylesheet/src/transform/units.js index 01ec54b2e..cfed663d9 100644 --- a/packages/stylesheet/src/transform/units.js +++ b/packages/stylesheet/src/transform/units.js @@ -22,13 +22,14 @@ const parseValue = (value) => { const transformUnit = (container, value) => { const scalar = parseValue(value); - const dpi = 72; // Removed: container.dpi || 72 - const mmFactor = (1 / 25.4) * dpi; - const cmFactor = (1 / 2.54) * dpi; + const outputDpi = 72; + const inputDpi = container.dpi || 72; + const mmFactor = (1 / 25.4) * outputDpi; + const cmFactor = (1 / 2.54) * outputDpi; switch (scalar.unit) { case 'in': - return scalar.value * dpi; + return scalar.value * outputDpi; case 'mm': return scalar.value * mmFactor; case 'cm': @@ -37,6 +38,8 @@ const transformUnit = (container, value) => { return scalar.value * (container.height / 100); case 'vw': return scalar.value * (container.width / 100); + case 'px': + return Math.round(scalar.value * (outputDpi / inputDpi)); default: return scalar.value; } From 7275d7fe61a3733ad3108317395ee9027e249ab8 Mon Sep 17 00:00:00 2001 From: Tim Nikischin <49103409+nikischin@users.noreply.github.com> Date: Sun, 1 Dec 2024 19:41:13 +0100 Subject: [PATCH 5/6] Update old-otters-marry.md Update version update to minor (see separate comment) --- .changeset/old-otters-marry.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/old-otters-marry.md b/.changeset/old-otters-marry.md index 839289156..92e6ac418 100644 --- a/.changeset/old-otters-marry.md +++ b/.changeset/old-otters-marry.md @@ -1,6 +1,6 @@ --- -"@react-pdf/stylesheet": major -"@react-pdf/layout": major +"@react-pdf/stylesheet": minor +"@react-pdf/layout": minor --- Changed unit behavior according to PDF spec. Please note that all unitless values are considered as user unit which is a 72dpi equality of the value. This is according to PDF spec and ensures a consistent layout independent of the dpi setting. From baec01d19e5b6a09f9bfcd941262cf77516beb37 Mon Sep 17 00:00:00 2001 From: Tim Nikischin <49103409+nikischin@users.noreply.github.com> Date: Sun, 1 Dec 2024 19:43:34 +0100 Subject: [PATCH 6/6] Update packages/layout/src/page/getSize.js Co-authored-by: Diego Muracciole --- packages/layout/src/page/getSize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/layout/src/page/getSize.js b/packages/layout/src/page/getSize.js index 8e092b2aa..f78533009 100644 --- a/packages/layout/src/page/getSize.js +++ b/packages/layout/src/page/getSize.js @@ -1,6 +1,6 @@ import isLandscape from './isLandscape'; -// page sizes for 72dpi. 72dpi is used internally by pdfkit +// Page sizes for 72dpi. 72dpi is used internally by pdfkit. const PAGE_SIZES = { '4A0': [4767.87, 6740.79], '2A0': [3370.39, 4767.87],