Skip to content

Commit

Permalink
Add PDFDocument.load.capNumbers option (#461)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hopding authored May 25, 2020
1 parent 3256473 commit 13ed2ef
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 23 deletions.
3 changes: 3 additions & 0 deletions src/api/PDFDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export interface LoadOptions {
parseSpeed?: ParseSpeeds | number;
throwOnInvalidObject?: boolean;
updateMetadata?: boolean;
capNumbers?: boolean;
}

export interface CreateOptions {
Expand Down Expand Up @@ -146,6 +147,7 @@ export default class PDFDocument {
parseSpeed = ParseSpeeds.Slow,
throwOnInvalidObject = false,
updateMetadata = true,
capNumbers = false,
} = options;

assertIs(pdf, 'pdf', ['string', Uint8Array, ArrayBuffer]);
Expand All @@ -158,6 +160,7 @@ export default class PDFDocument {
bytes,
parseSpeed,
throwOnInvalidObject,
capNumbers,
).parseDocument();
return new PDFDocument(context, ignoreEncryption, updateMetadata);
}
Expand Down
15 changes: 11 additions & 4 deletions src/core/parser/BaseParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ const { Newline, CarriageReturn } = CharCodes;
// TODO: Throw error if eof is reached before finishing object parse...
class BaseParser {
protected readonly bytes: ByteStream;
protected readonly capNumbers: boolean;

constructor(bytes: ByteStream) {
constructor(bytes: ByteStream, capNumbers = false) {
this.bytes = bytes;
this.capNumbers = capNumbers;
}

protected parseRawInt(): number {
Expand Down Expand Up @@ -60,9 +62,14 @@ class BaseParser {
}

if (numberValue > Number.MAX_SAFE_INTEGER) {
const msg = `Parsed number that is too large for some PDF readers: ${value}, using Number.MAX_SAFE_INTEGER instead.`;
console.warn(msg);
return Number.MAX_SAFE_INTEGER;
if (this.capNumbers) {
const msg = `Parsed number that is too large for some PDF readers: ${value}, using Number.MAX_SAFE_INTEGER instead.`;
console.warn(msg);
return Number.MAX_SAFE_INTEGER;
} else {
const msg = `Parsed number that is too large for some PDF readers: ${value}, not capping.`;
console.warn(msg);
}
}

return numberValue;
Expand Down
20 changes: 13 additions & 7 deletions src/core/parser/PDFObjectParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,22 @@ import { charFromCode } from 'src/utils';

// TODO: Throw error if eof is reached before finishing object parse...
class PDFObjectParser extends BaseParser {
static forBytes = (bytes: Uint8Array, context: PDFContext) =>
new PDFObjectParser(ByteStream.of(bytes), context);

static forByteStream = (byteStream: ByteStream, context: PDFContext) =>
new PDFObjectParser(byteStream, context);
static forBytes = (
bytes: Uint8Array,
context: PDFContext,
capNumbers?: boolean,
) => new PDFObjectParser(ByteStream.of(bytes), context, capNumbers);

static forByteStream = (
byteStream: ByteStream,
context: PDFContext,
capNumbers = false,
) => new PDFObjectParser(byteStream, context, capNumbers);

protected readonly context: PDFContext;

constructor(byteStream: ByteStream, context: PDFContext) {
super(byteStream);
constructor(byteStream: ByteStream, context: PDFContext, capNumbers = false) {
super(byteStream, capNumbers);
this.context = context;
}

Expand Down
9 changes: 6 additions & 3 deletions src/core/parser/PDFParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ class PDFParser extends PDFObjectParser {
pdfBytes: Uint8Array,
objectsPerTick?: number,
throwOnInvalidObject?: boolean,
) => new PDFParser(pdfBytes, objectsPerTick, throwOnInvalidObject);
capNumbers?: boolean,
) =>
new PDFParser(pdfBytes, objectsPerTick, throwOnInvalidObject, capNumbers);

private readonly objectsPerTick: number;
private readonly throwOnInvalidObject: boolean;
Expand All @@ -38,10 +40,11 @@ class PDFParser extends PDFObjectParser {

constructor(
pdfBytes: Uint8Array,
objectsPerTick: number = Infinity,
objectsPerTick = Infinity,
throwOnInvalidObject = false,
capNumbers = false,
) {
super(ByteStream.of(pdfBytes), PDFContext.create());
super(ByteStream.of(pdfBytes), PDFContext.create(), capNumbers);
this.objectsPerTick = objectsPerTick;
this.throwOnInvalidObject = throwOnInvalidObject;
}
Expand Down
43 changes: 34 additions & 9 deletions tests/core/parser/PDFObjectParser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,23 @@ import {
numberToString,
} from 'src/index';

const parse = (value: string | Uint8Array) => {
type ParseOptions = { capNumbers?: boolean };

const parse = (value: string | Uint8Array, options: ParseOptions = {}) => {
const context = PDFContext.create();
const parser = PDFObjectParser.forBytes(typedArrayFor(value), context);
const parser = PDFObjectParser.forBytes(
typedArrayFor(value),
context,
options.capNumbers,
);
return parser.parseObject();
};

const expectParse = (value: string | Uint8Array) => expect(parse(value));
const expectParse = (value: string | Uint8Array, options?: ParseOptions) =>
expect(parse(value, options));

const expectParseStr = (value: string | Uint8Array) =>
expect(String(parse(value)));
const expectParseStr = (value: string | Uint8Array, options?: ParseOptions) =>
expect(String(parse(value, options)));

describe(`PDFObjectParser`, () => {
const origConsoleWarn = console.warn;
Expand Down Expand Up @@ -154,21 +161,39 @@ describe(`PDFObjectParser`, () => {
expect(parser.parseObject().toString()).toBe('-0.1');
});

it(`caps numbers at Number.MAX_SAFE_INTEGER`, () => {
it(`caps numbers at Number.MAX_SAFE_INTEGER when capNumbers=true`, () => {
expectParseStr(numberToString(Number.MAX_SAFE_INTEGER - 1), {
capNumbers: true,
}).toBe('9007199254740990');
expectParseStr(numberToString(Number.MAX_SAFE_INTEGER), {
capNumbers: true,
}).toBe('9007199254740991');
expectParseStr(numberToString(Number.MAX_SAFE_INTEGER + 1), {
capNumbers: true,
}).toBe('9007199254740991');
expectParseStr('340282346638528900000000000000000000000', {
capNumbers: true,
}).toBe('9007199254740991');
expectParseStr('340282346638528859811704183484516925440', {
capNumbers: true,
}).toBe('9007199254740991');
});

it(`does not cap numbers at Number.MAX_SAFE_INTEGER when capNumbers=false`, () => {
expectParseStr(numberToString(Number.MAX_SAFE_INTEGER - 1)).toBe(
'9007199254740990',
);
expectParseStr(numberToString(Number.MAX_SAFE_INTEGER)).toBe(
'9007199254740991',
);
expectParseStr(numberToString(Number.MAX_SAFE_INTEGER + 1)).toBe(
'9007199254740991',
'9007199254740992',
);
expectParseStr('340282346638528900000000000000000000000').toBe(
'9007199254740991',
'340282346638528900000000000000000000000',
);
expectParseStr('340282346638528859811704183484516925440').toBe(
'9007199254740991',
'340282346638528900000000000000000000000',
);
});
});
Expand Down

0 comments on commit 13ed2ef

Please sign in to comment.