Technical FAQs

Question

I am trying to perform OCR on a PDF created from a scanned document. I need to rasterize the PDF page before importing the page into the recognition engine. When rasterizing the PDF page I want to set the bit depth of the generated page to be equal to the bit depth of the embedded image so I may use better compression methods for 1-bit and 8-bit images.

ImGearPDFPage.DIB.BitDepth will always return 24 for the bit depth of a PDF. Is there a way to detect the bit depth based on the PDF’s embedded content?

Answer

To do this:

  1. Use the ImGearPDFPage.GetContent() function to get the elements stored in the PDF page.
  2. Then loop through these elements and check if they are of the type ImGearPDEImage.
  3. Convert the image to an ImGearPage and find it’s bit depth.
  4. Use the highest bit depth detected from the images as the bit depth when rasterizing the page.

The code below demonstrates how to do detect the bit depth of a PDF page for all pages in a PDF document, perform OCR, and save the output while using compression.

private static void Recognize(ImGearRecognition engine, string sourceFile, ImGearPDFDocument doc)
    {
        using (ImGearPDFDocument outDoc = new ImGearPDFDocument())
        {
            // Import pages
            foreach (ImGearPDFPage pdfPage in doc.Pages)
            {
                int highestBitDepth = 0;
                ImGearPDEContent pdeContent = pdfPage.GetContent();
                int contentLength = pdeContent.ElementCount;
                for (int i = 0; i < contentLength; i++)
                {
                    ImGearPDEElement el = pdeContent.GetElement(i);
                    if (el is ImGearPDEImage)
                    {
                        //create an imGearPage from the embedded image and find its bit depth
                        int bitDepth = (el as ImGearPDEImage).ToImGearPage().DIB.BitDepth; 
                        if (bitDepth > highestBitDepth)
                        {
                            highestBitDepth = bitDepth;
                        }
                    }
                }
                if(highestBitDepth == 0)
                {
                    //if no images found in document or the images are embedded deeper in containers we set to a default bitDepth of 24 to be safe
                    highestBitDepth = 24;
                }
                ImGearRasterPage rasterPage = pdfPage.Rasterize(highestBitDepth, 200, 200);
                using (ImGearRecPage recogPage = engine.ImportPage(rasterPage))
                {
                    recogPage.Image.Preprocess();
                    recogPage.Recognize();
                    ImGearRecPDFOutputOptions options = new ImGearRecPDFOutputOptions() { VisibleImage = true, VisibleText = false, OptimizeForPdfa = true, ImageCompression = ImGearCompressions.AUTO, UseUnicodeText = false };
                    recogPage.CreatePDFPage(outDoc, options);
                }
            }
            outDoc.SaveCompressed(sourceFile + ".result.pdf");
        }
    }

For the compression type, I would recommend setting it to AUTO. AUTO will set the compression type depending on the image’s bit depth. The compression types that AUTO uses for each bit depth are: 

  • 1 Bit Per Pixel – ImGearCompressions.CCITT_G4
  • 8 Bits Per Pixel – ImGearCompressions.DEFLATE
  • 24 Bits Per Pixel – ImGearCompressions.JPEG

Disclaimer: This may not work for all PDF documents due to some PDF’s structure. If you’re unfamiliar with how PDF content is structured, we have an explanation in our documentation. The above implementation of this only checks one layer into the PDF, so if there were containers that had images embedded in them, then it will not detect them.

However, this should work for documents created by scanners, as the scanned image should be embedded in the first PDF layer. If you have more complex documents, you could write a recursive function that goes through the layers of the PDF to find the images.

The above code will set the bit depth to 24 if it wasn’t able to detect any images in the first layer, just to be on the safe side.

Question

In PrizmDoc, my document appears to be small on the page relative to the viewer. How can I fix this?

enter image description here

Answer

By default, PrizmDoc renders a PDF file according to the MediaBox, which is normally the same as CropBox, though sometimes this is not the case. The larger area you see in the PrizmDoc Viewer is the size of the MediaBox. Please note that the product provides the fileTypes.pdf.pageBoundaries control option (or useCropBox in the older versions) to change the default behavior. Try setting the option to cropBox in the Central Configuration File in order to get the PDF content rendered according to the CropBox. You can read more about configuring image frame rendering in our documentation here.

For additional reading, see 7.7.3.3 on “User Space” of Adobe’s PDF 1.7 specification:

https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf

Note: In some older versions of PrizmDoc, there exists an issue where setting the pageBoundaries field to cropBox can cause light blurring/distorting on the page. This issue was addressed in version 13.4.

Question

In PrizmDoc, my document appears to be small on the page relative to the viewer. How can I fix this?

enter image description here

Answer

By default, PrizmDoc renders a PDF file according to the MediaBox, which is normally the same as CropBox, though sometimes this is not the case. The larger area you see in the PrizmDoc Viewer is the size of the MediaBox. Please note that the product provides the fileTypes.pdf.pageBoundaries control option (or useCropBox in the older versions) to change the default behavior. Try setting the option to cropBox in the Central Configuration File in order to get the PDF content rendered according to the CropBox. You can read more about configuring image frame rendering in our documentation here.

For additional reading, see 7.7.3.3 on “User Space” of Adobe’s PDF 1.7 specification:

https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf

Note: In some older versions of PrizmDoc, there exists an issue where setting the pageBoundaries field to cropBox can cause light blurring/distorting on the page. This issue was addressed in version 13.4.

Question

After searching a document, an error icon appears in the search results panel. Clicking on it displays the following error message: “x page(s) cannot be searched.” Why does this occur and how can I find out which specific pages couldn’t be searched?

Answer

When the PrizmDoc Viewer text-service cannot find any text for a given page in the document, it provides an array of all the pages without text in the response from searchTask results.

In short, the document is fine and simply contains pages without text. If you look at the pagesWithoutText array contained within the response data from searchTasks, you’ll see something like this:

[0, 1, 7, 17, 43, 45, 65, 67, 77, 79,…]

The values reported are pages that do not contain any text but instead are either blank or contain an image. This data can then be used to inform the user of how many pages are not searchable.

Question

After searching a document, an error icon appears in the search results panel. Clicking on it displays the following error message: “x page(s) cannot be searched.” Why does this occur and how can I find out which specific pages couldn’t be searched?

Answer

When the PrizmDoc Viewer text-service cannot find any text for a given page in the document, it provides an array of all the pages without text in the response from searchTask results.

In short, the document is fine and simply contains pages without text. If you look at the pagesWithoutText array contained within the response data from searchTasks, you’ll see something like this:

[0, 1, 7, 17, 43, 45, 65, 67, 77, 79,…]

The values reported are pages that do not contain any text but instead are either blank or contain an image. This data can then be used to inform the user of how many pages are not searchable.

.net document viewer

Adding document viewing features to an application can be a challenge. Although there are many open source options available, finding a suitable ASP.NET document viewer or .NET image viewer that provides the right level of flexibility and functionality often requires a more specialized solution. Fortunately, .NET developers have good options when the time comes to integrate document viewing into their software, which helps them to focus on other application features.

The API Document Solution

Rather than building a dedicated viewer within their .NET application, many developers instead turn to an HTML5 viewer integration that uses REST APIs for their document needs. Since these viewers work within the web browser and can support any programming language, they provide the right balance of versatility and performance for most software applications. An HTML5 document viewer ensures a consistent viewing experience across multiple software platforms and browsers used within an enterprise environment.

Of course, if all an application needed to do was view files, it could simply use one of many open-source solutions. The problem is that these viewers typically only view one type of file, which means that the application also needs the ability to convert files into different formats. Depending upon the library in question, this could quickly escalate into a code-heavy solution that bogs down application performance and introduces multiple security vulnerabilities. If poor conversion tools are put in place, there’s also a strong likelihood that documents will not render accurately.

An HTML5 viewer with the right APIs can easily overcome these document management challenges for a .NET application. Conversion, annotation, comparison, and redaction features can all be integrated as part of a comprehensive viewing framework that doesn’t require developers to build anything from scratch or rely upon intrusive plugins that create risky dependencies.

How Accusoft APIs Enhance Your .NET Application

Accusoft’s PrizmDoc Viewer was designed to provide a broad range of document processing capabilities in addition to its core HTML5 viewing features. Once integrated into a .NET application, it allows developers to deploy REST API calls to convert files into new formats, split and merge documents, create page thumbnails, markup documents, and perform high-volume text searches. As an HTML5 viewer, PrizmDoc Viewer can deliver all of that functionality right within the browser rather than resorting to external applications.

The primary advantage of REST APIs is that they can be used from any programming language, so they don’t have to be custom-built to serve as an ASP.NET document viewer. That versatility does come with a tradeoff, however. Processes like uploading files, converting them, and then downloading outputs all require a series of HTTP requests. While this isn’t a particularly difficult process, it is slightly more resource-intensive than a solution built using the same programming language as the application. 

That’s why we developed a .NET SDK library that wraps around the server-related functions of PrizmDoc Viewer. Available for both .NET Core and .NET Framework, this SDK library wraps around the server’s REST APIs to make it easier to utilize server functionality in .NET applications.

For .NET developers looking for a better way to view and process documents, the PrizmDoc .NET SDK can help them access conversion, redaction, and annotation features without compromising the performance of their .NET applications.

Getting Started with PrizmDoc .NET SDK

In order to implement the .NET wrapper, developers just need to follow a few simple steps. 

1. Gain Access to a PrizmDoc Server Deployment

There are two ways to access PrizmDoc Server, which will allow you to carry out a variety of document processing functions. You can host a server on-premises as part of a PrizmDoc Viewer integration or sign up for a PrizmDoc Cloud account to use Accusoft’s cloud-hosted deployment.

2. Add the PrizmDoc Server .NET SDK Package

Next, download the free, open source .NET SDK library from NuGet or GitHub and add it to your application project.

dotnet add package Accusoft.PrizmDocServerSDK

 

3. Create a new PrizmDocServerClient

Once the .NET wrapper is in place, it’s time to construct a new PrizmDocServerClient and connect it to the server.

For a self-hosted PrizmDoc Server that’s part of a PrizmDoc Viewer deployment, the base URL is all that’s needed:

var prizmDocServer = new PrizmDocServerClient("http://localhost:18681");

 

If the you’re using PrizmDoc Cloud, you’ll need to provide the base URL along with your API key:  

var prizmDocServer = new PrizmDocServerClient("https://api.accusoft.com", "YOUR_API_KEY");

 

4. Begin Document Processing

Now that everything is in place, you can start processing and viewing documents within your .NET application. Our How To Guides provide some examples of common use cases, and you can also turn to the API Reference for additional guidance and information.

Get the Document Viewing Features Your .NET Application Needs

Accusoft’s PrizmDoc Viewer delivers the versatile HTML5 viewing capabilities that can set your .NET application apart from the competition. Thanks to the PrizmDoc Server .NET SDK wrapper, you can leverage the power of our REST APIs without needing to build out a customized viewing solution from the ground up.

Find out how easily you can manage your document needs with PrizmDoc Viewer’s browser-based functionality today. Sign up for a free trial to test our HTML5-powered viewer in your .NET environment.

For today’s healthcare organizations, having a versatile electronic health records (EHR) system is essential for running an efficient practice and connecting to other medical providers. Thanks to EHRs, practices can ensure that they’re getting a complete picture of a patient’s health and treatment history, which allows them to deliver much better care outcomes. As developers continue to refine the usability of these systems, they need to consider how they can improve core features like healthcare electronic document management and medical imaging support.

Managing Medical Documents

A typical EHR system has to be able to handle quite a lot of document types. Anyone who has visited a healthcare provider is quite familiar with the myriad forms used to gather patient information. Many of those forms end up being converted into digital formats that need to be managed within the EHR system. Then there are digital versions of lab reports, physician notes, invoices, and financial documents. 

While EHR systems may utilize databases to store much of the information they need, healthcare providers still need to be able to produce physical documents and view digital files in many situations. This could include communicating information to patients, complying with regulatory requests, or filing a financial claim of some kind. More importantly, they also rely on digital documents to enter data into the EHR system. The push toward interoperability between EHR systems has improved information sharing, but there are still many instances where medical records are delivered in the form of a document that needs to be managed securely.

Document Conversion

