0

Using the great solution How to replace <w:sdt> Xml in Word Doc using Apache POI as XWPFSDT and XWPFSDTContent object is read-only that I received, I am able to do almost everything that I need to do with content controls, except one.

I need to change the font size and color within the content control. We are sending the document to DocuSign and the content of the ContentControl is going to be used as an anchor text to tell DocuSign where a signature should be.

So a text like "Real Estate Buyer/s" is the content within the Control, and I need to make it visually invisible, so font size 1 and text color white.

With the solution linked, I can set the content and get the tag, but I have no ability to change the font size or color.

How can I change the size or color of the content within the Content Control?

bytor99999
  • 726
  • 7
  • 26

1 Answers1

1

Using my code provided in How to replace <w:sdt> Xml in Word Doc using Apache POI as XWPFSDT and XWPFSDTContent object is read-only one could extend the SDTContentControl like so:

...
 public void setContent(org.apache.poi.xwpf.usermodel.XWPFParagraph paragraph) {
  if (this.object instanceof CTSdtBlock) {
   CTSdtBlock ctSdtBlock = (CTSdtBlock)this.object;
   if (ctSdtBlock.isSetSdtContent()) {
    CTSdtContentBlock sdtContentBlock = ctSdtBlock.getSdtContent();
    CTP ctP = paragraph.getCTP();
    sdtContentBlock.setPArray(new CTP[]{ctP});
   }
  } else if (this.object instanceof CTSdtRun) {
   CTSdtRun ctSdtRun = (CTSdtRun)this.object;
   if (ctSdtRun.isSetSdtContent()) {
    CTSdtContentRun sdtContentRun = ctSdtRun.getSdtContent();
    CTR[] ctRArray = paragraph.getCTP().getRArray();
    sdtContentRun.setRArray(ctRArray);
   }
  } else if (this.object instanceof CTSdtCell) {
   CTSdtCell ctSdtCell = (CTSdtCell)this.object;
   if (ctSdtCell.isSetSdtContent()) {
    CTSdtContentCell sdtContentCell = ctSdtCell.getSdtContent();
    for (int c = 0; c < sdtContentCell.getTcList().size(); c++) {  
     CTTc ctTc = sdtContentCell.getTcList().get(c);
     CTP ctP = paragraph.getCTP();
     ctTc.setPArray(new CTP[]{ctP});
    }
   }
  }
 }

...

 public void setContent(Object content) {
  if (content instanceof String) {
   this.setContent((String)content);  
  } else if (content instanceof Calendar) {
   this.setContent((Calendar)content);       
  } else if (content instanceof BigDecimal) {
   this.setContent((BigDecimal)content);       
  } else if (content instanceof org.apache.poi.xwpf.usermodel.XWPFParagraph) {
   this.setContent((org.apache.poi.xwpf.usermodel.XWPFParagraph)content);  
//} else if (content instanceof ...) {
   //ToDo
  } else {
   this.setContent(String.valueOf(content));         
  }
 }
...

After this one of the objects in Object[] contents in WordFillContentControls can be a XWPFParagraph like so:

...
  Object[] contents = new Object[]{
  "Axel Richter", "male", new GregorianCalendar(2022, 0, 1), BigDecimal.valueOf(1234.56), 
  "Lorem ipsum semit dolor ... dolor semit ...", "Blah blah", "Blubb blubb", 
   new GregorianCalendar(1964, 11, 21), "My choice"
  };
  
  XWPFParagraph paragraph = new XWPFParagraph(org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP.Factory.newInstance(), new XWPFDocument());
  XWPFRun run = paragraph.createRun();
  run.setText("Lorem ipsum");
  run = paragraph.createRun();
  run.setText(" semit dolor ");
  run.setColor("FF0000");
  run.setFontSize(24);
  run = paragraph.createRun();
  run.setText("... dolor semit ...");
  
  //contents[0] = paragraph;
  contents[4] = paragraph;
  //contents[6] = paragraph;
  //contents[8] = paragraph;
...

So one is able to use all the formatting possibilities of XWPFRun.

See complete example in my above linked answer to get what Object[] contents is for.

The XWPFParagraph paragraph gets created outside the current used XWPFDocument in a new XWPFDocument() because it does not belong to the current used document. It only transports the formatted rich text in its XWPFRuns to be used in my SDTContentControl then.

The fully correct way would be to make the block contents of the SDTContentControl implement IBody and the inline (run) contents of the SDTContentControl make implement IRunBody. Then one could create XWPFparagraphs respectively XWPFRuns directly there. But that would lead to too much code to provide it in an answer here.

Axel Richter
  • 56,077
  • 6
  • 60
  • 87
  • I am a little bit confused on the last bits of code. What is Object[] contents? Is that supposed to be an array of ContentControls? When you are creating a new XWPFParagraph you pass in a new XWPFDocument(), I already have an instance of one from the actual document already created from SharePoint byte[], should I be passing that instead? – bytor99999 Feb 21 '23 at 20:05
  • 1
    @bytor99999: See complete example in my above linked answer to get what `Object[] contents` is for. And see the supplements to my answer now. – Axel Richter Feb 22 '23 at 04:45
  • The code does not throw any errors, but I am not seeing it appear in the template document. Whereas if I put text into the content control alone I see that text. (I changed the font size and color to be one that should show up for testing) So it is adding the Paragraph, but it isn't showing up as being in the merged document. Thanks. – bytor99999 Feb 22 '23 at 20:09
  • @bytor99999: Not clear where your problem is. Code in my answer is tested and works in combination with the code of my previous answer, which is linked here. I do not see **your** actual used code. I suggest asking a new question about your current problem providing a [Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) which shows what you are actually doing and shows reproducible what is the problem. – Axel Richter Feb 23 '23 at 05:54
  • My code is exactly, your code. I just change the setFontSize() call to pass 1 to it. To reproduce you have to send it to DocuSign where it uses the text in the content control as the AnchorText to put the Sign Here portion. If I call setContent with a XWPFParagraph, DocuSign does not put a Sign Here. If I pass setContent() just the String text then DocuSign puts the Sign Here portion right next to that text. I'm sorry but since the code is identical except for a passed value of "1" instead of "24" that is the Minimal Reproducible Example. – bytor99999 Feb 25 '23 at 01:56
  • The reason I want the text to be size 1 and color to be the same as the document background color is because the text needs to be there for DocuSign to find it and put a SignHere marker there, but the text is only there for DocuSign and the user/signer should not actually see the text. DocuSign is a Pain and it is the only solution to tell DocuSign where to place Sign Here markers into a document (from code) The whole process is automated from merge to sending to DocuSign to be signed, no manual intervention. – bytor99999 Feb 25 '23 at 02:01
  • Axel, I know you've been on the edge of your seat. ;) I definitely can see the Paragraph/Text in the final Word document, so the code is definitely working, I just have to now figure on my end why DocuSign isn't matching the anchorText I put into the content. Thanks so much for your help, it is hugely appreciated. – bytor99999 Feb 27 '23 at 15:02