Skip to content

Commit

Permalink
Add Gc test, remove load(buffer)
Browse files Browse the repository at this point in the history
loading from a buffer is throwing zlib errors, research required to implement properly
  • Loading branch information
corymickelson committed Mar 2, 2018
1 parent cdc44ea commit f33f334
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 57 deletions.
16 changes: 16 additions & 0 deletions lib/document.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,22 @@ tap('Document Api', sub => {
})
})
})

sub.test('gc', standard => {
const output = `/tmp/${v4()}.pdf`
Document.gc(filePath, "", output, e => {
if(e instanceof Error) standard.fail()
else {
access(output, F_OK, err => {
if(err) standard.fail()
else {
standard.pass('Gc doc written to output location')
standard.end()
}
})
}
})
})
})

})
18 changes: 12 additions & 6 deletions lib/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {Encrypt, EncryptOption, ProtectionOption} from './encrypt';
import {EventEmitter} from 'events';
import {Font} from "./painter";
import {Signer} from './signer';
import {F_OK} from "constants";

export const __mod = require('bindings')('npdf')

Expand Down Expand Up @@ -81,6 +82,14 @@ export class Document extends EventEmitter {
} else return null
}

static gc(file:string, pwd:string, output:string, cb:(e:Error, d:string|Buffer) => void): void {
access(file, F_OK, err => {
if(err) {
throw Error('File not found')
}
__mod.Document.gc(file, pwd, output, cb)
})
}

/**
* File is loaded asynchronously, extends eventEmitter, will publish a 'ready'event when document has been loaded
Expand All @@ -89,28 +98,25 @@ export class Document extends EventEmitter {
* @param update
* @returns void
*/
constructor(file: string|Buffer, update: boolean = false) {
constructor(file: string, update: boolean = false) {
super()
this._instance = new __mod.Document()
if (typeof(file) === 'string') {
access(file, constants.F_OK | constants.R_OK, err => {
if (err){
this.emit('error', Error('file not found'))
} else {
this.load(file, update)
}
})
} else if(Buffer.isBuffer(file)) {
this.load(file, update)
}

}

/**
* load pdf file, emits 'ready' || 'error' events
* @param file - file path
* @param update - load document for incremental updates
*/
private load(file: string | Buffer, update: boolean = false): void {
private load(file: string, update: boolean = false): void {
this._instance.load(file, (e: Error) => {
if (e) {
this.emit('error', e)
Expand Down
89 changes: 38 additions & 51 deletions src/doc/Document.cc
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Document::GetPage(const CallbackInfo& info)
try {
if (info.Length() < 1 || !info[0].IsNumber()) {
throw Error::New(info.Env(),
"getPage takes an argument of 1, of type number.");
"getPage takes an argument of 1, of type number.");
}
int n = info[0].As<Number>();
int pl = document->GetPageCount();
Expand Down Expand Up @@ -152,7 +152,7 @@ Document::GetVersion(const CallbackInfo& info)
}
if (v == 0.0) {
throw Error::New(info.Env(),
"Failed to parse document. Pdf version unknown.");
"Failed to parse document. Pdf version unknown.");
}
return Number::New(info.Env(), v);
}
Expand Down Expand Up @@ -189,8 +189,8 @@ Document::MergeDocument(const CallbackInfo& info)
} catch (PdfError& err) {
if (err.GetError() == ePdfError_InvalidPassword && info.Length() != 2) {
throw Error::New(info.Env(),
"Password required to modify this document. Call "
"MergeDocument(filePath, password)");
"Password required to modify this document. Call "
"MergeDocument(filePath, password)");
} else if (err.GetError() == ePdfError_InvalidPassword &&
info.Length() == 2 && info[1].IsString()) {
string password = info[1].As<String>().Utf8Value();
Expand Down Expand Up @@ -229,10 +229,10 @@ Document::SetEncrypt(const CallbackInfo& info, const Napi::Value& value)
AssertFunctionArgs(info, 1, { napi_valuetype::napi_object });
if (!value.IsObject()) {
throw Error::New(info.Env(),
"Set encrypt requires a single argument of"
" type Object<{userPassword:string,"
" ownerPassword:string, protection:Array<string>,"
" algorithm: string, keyLength: int");
"Set encrypt requires a single argument of"
" type Object<{userPassword:string,"
" ownerPassword:string, protection:Array<string>,"
" algorithm: string, keyLength: int");
}
auto encryption = value.As<Object>();
string ownerPwd;
Expand Down Expand Up @@ -361,8 +361,7 @@ Document::GetEncrypt(const CallbackInfo& info)
const PdfEncrypt* enc = document->GetEncrypt();
auto ptr = const_cast<PdfEncrypt*>(enc);
auto encryptPtr = External<PdfEncrypt>::New(info.Env(), ptr);
auto instance = Encrypt::constructor.New(
{ this->Value(), encryptPtr });
auto instance = Encrypt::constructor.New({ this->Value(), encryptPtr });
return instance;
}

Expand Down Expand Up @@ -562,32 +561,18 @@ class DocumentLoadAsync : public AsyncWorker
}

