Quote Templates – Custom Content – Lines Section – Rich Description – Error generating document: Bad Request

Error generating document: Bad Request is not the best message. Sometimes it means you have a custom template content that’s blowing up. One of the reasons this might be happening is that you are trying to output the description field for each line. Well, that’s a bummer because the VF page for the custom content has to be in XSL:FO, not HTML. But rich text fields are stored in HTML. SO! How do you get your rich text field to display in a custom content?? Two ways: either strip all HTML tags out of your rich text field’s value -OR- use the code below to translate your rich text HTML tags to XSL:FO! Yep. This translates most tags. Leave a comment with any tags that aren’t translating!

    public String htmlToFo(String html){
        String output = '';
        
        if (html != null) {
            System.debug('html: '+html);
            DOM.Document doc = new DOM.Document();
            Dom.Document newdoc = new DOM.Document();
            
            doc.load('<block>'+html.replace('<br>', '<br />').replace('&', '&amp;')+'</block>');
            
            Dom.XMLNode root = doc.getRootElement();
            Dom.XMLNode newroot = newdoc.createRootElement('block', null, null);
            
            newroot = convertNodes(root.getChildren(), newroot, 0, '');
            output = newdoc.toXmlString().replace('<?xml version="1.0" encoding="UTF-8"?>', '');
            System.debug('output: '+output);
        }
        
        return output;
    }
    
    private Map<Decimal, String> listBulletsByLevel = new Map<Decimal, String>{
        0 => '•',
        1 => '◦',
        2 => '▪︎'
    };
    
    private Dom.XMLNode convertNodes(List<Dom.XMLNode> childrenToConvert, Dom.XMLNode newroot, Decimal listlevel, String listtype) {
        if (newroot != null) {
            Decimal orderedListCurrentItem = 1;
            for(Dom.XMLNode child : childrenToConvert) {
                Dom.XMLNode newchild = null;
                if (child.getName() == 'br') {
                    newchild = newroot.addChildElement('block', null, null);
                    newchild.setAttribute('padding-top', '0pt');
                    newchild.setAttribute('padding-bottom', '0pt');
                    newchild.addTextNode(' ');
                } else if (child.getName() == 'p') {
                    newchild = newroot.addChildElement('block', null, null);
                    newchild.setAttribute('padding-top', '10pt');
                    newchild.setAttribute('padding-bottom', '4pt');
                } else if (child.getName() == 'div') {
                    newchild = newroot.addChildElement('block', null, null);
                    newchild.setAttribute('padding-top', '0pt');
                    newchild.setAttribute('padding-bottom', '0pt');
                } else if (child.getName() == 'ul') {
                    listtype='unordered';
                    newchild = newroot.addChildElement('list-block', null, null);
                    newchild.setAttribute('padding-top', '4pt');
                } else if (child.getName() == 'ol') {
                    orderedListCurrentItem = 1;
                    listtype='ordered';
                    newchild = newroot.addChildElement('list-block', null, null);
                    newchild.setAttribute('padding-top', '4pt');
                } else if (child.getName() == 'li') {
                    newchild = newroot.addChildElement('list-item', null, null);
                    Dom.XMLNode label = newchild.addChildElement('list-item-label', null, null);
                    Decimal indent = listtype == 'unordered' ? 19 : 24;
                    label.setAttribute('start-indent', (13+(listlevel*13))+'pt');
                    if (listtype == 'unordered') {
                        label.addChildElement('block', null, null).addTextNode((String)(listBulletsByLevel.get(0)));//listlevel>2?0:listlevel)));
                    } else {
                        label.addChildElement('block', null, null).addTextNode(orderedListCurrentItem+'.');
                    }
                    Dom.XMLNode body = newchild.addChildElement('list-item-body', null, null);
                    body.setAttribute('start-indent', (indent+(listlevel*13))+'pt');
                    Dom.XMLNode bodytext = body.addChildElement('block', null, null);
                    newchild = bodytext;
                    listlevel++;
                    orderedListCurrentItem++;
                } else if (child.getName() == 'strong') {
                    newchild = newroot.addChildElement('inline', null, null);
                    newchild.setAttribute('font-weight', 'bold');
                } else if (child.getName() == 'em' || child.getName() == 'i') {
                    newchild = newroot.addChildElement('inline', null, null);
                    newchild.setAttribute('font-style', 'italic');
                } else if (child.getName() == 'u') {
                    newchild = newroot.addChildElement('inline', null, null);
                    newchild.setAttribute('text-decoration', 'underline');
                } else if (child.getName() == 's') {
                    newchild = newroot.addChildElement('inline', null, null);
                    newchild.setAttribute('text-decoration', 'line-through');
                } else if (child.getName() == 'sub') {
                    newchild = newroot.addChildElement('inline', null, null);
                    newchild.setAttribute('vertical-align', 'sub');
                    newchild.setAttribute('font-size', '8pt');
                } else if (child.getName() == 'sup') {
                    newchild = newroot.addChildElement('inline', null, null);
                    newchild.setAttribute('vertical-align', 'sup');
                    newchild.setAttribute('font-size', '8pt');
                } else if (child.getName() == 'blockquote') {
                    newchild = newroot.addChildElement('block', null, null);
                    newchild.setAttribute('start-indent', '13pt');
                } else if (child.getName() == 'img') {
                    newchild = newroot.addChildElement('external-graphic', null, null);
                    if (child.getAttributeValue('width', null) != null && child.getAttributeValue('width', null) != '') {
                        newchild.setAttribute('content-width', child.getAttributeValue('width', null));
                    }
                    if (child.getAttributeValue('height', null) != null && child.getAttributeValue('height', null) != '') {
                    	newchild.setAttribute('content-height', child.getAttributeValue('height', null));
                    }
                    newchild.setAttribute('src', child.getAttributeValue('src', null));
                } else if (child.getName() == 'a') {
                    newchild = newroot.addChildElement('basic-link', null, null);
                    newchild.setAttribute('color', 'blue');
                    newchild.setAttribute('text-decoration', 'underline');
                    newchild.setAttribute('external-destination', child.getAttributeValue('href', null));
                } else {
                    newchild = newroot.addChildElement('inline', null, null);
                    newchild.addTextNode(child.getText());
                }
                if (child.getAttributeValue('style', null) != null && child.getAttributeValue('style', null) != '') {
                    String style = child.getAttributeValue('style', null);
                    if (style.contains('background-color')) {
						Pattern pat = Pattern.compile('.*?background-color:\\s*([^;]+).*?');
                        Matcher match = pat.matcher(style);
                        match.find();
                        Integer groupsToCapture = match.groupCount();
                        if (groupsToCapture > 0) {
                            String bgcolor = match.group(1).replace(';', '');
                            newchild.setAttribute('background-color', bgcolor);
                        }
                        style = style.replace('background-color', '');
                    }
                    if (style.contains('color')) {
						Pattern pat = Pattern.compile('.*?color:\\s*([^;]+).*?');
                        Matcher match = pat.matcher(style);
                        match.find();
                        Integer groupsToCapture = match.groupCount();
                        if (groupsToCapture > 0) {
                            String color = match.group(1).replace(';', '');
                            newchild.setAttribute('color', color);
                        }
                    }
                    if (style.contains('font-size')) {
						Pattern pat = Pattern.compile('.*?font-size:\\s*([^;]+).*?');
                        Matcher match = pat.matcher(style);
                        match.find();
                        Integer groupsToCapture = match.groupCount();
                        if (groupsToCapture > 0) {
                            String fontSize = match.group(1).replace(';', '');
                            newchild.setAttribute('font-size', fontSize);
                        }
                    }
                }
                
                // process any child elements of this element.
                List<Dom.XMLNode> grandkids = child.getChildren();
                if (grandkids != null && grandkids.size() > 0) {
                    convertNodes(grandkids, newchild, listlevel, listtype);
                }
                
                if (child.getName() == 'li') {
                    listlevel--;
                }
            }
        }
        return newroot;
    }

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.