If an EHR application lacks the right file conversion capabilities, viewing and extracting data from those documents could prove difficult. The last thing a practice wants to do is actually remove them from the secure EHR system to open and convert the files using separate software that may not be compliant when it comes to handling healthcare information. Even if the external application is secure, transferring files over, converting them, and then transferring them back is both inefficient and creates unnecessary risk (especially if someone forgets to delete the original file or move it back into the EHR environment).

ImageGear Medical has a document conversion feature that supports a wide range of file types, allowing developers to build EHR applications capable of quickly converting incoming documents. They can even set up their solution to perform conversion tasks programmatically to help streamline workflows and minimize human error. This helps practices to get a better handle on document management, ensuring that they will be able to do everything they need with files completely within the EHR application.

Other Essential Document Features

But ImageGear Medical’s document capabilities go far beyond just conversion. With full annotation support, developers can provide markup tools within the EHR system that allow physicians to make notes and comments on various documents. This allows them to share information much more easily. If a physician has a question about a diagnosis or a prescription, for instance, they can simply leave an annotation note directly on the document rather than referring to it in a separate message.

ImageGear Medical also allows applications to perform full-page optical character recognition (OCR), which can quickly read and extract text from document and image files. This feature is especially useful for capturing text from scanned images of documents, which can then be used to create a searchable PDF or fill form fields within the EHR system. The OCR engine not only reads most Western languages, but also detects and reads several Eastern language characters.

Managing DICOM Files

One of the biggest challenges healthcare organizations face is with managing medical imaging files. When providers need to send X-Rays, MRIs, or CT Scans, they use a standardized file format known as Digital Imaging and Communications in Medicine (DICOM) files. These files are more than just image files, however. They contain extensive datasets that provide a patient’s information along with image pixel data for multi-dimensional medical scans. A DICOM file can be quite large due to the high-resolution image data used by most medical imaging equipment.

Although most EHR systems are capable of transmitting DICOM files (via a DICOM out or DICOM send feature), they usually can’t actually view them in their native format. Since Windows doesn’t recognize them as image files, additional viewing software is typically needed to open and view them. This is why physical storage, like discs and flash drives, are often used to transfer DICOM files along with the necessary viewing software.

ImageGear Medical helps to solve the DICOM dilemma thanks to its extensive conversion and compression capabilities. By decoding the complex data contained within the file, ImageGear Medical can convert DICOM files into image formats that are much easier to view and manage. This is especially useful for smaller practices that don’t have a picture archiving and communication system (PACS) capable of storing, retrieving, distributing, and viewing high-quality medical images. 

Converting DICOM files makes it possible for healthcare professionals to view them on any device connected with their EHR system. That could include tablets or other IoT devices that healthcare technology companies are rolling out to put critical medical data on the front lines of everyday care. Developers can also use ImageGear Medical’s conversion tools to allow their EHR system to share viewable versions of diagnostic scans with patients, allowing practices to make good on the promise of providing patients access to their essential health data at all times. 

The sheer size of DICOM files makes them difficult for many practices to manage. Simply compressing them tends to degrade the image data, which can create significant problems when files are unpacked and opened for viewing. Losing even a small degree of image quality can make it much harder to render an accurate diagnosis. In some cases, poorly designed compression can even make it nearly impossible to uncompress again at all. Thanks to powerful lossless compression technology, ImageGear Medical makes it easier to share medical images between providers without damaging the integrity of the original data.

Expand EHR Capabilities with ImageGear Medical

Accusoft’s imaging, conversion, and compression technology has been supporting the needs of the healthcare industry for decades. As developers work to expand the capabilities of their EHR applications, our engineers are busy improving the medical SDKs that will provide them with the features they need to stand out in a competitive market. 

ImageGear Medical utilizes a combination of efficient code and elegant APIs to deliver the document and image processing tools EHR systems require. For a closer look at this dynamic SDKs capabilities, check out our extensive developer resources today or download a free trial to get started.

JavaScript PDF annotation

Ever since Mozilla’s development of the open-source PDF.js library in 2011, many developers have been quick to utilize the JavaScript-based toolkit to quickly integrate PDF viewing capabilities into their applications. Given the severe limitations and security concerns associated with external reader plug-ins, it’s easy to see why the open source library was so appealing. 

