Below, please find the release notes for VirtualViewer v5.6.0 and associated minor releases. For questions, please contact us at info@accusoft.com or by phone at (617) 607-2010.

v5.6.0 - December 18, 2020

General availability release.

Known Issues

  • Streaming media from the content handler may prevent seeking on the client media player when the media is first opened.
  • Fit to page is currently incompatible with watermarks. Any pages printed with a watermark will not be fit to page.

Bug Fixes

  • The pan tool is no longer disabled after using rubberband zoom.
  • Previously, loading a document could cause multiple simultaneous calls to the content handler’s getDocumentContent API. Now, while the document cache is enabled, VirtualViewer will send only one request to getDocumentContent when loading a document.
  • Improved security of XML parsers resolving external entities.
  • Clearing callback functions with the Javascript API virtualViewer.removeCallbacks(callbackString) will no longer incidentally remove VirtualViewer internal callbacks.
  • A new element in index.html configures require.js to no longer time out while waiting for VirtualViewer javascript to load, allowing VirtualViewer to function more gracefully with very slow internet connection.
  • Calling the virtualViewer.saveDocument() Javascript API with a new document ID and complex page range, and without specifying the target document format, would default to copying the entire document in its original format. Now, the document will “Save As” with the correct subset of pages, as a PDF or TIFF.
  • Document compare is now properly disabled for audio files.
  • Previously, cancelling out of the Picture Controls dialog would reset image manipulation values to their defaults. Now, a new reset button will revert all values to their defaults, but otherwise, previously-set values will be preserved after closing the Picture Controls dialog.

New features

Text Annotation Background Colors

Background color can now be set on text annotations. This works similarly to sticky note annotations except without the 3D shadow effects. A user-by-user default background color for new text annotations can be set by in User Preferences; admins can set a global default by changing textBackgroundColor in annotationDefaults in the config.js file, or leave it at null for the original transparent default.

Text Annotation Footnotes

A user may now add a footnote to the body of a text annotation. The footnote will display the user who created the annotation and the date that the annotation was created. A new button in the annotation context menu will place the footnote text into the body of the text annotation.

“Fit to Page” Print Option

Documents can now be printed with contents “fit to page.” With this option set, images will be scaled up or down to fit onto a portrait-oriented 8.5”x11” US letter-size page with margins when printing a document. Document pages that are already 8.5”x11” (such as scanned document images), or pages that contain text, will not be scaled. This provides a fast and useful alternative to any browser print options that may be available.

The details of this feature are subject to change, so the “fit to page” option is hidden by default. Let us know if you have suggestions about this feature or improvements to it that would help your workflow.

To activate the “fit to page” feature when printing a document:

  • If printing via the Javascript API’s virtualViewer.printDocument, add fitToPage: true to the argument object.
  • Set showFitToPageOption in config.js to true and the Print dialog will show a “Fit to page” checkbox.

Media Streaming from Content Handler

The content handler can now stream video and audio files to VirtualViewer, so that VirtualViewer can start playing them before they are fully loaded from the content store. Previously, the VirtualViewer server could stream to the client web browser, but the content handler had to provide the entire media file to the VirtualViewer server before it would start playing, which could incur a long initial wait for large files. Now the content handler can provide VirtualViewer with a continuous stream instead of the full video file, so VirtualViewer can start displaying the media on the client immediately (if the media’s format supports streaming).

To take advantage of media streaming, the content handler’s getDocumentContent method should return an InputStream to the loading file data, such as the InputStream from an HTTP response, under ContentHandlerResult’s KEY_DOCUMENT_INPUT_STREAM. The InputStream must be progressively loading data from the client for the streaming feature to work - a fully cached InputStream (such as ByteArrayInputStream) will be the same as providing VirtualViewer with the complete file all at once. VirtualViewer will start playing the media on the client as soon as the stream has provided enough header data, while caching the downloaded data for faster performance later (if the document cache is enabled).

Note that although getDocumentContent already supported returning an InputStream, this will likely require a code change to ensure VirtualViewer receives an active progressively-loading stream. A simplified example of what this could look like in getDocumentContent is below, using Java’s basic URL library to download a video file over HTTP.

  
  InputStream mediaStream = new URL("http://example.com/MyMedia.mp4").openStream();
  // VirtualViewer will continously read data from this stream until the file is downloaded,
  // while also streaming the media to the requesting client browser.
  result.put(ContentHandlerResult.KEY_DOCUMENT_INPUT_STREAM, mediaStream);
  result.put(ContentHandlerResult.KEY_DOCUMENT_DISPLAY_NAME, "My Media");
  
  // We should not close the stream in getDocumentContent: VirtualViewer will close the stream once it is completed.
  return result;

Thread Pool Configuration

In the background, VirtualViewer will use a separate thread from a thread pool for every InputStream returned from getDocumentContent, to read and close the stream while the main thread sends media data to the client browser. Most streams should be completed quickly, so VirtualViewer uses a thread pool to avoid the overhead of creating new threads.

