DocumentViewer uses the commands 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).
Why Use Commands
DocumentViewer contains many parts that must be kept in sync. These parts uses public LEADTOOLS classes and controls such as ImageViewer or AnnAutomation and all actions is performed using the public and documented properties and methods of the classes.
For example, the application has a menu item when clicked, sets the current interactive mode of the view to Pan/Zoom.
Pan/Zoom is one of the interactive modes added by DocumentViewer by default. Its ID is ImageViewerInteractiveMode.PanZoomModeId. Here are the steps required:
First thing, the application update the enabled/disabled state of the element as follows:
If the document viewer does not have a document set, then the element must be disabled
If the current interactive mode is already Pan/Zoom, then the element must be disabled (or checked and should not be invoked), but we will use disabled for this example
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);
}
}
This will take care of the state of the element.
Next, when the user clicks the Pan/Zoom menu item, the application must perform the following:
We cannot just call ImageViewerInteractiveModes.EnableById since this will disable all the other interactive modes. We have to keep Annotations and Page Links modes enabled because they can be used along Pan/Zoom - when the user clicks an annotation object, it gets automated, when the user clicks on a page links, it must run and panning and zoom only happens when the user clicks outside these objects. So we must loop through all the modes, disable all but Pan/Zoom, Annotations and Page Links
If annotations is used, then we must cancel any current designers (for example, if we are in the middle of a multi-click operation to draw a new polyline) and we must set the current object ID in the toolbar back to Select
.
Finally, we must 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();
}
Running Commands
The above code must be exactly right to get all the parts of the document viewer in sync and this is only for a single simple action: Setting the current interactive mode.
DocumentViewer comes with a built in command that sets the pan/zoom interactive mode. The name of the command is %"Interactive.PanZoom":F:Leadtools.Documents.UI.DocumentViewerCommands.InteractivePanZoom%. We can use the commands system to replace the code in the two methods above 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 built-in commands handle more complex actions cleanly and ensures that all part of the document viewer are kept in sync. Any command can be identified by a unique simple constant string, and thus, binding commands with the user interface can be achieved easily.
Command Parameters and Results
The null parameters passed to the CanRun and Run methods is because InteractivePanZoom does not take any parameters. Some commands such PageGoto required a parameter (the page number in this case) and must be invoked like this:
// Goto page number 10 in the viewer
documentViewer.commands.run(lt.Documents.UI.DocumentViewerCommands.pageGoto, 10);
The parameters of a command is a generic object type. The command will perform the necessary conversion if required - to an integer value in the case of PageGoto.
CanRun will also take a parameter for these commands:
value = documentViewer.commands.canRun(lt.Documents.UI.DocumentViewerCommands.pageGoto, 10);
And "value" will be true if we have a document, the document has at least 10 pages or more and the current page number is not 10 already. Otherwise; false.
Run always return the result from running the command and this result is also a generic object. The commands we discussed above all return null since they do not return any results. Some commands like TextExport will return the text of a page (or the document) as a string object. Note that TextExport also accepts the page number as a parameter, or if null or 0 passed, 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);
State Commands
In addition to CanRun and Run, some commands contain a boolean state. For example, the InteractiveAutoPan can be used to enable/disable auto-panning that is used along the other interactive modes. The user interface element these commands usually uses a check mark to indicate the state. The application can use this command as follows:
function updateAutoPanUItate() {
// 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);
}
Slow Commands
Although most of the actions performed on the document viewer are instant, some operations might take a considerable amount of time depending on the viewer state and the data requested.
For example, the TextExport command discussed above 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 caches the DocumentPageText objects for the pages internally once they are obtained. The first time the text of a page might be slow, but subsequent requests are instant.
TextExport performs the following (simplified and ignoring the DocumentViewer.Operation events involved):
Checks if Text has a DocumentPageText object for the requested page cached, if so, it will parse the text and return the result and exits - this operation is instant.
Next, it will check the value Text.AutoGetText, if this value is false, then the application requested that DocumentPage.GetText not to be invoked instantly. Thus the method returns an empty string - this operation is instant.
If the value of Text.AutoGetText is true, then DocumentPage.GetText is invoked and if the original document does not support SVG (such as a TIFF or raster PDF document), then the operation will use OCR and might take a few seconds to return. Text will wait for this to finish, caches the DocumentPageText before parses the data from it and returning - this operation is slow.
The next time TextExport is called on this same page, and as described in the first step, the text is parsed from the cached data and the result is returned. DocumentPage.GetText is not called - this operation is instant.
From the above, calling Run with TextExport may not return control to the application immediately. If this command is to be performed from a user interface element directly then the UI thread of the application will freeze and not be responsive till the method return.
One solution is to perform the following when the user select 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 have the negative effect of showing/hiding this busy dialog always, even if the command will be instant (as in most of the cases described above). In most applications this will result in the undesirable action of the screen flickering for a moment.
The other option is to use the DocumentViewerCommands.IsSlow method. Each command will return true or false based on its internal current state and calculation. As described above in the case of TextExport. Here is the solution described above to handle Text Export UI element but with using IsSlow:
Call documentCommands.isSlow(DocumentViewerCommands.textExport, 10)
If the result is true, show a busy dialog, run the command in a separate thread and then hide the busy dialog on completion
If the result is false, run the command directly in the UI thread
This will eliminate the flickering problem described above but not showing the busy dialog unless required.
DocumentViewerCommand Class
DocumentViewerCommand 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 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 run using a value |
Value |
Current value of the command |
HasState |
Indicates whether this command uses a state |
State |
Current state value of the command |
All the built-in commands are instance of DocumentViewerCommand objects that are initialized and ready to use. The callbacks are what gets used with DocumentViewerCommands.Run, DocumentViewerCommands.CanRun and DocumentViewerCommands.IsSlow are called.
DocumentViewerCommands Class
DocumentViewerCommands 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 document viewer. The key is the command 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 a specified value |
Run |
Runs a command with the specified value |
IsSlow |
Checks if running a command will be slow with the specified value |
Built-in Commands
DocumentViewer 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 DocumentViewerView and its ImageViewer
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
Command | Value parameter | Returns | Description |
---|---|---|---|
Layout.Single | None | None |
Sets the single layout in the View |
Layout.Vertical | None | None |
Sets the vertical layout in the View |
Layout.Double | None | None |
Sets the double layout in the View |
Layout.Horizontal | None | None |
Sets the horizontal layout in the view |
These commands use pre-defined ImageViewerInteractiveMode objects to set in the view's ImageViewer
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 |
Enable/Disable auto pan interactive mode |
These commands use methods from DocumentViewerText.
Command | Value parameter | Returns | Description |
---|---|---|---|
Text.Copy | int pageNumber | None |
Calls DocumentViewerText.Copy(pageNumber) |
Text.SelectAll | None | None | |
Text.ClearSelection | None | None | |
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 DocumentViewerAnnotations and AnnAutomation. These commands provides the necessary checks to make sure the methods can be called without errors.