NotesStream and NotesDXLExporter

I’m trying to store a document’s DXL in a rich text field for later use. I’m running into a 64k paragraph limit issue. The reason seems to be that NotesStream.ReadText(0) is not giving me a single line of the output of NotesDXLExporter. I’m doing something simple like this:

Dim Session As New NotesSession

Dim SourceDoc As NotesDocument

Dim DestDoc As NotesDocument

Dim Text As NotesRichTextItem

Dim Exporter As NotesDXLExporter

Dim Stream As NotesStream

Dim NewLine As String

Set SourceDoc = Session.CurrentDatabase.UnprocessedDocuments.GetFirstDocument

Set DestDoc = Session.CurrentDatabase.CreateDocument

DestDoc.Form = “SimpleFormWithSingleRichTextField”

Set Text = DestDoc.CreateRichTextItem( “RichTextFieldName” )

Set Stream = Session.CreateStream

Set Exporter = Session.CreateDXLExporter( SourceDoc, Stream )

Call Exporter.Process

Stream.Position = 0

While Not Stream.IsEOS

NewLine = Stream.ReadText( 0, 4 )

Call Text.AppendText( NewLine )

Wend

Call Workspace.EditDocument( False, DestDoc )

No matter what I use for parameters to Stream.ReadText, I always get the whole DXL document. I can use 4 (EOL_ANY), or even specifically 1 (EOL_LF ASCII 10), but either way NewLine clearly include multiple lines in debugger (little box for EOL and continuing), and the loop only happens twice: once for everything, and then again for an EOF character. I can use this line:

NewLine = Replace( NewLine, Chr(10), | + NewLine| )

And see in the output that the input obviously includes chr(10)'s, but no matter what I try I can’t get NotesStream.ReadText to treat them as EOL characters. I can even add a line like:

NewLine = |line starts here| & NewLine

In the loop, and only see that on the first line of the result and not on any other line, and the result is cut off around 64k :frowning:

I’d like to use NotesRichTextItem.AddNewLine( 1, True ) to try and get around the paragraph limit issues, but if I can’t get less than the entire DXL output from NotesStream, then how??

Am I doing something wrong? Any pointers greatly appreciated!

Subject: Does it really must be RichText?

Hi Mike,

Why not to store the DXL as a binary file attachment? In this case, you shouldn’t care about CR/LF.

Subject: RE: Does it really must be RichText?

That could work, but what I’m trying to do is give a rich text field a rich text default value. So if it requires an attachment in a rich text field, then where does that rich text field get its default value from? I want to have it all part of the .nsf so that you dont need to go out to anywhere else to copy anything in. I thought about file resources, but couldn’t find a way to access them to suck the DXL out?

Subject: RE: Does it really must be RichText?

You should be able to have a file attachment as default value for a rich text field, by attaching the file to a rich text field in a profile document and using @GetProfileField in the default formula of the RTF on your form.

Subject: NotesStream and NotesDXLExporter

Well, you could just read the stream into a String as a unit as a workaround. (Strings can be huge as long as you don’t try to create them from literals.) From there, you can read using StrToken or Split into an array. Not that it wouldn’t be nicer to have the ReadText work as advertised…

Subject: Well that was a weird one!

First of all, thank you sir, that got me on the right track.

Now for those who are interested (working code at the end):

I started out using Split and working on those lines, but came up with the same result. Thinking I had hit an array limitation, I moved on to using StrToken to walk through it. Same result but slower. Then I started looking into it. Found that if I put in a Stop at a certain point when debugging, everything up to that point would come through. Weird. Tried putting in a NotesRichText.Update instead of a Stop and running without debugger, and still got the extra. Put an Update on every line, and got everything. Hmmm. Went back to Split with Update added, and it worked. Went back to my original with Update added, and it worked too (except for a weirdness with some formatting I’m trying to do, the chr(10)s i was putting (back) in stopped appearing at around the old cutoff point…). Then tried putting just one Update at the end, and that did the trick as well. It slices, it dices, it cures what ails ya, before posting here try throwing in a NotesRichText.Update somewhere in your code and chances are it might save you the bother! :wink:

