Skip to content

Commit cb8603c

Browse files
committed
Fix further LineWrapper precision issues
1 parent f3c1776 commit cb8603c

File tree

4 files changed

+55
-7
lines changed

4 files changed

+55
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### Unreleased
44

55
- Fix null values in table cells rendering as `[object Object]`
6+
- Fix further LineWrapper precision issues
67

78
### [v0.17.0] - 2025-04-12
89

lib/line_wrapper.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ class LineWrapper extends EventEmitter {
8585
}
8686

8787
wordWidth(word) {
88-
return (
88+
return PDFNumber(
8989
this.document.widthOfString(word, this) +
90-
this.characterSpacing +
91-
this.wordSpacing
90+
this.characterSpacing +
91+
this.wordSpacing,
9292
);
9393
}
9494

lib/utils.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,27 @@
1-
export function PDFNumber(n) {
1+
const fArray = new Float32Array(1);
2+
const uArray = new Uint32Array(fArray.buffer);
3+
4+
export function PDFNumber(n, roundUp = false) {
25
// PDF numbers are strictly 32bit
3-
// so convert this number to the nearest 32bit number
6+
// so convert this number to a 32bit number
47
// @see ISO 32000-1 Annex C.2 (real numbers)
5-
return Math.fround(n);
8+
const rounded = Math.fround(n);
9+
if (roundUp) {
10+
if (rounded >= n) return rounded;
11+
} else if (rounded <= n) return rounded;
12+
13+
// Will have to perform 32bit float truncation
14+
fArray[0] = n;
15+
16+
// Get the 32-bit representation as integer and shift bits
17+
if (!(roundUp ^ (n > 0))) {
18+
uArray[0] += 1;
19+
} else {
20+
uArray[0] -= 1;
21+
}
22+
23+
// Return the float value
24+
return fArray[0];
625
}
726

827
/**

tests/unit/utils.spec.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { normalizeSides } from '../../lib/utils';
1+
import { normalizeSides, PDFNumber } from '../../lib/utils';
22

33
describe('normalizeSides', () => {
44
test.each([
@@ -54,3 +54,31 @@ describe('normalizeSides', () => {
5454
});
5555
});
5656
});
57+
58+
describe('PDFNumber', () => {
59+
test.each([
60+
[0, 0],
61+
[0.04999999701976776], //float32 rounded down
62+
[0.05],
63+
[0.05000000074505806], //float32 rounded up
64+
[1],
65+
[-1],
66+
[-5.05],
67+
[5.05],
68+
])('PDFNumber(%f) -> %f', (n) => {
69+
expect(PDFNumber(n)).toBeLessThanOrEqual(n);
70+
expect(PDFNumber(n, false)).toBeLessThanOrEqual(n);
71+
});
72+
test.each([
73+
[0],
74+
[0.04999999701976776], //float32 rounded down
75+
[0.05],
76+
[0.05000000074505806], //float32 rounded up
77+
[1],
78+
[-1],
79+
[-5.05],
80+
[5.05],
81+
])('PDFNumber(%f, true) -> %f', (n) => {
82+
expect(PDFNumber(n, true)).toBeGreaterThanOrEqual(n);
83+
});
84+
});

0 commit comments

Comments
 (0)