logo

Lost Images When Copying Rich Text Between Documents

Why is it always the little things in Domino that bring your house of cards crashing down? I've just been working on a "killer app" for a client. All was going well until I found a niggly little bug that's scuppered my plans for a solution to one of the must-have features: a form that lets you print more than one document at a time.

If you don't want to read all of this, here's the summary: Why does the AppendRTItem() method not copy over in-line images from a rich-text field?


Say you've got a document that's simply a container for other (child) documents. On this document you have a print-friendly link, which opens the same document using a print-friendly form. This new form has a WQO agent that loops each child document and copies the Rich Text "Body" field from each child to a Rich Text field called BodyPrint on the new form. You can now print all of the documents in one go!

The basics of the code required are:

'Before loop begins
Set rti1 = pDoc.GetFirstItem("BodyPrint")
'The following happens inside a loop!
Call rti1.AppendText("<h4>"+cDoc.Title(0)+"</h4>")
Set rti2 = cDoc.GetFirstItem( "Body" )
Call rti1.AppendRTItem( rti2 )
Call rti1.Appendtext("<hr />")

At first I was unsure how well this would work, but it seemed okay, even with multiple large documents. The problems is when the Body field contained an in-line image. Whereas font-styling, tables and other bits remained the images were broken.

The content is edited in the client and would look something like this:

When the normal child document is viewed on the web it looks like this:

In this case the URL for this image is in the following format:

http://server/db/viewID/docID/Body/0.11C?OpenElement&FieldElemFormat=gif

When the print-friendly form is used we get a broken image:

In this case the URL for the image is in the following format:

http://server/db/viewID/docID/Body/2.10C?OpenElement&FieldElemFormat=gif

As a "solution" I had written some onload JavaScript that looped all images and tried to re-point them to the image stored in the original document. To do this I added some LotusScript in the WQO agent that wrapped each document's content in a <div> with the ID set to that of the child document. The JavaScript then replaced the parent doc ID in the image's URL with the child doc ID. What I didn't realise is that the bit after /Body/ that looks like 9.99X (highlighted in above URLs) had changed and there was no way of knowing what it is in the child. I was stuck!

Has anybody got any brainwaves? I'll probably ask Ben "Geniisoft" if his Midas LSX kit can solve it or if he knows why the LS doesn't work in the first place...