So here’s how it came out, changes from the original in bold:

Dim Session As New NotesSession

Dim SourceDoc As NotesDocument

Dim DestDoc As NotesDocument

Dim Text As NotesRichTextItem

Dim Exporter As NotesDXLExporter

Dim Stream As NotesStream

Dim NewLine As String

Dim DXL As String

Dim NewLines As Variant

Set SourceDoc = Session.CurrentDatabase.UnprocessedDocuments.GetFirstDocument

Set DestDoc = Session.CurrentDatabase.CreateDocument

DestDoc.Form = “SimpleFormWithSingleRichTextField”

Set Text = DestDoc.CreateRichTextItem( “RichTextFieldName” )

Set Stream = Session.CreateStream

Set Exporter = Session.CreateDXLExporter( SourceDoc, Stream )

Call Exporter.Process

Stream.Position = 0

While Not Stream.IsEOS

NewLine = Stream.ReadText( 0, 4 )

Call Text.AppendText( NewLine )

Wend

DXL = Stream.ReadText

NewLines = Split( DXL, Chr(10) )

Forall NewLine In NewLines

Call Text.AppendText( NewLine )

End Forall

Call Text.Update

Call Workspace.EditDocument( False, DestDoc )

Subject: You might not actually want a separate paragraph for each line…

…because there’s also a practical limit to the number of paragraphs. The ReportGenerator class on openntf.org shows a way to get the maximum amount of text. But if I recall correctly, the 64K limit has been raised to some much larger number, and even then the rich text classes will split it into paragraphs for you. What problem were you seeing with the original code, that led you to conclude that you couldn’t have it all in one huge paragraph?

Subject: RE: You might not actually want a separate paragraph for each line…

Thanks Andre, I’ll take a look at that if I ever run into limitations with what I’ve come up with. What I was seeing without calling Update before editing the doc was truncation. And then even with Update added, the formatting I was doing went all weird - I was replacing the EOL character with my own (plus stuff), and at about the point where it truncated without using Update, with Update the EOL characters stopped appearing (but the other stuff was still there). Very weird, handling it with an array works nicely though so I don’t want to mess with it. :wink:

So here is the long story of what I have ended up doing. If anyone can think of a better way and doesn’t mind sharing, I’d be interested to hear about it.

What I’m trying to do is get a default value for a rich text field, including formatting and tables and embedded images and whatnot. Without having a source rich text field available to copy from. So:

  1. Get the rich text content as DXL.

  2. Store it somewhere in the .nsf.

  3. …create the new doc with the rich text field(s) on it that need default rich text content:

    a. Get the stored DXL into a NotesStream.

    b. Extract the DXL for the particular rich tex field that you want.

    c. Make a copy of that element and append it to the new doc.

  4. Easy Enough.

  5. I tried getting the DXL and formatting it as a formula intending to just use it as a default value for an intermediate rich text field, but formulas have a size limit. So I tried formatting it as a LotusScript string intending to store it in code, but strings have a size limit. So I tried formatting it to store each line of DXL as a list value, and that works great. I’m sure I could bump into a limit on script library size if I tried (a bunch of text and tables and a couple medium images gives ~300k in ~900 lines), but hopefully by shrinking images and whatnot I can keep that in the distance. So I use this bit of code to grab the DXL of the selected doc and format it as code to populate a list in LotusScript and pop it up on a temp form:

Dim Session As New NotesSession

Dim SourceDoc As NotesDocument

Dim DestDoc As NotesDocument

Dim Text As NotesRichTextItem

Dim Exporter As NotesDXLExporter

Dim Stream As NotesStream

Dim DXL As String

Dim NewLines As Variant

Dim Counter As Long

Set SourceDoc = Session.CurrentDatabase.UnprocessedDocuments.GetFirstDocument

Set DestDoc = Session.CurrentDatabase.CreateDocument

DestDoc.Form = “SimpleFormWithSingleRichTextField”

