From 15b734821c4c533f97fbe690108b6f595b78efa0 Mon Sep 17 00:00:00 2001 From: Jesse Gallagher Date: Mon, 2 Oct 2023 13:52:19 -0400 Subject: [PATCH 1/3] Add basic test for embedding and rendering three attachments --- .../test/richtext/TestRichtextNavigator.java | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/test/it-domino-jnx/src/test/java/it/com/hcl/domino/test/richtext/TestRichtextNavigator.java b/test/it-domino-jnx/src/test/java/it/com/hcl/domino/test/richtext/TestRichtextNavigator.java index 0f7f5b8f..06fc5079 100644 --- a/test/it-domino-jnx/src/test/java/it/com/hcl/domino/test/richtext/TestRichtextNavigator.java +++ b/test/it-domino-jnx/src/test/java/it/com/hcl/domino/test/richtext/TestRichtextNavigator.java @@ -18,7 +18,7 @@ import static it.com.hcl.domino.test.util.ITUtil.toCr; import static it.com.hcl.domino.test.util.ITUtil.toLf; - +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -44,8 +44,10 @@ import com.hcl.domino.commons.richtext.RichTextUtil; import com.hcl.domino.data.Attachment; +import com.hcl.domino.data.Attachment.Compression; import com.hcl.domino.data.Document; import com.hcl.domino.data.Document.IAttachmentProducer; +import com.hcl.domino.html.RichTextHTMLConverter; import com.hcl.domino.data.DocumentClass; import com.hcl.domino.data.FontAttribute; import com.hcl.domino.data.FormulaQueryResult; @@ -53,6 +55,7 @@ import com.hcl.domino.data.StandardFonts; import com.hcl.domino.richtext.RichTextWriter; import com.hcl.domino.richtext.TextStyle.Justify; +import com.hcl.domino.richtext.conversion.AppendFileHotspotConversion; import com.hcl.domino.richtext.conversion.RemoveAttachmentIconConversion; import com.hcl.domino.richtext.process.ExtractFileResourceProcessor; import com.hcl.domino.richtext.process.ExtractImageResourceProcessor; @@ -682,4 +685,53 @@ public void testTextExtraction() throws Exception { Assertions.assertEquals(txtIn, txtOut); }); } + + /** + * Tests for correct behavior when attaching multiple files to a + * document and then adding references to them in the same RT field + */ + @Test + public void testMultipleAttachmentsInBody() throws Exception { + withTempDb(database -> { + Path tempA = Files.createTempFile("tempA", ".txt"); + Path tempB = Files.createTempFile("tempB", ".txt"); + Path tempC = Files.createTempFile("tempC", ".txt"); + try { + Files.write(tempA, Collections.singleton("hello A")); + Files.write(tempB, Collections.singleton("hello B")); + Files.write(tempC, Collections.singleton("hello C")); + + Document doc = database.createDocument(); + + // Attach each, saving after each one as upstream does + { + Attachment att = doc.attachFile(tempA.toString(), tempA.getFileName().toString(), Compression.NONE); + final RichTextWriter rtWriter = doc.createRichTextItem("Body"); + rtWriter.addAttachmentIcon(att, tempA.getFileName().toString()); + doc.save(); + } + { + Attachment att = doc.attachFile(tempB.toString(), tempB.getFileName().toString(), Compression.NONE); + doc.convertRichTextItem("Body", new AppendFileHotspotConversion(att, tempB.getFileName().toString())); + doc.save(); + } + { + Attachment att = doc.attachFile(tempC.toString(), tempC.getFileName().toString(), Compression.NONE); + doc.convertRichTextItem("Body", new AppendFileHotspotConversion(att, tempC.getFileName().toString())); + doc.save(); + } + + // Convert the Body field to HTML + RichTextHTMLConverter conv = database.getParentDominoClient().getRichTextHtmlConverter(); + String html = conv.renderItem(doc, "Body").convert().getHtml(); + assertTrue(html.contains(tempA.getFileName().toString()), () -> "Body is missing tempA"); + assertTrue(html.contains(tempB.getFileName().toString()), () -> "Body is missing tempB"); + assertTrue(html.contains(tempC.getFileName().toString()), () -> "Body is missing tempC"); + } finally { + Files.deleteIfExists(tempA); + Files.deleteIfExists(tempB); + Files.deleteIfExists(tempC); + } + }); + } } From 07f01cc2c599dad2df510b0b2025a94a73dde552 Mon Sep 17 00:00:00 2001 From: Jesse Gallagher Date: Tue, 3 Oct 2023 15:42:43 -0400 Subject: [PATCH 2/3] Add some tests for attaching files, adding them to an RT field, and then rendering the HTML --- .../test/richtext/TestRichtextNavigator.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/test/it-domino-jnx/src/test/java/it/com/hcl/domino/test/richtext/TestRichtextNavigator.java b/test/it-domino-jnx/src/test/java/it/com/hcl/domino/test/richtext/TestRichtextNavigator.java index 06fc5079..5f97763e 100644 --- a/test/it-domino-jnx/src/test/java/it/com/hcl/domino/test/richtext/TestRichtextNavigator.java +++ b/test/it-domino-jnx/src/test/java/it/com/hcl/domino/test/richtext/TestRichtextNavigator.java @@ -45,6 +45,7 @@ import com.hcl.domino.commons.richtext.RichTextUtil; import com.hcl.domino.data.Attachment; import com.hcl.domino.data.Attachment.Compression; +import com.hcl.domino.data.Database; import com.hcl.domino.data.Document; import com.hcl.domino.data.Document.IAttachmentProducer; import com.hcl.domino.html.RichTextHTMLConverter; @@ -734,4 +735,63 @@ public void testMultipleAttachmentsInBody() throws Exception { } }); } + + /** + * Tests for correct behavior when attaching multiple files to a + * document and then adding references to them in the same RT field + */ + @Test + public void testMultipleAttachmentsInBodyReopen() throws Exception { + withTempDb(database -> { + Path tempA = Files.createTempFile("tempA", ".txt"); + Path tempB = Files.createTempFile("tempB", ".txt"); + Path tempC = Files.createTempFile("tempC", ".txt"); + try { + Files.write(tempA, Collections.singleton("hello A")); + Files.write(tempB, Collections.singleton("hello B")); + Files.write(tempC, Collections.singleton("hello C")); + + Document doc = database.createDocument(); + String unid; + + // Attach each, saving after each one as upstream does + { + Attachment att = doc.attachFile(tempA.toString(), tempA.getFileName().toString(), Compression.NONE); + final RichTextWriter rtWriter = doc.createRichTextItem("Body"); + rtWriter.addAttachmentIcon(att, tempA.getFileName().toString()); + doc.save(); + unid = doc.getUNID(); + } + { + Database db = database.reopen(); + doc = db.getDocumentByUNID(unid).get(); + Attachment att = doc.attachFile(tempB.toString(), tempB.getFileName().toString(), Compression.NONE); + doc.convertRichTextItem("Body", new AppendFileHotspotConversion(att, tempB.getFileName().toString())); + doc.save(); + } + { + Database db = database.reopen(); + doc = db.getDocumentByUNID(unid).get(); + Attachment att = doc.attachFile(tempC.toString(), tempC.getFileName().toString(), Compression.NONE); + doc.convertRichTextItem("Body", new AppendFileHotspotConversion(att, tempC.getFileName().toString())); + doc.save(); + } + + // Convert the Body field to HTML + { + Database db = database.reopen(); + doc = db.getDocumentByUNID(unid).get(); + RichTextHTMLConverter conv = database.getParentDominoClient().getRichTextHtmlConverter(); + String html = conv.renderItem(doc, "Body").convert().getHtml(); + assertTrue(html.contains(tempA.getFileName().toString()), () -> "Body is missing tempA"); + assertTrue(html.contains(tempB.getFileName().toString()), () -> "Body is missing tempB"); + assertTrue(html.contains(tempC.getFileName().toString()), () -> "Body is missing tempC"); + } + } finally { + Files.deleteIfExists(tempA); + Files.deleteIfExists(tempB); + Files.deleteIfExists(tempC); + } + }); + } } From 9f8a5509de4b13fe4a5ec09f44361a21b0a74024 Mon Sep 17 00:00:00 2001 From: Jesse Gallagher Date: Tue, 3 Oct 2023 16:40:24 -0400 Subject: [PATCH 3/3] Modify RichTextWriter implementation to move more to IDefaultRichTextWriter and to explicitly use the non-versioned CDHotspot signatures --- .../richtext/IDefaultRichTextWriter.java | 57 ++++++++++++++++++- .../jna/richtext/JNARichtextWriter.java | 55 ++---------------- 2 files changed, 60 insertions(+), 52 deletions(-) diff --git a/domino-jnx-commons/src/main/java/com/hcl/domino/commons/richtext/IDefaultRichTextWriter.java b/domino-jnx-commons/src/main/java/com/hcl/domino/commons/richtext/IDefaultRichTextWriter.java index 74f06ac3..69433b4d 100644 --- a/domino-jnx-commons/src/main/java/com/hcl/domino/commons/richtext/IDefaultRichTextWriter.java +++ b/domino-jnx-commons/src/main/java/com/hcl/domino/commons/richtext/IDefaultRichTextWriter.java @@ -16,6 +16,7 @@ */ package com.hcl.domino.commons.richtext; +import static java.text.MessageFormat.format; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; @@ -26,7 +27,6 @@ import java.util.function.Consumer; import com.hcl.domino.DominoException; -import com.hcl.domino.commons.design.view.DominoCollationInfo; import com.hcl.domino.commons.structures.MemoryStructureUtil; import com.hcl.domino.data.Attachment; import com.hcl.domino.data.Database; @@ -297,6 +297,61 @@ default RichTextWriter addText(final String txt) { default RichTextWriter addText(final String txt, final TextStyle textStyle, final FontStyle fontStyle) { return this.addText(txt, textStyle, fontStyle, true); } + + @Override + default RichTextWriter addAttachmentIcon(String attachmentProgrammaticName, String captionTxt) { + InputStream in = getClass().getResourceAsStream("file-icon.gif"); //$NON-NLS-1$ + if (in==null) { + throw new IllegalStateException("Default icon file not found"); + } + + try { + return addAttachmentIcon(attachmentProgrammaticName, captionTxt, captionTxt, createFontStyle(), + CaptionPosition.BELOWCENTER, 0, 0, 0, -1, -1, null, in); + + } catch (IOException e) { + throw new DominoException(format("Could not add attachment icon for {0}", attachmentProgrammaticName), e); + } + finally { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @Override + default RichTextWriter addAttachmentIcon(Attachment att, String filenameToDisplay, String captionText, FontStyle captionStyle, + CaptionPosition captionPos, int captionColorRed, int captionColorGreen, int captionColorBlue, + int resizeToWidth, int resizeToHeight, Path imagePath) { + + try { + return addAttachmentIcon(att.getFileName(), filenameToDisplay, captionText, captionStyle, + captionPos, captionColorRed, captionColorGreen, captionColorBlue, resizeToWidth, + resizeToHeight, imagePath, null); + } catch (IOException e) { + throw new DominoException(format("Could not add attachment icon for {0}", att.getFileName()), e); + } + } + + @Override + default RichTextWriter addAttachmentIcon(String attachmentProgrammaticName, String filenameToDisplay, String captionText, + FontStyle captionStyle, + CaptionPosition captionPos, int captionColorRed, int captionColorGreen, int captionColorBlue, + int resizeToWidth, int resizeToHeight, InputStream imageData) throws IOException { + + return addAttachmentIcon(attachmentProgrammaticName, filenameToDisplay, captionText, + captionStyle, + captionPos, captionColorRed, captionColorGreen, captionColorBlue, + resizeToWidth, resizeToHeight, null, imageData); + + } + + RichTextWriter addAttachmentIcon(String attachmentProgrammaticName, String filenameToDisplay, String captionText, + FontStyle captionStyle, + CaptionPosition captionPos, int captionColorRed, int captionColorGreen, int captionColorBlue, + int resizeToWidth, int resizeToHeight, Path imagePath, InputStream imageData) throws IOException; @Override default FontStyle createFontStyle() { diff --git a/domino-jnx-jna/src/main/java/com/hcl/domino/jna/richtext/JNARichtextWriter.java b/domino-jnx-jna/src/main/java/com/hcl/domino/jna/richtext/JNARichtextWriter.java index 2a7b34eb..cd7038be 100644 --- a/domino-jnx-jna/src/main/java/com/hcl/domino/jna/richtext/JNARichtextWriter.java +++ b/domino-jnx-jna/src/main/java/com/hcl/domino/jna/richtext/JNARichtextWriter.java @@ -61,7 +61,6 @@ import com.hcl.domino.commons.structures.MemoryStructureUtil; import com.hcl.domino.commons.util.NotesErrorUtils; import com.hcl.domino.commons.util.StringUtil; -import com.hcl.domino.data.Attachment; import com.hcl.domino.data.Document; import com.hcl.domino.data.FontAttribute; import com.hcl.domino.data.ItemDataType; @@ -494,57 +493,8 @@ public RichTextWriter addDocLink(String dbReplicaId, String viewUnid, String doc return this; } - @Override - public RichTextWriter addAttachmentIcon(String attachmentProgrammaticName, String captionTxt) { - InputStream in = getClass().getResourceAsStream("file-icon.gif"); //$NON-NLS-1$ - if (in==null) { - throw new IllegalStateException("Default icon file not found"); - } - - try { - return addAttachmentIcon(attachmentProgrammaticName, captionTxt, captionTxt, createFontStyle(), - CaptionPosition.BELOWCENTER, 0, 0, 0, -1, -1, null, in); - - } catch (IOException e) { - throw new DominoException(format("Could not add attachment icon for {0}", attachmentProgrammaticName), e); - } - finally { - try { - in.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - @Override - public RichTextWriter addAttachmentIcon(Attachment att, String filenameToDisplay, String captionText, FontStyle captionStyle, - CaptionPosition captionPos, int captionColorRed, int captionColorGreen, int captionColorBlue, - int resizeToWidth, int resizeToHeight, Path imagePath) { - - try { - return addAttachmentIcon(att.getFileName(), filenameToDisplay, captionText, captionStyle, - captionPos, captionColorRed, captionColorGreen, captionColorBlue, resizeToWidth, - resizeToHeight, imagePath, null); - } catch (IOException e) { - throw new DominoException(format("Could not add attachment icon for {0}", att.getFileName()), e); - } - } - @Override public RichTextWriter addAttachmentIcon(String attachmentProgrammaticName, String filenameToDisplay, String captionText, - FontStyle captionStyle, - CaptionPosition captionPos, int captionColorRed, int captionColorGreen, int captionColorBlue, - int resizeToWidth, int resizeToHeight, InputStream imageData) throws IOException { - - return addAttachmentIcon(attachmentProgrammaticName, filenameToDisplay, captionText, - captionStyle, - captionPos, captionColorRed, captionColorGreen, captionColorBlue, - resizeToWidth, resizeToHeight, null, imageData); - - } - - private RichTextWriter addAttachmentIcon(String attachmentProgrammaticName, String filenameToDisplay, String captionText, FontStyle captionStyle, CaptionPosition captionPos, int captionColorRed, int captionColorGreen, int captionColorBlue, int resizeToWidth, int resizeToHeight, Path imagePath, InputStream imageData) throws IOException { @@ -567,6 +517,7 @@ private RichTextWriter addAttachmentIcon(String attachmentProgrammaticName, Stri m_hasData=true; addRichTextRecord(CDHotspotBegin.class, hotspotBegin -> { + hotspotBegin.getHeader().setSignature(RecordType.HOTSPOTBEGIN.getConstant()); hotspotBegin.setHotspotType(HotspotType.FILE); hotspotBegin.setFlags(EnumSet.of(CDHotspotBegin.Flag.NOBORDER)); @@ -596,7 +547,9 @@ private RichTextWriter addAttachmentIcon(String attachmentProgrammaticName, Stri caption.setCaptionText(captionText); }); - addRichTextRecord(CDHotspotEnd.class, hotspotEnd -> { }); + addRichTextRecord(CDHotspotEnd.class, hotspotEnd -> { + hotspotEnd.getHeader().setSignature((byte)RecordType.HOTSPOTEND.getConstant()); + }); addRichTextRecord(CDEnd.class, end -> { end.setVersion(0); end.setSignature(RecordType.V4HOTSPOTEND.getConstant());