Comments

  1. Is there any way you can have user's insert images via a button that you place code behind? If so, you could have the button select the image (just as they would do) and create a new document with that image. The inserted text could then be HTML referencing the image.

    Rich-text fields have always been tricky. I remember working on a web application myself where I was trying to allow users to append to its content. The rich-text field was computed and as users added text to another field on the form I would keep appending to the end. Every time the document was saved, the text in the rich-text field would get larger and larger.

    Good luck!

    • avatar
    • Greg
    • Mon 14 Feb 2005 07:25

    DXL Might solve this. I haven't tried it myself, but it does seem like it might be a good solution to a nasty problem...

    • avatar
    • Jake
    • Mon 14 Feb 2005 07:57

    Give us some clues Greg. Would the WQO agent get a DXLImport handle on each child and import the rich-text content as XML?

    • avatar
    • Craig
    • Mon 14 Feb 2005 09:04

    Not sure it's LS which is the problem, might be just rich-text being crap.

    I had something similar where employees could import a picture of themselves into their employee directory entry. This worked fine in the client, but if you edit your entry through a browser and save it, the in-line image disappears.

    Not much of a help, but might point someone in the right direction.

    C.

    • avatar
    • Mike
    • Mon 14 Feb 2005 10:05

    Jake -

    This is a big stab in the dark and I haven't tested it myself, but did you look at rendertortitem instead?

    -- Mike

  2. Doubt rendertortitem would work. I have a db with a form that users would like to email around. I have an option to email the document that copies the doc to the body using rendertortitem. This method will not include a header image in the email.

    One solution I found (at LDD I think) for a printer friendly document was to put the following in a Print Preview button - basically just gets rid of Java when printed. It probably won't help your problem though.

    FIELD $$HTMLHead:=$$HTMLHead;

    @SetField("$$HTMLHead";

    "<STYLE type=\"text/css\">"+

    "APPLET { display: none }"+

    "#subform"+

    "{"+

    "DISPLAY: none"+

    "}"+

    ".noprint { " +

    "visibility: hidden" +

    " }" +

    "}" +

    "</STYLE>")

    • avatar
    • Jennifer
    • Mon 14 Feb 2005 10:22

    Inline images in a rich text field are actually stored in $FILE. Instead of trying to point to the image displayed in the RTF, could it loop thru the elements in $FILE?

    Or, have the print page only be a rich text field (or very little else) and use the RenderToRTItem to take all the selected documents and render them for printing.

    • avatar
    • Mike
    • Mon 14 Feb 2005 10:42

    Jake -

    I've just tested rendertortitem and got the same results as you.

    Here's another idea, albeit very hacky:

    How much control do you have over how the inline images are placed in the document? If you build an image repository, then allow the users to select an image from the repository to paste in ... and make sure they give the image a name attribute in the html tab of the properties box that corresponds to the name in the image repos., then in the onLoad you could grab the "real" url for each image based on the name. Very hacky, probably not realistic for what you need, but it would probably work!

    -- Mike

    • avatar
    • Mike
    • Mon 14 Feb 2005 10:51

    Hacky idea #2:

    A view of child documents that renders html. Column of IFRAMEs with the child doc as source. Dynamically resize IFRAMEs based on content size, although getting this to work is never fun. Long way to go just to get printing to work.

    -- Mike

    • avatar
    • Patrick
    • Mon 14 Feb 2005 11:15

    Hi Jake...

    What you could try is using DHTML to do this. Instead of copying the contents of the rich text fields into the new rich text field, you instead do this.

    1) Web Query Open Agent.

    i) Pass in the UNID's of all the pages you want to print.

    ii) Loop through all the UNID's and create a hidden iframe (used to load the document) and a print "DIV" (used to display the document contents) with an "ID" equal to the UNID of it's specific document.

    2) Print Form

    i) Displays all the print DIV's created by the WQO agent. You can format how to print them (also using CSS you can put a page break between each document).

    ii) Let each of the hidden IFrames load with their specific documents. When they load, all they do is replace the HTML of it's specific print "DIV".

    Once all the Iframes have loaded, all you need to do is call the Print command!

    You can add fancy routines to inform the user that all is loading and check to see if all DIV's have loaded!

    This way you will get around the Image link problem and will be able to print as many documents as you want!

    Let me know what you think?

    Later

    Patrick

    • avatar
    • Patrick
    • Mon 14 Feb 2005 11:24

    Hi Jake....

    Further to the solution above, you don't even need to create a form. All this can be done from an agent.

    Plus, there is not much overhead to this solution as all it really does is to replace the Rich Text copying and get's Javascript to do it!

    Later

    Patrick

    • avatar
    • Patrick
    • Mon 14 Feb 2005 11:31

    Hi Jake...

    One more thing...... You can create this page all in Javascript, so there is no need to call an agent at all!!!!!

    Patrick

    • avatar
    • Patrick
    • Mon 14 Feb 2005 11:39

    Hi Jake....

    Here's an example.....

    1..... Print Page

    <html>

    <link rel="stylesheet" type="text/css" href="print.css" />

    <body>

    <div style="display: none">

    <iframe scr="../view/UNID1?opendocument" name="UNID1_Iframe"><iframe>

    <iframe scr="../view/UNID2?opendocument" name="UNID2_Iframe"><iframe>

    <iframe scr="../view/UNID3?opendocument" name="UNID3_Iframe"><iframe>

    <iframe scr="../view/UNID4?opendocument" name="UNID4_Iframe"><iframe>

    .

    .

    .

    .

    <iframe scr="../view/UNID4?opendocument" name="UNIDX_Iframe"><iframe>

    </div>

    <div id="UNID1"></div>

    <div id="UNID2"></div>

    <div id="UNID3"></div>

    <div id="UNID4"></div>

    .

    .

    .

    .

    <div id="UNIDX"></div>

    </body>

    <html>

    2...... Specific Document (UNID1, UNID2, UNID3......... UNIDX)

    <html>

    <body onload="parent.document.getElementById('UNIDX').innerHTML = document.body.innerHTML">

    .

    .

    .

    .

    </body>

    <html>

    This should work and should be easy to implement.

    Later

    Patrick

    • avatar
    • djc
    • Mon 14 Feb 2005 12:04

    Has anyone tried using CopyItemToDocument in the WQO agent? I have an agent that replaces the body of a document (public database) with the contents of another document (restricted access database) and when I add inline images, they can be seen in a web browser on both documents.

    If this form is simply a printer-friendly display form, and there is no default content in the "BodyPrint" field, this might work. Calling this method in the loop would create multiple instances of the BodyPrint field, but on the web they should all appear correctly.

    The main issue I see here is that you might lose your flexibility of easily placing the document title and horizontal rule in the body.

    • avatar
    • Louis
    • Mon 14 Feb 2005 12:45

    Maybe this helps???

    What I have discovered is that the RichText fields like to display in a field that has the same name as the RT collector field and does not like displaying in fields that designated computed or in computed fields. For example, my RT collector forms captures RT in a field named LNSubject. If I then try to display the contents of this RT field on a form that uses a computed field name something other than LNSubject like DisplayLNSubject that referes to LNSubject all hell breaks loose, broken links etc.

    • avatar
    • IanB
    • Mon 14 Feb 2005 14:07

    I'm prob. being thick, Jake - but do you need a seperate form?

    Cant you just use CSS media="print" and hide/reformat sections?

    I know you use it here and, of course, it isn't answering your question....

  3. Jake,

    Here's a trick I have used in the past - it may work for you.

    1) In your script delete all items from your source doc that you don't want i.e. leave only body and possibly $file fields. (Don't save this!) Remember that there may be multiple body fields.

    2) Use doc.CopyAllItems to copy this stripped down source doc to your target doc. (The Lotus documenation is abiguous as to which NotesDoc is the target doc. You may have to try both ways.) Of course "Replace" must be false.

    Regards,

    Chris

    • avatar
    • Ray Mitchell
    • Mon 14 Feb 2005 14:57

    Base Href ???

  4. If all else fails: MIDAS. Ben has spent years making things like this happen.

    -rich

    • avatar
    • owen
    • Mon 14 Feb 2005 20:55

    this also may be dumb but in R3 of Notes we used to used formula (not much choice) to forward selected documents - Notes would create a new doc with all of the selected documenst contents in the body.

    some variant of this old concept may be useful?

    • avatar
    • Greg
    • Mon 14 Feb 2005 23:38

    Hi Jake,

    I finally have a chance to get back to you about my previous comment. You could use DXL to export the child images and then use XSLT to extract the inline images. You could then import the result back into your database as a new document. It sounds a bit painful but I think you could make it work reasonably easily.

    Greg

    • avatar
    • Greg
    • Mon 14 Feb 2005 23:39

    Oops, previous comment should have read...

    ...export the child docs and then use...

  5. Another good alternative for handling rich text elements is the script libraries from rtlib.

    You can download a free trial at {Link}

    There's a very good documentation database with sample code included and it will get you started right away.

    Registration is also very cheap and it's just as flexible as the Midas thing. Big advantage of rtlib is that you don't have to declare any C API functions, just include the script libraries and bam, there you go.

    You can do anything with rich text fields you ever wanted.

  6. Cool. Give them folks some nifty little programming problem and the comments keep rolling in.

    However, I think the simple solution is somehow related to Louis' comment above.

    I wrote a very similar code some months ago and what I did was rather straight and simple: Within a loop, create a temporary RTItem for each doc print, fill it using RenderToRTItem, call AppendRTItem to the RT field actually holding the complete output, add some code for page breaks and remove the temp RTItem. Works fine, even with inline images, on R5 and R6 servers.

    The only "special" thing I can see is, that the RT filed containig the output is called "Body".

    Right now I don't have the time to dig any deeper into my old code, but obviously it MUST be possible, just using RenderToRTItem.

  7. The problem is fairly simple, even if the solution is slightly more difficult. The "bit after the Body" is an offset into the rich text field, with the 2.10c in the URL {Link} referring to the third (0,1,2) Body item, and the offset 10c bytes into that item's data. Yuck! Midas could calculate the correct offset for you, or you could cycle through the rich text CD records yourself, or Midas could simply copy the darn things correctly in the first place, or you could use DXL to get the offsets, or you could ...

    Anyway, the part I am most surprised by is that AppendRTItem doesn't copy the images. That doesn't sound correct. In-line images are usually just CD records, and thus are copied as part of the CD stream. Is it possible that these are CDStorage images? Check the database setting to see if the Display images after loading flag is set on the source database (it is on the first tab). This nasty setting forces the images into CDStorage files, which are very handy when you need them (I use them in CoexEdit), but a pain in the neck when you don't. Can you see if the setting is set? If so, the images will be broken because the attachments are not there.

    • avatar
    • Jake
    • Wed 16 Feb 2005 04:04

    Thanks for all your suggestions guys. Needless to say I won't be using any hacks in my "killer app". In the end the solution was fairly simple and I'll cover it in a blog today.

    Ben. Thanks for your time. This database setting is NOT set though.

Your Comments

Name:
E-mail:
(optional)
Website:
(optional)
Comment:


About This Page

Written by Jake Howlett on Mon 14 Feb 2005

Share This Page

# ( ) '

Comments

The most recent comments added:

Skip to the comments or add your own.

You can subscribe to an individual RSS feed of comments on this entry.

Let's Get Social


About This Website

CodeStore is all about web development. Concentrating on Lotus Domino, ASP.NET, Flex, SharePoint and all things internet.

Your host is Jake Howlett who runs his own web development company called Rockall Design and is always on the lookout for new and interesting work to do.

You can find me on Twitter and on Linked In.

Read more about this site »

Elsewhere

Here are the external links posted on the same day.

More links are available in the archive »

More Content