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
102 changes: 81 additions & 21 deletions src/api/PDFDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
CreateOptions,
EmbedFontOptions,
SetTitleOptions,
FileSaveOptions,
} from './PDFDocumentOptions';
import PDFObject from '../core/objects/PDFObject';
import PDFRef from '../core/objects/PDFRef';
Expand Down Expand Up @@ -1358,28 +1359,10 @@ export default class PDFDocument {
* @returns Resolves with the bytes of the serialized document.
*/
async save(options: SaveOptions = {}): Promise<Uint8Array> {
const {
useObjectStreams = true,
addDefaultPage = true,
objectsPerTick = 50,
updateFieldAppearances = true,
} = options;

assertIs(useObjectStreams, 'useObjectStreams', ['boolean']);
assertIs(addDefaultPage, 'addDefaultPage', ['boolean']);
assertIs(objectsPerTick, 'objectsPerTick', ['number']);
assertIs(updateFieldAppearances, 'updateFieldAppearances', ['boolean']);

if (addDefaultPage && this.getPageCount() === 0) this.addPage();

if (updateFieldAppearances) {
const form = this.formCache.getValue();
if (form) form.updateFieldAppearances();
}

await this.flush();
const defaultOptions = this.getDefaultSaveOptions(options);
const { objectsPerTick } = defaultOptions;

const Writer = useObjectStreams ? PDFStreamWriter : PDFWriter;
const Writer = await this.validateAndGetAdaptWriter(defaultOptions);
return Writer.forContext(this.context, objectsPerTick).serializeToBuffer();
}

Expand All @@ -1406,6 +1389,32 @@ export default class PDFDocument {
return dataUri ? `data:application/pdf;base64,${base64}` : base64;
}

/**
*
* Serialize this document to specific directory path formed with A PDF file
* For example:
* ```js
* const pdfBuffer = await saveToTargetPath { destPath: "/some/your/directory.pdf" }
* ```
*
* @param options The options are used to determine which path to write
* @returns Serialized Buffer Array from input Destination Path which is located The PDF file
*
*/
async saveToTargetPath(options: FileSaveOptions): Promise<Uint8Array> {
options && options.outputPath && assertIsValidString(options.outputPath);
const resolvedSaveOptions = this.getDefaultSaveOptions(options);
const { objectsPerTick } = resolvedSaveOptions;

const Writer = await this.validateAndGetAdaptWriter(resolvedSaveOptions);
const { outputPath, forceWrite } = options;

return Writer.forContext(this.context, objectsPerTick).writeToTargetPath({
outputPath,
forceWrite: forceWrite !== undefined ? forceWrite : true,
});
}

findPageForAnnotationRef(ref: PDFRef): PDFPage | undefined {
const pages = this.getPages();
for (let idx = 0, len = pages.length; idx < len; idx++) {
Expand All @@ -1420,6 +1429,53 @@ export default class PDFDocument {
return undefined;
}

private async validateAndGetAdaptWriter(
options: SaveOptions,
): Promise<typeof PDFStreamWriter | typeof PDFWriter> {
const {
useObjectStreams,
addDefaultPage,
objectsPerTick,
updateFieldAppearances,
} = options;

assertIs(useObjectStreams, 'useObjectStreams', ['boolean']);
assertIs(addDefaultPage, 'addDefaultPage', ['boolean']);
assertIs(objectsPerTick, 'objectsPerTick', ['number']);
assertIs(updateFieldAppearances, 'updateFieldAppearances', ['boolean']);

if (addDefaultPage && this.getPageCount() === 0) this.addPage();

if (updateFieldAppearances) {
const form = this.formCache.getValue();
if (form) form.updateFieldAppearances();
}

await this.flush();
return useObjectStreams ? PDFStreamWriter : PDFWriter;
}

private getDefaultSaveOptions(options: SaveOptions): {
useObjectStreams: boolean;
addDefaultPage: boolean;
objectsPerTick: number;
updateFieldAppearances: boolean;
} {
const {
useObjectStreams = true,
addDefaultPage = true,
objectsPerTick = 50,
updateFieldAppearances = true,
} = options;

return {
useObjectStreams,
addDefaultPage,
objectsPerTick,
updateFieldAppearances,
};
}

private async embedAll(embeddables: Embeddable[]): Promise<void> {
for (let idx = 0, len = embeddables.length; idx < len; idx++) {
await embeddables[idx].embed();
Expand Down Expand Up @@ -1486,3 +1542,7 @@ function assertIsLiteralOrHexString(
throw new UnexpectedObjectTypeError([PDFHexString, PDFString], pdfObject);
}
}

function assertIsValidString(str?: any): str is string {
return str !== null && str !== undefined && typeof str === 'string';
}
5 changes: 5 additions & 0 deletions src/api/PDFDocumentOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ export interface Base64SaveOptions extends SaveOptions {
dataUri?: boolean;
}

export interface FileSaveOptions extends SaveOptions {
outputPath: string;
forceWrite?: boolean;
}

export interface LoadOptions {
ignoreEncryption?: boolean;
parseSpeed?: ParseSpeeds | number;
Expand Down
46 changes: 45 additions & 1 deletion src/core/document/PDFCrossRefSection.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Writable } from 'stream';
import PDFRef from '../objects/PDFRef';
import CharCodes from '../syntax/CharCodes';
import { copyStringIntoBuffer, padStart } from '../../utils';
import {
convertStringToUnicodeArray,
copyStringIntoBuffer,
padStart,
} from '../../utils';

export interface Entry {
ref: PDFRef;
Expand Down Expand Up @@ -82,6 +87,45 @@ class PDFCrossRefSection {
return size;
}

writeBytesInto(stream: Writable) {
stream.write(
Buffer.from([
CharCodes.x,
CharCodes.r,
CharCodes.e,
CharCodes.f,
CharCodes.Newline,
]),
);

const writeEntriesIntoStream = (entries: Entry[], stream: Writable) => {
entries.forEach((entry) => {
const entryOffset = padStart(String(entry.offset), 10, '0');
stream.write(convertStringToUnicodeArray(entryOffset));
stream.write(Buffer.from([CharCodes.Space]));

const entryGen = padStart(String(entry.ref.generationNumber), 5, '0');
stream.write(convertStringToUnicodeArray(entryGen));
stream.write(Buffer.from([CharCodes.Space]));
stream.write(Buffer.from([entry.deleted ? CharCodes.f : CharCodes.n]));
stream.write(Buffer.from([CharCodes.Space]));
stream.write(Buffer.from([CharCodes.Newline]));
});
};

// NOTE: String그냥쓰는 코드 있는지 확인해야해....
this.subsections.forEach((subsection) => {
stream.write(
convertStringToUnicodeArray(String(subsection[0].ref.objectNumber)),
);
stream.write(Buffer.from([CharCodes.Space]));

stream.write(convertStringToUnicodeArray(String(subsection.length)));
stream.write(Buffer.from([CharCodes.Newline]));
writeEntriesIntoStream(subsection, stream);
});
}

copyBytesInto(buffer: Uint8Array, offset: number): number {
const initialOffset = offset;

Expand Down
24 changes: 23 additions & 1 deletion src/core/document/PDFHeader.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Writable } from 'stream';
import CharCodes from '../syntax/CharCodes';
import { charFromCode, copyStringIntoBuffer } from '../../utils';
import {
charFromCode,
convertStringToUnicodeArray,
copyStringIntoBuffer,
} from '../../utils';

class PDFHeader {
static forVersion = (major: number, minor: number) =>
Expand Down Expand Up @@ -48,6 +53,23 @@ class PDFHeader {

return offset - initialOffset;
}

writeBytesInto(stream: Writable): void {
stream.write(
Buffer.from([
CharCodes.Percent,
CharCodes.P,
CharCodes.D,
CharCodes.F,
CharCodes.Dash,
]),
);
stream.write(convertStringToUnicodeArray(this.major));
stream.write(Buffer.from([CharCodes.Period]));
stream.write(convertStringToUnicodeArray(this.minor));
stream.write(Buffer.from([CharCodes.Newline]));
stream.write(Buffer.from([CharCodes.Percent, 129, 129, 129, 129]));
}
}

export default PDFHeader;
33 changes: 32 additions & 1 deletion src/core/document/PDFTrailer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import CharCodes from '../syntax/CharCodes';
import { copyStringIntoBuffer } from '../../utils';
import { convertStringToUnicodeArray, copyStringIntoBuffer } from '../../utils';
import { Writable } from 'stream';

class PDFTrailer {
static forLastCrossRefSectionOffset = (offset: number) =>
Expand Down Expand Up @@ -44,6 +45,36 @@ class PDFTrailer {

return offset - initialOffset;
}

writeBytesInto(stream: Writable): void {
stream.write(
Buffer.from([
CharCodes.s,
CharCodes.t,
CharCodes.a,
CharCodes.r,
CharCodes.t,
CharCodes.x,
CharCodes.r,
CharCodes.e,
CharCodes.f,
CharCodes.Newline,
]),
);

stream.write(convertStringToUnicodeArray(this.lastXRefOffset));

stream.write(
Buffer.from([
CharCodes.Newline,
CharCodes.Percent,
CharCodes.Percent,
CharCodes.E,
CharCodes.O,
CharCodes.F,
]),
);
}
}

export default PDFTrailer;
18 changes: 18 additions & 0 deletions src/core/document/PDFTrailerDict.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Writable } from 'stream';
import PDFDict from '../objects/PDFDict';
import CharCodes from '../syntax/CharCodes';

Expand Down Expand Up @@ -34,6 +35,23 @@ class PDFTrailerDict {

return offset - initialOffset;
}

writeBytesInto(stream: Writable): void {
stream.write(
Buffer.from([
CharCodes.t,
CharCodes.r,
CharCodes.a,
CharCodes.i,
CharCodes.l,
CharCodes.e,
CharCodes.r,
CharCodes.Newline,
]),
);

this.dict.writeBytesInto(stream);
}
}

export default PDFTrailerDict;
12 changes: 12 additions & 0 deletions src/core/objects/PDFArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import PDFContext from '../PDFContext';
import CharCodes from '../syntax/CharCodes';
import { PDFArrayIsNotRectangleError } from '../errors';
import PDFRawStream from './PDFRawStream';
import { Writable } from 'stream';

class PDFArray extends PDFObject {
static withContext = (context: PDFContext) => new PDFArray(context);
Expand Down Expand Up @@ -171,6 +172,17 @@ class PDFArray extends PDFObject {
return offset - initialOffset;
}

writeBytesInto(stream: Writable): void {
stream.write(Buffer.from([CharCodes.LeftSquareBracket, CharCodes.Space]));

this.array.forEach((obj) => {
obj.writeBytesInto(stream);
stream.write(Buffer.from([CharCodes.Space]));
});

stream.write(Buffer.from([CharCodes.RightSquareBracket]));
}

scalePDFNumbers(x: number, y: number): void {
for (let idx = 0, len = this.size(); idx < len; idx++) {
const el = this.lookup(idx);
Expand Down
17 changes: 17 additions & 0 deletions src/core/objects/PDFBool.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { PrivateConstructorError } from '../errors';
import PDFObject from './PDFObject';
import CharCodes from '../syntax/CharCodes';
import { Writable } from 'stream';

const ENFORCER = {};

Expand Down Expand Up @@ -48,6 +49,22 @@ class PDFBool extends PDFObject {
return 5;
}
}

writeBytesInto(stream: Writable): void {
this.value
? stream.write(
Buffer.from([CharCodes.t, CharCodes.r, CharCodes.u, CharCodes.e]),
)
: stream.write(
Buffer.from([
CharCodes.f,
CharCodes.a,
CharCodes.l,
CharCodes.s,
CharCodes.e,
]),
);
}
}

export default PDFBool;
Loading
Loading