« firstObject XML Editor - a cool and fast XML editor | Main| Part 5: Continuing with DXL to create the Gallery feature in the Icon and Images database »

Part 4: Using DXL to create imagery in documents - continued

Tags: Lotus Notes Software DXL LotusScript
0
In this the fourth part of  article about the Icon and Images database (started in this article), It continues where the third article left off.
3. Analyze the exported DXL file to understand the construction of DXL

When you have created a template document, then export it to a file and review the generated DXL file. To view the DXL file, you can use any editor such as Notepad or a specialized XML editor. My definitive XML editor is the firstObject XML Editor from firstObject.com. This is even free!

Don't be intimidated by the shear size and complex looks of the DXL file! I describe what you look for! I the screenshot below you see a sample DXL file in the firstObject XML Editor;

A picture named M2

On the left you see the tree representation of the DXL file, and on the right you see the actual DXL file content. Note how firstObject XML Editor automatically navigates to the correct position within the huge DXL file when you double click on an element in the tree-view. The important stuff about the DXL file can be summarized like this;

The top-level database element contain all other elements, such as the databaseinfo element

A picture named M3
This identifies which database we have dumped the documents from, and will be used when we import DXL again.

You may have one or more document  elements beneath the database-element. Each document element represent a single document within the Notes database. So, if you selected just one document before you called the Export selected documents as DXL-agent, you will have only a single document-element.

Each document-element contains a list of item-elements, representing each and every Notes field in your document. Again, don't be too intimidated by all the items. We are going mainly for the rich text item Body  when we are analyzing the content that Notes has generated for us.

A picture named M4

So, basically the DXL contains a bunch of item-elements within a document-element.

If you dive into the rich text element from the Body-field, you will see that the structure of the DXL again has a hierarchical structure of elements. Below you see a sample;

A picture named M5

At
A picture named M6 we have a pardef  and par-element. These are paragraph-elements representing what the following paragraph should look like when it comes to fonts and sizes etc. A Notes rich text is stuffed with these elements, and you will see that there are lots and lots of these elements. Even empty rich text fields contains these elements.

At
A picture named M7 you see the first table element. When I created the sample here, I first created an empty two-row table. Only one cell in each row. This is purely because I want to control the look and feel of the table. Think old fashioned web design here, were you used to design the web page by tables within tables etc. At A picture named M8 you see the tablecolumn-element and the tablerow-element for the first single cell row. The tablecoloum define the with of the column and the tablerow contains the content of the row itself. In my sample, this quickly leads to another table at A picture named M9, which again contain its own tablecolumns and tablerow elements. Down at A picture named M10 the content of the actual cell starts and this is where the fun start!

When you get down to the tablerow- and tablecell elements, you expand it further and see the following elements within the par-element;

A picture named M11

At
A picture named M12 you see a run-element. You can compare a run with a part of the rich text. For example may the simple sentence "The quick brown fox" consist of a single run. The sentence "The quick brown fox" (note the different decorations!) consist of 4 runs. The first run contain of the text "The", while the second run contain "quick" and so forth.,This makes sense if you try to remember that Notes actually needs to know  how the different decorations should look like. Italics, bold and underline definitely are different than plain text. The exact same principle is also visible within HTML where the first sentence would look something like this; "<p>The quick brown fox", and the second sentence looks like this; "The <i>quick</i> <b>brown</b> <u>fox</u>".

Now that you know about runs for text, know that the same principle goes for any other content within the rich text field.

At
A picture named M13 you see a cool element, the actionhotspot  sounds like something I've described above doesn't it! You can even see the plain text formula code in the formula-element!! Finally you see the picture-element at A picture named M14, containing some strange characters in the gif-element, The strange characters are indeed the gif image encoded as base64. Base64 is quickly explained a way of encoding a binary file as text, so it can be sent over plain text protocols. You find Base64 described on the internet, and you find several examples on how to implement Base64 also. I use the eminent Base64 LotusScript class described in this article, by Johan Kanngard.

A cool thing about the structure above, is that you can also see the containment, meaning how the actionspot-element surround the picture-element by its start- and end tags. This is how the Notes rich text create the action hotspot aorund the image!


Now you know something about DXL and the strict hierarchy amongst its elements, I hope you see that it makes sense to make Notes tell you how to do this, rather than trying to do this all by yourself.

The remaining concept form here on, is to extract only the necessary parts of the DXL in order to programmatically construct similar DXL by ourselves.

4. Recreate an DXL file similar to the one Notes exported in the second step.

