Hi there,
Thank you for contacting Support. We will provide you with more information on how to do this as soon as possible.
Thank you.
Hi there,
Thank you for contacting Support. We will provide you with more information on how to do this as soon as possible.
Thank you.
Hi there,
Could you please provide the code and files you used for each attempt? Any videos or screenshots of console errors and the issue itself would also be helpful.
Best Regards,
Darian
Dear darian:
Thank you for your reply.
Could you please provide the code and files you used for each attempt? Any videos or screenshots of console errors and the issue itself would also be helpful.
【Attempt 1】
Code:
documentViewer.addEventListener('documentLoaded', () => {
this.myMethod();
});
protected myMethod(): void {
this.isDocumentLoadead = true;
this.annotationListLogicService
.callAnnotationListApi(this.manuscriptModel.$id)
.subscribe({
next: async (annotationInfoModels: AnnotationInfoModel[]) => {
this.responseAnnotationInfoModels = annotationInfoModels;
const redactInfoModels = annotationInfoModels.filter(
(annotationInfoModel) =>
annotationInfoModel.$manuscriptId === this.manuscriptModel.$id &&
annotationInfoModel.$annotationContent.indexOf('</redact>') > -1
);
await this.redrawAnnotationFromXfdf(redactInfoModels);
setTimeout(async () => {
const redactionList = this.viewerInstance.Core.annotationManager
.getAnnotationsList()
.filter(
(annot) =>
annot instanceof
this.viewerInstance.Core.Annotations.RedactionAnnotation
);
this.viewerInstance.Core.annotationManager.applyRedactions(
redactionList
);
setTimeout(async () => {
const notRedactAnnots = annotationInfoModels.filter(
(annotationInfoModel) =>
annotationInfoModel.$annotationContent.indexOf(
'</redact>'
) === -1
);
await this.redrawAnnotationFromXfdf(notRedactAnnots);
this.setAnnotationReadOnlyAndNoReply();
}, 800);
}, 500);
},
error: (error) => {
this.commonErrorHandleService.handleError(error);
},
});
}
Video Capture:
【Attempt 2】
documentViewer.addEventListener('annotationsLoaded', () => {
this.myMethod();
});
Video Capture:
【Attempt 3】
documentViewer.addEventListener('annotationsLoaded', () => {
setTimeout(async () => {
const redactionList = this.viewerInstance.Core.annotationManager
.getAnnotationsList()
.filter(
(annot) =>
annot instanceof
this.viewerInstance.Core.Annotations.RedactionAnnotation
);
await this.viewerInstance.Core.annotationManager.applyRedactions(
redactionList
);
setTimeout(async () => {
const notRedactAnnots = this.responseAnnotationInfoModels.filter(
(annotationInfoModel) =>
annotationInfoModel.$annotationContent.indexOf('</redact>') === -1
);
await this.redrawAnnotationFromXfdf(notRedactAnnots);
this.setAnnotationReadOnlyAndNoReply();
}, 800);
}, 500);
});
documentViewer.addEventListener('documentLoaded', () => {
this.myMethod();
});
protected myMethod(): void {
this.isDocumentLoadead = true;
this.annotationListLogicService
.callAnnotationListApi(this.manuscriptModel.$id)
.subscribe({
next: async (annotationInfoModels: AnnotationInfoModel[]) => {
this.responseAnnotationInfoModels = annotationInfoModels;
const redactInfoModels = annotationInfoModels.filter(
(annotationInfoModel) =>
annotationInfoModel.$manuscriptId === this.manuscriptModel.$id &&
annotationInfoModel.$annotationContent.indexOf('</redact>') > -1
);
await this.redrawAnnotationFromXfdf(redactInfoModels);
},
error: (error) => {
this.commonErrorHandleService.handleError(error);
},
});
}
Best Regards,
Asahi Hayakawa
【Attempt 3】
Video Capture
Hello,
We are currently looking into how to do this and will provide an update when we have more information.
Thank you.
Hello Asahi,
After discussing with my team, I think the best solution would be to apply the redactions before loading the document.
instance.Core.createDocument('/files/file.pdf', { extension: 'pdf' }).then(async doc => {
const xfdf = (await doc.extractXFDF()).xfdfString;
console.log(xfdf);
// redact the document
instance.UI.loadDocument(doc);
});
Once the document is fully loaded, you can try redrawing the annotations that were redacted with the XFDF string.
Note fullAPI must be set to true in the constructor when programattically applying redactions.
Redaction Guide: Apryse Documentation | Documentation
Thank you.
Best Regards,
Darian
Dear Darian:
Thank you for your reply.
I am sorry but could you give me a sample to redact a document using xfdf string or convert xfdf string to Core.Annotations.Annotation before loading the document?
I tried as the following
this.viewerInstance.Core.annotationManager
.importAnnotations(annotationInfo.$annotationContent)
.then(
(redactionList: Core.Annotations.Annotation[] | undefined) => {
await this.viewerInstance.Core.annotationManager.applyRedactions(
redactionList
);
}
)
.catch((reason) => reject(reason));
but I got an error since importAnnotations is used before loading the document.
Error: Error: importAnnotations was called before the document was loaded. Please wait for the 'documentLoaded' event before calling 'importAnnotations'. See https://docs.apryse.com/documentation/web/guides/documentviewer/ for more info.
Best Regards,
Asahi Hayakawa
Hello Asahi,
Thank you for the response.
It looks like we need to utilize ‘PDFNet’ in this case. Just to clarify a few things: you have a PDF document with annotations but no redactions, and you want to load it onto WebViewer to redact specific areas while preserving the annotations that were deleted by the redaction and the user should not see the redacted areas at all, is that correct?
This link shows how to search a document for specific keywords and redact those words before loading the document. It also contains sample code.
Here is the sample code for your use case. It should preserve all the annotations including the ones that were redacted before loading the document. You can import the annotations during the documentLoaded event.
fullAPI must be set to true in the WebViewer constructor.
// fullAPI: true
const { documentViewer, annotationManager, PDFNet } = instance.Core;
const { UI } = instance;
let xfdf;
const runScript = () => {
const runRedaction = async doc => {
// Relative path to the folder containing test files.
const inputFilePath = '/files/document.pdf';
try {
//create document
doc = await PDFNet.PDFDoc.createFromURL(inputFilePath);
doc.initSecurityHandler();
doc.lock();
// save the XFDF string
let fdfDoc = await doc.fdfExtract(PDFNet.PDFDoc.ExtractFlag.e_both);
xfdf = await fdfDoc.saveAsXFDFAsString();
console.log(xfdf);
//the array holding all the redactions
const redactionArray = [];
// create redaction on page 1
redactionArray.push(await PDFNet.Redactor.redactionCreate(1, (await PDFNet.Rect.init(0, 24.4, 371.03, 792)), false, ''));
//sets the appearance of the redaction
const app = {};
app.redaction_overlay = true;
app.border = false;
app.show_redacted_content_regions = true;
app.positive_overlay_color = await new PDFNet.ColorPt.init(0, 0, 0);
app.redacted_content_color = await new PDFNet.ColorPt.init(0, 0, 0);
await PDFNet.Redactor.redact(doc, redactionArray, app, false, false);
return doc;
} catch (err) {
console.log(err);
}
};
//calls the function to preprocess the document
const main = async () => {
let doc = null;
try {
return await runRedaction(doc);
} catch (err) {
console.log(err.stack);
} finally {
if (doc) {
doc.unlock();
}
}
};
return PDFNet.runWithoutCleanup(main);
}
// loads webviewer and runs scripts after it's loaded
UI.addEventListener('viewerLoaded', () => {
PDFNet.initialize()
.then(() => runScript())
.then(async doc => {
UI.loadDocument(doc);
console.log('finished script');
});
});
// import annotations
documentViewer.addEventListener('documentLoaded', async () => {
await annotationManager.importAnnotations(xfdf);
console.log('annotations imported');
});
Annotations should be preserved with this method.
PDFNet: Apryse WebViewer Namespace: PDFNet
Create Redaction PDFNet: Apryse WebViewer Class: Redactor
PDFNet Create Redaction Sample Code: Apryse Documentation | Documentation
Best Regards,
Darian
Dear Darian:
Thank you for your support.
I am trying this with my colleague and I will contract you when the result comes out.
Best Regards,
Asahi Hayakawa
Hello Asahi,
Yes, please let me know how it goes.
Best Regards,
Darian
Dear Darian:
Sorry for the late reply.
I tried to do the coding referring to your sample these days,
and it seems that this is a good solution to my problem.
Thank you very much.
By the way, since our service also support document formatted in office(docx, xlsx, etc…) and pictures,
in this situation I got an error when doing
doc = await PDFNet.PDFDoc.createFromURL(downloadUrl);
{
type: "PDFWorkerError",
message: "Exception: \n\t Message: PDF header not found. The file is not a valid PDF document.\n\t Filename: Parser.cpp\n\t Function: SkipHeader\n\t Linenumber: �ֿѴ�\u000bI��?",
}
Is there any methods to resolve this problem?
Best Regards,
Asahi Hayakawa
Hello Asahi,
You can convert the file to a PDF in this case.
In this example, we are creating an array buffer and then passing it into our runScript function.
Please look at this API page for more information.
https://docs.apryse.com/api/web/Core.html#.officeToPDFBuffer
// loads webviewer and runs scripts after it's loaded
UI.addEventListener('viewerLoaded', async () => {
const buf = await Core.officeToPDFBuffer('https://pdftron.s3.amazonaws.com/downloads/pl/report.docx',{l: 'your_license_key'});
runScript(buf)
.then(async doc => {
UI.loadDocument(doc);
console.log('finished script');
});
});
In that function we can use createFromBuffer which returns an object of type: “PDFNet.PDFDoc”.
let doc = await PDFNet.PDFDoc.createFromBuffer(buf);
Best Regards,
Darian
Dear Darian:
We decided to solve this problem in the next release of our application,
but I got some problems when trying the solution which you suggested me.
I did the coding as the following:
try {
const buf1 = await (<any>(
this.viewerInstance.Core
)).officeToPDFBuffer(
// download from our server
downloadUrl,
{
l: SccConfig.pdfTronLicenseKey,
}
);
console.log(buf1);
doc = await PDFNet.PDFDoc.createFromBuffer(buf1);
// await this.afterLoad(doc, annotationInfoModels);
} catch (e) {
console.log(e);
}
const buf2 = await (<any>(
this.viewerInstance.Core
)).officeToPDFBuffer(
'https://pdftron.s3.amazonaws.com/downloads/pl/report.docx',
{
l: SccConfig.pdfTronLicenseKey,
}
);
console.log(buf2);
doc = await PDFNet.PDFDoc.createFromBuffer(buf2);
and I got an error at 「console.log(e);」
{type: 'InvalidPDF', message: 'Exception: \n\t Message: PDF header not found. The f…pp\n\t Function: SkipHeader\n\t Linenumber: �ֿѴ�\vI��?'}
With a same word file, this error does not occur when downloading from 「https://pdftron.s3.amazonaws.com/downloads/pl/report.docx」 directly, but instead in this case the result comes from 「officeToPDFBuffer」comes to be an empty ArrayBuffer.
The response header from our server is as the following:
HTTP/1.1 200 OK
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
Date: Thu, 01 Feb 2024 10:55:44 GMT
Server: Kestrel
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Content-Type,Accept-Ranges,Content-Length,Content-Range
Transfer-Encoding: chunked
We are using WebViewer Version 10.3.0.
Thank you very much!
Best Regards,
Asahi Hayakawa
Hello Asahi,
When the error happens at 「console.log(e);」, do you know which line in the try block is causing the error? Is it the officeToPDFBuffer or createFromBuffer? Is the downloadUrl fetching this document? https://pdftron.s3.amazonaws.com/downloads/pl/report.docx
You can actually load a file without createFromBuffer like so:
const buf = await Core.officeToPDFBuffer('https://pdftron.s3.amazonaws.com/downloads/pl/report.docx', { extension: 'docx' });
console.log(buf, 'buf');
UI.loadDocument(buf, { filename: 'converted.pdf' }); // specify filename
Could you provide a sample project that reproduces the issues? You can provide a download link or GitHub repository.
Best Regards,
Darian
Could you provide a sample project that reproduces the issues? You can provide a download link or GitHub repository.
Sorry for the late reply.
I tried to make a same project with the minimum configurations, but this issue cannot be reproduced.
It seems that there are some problems in our application but not PDFTron.
Thank you all the same!
Dear Darian:
Sorry for bothering you again.
I found where the problem is.
This issue happens when I tried to set a file name when calling loadDocument
.
For example,
let doc: Core.PDFNet.PDFDoc | null = null;
const { PDFNet } = instance.Core;
await PDFNet.initialize();
const buf2 = await (<any>(
instance.Core
)).officeToPDFBuffer(
'https://pdftron.s3.amazonaws.com/downloads/pl/report.docx',
{extension: 'docx'}
);
doc = await PDFNet.PDFDoc.createFromBuffer(buf2);
instance.UI.loadDocument(doc);
});
works very well, however
doc = await PDFNet.PDFDoc.createFromBuffer(buf2);
instance.UI.loadDocument(doc, {
filename: 'test.docx',
});
will cause an error.
I made an application which could reproduce this.
redacttest.zip (212.3 KB)
By the way, I knew that officeToPDFBuffer method can be used to convert an office file to a pdfdoc,
is there a same method for convert pictures(jpg, bmp, png, tif, etc.) to PDF?
Thank you very much!
Hello Asahi,
If you are going to use the filename option under loadDocument, the extension should be pdf. Changing from test.docx to test.pdf, I was able to load the document successfully in your application.
instance.UI.loadDocument(doc, {
filename: 'test.pdf', // test.pdf
});
Best Regards,
Darian
Dear Darian:
If you are going to use the filename option under loadDocument, the extension should be pdf. Changing from test.docx to test.pdf, I was able to load the document successfully in your application.
It works! Thank you very much!
By the way,
I knew that officeToPDFBuffer method can be used to convert an office file to a pdfdoc,
is there a same method for convert pictures(jpg, bmp, png, tif, etc.) to PDF?
Best regards,
Aasahi Hayakawa
Hello Asahi,
Apologies for missing your other question.
We have a guide showing how to convert TIFF files to PDF.
We also have another API, toPDFWithBuffer, that can convert images to PDF.
https://docs.apryse.com/api/web/Core.PDFNet.Convert.html#.toPdfWithBuffer
Best Regards,
Darian
Dear Darian:
My problems are solved completely.
Thank you very much for your support!
Best regards
Aasahi Hayakawa