This async stream thread pool is fully configurable in web.xml. We strongly recommend that you configure the thread pool from the defaults to suit your needs and server hardware. The initialization parameters are listed below:

  • asyncStreamBufferCorePoolSize {int} The minimum number of threads in the pool. The default is 5.
  • asyncStreamBufferPrestartCoreThreads {boolean} If false, the pool will create even core threads on-demand as documents are requested. If true, the pool will create its core threads when the server starts, which will slow down VirtualViewer initialization but make initial media requests faster. The default is true.
  • asyncStreamBufferMaxPoolSize {int} The maximum number of threads in the pool. Threads will be created up to this limit if needed during high demand. The default is 30.
  • asyncStreamBufferKeepAliveTime {long} The amount of time before an idle thread will be removed from the pool when combined with asyncStreamBufferKeepAliveTimeUnits. The default is 1.
  • asyncStreamBufferKeepAliveTimeUnits {java.util.concurrent.TimeUnit} The unit of time measurement for asyncStreamBufferKeepAliveTime. Valid values are NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, and DAYS. The default is MINUTES.
  • asyncStreamBufferAllowCoreThreadsTimeout {boolean} Whether to allow core threads to time out. If false, a minimum number of threads up to the core pool size will always be kept alive even in times of low demand. The default is false.

More details on these parameters and their configuration are available at https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html.

Configurable Document Viewed Indicator

A blue checkmark indicator can now be added to a thumbnail image on the document list. The indicator lasts until the user reloads the page or the indicator is removed by an API call. This feature is available only through Javascript API, for use in custom code. No configuration is necessary to use this feature.

This can be used for many purposes, including visually marking documents that have been viewed in the current session, or indicating documents that the user has not yet opened. For both these example use cases, custom code would call the new Javascript API within a callback function registered to the onDocumentLoad callback.

New Javascript API

  • addDocumentViewedIndicator(documentId) Add the checkmark indicator to the specified document’s thumbnail.
    • documentId {String} The ID of the document that should be marked.
  • addDocumentViewedIndicatorBatch(documentIdArray) Add the checkmark indicator to all of the specified documents’ thumbnails.
    • documentIdArray {String[]} List of document IDs to be marked.
  • removeDocumentViewedIndicator(documentId) Remove the checkmark indicator from the specified document’s thumbnail.
    • documentId {String} The ID of the document whose mark should be removed.
  • removeAllDocumentViewedIndicator() Remove checkmark indicators from all documents’ thumbnails.

New Callbacks

  • onFinishedMarkingDocumentThumbnail Triggered when a checkmark indicator is added to a document thumbnail. The following parameters will be provided to the callback in the argument object:
    • documentId {String} The ID of the document that received a checkmark indicator.
  • onFinishedRemoveMarkingDocumentThumbnail Triggered when a checkmark indicator is removed from a document thumbnail. The following parameters will be provided to the callback in the argument object:
    • documentId {String} The ID of the document which has lost its checkmark indicator.

Colors and Fonts for Watermarks

Watermarks can now be customized with multiple colors and fonts. Previously, watermarks could only be black Times New Roman text. Now, new options in the watermarks dialog allow a user to choose between several fonts and colors for a watermark. Additionally, a new Javascript API allows custom code to add and manipulate watermarks.

