The DocumentViewer uses a command system to perform actions. Each command is a data structure that contains the unique name of the command, whether it can run at the current state of the Document Viewer, optional arguments and results. DocumentViewerCommands manages the commands of the DocumentViewer and can be accessed by the DocumentViewer.Commands property.
Commands are generally tied to a user interface element in the application. The command properties can be used to enable or disable the element (if the command cannot be run at this time), set its state (for example, a checked box), and run the action when the user select the element (such as clicking on a button or a menu item).
The DocumentViewer contains many parts that must be kept synchronized. These parts use public LEADTOOLS classes and controls such as the ImageViewer class or the AnnAutomation class, and all actions are performed using the public and documented properties and methods of the classes.
For example, the application has a menu item that sets the current interactive mode of the view to Pan/Zoom.
Pan/Zoom is one of the interactive modes added to the DocumentViewer by default. Its ID is ImageViewerInteractiveMode.PanZoomModeId. Here are the steps required:
First, the application should update the enabled/disabled state of the element as follows:
The above can be performed using this code snippet:
function updatePanZoomUIState() {
// First test
$("#panZoomElement").prop("disabled", !documentViewer.hasDocument);
if (documentViewer.hasDocument) {
// Find the Pan/Zoom interactive mode, see if it is already enabled
var panZoom = documentViewer.view.imageViewer.interactiveModes.findById(lt.Controls.ImageViewerInteractiveMode.pPanZoomMode);
// Second test, see if the mode is already enabled
$("#panZoomElement").prop("disabled", panZoom.isEnabled);
}
}
Next, when the user clicks the Pan/Zoom menu item, the application must perform the following:
One cannot just call ImageViewerInteractiveModes.EnableById since this will disable all the other interactive modes. The developer has to keep Annotations and Page Links modes enabled because those can be enabled with Pan/Zoom. When the user clicks an annotation object, it should be automated. When the user clicks on a page link, it must run. Pan and zoom should only happen when the user clicks outside those objects. To accomplish this, enumerate all of the modes to disable all except Pan/Zoom, Annotations, and Page Links
If the annotations mode is used, then cancel any current designers (for example, if the user is in the middle of a multi-click operation to draw a new polyline) and set the current object ID in the toolbar back to Select
.
Finally, call UpdatePanZoomUIState
again to disable the menu item
The above can be performed using this code snippet:
function setPanZoom() {
// First action
for (var i = 0; i < documentViewer.view.imageViewer.count; i++) {
var mode = documentViewer.view.imageViewer.interactiveModes[i]; {
mode.isEnabled =
mode.id == lt.Controls.ImageViewerInteractiveMode.panZoomMode ||
mode.id == lt.Documents.UI.DocumentViewer.annotationsInteractiveModeId ||
mode.id == lt.Documents.UI.DocumentViewer.pageLinksInteractiveModeId;
}
// Second action
// We cannot just call .automation, the annotations might not be loaded yet, so we must check for that
if(documentViewer.annotations != null && documentViewer.annotations.automation != null) {
documentViewer.annotations.automation.cancel();
documentViewer.annotations.automationManager.currentObjectId = lt.Annotations.Core.AnnObject.selectObjectId;
}
// Third action
updatePanZoomUIState();
}
The main advantage of implementing a high-level command system is that LEADTOOLS handles the command "plumbing". LEADTOOLS provides several built-in commands that can simplify code and reduce development time. The built-in commands handle more complex actions cleanly and ensure that all parts of the DocumentViewer are kept in sync. All commands are identified by unique simple string constants, so binding built-in commands to the user interface is easy.
The DocumentViewer comes with a built-in command that sets the pan/zoom interactive mode. The name of the command is lt.Documents.UI.DocumentViewerCommands.interactivePanZoom
. We can use the command system to replace the code in the two methods "Low-Level Implementation" by using one line of code:
function updatePanZoomUIState() {
$("#panZoomElement").prop("disabled", !documentViewer.commands.canRun(lt.Documents.UI.DocumentViewerCommands.interactivePanZoom, null));
}
function setPanZoom() {
documentViewer.commands.run(lt.Documents.UI.DocumentViewerCommands.interactivePanZoom, null);
}
The value null
is passed to the CanRun and Run methods because InteractivePanZoom does not take any parameters. Some commands such PageGoto require a parameter (the page number) and must be invoked like this:
// Go to page number 10
documentViewer.commands.run(lt.Documents.UI.DocumentViewerCommands.pageGoto, 10);
The parameters of a command are a generic object type. The command will perform the necessary conversion, if required. In the case of PageGoto, the parameter will be converted to an integer.
CanRun will also take a parameter for these commands:
value = documentViewer.commands.canRun(lt.Documents.UI.DocumentViewerCommands.pageGoto, 10);
If we have a document, the document has at least 10 pages, and the current page number is not 10 already, value
will be true
; otherwise, value will be
false`.
The Run method always returns the result of running the command as a generic object. Some commands such as TextExport return the text of a page or the document as a string object. Note that TextExport also accepts the page number as a parameter. If null or 0 is passed, it will return the text for all the pages:
// Get the text for page number 10 as a string
var text = documentViewer.commands.run(lt.Documents.UI.DocumentViewerCommands.textExport, 10);
In addition to CanRun and Run, some commands contain a boolean state property. For example, the InteractiveAutoPan command is used to enable/disable auto-panning. These commands usually uses a check mark as a user interface element to indicate the state. For example:
function updateAutoPanUIState() {
// Can we run the command?
$("#autoPanElement").prop("disabled", !documentViewer.commands.canRun(lt.Documents.UI.DocumentViewerCommands.interactiveAutoPan, null));
// Get the command
var command = documentViewer.commands.getCommand(lt.Documents.UI.DocumentViewerCommands.interactiveAutoPan);
// Use its current state
$("#autoPanElement").prop("checked", command.state);
}
function flipAutoPan() {
// Just call Run, the command knows it has a state and will flip it automatically, enabling or disabling auto-pan accordingly
documentViewer.commands.run(lt.Documents.UI.DocumentViewerCommands.interactiveAutoPan, null);
}
Although most of the actions performed on the DocumentViewer are instant, some operations can take a considerable amount of time depending on the viewer state and the data requested.
For example, the TextExport command parses the text from the DocumentPageText object of a page obtained using DocumentPage.GetText. This method parses the text from the page using either SVG or OCR technologies and, especially in the latter case, can take seconds to return. DocumentViewerText internally caches the DocumentPageText objects once they are obtained. The first request for page text will take longer than subsequent requests, which are relatively instant.
TextExport performs the following (simplified and ignoring the DocumentViewer.Operation events involved):
Checks if Text has a DocumentPageText object cached for the requested page. If so, it will parse the text and return the result; this operation is instant. If not, continue to the next step.
Checks the value of Text.AutoGetText.
false
, then the application has requested that DocumentPage.GetText not to be automatically invoked. This method returns an empty string; this operation is instant. true
, then DocumentPage.GetText is invoked. If the original document does not support SVG (such as a TIFF or raster PDF document), then the operation will use OCR. Text will wait for this to finish, cache the DocumentPageText before parsing the data and returning; this operation is long running.Based on the steps above, calling Run with TextExport may not immediately return control to the application. If this command is to be performed directly from a user interface element, then the UI thread of the application will be unresponsive until the method returns.
One solution is to perform the following when the user selects the Text Export UI element:
Show a busy dialog
Run the command in a separate thread
When the command returns, hide the busy dialog
This will work, but will have the negative effect of always showing/hiding a busy dialog, even if the command is instant. In most applications, this will result in an undesirable screen flicker.
Another option is to use the DocumentViewerCommands.IsSlow method. Each command will return true
or false
based on its internal current state and calculations. The following a solution to handle the Text Export UI element using the IsSlow method:
Call documentCommands.isSlow(DocumentViewerCommands.textExport, 10)
If the result is true
, show a busy dialog, run the command in a separate thread, then hide the busy dialog on completion
If the result is false
, run the command in the UI thread
This will eliminate the screen-flicker problem described above by only showing the busy dialog when required.
The DocumentViewerCommand class holds the data for each command. This class contains the following members:
Member | Description |
---|---|
Name |
Unique name of the command |
CanRunHandler |
Callback to use when checking that this command can run using a value |
RunHandler |
Callback to use to run the command with a value |
IsSlowHandler |
Callback to use to check if the command will be slow when using a value |
Value |
Current value of the command |
HasState |
Indicates if this is a state command |
State |
Current state value of the command |
All of the built-in commands are instances of DocumentViewerCommand objects that are initialized and ready to use. The callbacks are called when DocumentViewerCommands.Run, DocumentViewerCommands.CanRun, and DocumentViewerCommands.IsSlow are called, respectively.
The DocumentViewerCommands class manages the commands of the DocumentViewer and can be accessed by the DocumentViewer.Commands property. Internally, it stores a dictionary of all the commands of the DocumentViewer. The key is the command's unique name, and the value is the corresponding DocumentViewerCommand object. It has the following members:
Member | Description |
---|---|
GetCommand |
Gets the DocumentViewerCommand with the specified name |
CanRun |
Checks if a command can run with the specified value |
Run |
Runs a command with the specified value |
IsSlow |
Checks if running a command will be slow with the specified value |
The DocumentViewer class comes with the following built-in commands that can used right away. All the commands are initialized when DocumentViewer is created and stored in the DocumentViewerCommands class.
These commands use DocumentViewer.GotoPage.
Command | Value parameter | Returns | Description |
---|---|---|---|
Page.First | None | None | Go to the first page in the document |
Page.Next | None | None | Go to the next page in the document |
Page.Previous | None | None | Go to the previous page in the document |
Page.Last | None | None | Go to the last page in the document |
Page.Goto | int pageNumber | None | Go to the specified page number |
These commands use the DocumentViewerView and its ImageViewer control.
Command | Value parameter | Returns | Description |
---|---|---|---|
View.ZoomIn | None | None | Zooms the view in by DocumentViewerView.ZoomRatio |
View.ZoomOut | None | None | Zooms the view out by ZoomRatio |
View.ZoomPercentage | double percentage | None | Zooms the view by the specified percentage |
View.FitWidth | None | None | Fits the page width in the view |
View.FitPage | None | None | Fits the page in the view |
View.ActualSize | None | None | Show the actual size of the page in the view |
View.RotateClockwise | None | None | Rotates the view 90 degrees clockwise |
View.RotateCounterClockwise | None | None | Rotates the view 90 degrees counter-clockwise |
View.ItemType | DocumentViewerItemType value | None | Sets the view item type to the value |
These commands use pre-defined ImageViewerViewLayout objects to set in the view's ImageViewer control.
Command | Value parameter | Returns | Description |
---|---|---|---|
Layout.Single | None | None | Sets the layout as single in the view |
Layout.Vertical | None | None | Sets the layout as vertical in the view |
Layout.Double | None | None | Sets the layout as double in the view |
Layout.Horizontal | None | None | Sets the layout as horizontal in the view |
These commands use pre-defined ImageViewerInteractiveMode objects to set in the view's ImageViewer control.
Command | Value parameter | Returns | Description |
---|---|---|---|
Interactive.PanZoom | None | None | Sets pan/zoom as the current interactive mode |
Interactive.Pan | None | None | Sets pan as the current interactive mode |
Interactive.Zoom | None | None | Sets zoom as the current interactive mode |
Interactive.ZoomTo | None | None | Sets zoom-to as the current interactive mode |
Interactive.MagnifyGlass | None | None | Sets magnify glass as the current interactive mode |
Interactive.RubberBand | None | None | Sets the generic rubber band as the current interactive mode |
Interactive.SelectText | None | None | Sets select text as the current interactive mode |
Interactive.AutoPan | None | None | Enables/Disables the auto pan interactive mode |
These commands use methods from the DocumentViewerText object.
Command | Value parameter | Returns | Description |
---|---|---|---|
Text.Copy | int pageNumber | None | Calls DocumentViewerText.Copy(pageNumber) |
Text.SelectAll | None | None | Calls DocumentViewerText.SelectAll |
Text.ClearSelection | None | None | Calls DocumentViewerText.ClearSelection |
Text.Export | int pageNumber | string | Calls DocumentViewerText.ExportText(pageNumber) |
Text.FindNext | None | List of DocumentViewerTextItem or null | Calls DocumentViewerText.Find(DocumentViewerText.LastFindText, false, true) |
Text.FindPrevious | None | List of DocumentViewerTextItem or null | Calls DocumentViewerText.Find(DocumentViewerText.LastFindText, false, false) |
Text.Get | int pageNumber | None | Calls DocumentViewerText.GetDocumentPageText(pageNumber) or DocumentViewerText.GetAllDocumentPageText, if pageNumber is 0 |
These commands use methods from the DocumentViewerAnnotations and AnnAutomation objects. These commands provide the necessary checks to make sure the methods can be called without errors.