Not able to add bullet points and styles like bold and italic for some portion of a line using Apache POI

Viewed 80

I'm reading one string in that some li tags have spans with styles so I'm trying to add styles for a particular word in the li tag (not for the whole line), but for every styles change it is going for new bullet point instead of appending to the same li,

String:

"<p><span style=\"font-weight: bold;\">Title</span></p> <ul> <li> <span style=\"font-weight: bold;\"> </span><span>Laser cutting of sheet </span><span style=\"font-weight: bold;\">metal</span><span> generates a continuous heat source which can led to high levels of thermal stress </span> </li> <li><span>Thermal Stress can deform the material at the micron level and the slightest deformation reduces the effectiveness of the sheet metal components</span></li> <br /> </ul> <p><span> </span><span style=\"font-weight: bold;\">Mass Level Production</span></p> <ul> <li><span>Laser cutting cannot produce more component more than one part at a time</span></li> <li><span>When production is required at a lower level , laser cutting may be apt but inefficient for mass level production</span></li> </ul> <p><span style=\"font-weight: bold;\">Metal etching with aluminum</span></p> <p><span style=\"font-weight: bold;\"> </span></p> <ul> <li><span>Aluminum is a reflective material that is reactive to heat in the manufacturing process and therefore, makes it less suitable for laser cutting and wire EDM processes</span></li> <li> <span>As the use of aluminum is growing in many industries, there is a requirement for a suitable manufacturing process for thin </span><span style=\"font-weight: bold;\">aluminum</span><span>components</span> </li> </ul>"

Can anyone help me, like where I'm doing the mistake?
Code:

public static void advancedUlLiConvert(XSLFTextShape textShape, String str) {
    System.out.println("Recived String: "+str);
        
    Document doc = null;
        
    try {
        doc = Jsoup.parse(str);
            
        Element element = doc.body();
        Elements lstElements = element.children();
        System.out.println("bodyChildEle : " +lstElements);
            
        for (Element ele: lstElements) {
            if (ele.tagName() == "p") {
                System.out.println("para" + ele);
                String para = ele.children().toString();

                System.out.println("String para" + para);
                //if any <p> tags are there then I'm calling this method to take care of that which is working fine,
                addAllStyledText(textShape,para, false);
            }
            //I'm facing problems in these below part
            if (ele.tagName() == "ul") {
                Elements lstLi = ele.children();
                System.out.println("lstsi" + lstLi);
                    
                for (Element li: lstLi) {
                    Elements spans = li.children();

                    XSLFTextParagraph paragraph = textShape.addNewTextParagraph();
                    paragraph.setBullet(true);
                    XSLFTextRun textRun = paragraph.addNewTextRun();
                        
                    System.out.println("allspans: " + spans);
                        
                    for (Element span: spans) {
                        System.out.println("loopspan" + span);
                            
                        if (!(span.hasText())) {
                            span.remove();
                            System.out.println("Removed Element: "+ span);
                        } else if (span.hasAttr("style")) {
                            System.out.println("Styled Span: " + span);
                            String strSpan = span.toString();
                            boolean isBold = false;
                            boolean isItalic = false;
                            boolean isUnderline = false;
                            boolean isLineThrough = false;
                                
                            if (strSpan.contains(": bold;"))
                                isBold = true;
                                
                            if (strSpan.contains(": italic;"))
                                isItalic = true;
                                
                            if (strSpan.contains(": underline;"))
                                isUnderline = true;
                                
                            if (strSpan.contains(": line-through;"))
                                isLineThrough = true;
                                
                            String text = span.text();
                            textRun = textShape.appendText(text+" ", false);
                                
                            if (isBold) {
                                textRun.setBold(true);
                                //textRun.setFontColor(rgb);
                            }
                            if (isItalic) {
                                textRun.setItalic(true);
                            }
                            if (isUnderline) {
                                textRun.setUnderlined(true);
                            }
                            if (isLineThrough) {
                                textRun.setStrikethrough(true);
                            }
                            //textRun.setText(text);
                        } else {
                            System.out.println("Span without Style: " + span);
                            String text = span.text();
                            //if I make this as false than whole code braking not sure why
                            textRun = textShape.appendText(text + " ", true);
                            //textRun.setText(text);
                            //paragraph.setBullet(false);
                            //paragraph.setBullet(false);
                            //paragraph.addNewTextRun().setText(text);
                        }

                        XSLFTextRun dummyTextRun = textShape.appendText("", false);
                        dummyTextRun.setBold(false);
                        dummyTextRun.setItalic(false);
                        dummyTextRun.setUnderlined(false);
                        dummyTextRun.setStrikethrough(false);
                    }
                    paragraph.setBullet(false);
                }
            }
        }
    } catch (Exception e) {
        log.error("Exception in advancedUlLiConvert: " , e);
    }
}