void ForUpdate(bool v) { update = v; }
void LoadFromBuffer(bool v) { loadBuffer = v; }

private:
Document& doc;
string arg;
bool update = false;
bool loadBuffer = false;

// AsyncWorker interface
protected:
void Execute() override
{
try {
#if PODOFO_VERSION_PATCH < 6
if (loadBuffer) {
SetError("LoadFromBuffer is only available on PoDoFo version 0.9.6+");
}
#else
if (loadBuffer) {
doc.GetDocument()->LoadFromBuffer(arg.c_str(),
static_cast<long>(arg.length()));
}
#endif
else {
doc.GetDocument()->Load(arg.c_str(), update);
}
doc.GetDocument()->Load(arg.c_str(), update);
} catch (PdfError& e) {
if (e.GetError() == ePdfError_InvalidPassword) {
SetError("Password required to modify this document");
Expand All @@ -606,19 +591,7 @@ class DocumentLoadAsync : public AsyncWorker
Napi::Value
Document::Load(const CallbackInfo& info)
{
if (info.Length() < 2) {
throw Napi::Error::New(
info.Env(), "Load requires data (filepath or buffer) and callback");
}
string source;
if (info[0].IsString()) {
source = info[0].As<String>().Utf8Value();
}
bool loadBuffer = false;
if (info[0].IsBuffer()) {
source = info[0].As<Buffer<char>>().Data();
loadBuffer = true;
}
string source = info[0].As<String>().Utf8Value();
auto cb = info[1].As<Function>();
bool forUpdate = false;
if (info.Length() == 3) {
Expand All @@ -628,7 +601,6 @@ Document::Load(const CallbackInfo& info)
originPdf = source;
DocumentLoadAsync* worker = new DocumentLoadAsync(cb, *this, source);
worker->ForUpdate(forUpdate);
worker->LoadFromBuffer(loadBuffer);
worker->Queue();
return info.Env().Undefined();
}
Expand Down Expand Up @@ -680,10 +652,11 @@ Document::WriteBuffer(const CallbackInfo& info)
class GCAsync : public AsyncWorker
{
public:
GCAsync(const Function& callback, string doc, string pwd)
GCAsync(const Function& callback, string doc, string pwd, string output)
: AsyncWorker(callback)
, doc(std::move(doc))
, pwd(std::move(pwd))
, output(std::move(output))
{
}

Expand All @@ -699,31 +672,45 @@ class GCAsync : public AsyncWorker
if (parser.GetEncrypted()) {
writer.SetEncrypted(*(parser.GetEncrypt()));
}
PdfRefCountedBuffer r;
PdfOutputDevice device(&r);
writer.Write(&device);
value = Buffer<char>::Copy(Env(), r.GetBuffer(), r.GetSize());
if (output == "") {
PdfRefCountedBuffer r;
PdfOutputDevice device(&r);
writer.Write(&device);
value = string(r.GetBuffer());
size = r.GetSize();
} else {
writer.SetWriteMode(ePdfWriteMode_Compact);
writer.Write(output.c_str());
size = 0;
value = output;
}
}
void OnOK() override
{
HandleScope scope(Env());
Callback().Call({ Env().Null(), value });
if (size > 0) {
auto buffer = Buffer<char>::Copy(Env(), value.c_str(), size);
Callback().Call({ Env().Null(), buffer });
}
Callback().Call({ Env().Null(), String::New(Env(), value) });
}

private:
string doc;
string pwd;
Napi::Buffer<char> value;
string output;
string value;
long size;
};

Napi::Value
Document::GC(const Napi::CallbackInfo& info)
{
AssertFunctionArgs(info, 2, { napi_function, napi_string, napi_string });
string source = info[0].As<String>().Utf8Value();
string pwd = info[1].As<String>().Utf8Value();
auto cb = info[2].As<Function>();
auto worker = new GCAsync(cb, source, pwd);
string source = info[0].As<String>().Utf8Value(), pwd, output;
pwd = info[1].IsString() ? info[1].As<String>().Utf8Value() : "";
output = info[2].IsString() ? info[2].As<String>().Utf8Value() : "";
auto cb = info[3].As<Function>();
auto worker = new GCAsync(cb, source, pwd, output);
worker->Queue();
return info.Env().Undefined();
}
Expand Down

0 comments on commit f33f334

Please sign in to comment.