New and Changed Client-side API

  • removeAllWatermarks() Removes all the watermarks from the current document.
  • addWatermark(text, options) Adds a new watermark to the current document. When called without options, as virtualViewer.addWatermark(text), a horizontal black watermark will be created in the center of the document. If the method is called without any given text, an error will be displayed. Passing in additional parameters will customize the watermark. The API options control whether the watermark is transparent or opaque, the watermark’s position on the page and direction, what pages the watermark will be on, the formatting of text, and how far the watermark will be stretched across a page.
    • text {string} pass in the text for the watermark. A watermark’s text can hold both static text and premade tags that display dynamic data. This could be a simple string–"Draft", for example. Using the premade tags, a watermark might display “MyDocument.pdf Page 1” on the first page, and then “MyDocument.pdf Page 2” on the second, and so on. Valid tag values are vvDefines.watermarkTags.USERNAME, vvDefines.watermarkTags.TOTALPAGES, vvDefines.watermarkTags.PAGENUMBER, vvDefines.watermarkTags.PRINTTIME, vvDefines.watermarkTags.SERVERTIME and vvDefines.watermarkTags.DOCUMENTNAME. The tag and any custom static text must be concatenated into a single string, and passed into this parameter. The above example would be passed into the API like this: virtualViewer.addWatermark(vvDefines.watermarkTags.DOCUMENTNAME + " Page " + vvDefines.watermarkTags.PAGENUMBER);. The text string is created by concatenating any premade tags with static text.
    • options {object} pass in an object storing optional parameters. All following parameters should be passed as attributes in this object.
    • options.transparency {boolean} pass in this parameter set to true for a transparent watermark. Otherwise, it will be opaque. The default value for this is false.
    • options.appearance {string} pass in this parameter for the appearance and placement of the watermark. The options for this parameter are “center”, “bottom”, “top”, “diagonal ascending” and “diagonal descending”. The default for this parameter when not set is “center”.
    • options.pageRangeType {string} pass in this parameter to determine which pages the watermark will be created on. The options are “all”, “page range” and “current”. If not set, it will default to “current” and a watermark will only be created on the current page of the document.
    • options.pageRangeValue {string} pass in this parameter to set the range of pages watermarks will be added to. This will only be respected if options.pageRangeType is set to “page range.” It will throw an error if options.pageRangeType is anything besides “page range”. The value for options.pageRangeValue can have dashes and commas: for instance “7-13,20”.
    • options.stretchPercent {number} pass in the decimal representation of the percentage the watermark will be stretched across the page. The options for this parameter are 1, 0.5, and 0.33. If this parameter is not set, it will default to 1.
    • options.fontName {string} pass in the font of the watermark. If this is not set or an invalid font name is given, it will default to “Times New Roman”.
    • options.color {string} pass in the color of the watermark. The value must be the hexidecimal value of the desired color, without the leading hash (#). If not set, this value will be “000000”, i.e. black.

Enabling Pan Tool With Scrollbars

The pan tool allows users to scroll a document by clicking and dragging on the image pane. This tool is enabled in config.js by setting the imageScrollBars item to false. Previously, this configuration item was the only way to enable the pan tool. The tool could only be active while the scrollbars were disabled. Now, the pan tool can be enabled and the scrollbars can remain active.

To configure this behavior, the pan tool can be enabled or disabled in the User Preferences dialog under the General tab. In the dialog, there are options to turn on the pan tool without scrollbars, turn on the pan tool with scrollbars, and turn off the pan tool. Setting vvConfig.imageScrollbars to false will still turn on the pan tool and fully disable the scrollbars.

Improved Search of Large PDFs

Performance of text search on very long PDF documents has significantly improved. The performance increase is most notable for PDFs longer than one hundred pages, while searching for text that is uncommon or non-existent on the document. Shorter PDFs should have little change to performance while searching for a common term on a large PDF will still require processing time. No configuration or code changes are necessary to activate this feature.

Font Configuration for HTML File Format

HTML files may require a font source to be set manually; if a font requested by an HTML document is not available on the system, VirtualViewer will attempt to use a font from one of the following configured locations, and then will substitute a font if no appropriate .ttf file is found.

By default, VirtualViewer will set the font source to the virtualviewer/fonts directory. Any .ttf files in this directory will be available for VirtualViewer to use while rendering HTML files to SVG or raster. Note that this directory will be accessed as a directory on disk, rather than as a resource; VirtualViewer will not be able to use this default if deployed as a packed war. If VirtualViewer is deployed as a packed war, the directory must be configured in the init-param described below in order to correctly load fonts.

A new optional init-param in web.xml will configure VirtualViewer to use fonts in that directory, rather than the default /fonts directory. Set the value of the init-param htmlFontSourceDirectory to the absolute path of a directory containing font files.

New Callbacks and Javascript API

Several new callbacks and client-side API allow additional custom control of the viewer.

New Callbacks

  • saveAnnotationsError is triggered when the viewer attempted to save annotations but the process encountered an error. The following parameters will be provided to the callback in the argument object:
    • documentId {String} The ID of the document that was saved.
    • clientInstanceId {String} The clientInstanceId passed to the server in the save call.
    • exceptionMessage {String} One of the avenues for an error message. A message may be provided here if the server caught the exception, and gracefully passed the error message back.
    • statusText {String} The other avenue for the error message. This may be HTTP status information, most helpful for connection errors, or wholly-uncaught errors encountered during save.

New Client-side API

  • reloadDocument(documentId, documentPaneIndex, passthroughCallback) Completely reloads a document from the server, reloading unsaved crop, image manipulation, flip, rotation, and annotation modifications. All parameters are optional.
    • documentId {String} The ID of the document to reload. If no document ID is given, the currently open document will be reloaded.
    • documentPaneIndex {number} If document compare is open, the pane index–0 or 1–may be specified in case the same document is open in both panes of the viewer and only one should be reloaded.
    • passthroughCallback {function} This callback function will be called when the document model has finished reloading. It is not provided any parameters.
  • reloadAnnotations(documentId, documentPaneIndex) Reloads the annotations on the document from their server state.
    • documentId {String} The ID of the document whose annotations should be reloaded. If no document ID is given, the currently open document will reload its annotations.
    • documentPaneIndex {number} If document compare is open, the pane index–0 or 1–may be specified in case the same document is open in both panes of the viewer and only one should reload its annotations.
  • removeDocumentArrayFromCache(documentIdArray, clientInstanceId) Remove multiple documents from the server cache.
    • documentIdArray {string[]} An array of IDs specifying which documents should be removed from the cache.
    • clientInstanceId {String} Specify the clientInstanceId to set for this server request. If this parameter is null, the currently-set clientInstanceId will be used.