Code Output: enter image description here

My expectation : enter image description here

1 Answers

To program a HTML to XSLFTextParagraph converter, which considers all the ugly possibilities of that HTML tag-soup, would lead to program a whole library. HTML is a mess, nothing else. No real rules, all free interpretable by browsers.

JSoup is a helper but not the complete solution. I have adopted my attempt of How to set define different styles for the same paragraph to XSLF. As told there it uses NodeTraversor and NodeVisitor of JSoup. I have extended this to consider inline-CSS using style attributes in SPAN elements. Also I have extended it to consider LI elements in UL elements as bulleted paragraphs.

Complete example converting your given HTML-String:

import java.io.FileOutputStream;

import org.apache.poi.xslf.usermodel.*;
import org.apache.poi.sl.usermodel.*;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.jsoup.select.NodeVisitor;
import org.jsoup.select.NodeTraversor;

public class HTMLtoPPTX {

 private XMLSlideShow slideShow;

 public HTMLtoPPTX(String html, String pptxPath) throws Exception {

  this.slideShow = new XMLSlideShow();
  XSLFSlide slide = slideShow.createSlide();
  XSLFTextShape textShape = slide.createAutoShape();
  java.awt.Rectangle rect = new java.awt.Rectangle(100, 100, 500, 400);
  textShape.setAnchor(rect.getBounds2D());
  textShape.setShapeType(ShapeType.RECT);

  XSLFTextParagraph paragraph = null;
  Document htmlDocument = Jsoup.parse(html);
  Elements htmlElements = htmlDocument.select("p:not(li *), ul li:not(li *)");
  for(Element htmlElement : htmlElements) {

System.out.println(htmlElement);

   paragraph = textShape.addNewTextParagraph();
   createParagraphFromHTML(paragraph, htmlElement);
  }

  FileOutputStream out = new FileOutputStream(pptxPath);
  slideShow.write(out);
  out.close();
  slideShow.close();

 }

 void createParagraphFromHTML(XSLFTextParagraph paragraph, Element htmlElement) {

  ParagraphNodeVisitor nodeVisitor = new ParagraphNodeVisitor(paragraph);
  NodeTraversor.traverse(nodeVisitor, htmlElement);

 }

 private class ParagraphNodeVisitor implements NodeVisitor {

  String nodeName;
  boolean isItalic;
  boolean isBold;
  boolean isUnderlined;
  double fontSize;
  java.awt.Color fontColor;
  XSLFTextParagraph paragraph;
  XSLFTextRun run;
  boolean spanStyleSetBold;
  boolean spanStyleSetItalic;
  boolean spanStyleSetUnderlined;
  boolean spanStyleSetFontColor;
  boolean spanStyleSetFontSize;

  ParagraphNodeVisitor(XSLFTextParagraph paragraph) {
   this.paragraph = paragraph;
   this.run = paragraph.addNewTextRun(); this.run.setText("");
   this.nodeName = "";
   this.isItalic = false;
   this.isBold = false;
   this.isUnderlined = false;
   this.fontSize = 11d;
   this.fontColor = java.awt.Color.BLACK;
   this.spanStyleSetBold = false;
   this.spanStyleSetItalic = false;
   this.spanStyleSetUnderlined = false;
   this.spanStyleSetFontColor = false;
   this.spanStyleSetFontSize = false;

  }

  @Override
  public void head(Node node, int depth) {
   nodeName = node.nodeName();

System.out.println("Start "+nodeName+": " + node);

   if ("#text".equals(nodeName)) {
    run.setText(((TextNode)node).text​());
   } else if ("i".equals(nodeName)) {
    isItalic = true;
   } else if ("b".equals(nodeName)) {
    isBold = true;
   } else if ("u".equals(nodeName)) {
    isUnderlined = true;
   } else if ("br".equals(nodeName)) {
    run = run.getParagraph().addLineBreak();
   } else if ("font".equals(nodeName)) {
    String rgbS = (!"".equals(node.attr("color")))?node.attr("color"):"#000000";
    fontColor = new java.awt.Color(
     Integer.valueOf(rgbS.substring(1, 3), 16),
     Integer.valueOf(rgbS.substring(3, 5), 16),
     Integer.valueOf(rgbS.substring(5, 7), 16)
    );
    fontSize = (!"".equals(node.attr("size")))?Double.parseDouble(node.attr("size")):11d;
   } else if ("li".equals(nodeName)) {
    paragraph.setBullet(true); 
   } else if ("span".equals(nodeName)) {
    String style = node.attr("style");
    if (style.matches(".*\\bfont-weight:\\s*bold\\s*;.*")) {
     isBold = true;
     spanStyleSetBold = true;
    }
    if (style.matches(".*\\bfont-style:\\s*italic\\s*;.*")) {
     isItalic = true;
     spanStyleSetItalic = true;
    }
    if (style.matches(".*\\btext-decoration:\\s*underline\\s*;.*")) {
     isUnderlined = true;
     spanStyleSetUnderlined = true;
    }
    //if (style.matches(".*\\bcolor:\\s*(#[0-9a-fA-F]+)\\s*;.*")) { 
    // ToDo      
    //}
    //if (style.matches(".*\\bfont-size:\\s*(#[0-9]+)\\s*;.*")) { 
    // ToDo      
    //}  
   }
   
   run.setItalic(isItalic);
   run.setBold(isBold);
   if (isUnderlined) run.setUnderlined(true); else run.setUnderlined(false);
   run.setFontColor(fontColor); run.setFontSize(fontSize);
  }