Unfortunately, many development teams that lacked experience with document management and image rendering quickly discovered that PDF.js lacked many of the core features they expected from a PDF reader. Front and center among these capabilities was JavaScript PDF annotation, which is essential for almost any application that manages documents. That’s why Accusoft set out to build a lightweight SDK upon the PDF.js library that, among other things, added a vital annotation layer to the viewer.

Making PDF.js Draw Annotations

The PDF.js library consists of three different layers that all work together to render and display files. A core layer interprets the binary data in a file before passing it on to a display layer that renders the PDF itself into a <canvas> element. The viewer layer provides the main interface that allows people to view and interact with the file. Out of the box, however, the viewing layer doesn’t provide much in the way of functionality. Interaction is fairly limited without substantial extra development work to add capabilities like mobile screen responsiveness or high fidelity zoom. The core and display layers also don’t display the full PDF specification and struggle with lengthy documents and image-intensive files.

When our team started developing Accusoft PDF Viewer, the very first step was to shore up those support and rendering deficiencies. After making a number of key optimizations to improve rendering speed and fidelity, search speed, and mobile responsiveness, adding a PDF.js annotation layer was among the first priorities. This process was made a bit easier since Accusoft PDF Viewer uses a custom-built viewing layer rather than the default PDF.js viewing layer. In addition to providing a number of performance benefits, building a new viewer gave the team a great deal of control over how users can view and interact with documents.

One of the primary goals of developing this lightweight PDF SDK was that functionality could not come at the expense of performance. The new layer for JavaScript PDF annotations sits atop the PDF.js library, which preserves rendering speed and fidelity while also making it easy for developers to access and utilize annotation features. Since the rendered document is easily accessible, any annotations associated with it can still be retrieved and loaded within the viewing layer.

Anatomy of JavaScript PDF Annotations in Accusoft PDF Viewer

The Professional version of Accusoft PDF Viewer supports multiple different types of annotations, all of which can be incorporated into the customized user interface. This allows developers to quickly integrate PDF viewing capabilities into web applications, enable annotation controls, and then retrieve and load markups whenever the file is retrieved for viewing. 

In order to retrieve JavaScript PDF annotations, developers can use the getAnnotations function. This effectively exports a copy of the annotations made in one viewer so they can be loaded into a different viewer. It’s important to remember that the returned objects are a copy of the annotations, so any changes made to them will not affect the annotations in the original viewer. Furthermore, any changes made to the annotations in the original viewer will not be reflected in the exported versions. To update them, the getAnnotations function would need to be used again.

JavaScript PDF Annotation Tools in Accusoft PDF Viewer Professional

Accusoft PDF Viewer Professional offers several annotation tools. Since the SDK allows developers to customize the viewing interface to fit their application’s needs, one or all of these markup types may be removed from the toolbar if necessary. This may be desirable for situations where a document should only be available for viewing.

  • Ellipse/Circle: A classic oval shape, the ellipse tool can be adjusted in a number of ways to expand its functionality. Developers can set qualities such as opacity, fill color, and border thickness as needed.
  • Rectangle/Square: Typically used to draw boxes around text, the rectangle tool can be customized to meet annotation needs by altering the border thickness and color, adjusting opacity, or designating fill color.
  • Line: Line markups are one of the more versatile annotation types. Accusoft PDF Viewer allows users to not only change the color and thickness of the line, but also to determine whether the line features a triangle end head, which allows it to serve as an arrow.
  • Freehand Signature: Particularly useful for many document management applications, the freehand tool allows users to electronically sign documents or make freehand comments on a PDF. The JavaScript PDF annotation layer tracks the drawing path of the markup as a string value.
  • Free Hand Annotation: The freehand annotation tool allows users to freely draw on a document using their mouse, finger, or stylus. It’s often used to provide quick feedback on a document, especially on mobile devices.
  • Text Highlight: Much like a highlighter pen back in school, the text highlight tool can be used to select and apply a background color to text. When reviewing documents, it can be deployed to highlight sections of content that require closer attention in the future.

Unlock the PDF Potential of Your Application

