> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/web/get-started/samples/vieweredittest.md).

# Complex Document Editing

WebViewer gives you a chance to create and edit DOCX file without installing MS-Office server or any third-party platform. See the sample for editing DOCX file in WebViewer

This JavaScript sample lets you perform complex editing on PDF, DOCX, XLSX or PPTX documents as they are displayed in a web browser (no servers or other external dependencies required). In particular, this sample lets you change all text to blue and images to red in your document when you click the refresh button in the viewer. In the case of MS Office files, edits are saved into the PDF file. This sample works on all browsers (including IE11) and mobile devices without using plug-ins. Learn more about our [Web SDK](/web/get-started/guides.md) and [PDF Editing & Manipulation Library](/core/page-manipulation/manipulation.md).

<pre class="language-js" data-line-numbers><code class="lang-js">(exports => {
  const PDFNet = exports.Core.PDFNet;
  const refreshSVG = color =>
    '&#x3C;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="' +
    color +
    '">&#x3C;path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/>&#x3C;path d="M0 0h24v24H0z" fill="none"/>&#x3C;/svg>';

  const runElementEditTest = pdfDoc => {
    let ProcessElements = () => {};
    // getting around gulp error for recurive async functions
    ProcessElements = async (reader, writer, visited) => {
      await PDFNet.startDeallocateStack();
      console.log('Processing elements');
      let element;
      let gs;
      const colorspace = await PDFNet.ColorSpace.createDeviceRGB();
      const redColor = await PDFNet.ColorPt.init(1, 0, 0, 0);
      const blueColor = await PDFNet.ColorPt.init(0, 0, 1, 0);
      for (element = await reader.next(); element !== null; element = await reader.next()) {
        const elementType = await element.getType();
        let formObj;
        let formObjNum;
        let insertedObj;
        let newWriter;

        switch (elementType) {
          case PDFNet.Element.Type.e_image:
          case PDFNet.Element.Type.e_inline_image:
            // remove all images by skipping them
            break;
          case PDFNet.Element.Type.e_path:
            // Set all paths to red
            gs = await element.getGState();
            gs.setFillColorSpace(colorspace);
            gs.setFillColorWithColorPt(redColor);
            // Note: since writeElement does not return an object, the await is technically unneeded.
            // However, on a slower computer or browser writeElement may not finish before the page is
            // updated, so the await ensures that all changes are finished before continuing.
            await writer.writeElement(element);
            break;
          case PDFNet.Element.Type.e_text:
            // Set all text to blue
            gs = await element.getGState();
            gs.setFillColorSpace(colorspace);
            gs.setFillColorWithColorPt(blueColor);
            // Same as above comment on writeElement
            await writer.writeElement(element);
            break;
          case PDFNet.Element.Type.e_form:
            await writer.writeElement(element);
            formObj = await element.getXObject();
            formObjNum = formObj.getObjNum();
            // if XObject not yet processed
            if (visited.indexOf(formObjNum) === -1) {
              // Set Replacement
              insertedObj = await formObj.getObjNum();
              if (!visited.includes(insertedObj)) {
                visited.push(insertedObj);
              }
              newWriter = await PDFNet.ElementWriter.create();
              reader.formBegin();
              newWriter.beginOnObj(formObj, true);
              await ProcessElements(reader, newWriter, visited);
              newWriter.end();
              reader.end();
              if (newWriter) {
                newWriter.destroy();
              }
            }
            break;
          default:
            await writer.writeElement(element);
        }
      }
      await PDFNet.endDeallocateStack();
    };

    const main = async () => {
      let ret = 0;
      try {
        const doc = pdfDoc;
        doc.lock();
        doc.initSecurityHandler();

        const writer = await PDFNet.ElementWriter.create();
        const reader = await PDFNet.ElementReader.create();
        const visited = [];

        const pageCount = await doc.getPageCount();

        let pageCounter = 1;
        while (pageCounter &#x3C;= pageCount) {
          // This section is only required to ensure the page is available
          // for incremental download. At the moment the call to requirePage must be
          // be wrapped in this manner to avoid potential deadlocks and
          // allow other parts of the viewer to run while the page is being downloaded.
          doc.unlock();
          await PDFNet.finishOperation();
          await doc.requirePage(pageCounter);
          await PDFNet.beginOperation();
          doc.lock();

          // load the page and begin processing
          const page = await doc.getPage(pageCounter);
          const sdfObj = await page.getSDFObj();
          const insertedObj = await sdfObj.getObjNum();
          if (!visited.includes(insertedObj)) {
            visited.push(insertedObj);
          }
          reader.beginOnPage(page);
          writer.beginOnPage(page, PDFNet.ElementWriter.WriteMode.e_replacement, false);
          await ProcessElements(reader, writer, visited);
          writer.end();
          reader.end();
          console.log('page ' + pageCounter + ' finished editing');
          // refresh the cache with the newly updated document
          instance.Core.documentViewer.refreshPage(pageCounter);
          // update viewer with new document
          instance.Core.documentViewer.updateView([pageCounter]);
          pageCounter++;
        }
        console.log('Done.');
      } catch (err) {
        console.log(err.stack);
        ret = 1;
      }
      return ret;
    };

    // add your own license key as the second parameter, e.g. PDFNet.runWithCleanup(main, '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>')
    return PDFNet.runWithCleanup(main);
  };

  window.addEventListener('documentLoaded', () => {
    PDFNet.initialize().then(() => {
      const doc = instance.Core.documentViewer.getDocument();
      doc.getPDFDoc().then(pdfDoc => {
        instance.UI.setHeaderItems(headerItems => {
          headerItems.push({
            initialState: 'enabled',
            type: 'statefulButton',
            dataElement: 'refreshButton',
            states: {
              enabled: {
                img: refreshSVG('currentColor'),
                className: 'not-disabled',
                onClick: update => {
                  update('disabled');
                  runElementEditTest(pdfDoc).then(() => {
                    // re-enable our button
                    update('enabled');
                  });
                },
              },
              disabled: {
                img: refreshSVG('lightgray'),
                onClick: () => {},
                className: 'disabled',
              },
            },
            mount: () => {},
          });

          return headerItems;
        });
      });
    });
  });
})(window);
// eslint-disable-next-line spaced-comment
//# sourceURL=config.js
</code></pre>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/web/get-started/samples/vieweredittest.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