  @Override
  public void tail(Node node, int depth) {
   nodeName = node.nodeName();

System.out.println("End "+nodeName);

   if ("#text".equals(nodeName)) {
    run = paragraph.addNewTextRun(); run.setText(""); //after setting the text in the run a new run is needed
   } else if ("i".equals(nodeName)) {
    isItalic = false;
   } else if ("b".equals(nodeName)) {
    isBold = false;
   } else if ("u".equals(nodeName)) {
    isUnderlined = false;
   } else if ("br".equals(nodeName)) {
    run = paragraph.addNewTextRun(); run.setText(""); //after setting a break a new run is needed
   } else if ("font".equals(nodeName)) {
    fontColor = java.awt.Color.BLACK;
    fontSize = 11d;
   } else if ("li".equals(nodeName)) {
    // ToDo
   } else if ("span".equals(nodeName)) {
    if (spanStyleSetItalic) isItalic = false;
    if (spanStyleSetBold) isBold = false;
    if (spanStyleSetUnderlined) isUnderlined = false;
    //if (spanStyleSetFontColor) fontColor = java.awt.Color.BLACK; //ToDo
    //if (spanStyleSetFontSize) fontSize = 11d; //ToDo
    run = paragraph.addNewTextRun(); run.setText(""); //after a span a new run is needed
   }
   run.setItalic(isItalic);
   run.setBold(isBold);
   if (isUnderlined) run.setUnderlined(true); else run.setUnderlined(false);
   run.setFontColor(fontColor); run.setFontSize(fontSize);
  }
 }

 public static void main(String[] args) throws Exception {

  String html = 
   "<p><font size='32' color='#0000FF'><b>First paragraph.</font></b><br/>Just like a heading</p>"
  +"<p>This is my text <i>which now is in italic <b>but also in bold</b> depending on its <u>importance</u></i>.<br/>Now a <b><i><u>new</u></i></b> line starts <i>within <b>the same</b> paragraph</i>.</p>"
  +"<p><b>Last <u>paragraph <i>comes</u> here</b> finally</i>.</p>"
  +"<p>But yet <u><i><b>another</i></u></b> paragraph having <i><font size='22' color='#FF0000'>special <u>font</u> settings</font></i>. Now default font again.</p>"
  +"<p />"
  ;

  //html += 
  html = 
   "<p><span style=\"font-weight: bold; font-style: italic; text-decoration: underline; \">Title</span></p>"
  +"<ul> <li> <span style=\"font-weight: bold;\"> </span><span>Laser cutting of sheet </span><span style=\"font-weight: bold;\">metal</span><span> generates a continuous heat source which can led to high levels of thermal stress </span> </li> <li><span>Thermal Stress can deform the material at the micron level and the slightest deformation reduces the effectiveness of the sheet metal components</span></li> <br /> </ul>"
  +"<p><span> </span><span style=\"font-weight:bold;text-decoration:underline;\">Mass Level Production</span></p>"
  +"<ul> <li><span>Laser cutting cannot produce more component more than one part at a time</span></li> <li><span>When production is required at a lower level , laser cutting may be apt but inefficient for mass level production</span></li> </ul>"
  +"<p><span style=\"font-weight: bold;\">Metal etching with aluminum</span></p>"
  +"<p><span style=\"font-weight: bold;\"> </span></p>"
  +"<ul> <li><span>Aluminum is a reflective material that is reactive to heat in the manufacturing process and therefore, makes it less suitable for laser cutting and wire EDM processes</span></li> <li> <span>As the use of aluminum is growing in many industries, there is a requirement for a suitable manufacturing process for thin </span><span style=\"font-weight: bold;\">aluminum</span><span>components</span> </li> </ul>"
  ;
  
  HTMLtoPPTX htmlToPPTX = new HTMLtoPPTX(html, "./CreatePowerPointTextFromHTML.pptx");

 }
}

Again: Disclaimer: This is a working draft showing the principle. Neither it is fully ready nor it is code ready for use in productive environments.

Related