Set Text = DestDoc.CreateRichTextItem( “RichTextFieldName” )

Set Stream = Session.CreateStream

Set Exporter = Session.CreateDXLExporter( SourceDoc, Stream )

Call Exporter.Process

Stream.Position = 0

DXL = Stream.ReadText

NewLines = Split( DXL, Chr(10) )

Forall NewLine In NewLines

NewLine = Replace( NewLine, |"|, |""| )

NewLine = |	DefaultText( "| & Counter & |" ) = "| & NewLine & |"| & Chr(10)

Call Text.AppendText( NewLine )

Counter = Counter + 1

End Forall

Call Text.Update

Call Workspace.EditDocument( False, DestDoc )

Then I take the results of that code, copy it all and paste it into a new script library. The only other part of that script library is this line in declarations:

Dim DefaultText List As String

Now I have a global variable called DefaultText which is a list of all the lines of DXL needed to recreate that doc.

  1. By using that script library in any other code, I can read that list back into a NotesStream like this:

Set Stream = Session.CreateStream

Forall NewLine In DefaultText

Call Stream.WriteText( NewLine & Chr(10) )

End Forall

Then it’s relatively trivial to do your regular hook a NotesDOMParser up to the stream, and make copies of the rich text field elements you wan to get the content of (for that I found this very helpful ). Then create another NotesDXLImporter - NotesDOMParser - NOTESDXLImporter chain to stick them into your final destination doc.

So, a new database can be created anywhere based on this design, and with the touch of a button some rich text content can appear (not limited to what you could build by hand with the NotesRichText classes, and much more simply than attempting to roll your own DXL). And if the default rich text content ever needs to be changed, just lay out an example of what you want it to look like, hit a button, and copy the results into the script library and its there ready for the next time. Seems a little odd even to me, but hey it seems to work well :slight_smile:

I hope that helps someone, however unlikely it is that anyone else needs to do anything like this. Like I said if anyone has any advice (like, you don’t need to do any of that madness just do blah, or big deal so-and-so already did that over here) I’d be interested to hear it, even a simple opinion would be appreciated. Thanks again for your help everyone.

Subject: NotesStream and NotesDXLExporter

You might have a look at the NotesDomParser. Here you can walk the whole DOM-tree and get the DXL of each element. The Designer’s help has a good sample.

Subject: RE: NotesStream and NotesDXLExporter

Yes I am familiar with NotesDOMParser, that’s how I will scan the stored DXL for particular elements to copy out later on (see code below). But I don’t see how it will help me to store the DXL in the first place? There is no NotesDOMNode.Text property or anything like that? Sorry if I’m being dense, but I still don’t see any solution? Again, any help with getting a doc’s DXL stored into a rich text field is greatly appreciated. What I have worked great until I started testing it with real source data including images embedded in rich text, which is the whole point! I need some way to store DXL inside an .nsf so I can create some predefined rich text with only code, no original native rich text pasted in anywhere to copy from.

Dim Session As New NotesSession

Dim Doc As NotesDocument

Dim Storage As NotesRichTextItem

Dim Stream As NotesStream

Dim DOMParser As NotesDOMParser

Dim Exporter As NotesDXLExporter

Dim SecondDOMParser As NotesDOMParser

Dim Importer As NotesDXLImporter

Set Doc = Session.CurrentDatabase.UnprocessedDocuments.GetFirstDocument

Set Storage = Doc.GetFirstItem( “Storage” )

Set Stream = Session.CreateStream

Stream.WriteText( Storage.Text )

Set DOMParser = Session.CreateDOMParser( Stream )

On Event PostDOMParse From DOMParser Call GetElement

Call DOMParser.Process

ps - I even looked into storing in a file resource instead of a rich text field, wouldn’t it be great if NotesStream could be hooked up to a file resource for input/output? Or if there was some kind of NotesFileResource class? Maybe I could do something like that on the server somehow with URLs?

Again, any advice / tips / pointers / links to further reading greatly appreciated!