Remember, now I am only organizing my LotusScript code to facilitate the generation of DXL files similar to what Notes exported itself in step 2.

The use of templates
Before we start, let me explain the small concept of templates. By this I mean strings of code containing the skeleton of whatever I want to recreate, and rather have some variables within that skeleton. Below I dive straight into some LotusScript code. First we start in the Declaration-section where I define a g_strDXLTemplatePictureTablecell-string. Now how I use the vertical bar (|) instead of the quotes (") in order to much easier copy and paste from the DXL file!!

' The following will be repeated as the first tablerow
Const g_strDXLTemplatePictureTablecell = |
<tablecell borderwidth='0px 0px 0px 0px'>
<pardef id='$(PARID_COUNTER)' keepwithnext='true' keeptogether='true'/>
<par def='$(PARID_COUNTER)'>
<run>
<font name='Arial' pitch='variable' truetype='true' familyid='20'/>
</run>
<actionhotspot hotspotstyle='none'>
<code event='click'>
<formula>@Environment("IconLibraryData"; txtDocumentUNID + "||$(COMBINATIONKEY)");
@Command([ToolsRunMacro];"(agnIconClickedActionHandler)")</formula>
</code>
<run>
<font name='Arial' pitch='variable' truetype='true' familyid='20'/>
</run>
<picture width='$(IMGWIDTH)px' height='$(IMGHEIGHT)px'>
<gif>
$(BASE64)
</gif>
</picture>
<run>
<font name='Arial' pitch='variable' truetype='true' familyid='20'/>
</run>
</actionhotspot>
<run>
<font name='Arial' pitch='variable' truetype='true' familyid='20'/>
</run>
</par>
</tablecell>
|

The code above is a template. Note how the template also contains variables such as $(PARID_COUNTER), $(IMGWIDTH) and $(BASE64). These variables will be replaced with actual content when I use the template. When I want to use the code above, I have something similar to this;

Dim strPictureTableCell as String
strPictureTableCell = g_strDXLTemplatePictureTablecell

Then I use the strPictureTableCell when I process the code and fill the variables with actual content


strPictureTableCell = Replace(strPictureTableCell, "$(PARID_COUNTER)", "5")
strPictureTableCell = Replace(strPictureTableCell, "$(IMGWIDTH)", "32")
strPictureTableCell = Replace(strPictureTableCell, "$(BASE64)", strTheBase64EncodedGIF)

The code above replaces the variables with the actual content.

The use of GIFs

Lotus Notes can import GIF and JPEG with full fidelty. While it also can import other image formats, Notes will quickly convert the other formats to its own variant of TIFF. So to ensure that we have best possible quality,!

The overview of creating imagery

My code open the resulting DXL files and output the generated content as its created. This may seem like an overkill, but remember, the DXL files can grow pretty large. So if you try to create the complete DXL file say in a LotusScript string only, you will pretty fast run into memory limitations. By opening the resulting DXL file in the start of the code, and write the data as its generated, I don't run into these problems. However, there are probably places in my code that can be even further optimized for speed.

The function creating the icon imagery is the BatchProcessPass2IconImagery within the "Import Processing" script library. Basically it follows this procedure;

Set up the sequence I want to process the images in. I want to present the largest images first, so the 256x256 comes before the 128x128 and 72x72 and so forth. I also ensure that I present the "normal" icons before the "Hot" and "Disabled" icons. I also define the tablecolum-elements for each image size. Remember this is the DXL tablecolumn-element!

Idenitfy the GIF files amongst the all the files for an icon. This will make it easier (and much faster later on) when I create icon imagery.

Start to create the DXL file. The code starts like this;

' Initialise the DXL file with standard headers stuff, identifying the current document
Print #fout, "<?xml version='1.0' encoding='utf-8'?>"
Print #fout, "<!-- DXLExporter version 1.00 (build 72), schema (DTD) version 1.01 -->"
Print #fout, "<!DOCTYPE document SYSTEM 'xmlschemas/domino_6_5.dtd'>"
Print #fout, "<document xmlns='http://www.lotus.com/dxl' version='6.5' replicaid='" & db.ReplicaID & "' form='formIcon'>"
Print #fout, "<noteinfo noteid='" & docIcon.NoteID & "' unid='" & docIcon.UniversalID & "'></noteinfo>"
Print #fout, "<item name='Pass'><number>2</number></item>" ' THIS UPDATES THE PASS NUMBER
Print #fout, "<item name='Body'><richtext>"

Note how the initial DXL is based upon the same basis as the DXL exported by Notes earlier. Also note how I blend in my own content such as db.ReplicaID and doc.NoteID. This will later identify the document I am about to create.

I will now build the tables manually, based upon the DXL templates I have stored in the Declaration-section. One thing to note here is that I pay extreme attention to building the table tablecell-by-tablecell. This means that I need to keep track on how many icons I have per line, and whether or not I have enough icons or not for a complete tablerow. If you am short of icons filling up a complete tablerow, I create empty tablecells to adhere to the DXL specification.

You will see a lot of code like the one below where I grab a template, and fill in the real content;

strDXLTableElement = g_strDXLTemplateTableAElementPrefix ' Start with fresh table-element template
strDXLTableElement = Replace(strDXLTableElement,"$(STATE)", vStateAndShadow(0))
strDXLTableElement = Replace(strDXLTableElement,"$(SHADOW)", vStateAndShadow(1))


After the processing has been completed, I simply write the variable to the DXL file with

Print #g_foutTable, strDXLTableElement

Use intermediate files. An important concept in my code, is that I use some intermediate files for table constructs. The intermediate files contains snippets that I will use later on in the resulting DXL file. This because it make the code easier to track and maintain.

The base64 encoding means that I grab the original file, use the Kanngard LotusScript class to convert it to a base64 file, and finally inject the base64 code into the resulting DXL file.

' Now, base64 encode the GIF file
strB64FileName = strTempPath & "Temp.B64"
Kill strB64FileName
rcB64 = b64.encodeFile(strFileName,strB64FileName)


Note that I also store the raw base64 data for specific GIF files. I will use the base64 data later when I perform so-called Gallery- and Favorites processing. By storing the raw base64 data in separate Notes fields, I can use this base64 data directly later, making the generation of Gallery and/or Favorites data very, very fast.


5. Use the NotesDXLImporter to import the handmade DXL file from the previous step.

When the code has completed the generation of the DXL file, we are ready to import it with the DXL Importer. The complete code to do this can be foundin the simple function below;

Function ImportImageDXLImport(db As NotesDatabase, strDXLFileName As String) As Variant
        Dim session As New NotesSession
        Dim stream As NotesStream
        ImportImageDXLImport = True
        g_strDXLImportError = ""
       
        On Error Resume Next
       
        If Not db.isopen Then
                g_strDXLImportError = "ERROR: Database couldn't be opened."
                ImportImageDXLImport = False
                Exit  Function
        End If
       
        Set stream = session.CreateStream
        If Not stream.Open(strDXLFileName) Then
                g_strDXLImportError = "ERROR: The file """ & strDXLFileName & """ couldn't be opened."
                ImportImageDXLImport = False
                Exit  Function
        End If
        If stream.Bytes = 0 Then
                g_strDXLImportError = "ERROR: File """ & strDXLFileName & """ is empty."        
                ImportImageDXLImport = False
                Exit Function
        End If
       
       
        Dim importer As NotesDXLImporter
        Set importer = session.CreateDXLImporter(stream, db)
        importer.DocumentImportOption = DXLIMPORTOPTION_UPDATE_ELSE_CREATE
        Call importer.Process

        Call stream.Close
End Function

Just as when we exported selected documents as DXL, we create a connection between the database and the existing DXL file to import. The NotesDXLImporter is the class responsible for doing this. In the code above, we first open the DXL file with the CreateStream-method, and then a new NotesDXLImporter is created with the CreateDXLImporter-method, connecting the stream to the database. The line Call importer.Process kicks of the import as specified in the DXL file. And as stated previously, the import processing is much faster than the export processing!

How to detect if something went wrong?

Use a standard On Error Goto xxx, where you check the NotesDXLImporter member Log.  The Log-member contains a XML file describing pretty accurately where the importer stumbled and what the reason was. Use this output to correct and refine your DXL file accordingly.

Conclusion

By using the tecniques described above, you can create pretty cool solutions with imagery. DXL is very very powerful, both as a learning tool to better understand how Notes stores its data, and as a source for dynamic content creation.

DXL rocks!!

The next article will dive into some extra features, such as the Gallery ....

A picture named M15
The Gallery contains all icons and images for a given icon collection. You can click on any icon to go directly to the icon page!

The Favorites-feature let you "collect" different icons to your favorites for later use;

A picture named M16

When you click on the icon in the Favoites page, you will see actions directly for the specific image you initially selected to add to your favories, like this;

A picture named M17

I hope the favorite-feature makes it easier to collect images to your projects

Post A Comment

:-D:-o:-p:-x:-(:-):-\:angry::cool::cry::emb::grin::huh::laugh::lips::rolleyes:;-)