The customizable PDF features of Accusoft PDF Viewer Professional allow developers to easily integrate powerful annotation capabilities into their application with just a few lines of code. Since the viewer’s JavaScript PDF annotations are made entirely within the web application, there’s no need to configure complicated servers that could impact performance or create additional points of failure. Developers can adjust the viewer interface or even remove the Accusoft branding to create a seamless viewing experience that gives them total control over how their files are viewed and potentially marked up using annotation tools.

Accusoft PDF Viewer was designed with responsive support for mobile displays right out of the box, making it an ideal solution for web applications accessed from multiple devices. It also incorporates three decades of Acusoft’s experience with image processing and document rendering to deliver a viewing experience that’s head and shoulders above open-source PDF.js solutions. Find out how easily your application can add high-performance PDF capabilities by downloading a trial of Accusoft PDF Viewer today. 

Question

Can I host multiple PrizmDoc viewers on a single page?

Answer

It is possible to host multiple viewers on a single page. The following example leverages Bootstrap’s tab implementation:

<!DOCTYPE html>

<html lang="en">
<head>
    <!-- Metadata -->
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
    <meta name="description" content="" />

    <!-- Title -->
    <title>AccuSample</title>

    <!-- Libraries -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css">

    <!-- PrizmCSS -->
    <link rel="stylesheet" href="https://pcc-demos.accusoft.com/static/viewer-latest/css/viewercontrol.css">
    <link rel="stylesheet" href="https://pcc-demos.accusoft.com/static/viewer-latest/css/viewer.css">

    <!-- Inline Stylesheet -->
    <style>
        body {
            overflow-y: hidden;
        }
        #viewer1, #viewer2 {
            height: calc(100vh - 3em);
            width: 100%;
        }
    </style>

</head>
<body>
    <!-- #main -->
    <main class="container">
        <ul class="nav nav-tabs" role="tablist">
            <li class="nav-item">
                <a class="nav-link active" id="viewer1-tab" data-toggle="tab" href="#viewer1">Viewer 1</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" id="viewer2-tab" data-toggle="tab" href="#viewer2">Viewer 2</a>
            </li>
        </ul>

        <div class="tab-content">
            <div class="tab-pane fade show active" id="viewer1" role="tabpanel">
                <div id="viewer1">
                </div>
            </div>
            <div class="tab-pane fade" id="viewer2" role="tabpanel">
                <div id="viewer2">
                </div>
            </div>
        </div>
    </main>

    <!-- Libraries -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.1/umd/popper.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

    <!-- PrizmJS -->
    <script src="https://api.accusoft.com/v1/docstore/viewer/assets/classic/bundle.js"></script>
    <script src="https://pcc-demos.accusoft.com/static/viewer-latest/js/jquery.hotkeys.min.js"></script>
    <script src="https://pcc-demos.accusoft.com/static/viewer-latest/js/viewercontrol.js"></script>
    <script src="https://pcc-demos.accusoft.com/static/viewer-latest/js/viewer.js"></script>

    <!-- Inline Script -->
    <script>
        var viewingSessionId1;
        var viewerControl1;
        var viewingSessionId2;
        var viewerControl2;

        $(document).ready(function() {
            $.ajax({
                "type": "post",
                "url": "https://api.accusoft.com/PAS/V1/ViewingSession",
                "headers": {
                    "acs-api-key": ""
                },
                "data": JSON.stringify({
                    "source": {
                        "type": "url",
                        "url": ""
                    }
                })
            }).done(function(response) {
                viewingSessionId1 = response["viewingSessionId"];

                // Initialize viewer
                viewerControl1 = $("#viewer1").pccViewer({ 
                    documentID: viewingSessionId1,
                    imageHandlerUrl: "https://api.accusoft.com/v2/viewers/proxy",
                    language: languageItems,
                    template: htmlTemplates
                }).viewerControl;
            });

            $.ajax({
                "type": "post",
                "url": "https://api.accusoft.com/PAS/V1/ViewingSession",
                "headers": {
                    "acs-api-key": ""
                },
                "data": JSON.stringify({
                    "source": {
                        "type": "url",
                        "url": ""
                    }
                })
            }).done(function(response) {
                viewingSessionId2 = response["viewingSessionId"];

                // Initialize viewer
                viewerControl2 = $("#viewer2").pccViewer({ 
                    documentID: viewingSessionId2,
                    imageHandlerUrl: "https://api.accusoft.com/v2/viewers/proxy",
                    language: languageItems,
                    template: htmlTemplates
                }).viewerControl;
            });
        });
    </script>
