4

I have the Problem to wrap the Text in PDF and the Text is without Space something like that "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".

Please Help me to solve this. Thanks in Advance.

3 Answers 3

3

CSS3 supports a few styles for wrapping text: text-overflow, overflow-wrap and the now-deprecated word-wrap.

If the VF rendering from HTML into PDF supports these CSS properties, this may work for you.

New CSS3 Properties to Handle Text and Word Wrapping

4
  • Answer is possibly irrelevant; the user is referring to Flying Saucer's ability to wrap text, not a browser.
    – sfdcfox
    Commented Aug 31, 2013 at 18:31
  • SFDC uses Flying Saucer? Commented Sep 2, 2013 at 11:06
  • @sfdcfox, The answer may be irrelevant, but it's not common knowledge which CSS spec is used during the HTML to PDF conversion which is why I mentioned that it may work. Can you confirm that SFDC uses Flying Saucer for PDF rendering?
    – Mark Pond
    Commented Sep 6, 2013 at 21:10
  • 2
    Community - Table Header in each page in PDF. We have to use -fs vendor prefixes for some special CSS, such as table headers in each page.
    – sfdcfox
    Commented Sep 7, 2013 at 9:16
1

Unfortunately this doesn't work. Salesforce lists this as a known issue marked as no fix.

Long line of text doesn't wrap properly when PDF is generated via visualforce. It also doesn't respect CSS word-wrap and word-break properties and those do not work properly when set to break-work and break-all, respectively.

Salesforce has release a pilot version of their new improved pdf rendering engine (details here). Being based on a chromimum engine the output matches what debugging results in chrome and has full support for css paged media. While it does appear to have some scalability issues associated with the current version, it's stated intention is to add

broader support for modern HTML standards, such as CSS3, JavaScript, and HTML5

One might reasonably hope issues like this are marked as non-fix as they're expected to be addressed by the new and improved rendering engine.

However, if you're unable to join the pilot the only option is to manually implement line breaking on a case by case basis. If you're interested in more of an algorithm the discussion of the best text wrapping algorithm on stackoverflow may be useful and has some examples that should be portable.

0

I used the approach provided by Ralph Callaway here and expanded it a bit.

The basic concept is that the long words must be broken down so that they are automatically wrapped by the PDF rendering engine. The best way to split them is to wrap them with <span> tags so that a word such as SomethingVeryLong is represented as <span>Somethi</span><span>ngVeryL</span><span>ong</span>.

The Visualforce page rendering engine can then use the span elements to split the word if it needs to.

Here's the complete logic:

public class UTIL_Pdf {
    /**
    * Maximum word length
    */
    private final static Integer LENGTH = 7;

    /**
    * Reduce the long strings (i.e. words) inside the provided text into more manageable pieces.
    * NOTE: the total length of the final string might get significantly larger due to this.
    *
    * @param text  String to reduce
    *
    * @return      Same string as the one passed in, but each individual long string is
    *              split and surrounded with `<span></span>` tags.
    *              e.g.ABC-Something -> <span class="avoid-wrap"><span>ABC-</span><span>Somethi</span><span>ng</span></span>
    */
    public static String stringReducer(String text) {
        if (String.isBlank(text)) {
            return '';
        }

        String retVal = text;

        for (String str : new Set<String>(stripHtml(text).unescapeHtml4().split(' '))) {
            if (str.length() > LENGTH) {
                String newString = splitWord(str, LENGTH);

                // split into 4 replace calls becuse we had an issue that the html properites were getting replaced by accident:
                // e.g. <a href='somelongurl'>somelongurl</a> <- only "somelongurl" in content should be replaced.
                retVal = retVal.replace(' ' + str + ' ', ' <span class="avoid-wrap">' + newString + '</span> ')
                    .replace('>' + str + '<', '><span class="avoid-wrap">' + newString + '</span><')
                    .replace('>' + str + ' ', '><span class="avoid-wrap">' + newString + '</span> ')
                    .replace(' ' + str + '<', ' <span class="avoid-wrap">' + newString + '</span><');//"
            }
        }

        return retVal;
    }

    /**
    * Strip HTML tags from the provided string
    */
    private static String stripHtml(String s) {
        if (String.isBlank(s)) {
            return '';
        }

        return s.replaceAll('<[^>]+>', ' ');
    }

    /**
    * Split the provided string at special characters, 
    * then at any position if the length still exceeds `maxLength`
    *
    * @param str       String to split
    * @param maxLength Max lenght of each individual part of the string
    *
    * @return          ABC-Something -> <span>ABC-</span><span>Somethi</span><span>ng</span>
    */
    private static String splitWord(String str, final Integer maxLength) {
        String newString = '';

        for (String s2 : splitStringBySpecialCharacters(str, '-;:<>/\\\u00A0')) {
            if (s2.length() > maxLength) {
                // the string is still too long, so split it further without any character consideration
                for (String s3 : splitEqually(s2, maxLength)) {
                    newString = wrapAndAppend(newString, s3);
                }
            } else {
                // the string is of acceptable length, so just wrap it in <span></span> and append it
                newString = wrapAndAppend(newString, s2);
            }
        }

        return newString;
    }

    /**
    * Split the string by special characters.
    * If the special character is not a whitespace, it will be appended to the end of the first part of the string
    * (e.g. "ABC-Something" -> "ABC-", "Something")
    *
    * @param str               string to split
    * @param splitCharacters   single string of characters by which to split the string
    *
    * @return                  splitted string
    */
    private static List<String> splitStringBySpecialCharacters(String str, String splitCharacters) {
        List<String> parts = new List<String>();

        if (str != null) {
            Integer startIndex = 0;
            while (true) {
                Integer preIndex = str.subString(startIndex).indexOfAny(splitCharacters);
                Integer index = startIndex + preIndex;

                if (preIndex == -1) {
                    parts.add(str.subString(startIndex));
                    break;
                }

                String word = str.subString(startIndex, index);
                String nextChar = str.subString(index, index + 1);

                // Dashes and the likes should stick to the word occuring before it. Whitespace doesn't have to.
                if (nextChar.isWhiteSpace() || nextChar == '\u00A0') {
                    parts.add(word);
                    parts.add(nextChar);
                } else {
                    parts.add(word + nextChar);
                }

                startIndex = index + 1;
            }
        }

        return parts;
    }

    /**
    * Split the string equally into list of strings of provided size.
    * The trailing (last) string will usually be smaller than the rest.
    *
    * @param str   string to split
    * @param size  maximum size of each part
    *
    * @return      splitted string
    */
    private static List<String> splitEqually(String str, Integer size) {
        List<String> ret = new List<String>();

        for (Integer start = 0; start < str.length(); start += size) {
            ret.add(str.substring(start, Math.min(str.length(), start + size)));
        }
        return ret;
    }

    /**
    * Wrap `toAppend` string into `<span></span>` so that it can be wrapped automatically by PDF engine
    * and append it to the end of the provided `str`.
    *
    * @param str       String to append to and then return
    * @param toAppend  String to wrap and append
    *
    * @return          str<span>toAppend</span>
    */
    private static String wrapAndAppend(String str, String toAppend) {
        if (String.isBlank(toAppend)) {
            return str;
        }

        // don't wrap &nbsp; in spans
        // as it will just increase the string size without any benefits
        // (&nbsp; will wrap on its own)
        if (toAppend == '\u00A0') {
            str += toAppend;
        } else {
            str += '<span>' + toAppend + '</span>';
        }

        return str;
    }
}

You can then pass the HTML to UTIL_Pdf.stringReducer(..) before rendering in a Visualforce Page.

I detailed the solution a bit more in this blog post.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .