How to apply Reductions before display the document

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.

1 Like

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

1 Like

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:


The annotations overlapped with redaction (3 circles which are shown in attempt 1) are not shown.

【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

1 Like

【Attempt 3】

Video Capture

1 Like

Hello,

We are currently looking into how to do this and will provide an update when we have more information.

Thank you.

2 Likes

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

2 Likes

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

1 Like

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

2 Likes

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

1 Like

Hello Asahi,

Yes, please let me know how it goes.

Best Regards,
Darian

1 Like

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

1 Like

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

2 Likes

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.

image

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

1 Like

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

2 Likes

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!

2 Likes

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!

1 Like

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

2 Likes

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

1 Like

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

2 Likes

Dear Darian:

My problems are solved completely.

Thank you very much for your support!

Best regards
Aasahi Hayakawa

2 Likes