</body>
</html>
Question

Can I host multiple PrizmDoc viewers on a single page?

Answer

It is possible to host multiple viewers on a single page. The following example leverages Bootstrap’s tab implementation:

<!DOCTYPE html>

<html lang="en">
<head>
    <!-- Metadata -->
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
    <meta name="description" content="" />

    <!-- Title -->
    <title>AccuSample</title>

    <!-- Libraries -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css">

    <!-- PrizmCSS -->
    <link rel="stylesheet" href="https://pcc-demos.accusoft.com/static/viewer-latest/css/viewercontrol.css">
    <link rel="stylesheet" href="https://pcc-demos.accusoft.com/static/viewer-latest/css/viewer.css">

    <!-- Inline Stylesheet -->
    <style>
        body {
            overflow-y: hidden;
        }
        #viewer1, #viewer2 {
            height: calc(100vh - 3em);
            width: 100%;
        }
    </style>

</head>
<body>
    <!-- #main -->
    <main class="container">
        <ul class="nav nav-tabs" role="tablist">
            <li class="nav-item">
                <a class="nav-link active" id="viewer1-tab" data-toggle="tab" href="#viewer1">Viewer 1</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" id="viewer2-tab" data-toggle="tab" href="#viewer2">Viewer 2</a>
            </li>
        </ul>

        <div class="tab-content">
            <div class="tab-pane fade show active" id="viewer1" role="tabpanel">
                <div id="viewer1">
                </div>
            </div>
            <div class="tab-pane fade" id="viewer2" role="tabpanel">
                <div id="viewer2">
                </div>
            </div>
        </div>
    </main>

    <!-- Libraries -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.1/umd/popper.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

    <!-- PrizmJS -->
    <script src="https://api.accusoft.com/v1/docstore/viewer/assets/classic/bundle.js"></script>
    <script src="https://pcc-demos.accusoft.com/static/viewer-latest/js/jquery.hotkeys.min.js"></script>
    <script src="https://pcc-demos.accusoft.com/static/viewer-latest/js/viewercontrol.js"></script>
    <script src="https://pcc-demos.accusoft.com/static/viewer-latest/js/viewer.js"></script>

    <!-- Inline Script -->
    <script>
        var viewingSessionId1;
        var viewerControl1;
        var viewingSessionId2;
        var viewerControl2;

        $(document).ready(function() {
            $.ajax({
                "type": "post",
                "url": "https://api.accusoft.com/PAS/V1/ViewingSession",
                "headers": {
                    "acs-api-key": ""
                },
                "data": JSON.stringify({
                    "source": {
                        "type": "url",
                        "url": ""
                    }
                })
            }).done(function(response) {
                viewingSessionId1 = response["viewingSessionId"];

                // Initialize viewer
                viewerControl1 = $("#viewer1").pccViewer({ 
                    documentID: viewingSessionId1,
                    imageHandlerUrl: "https://api.accusoft.com/v2/viewers/proxy",
                    language: languageItems,
                    template: htmlTemplates
                }).viewerControl;
            });

            $.ajax({
                "type": "post",
                "url": "https://api.accusoft.com/PAS/V1/ViewingSession",
                "headers": {
                    "acs-api-key": ""
                },
                "data": JSON.stringify({
                    "source": {
                        "type": "url",
                        "url": ""
                    }
                })
            }).done(function(response) {
                viewingSessionId2 = response["viewingSessionId"];

                // Initialize viewer
                viewerControl2 = $("#viewer2").pccViewer({ 
                    documentID: viewingSessionId2,
                    imageHandlerUrl: "https://api.accusoft.com/v2/viewers/proxy",
                    language: languageItems,
                    template: htmlTemplates
                }).viewerControl;
            });
        });
    </script>
</body>
</html>