Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to wrap the text inside the table cell #204

Closed
ghost opened this issue Feb 17, 2015 · 36 comments
Closed

How to wrap the text inside the table cell #204

ghost opened this issue Feb 17, 2015 · 36 comments

Comments

@ghost
Copy link

ghost commented Feb 17, 2015

Hello,
When the cell content exceeds the column width, it is either getting truncated or the remaining content is overlapping into the next cell, How do we wrap the cell content

@jthoenes
Copy link
Contributor

Is this a duplicate of #203 ?

@ghost
Copy link
Author

ghost commented Feb 18, 2015

Its not same as #203, the issue is as shown below
error

The content in the "Data Type" column is getting overlapped into the next column "Length".
I want to wrap the text inside the Data Type column.
Note- I dynamically populate the data in the columns, so I have no idea what will be the length of the data populated.

@jthoenes
Copy link
Contributor

Ok, could you provide us with a (minimal) document definition to reproduced the problem?

@ghost
Copy link
Author

ghost commented Feb 18, 2015

below is the document definition for the table.

var docDefinition = {
                 pageSize: 'A4',
                 pageOrientation: 'landscape',
                     pageMargins: [ 0, 0, 0, 0 ],
                   content: [
                            {
                                margin: [20, 30, 60, 0],
                                table: {                    
                              widths: [180,60,60],
                              body: [   [{ text:'Description',style:'tableHeader', alignment:'center'},
                                      {text:'Data Type',style:'tableHeader', alignment:'center'},
                                      { text:'Length',style:'tableHeader', alignment:'center'}
                                                          ],
                                   ['','aabcdefghijklmno', ''],
                                    ]
                               }
                            }
                                  ],
                        styles: {
                                   tableHeader: {
                              bold: true,
                              fontSize: 12,
                              fillColor: '#0074c1',
                              color: 'white'
                               }
                                 }
               };

@jthoenes
Copy link
Contributor

Ah, I see. The cell is to small for the word. pdfmake never breaks words, it only breaks when you have a whitespace or punctation.

If you change the line

['','aabcdefghijklmno', ''],

to

['','aabcdef ghijklmno', ''],

it works.

This behaviour is not specific to tables. I'll tag it as feature request, but don't hope for a fast solution - breaking words is language dependant.

@ghost
Copy link
Author

ghost commented Feb 18, 2015

Thank you for the clarification.

@PaulL1
Copy link

PaulL1 commented Mar 30, 2015

@jthoenes: breaking words is language dependent if done well (i.e. put a hyphen in somewhere that breaks on the end of a syllable).

But breaking words badly would probably be OK here - since the problem only happens when a cell is too narrow to fit the whole word, it's likely that people would be happy with you just arbitrarily chopping at the cell width. Is that possible?

In fact, for my use case I'd even be happy with an ellipses on the end or a truncate. It just looks very unfinished for it to fall out of the cell.

@jthoenes
Copy link
Contributor

@PaulL1 There is no easy fix for this, because words are calculated before they are fitted onto the lines. That could of course be improved. Have a look in https://github.com/bpampuch/pdfmake/blob/master/src/textTools.js if you want to have a look yourself.

@PaulL1
Copy link

PaulL1 commented Mar 30, 2015

Ok, so I think what you're saying is that splitWords basically breaks all the text down into individual words, and puts them in a big array. But I don't see where the code is that writes an individual cell in the table and decides when to put a newline in. I don't think that's included in splitWords, because it never calls sizeOfString, and appears to have no knowledge of the column width it's writing in.

Leaving aside that I can't find the code, it seems that the logic would be to take the wordArray and write them one by one into a line until writing another would be wider than the available space. Then it should start a new line, and start writing words.

If that's the basic logic, then presumably it also has an exception rule that it must write at least one word (even if that word falls outside the available space). So when that exception is triggered then the change would be to instead have a go at breaking the word into two shorter strings, and only putting the first part on the current line.

Is that logic in the code somewhere? If I can navigate the code base I'm happy to have a go at it.

@jthoenes
Copy link
Contributor

Hi,

have a look at processRow in https://github.com/bpampuch/pdfmake/blob/master/src/layoutBuilder.js#L392. For rendering text this calls processNode and ultimately processLeaf. I've not written the code myself, so I don't know were it exactly happens.

@PaulL1
Copy link

PaulL1 commented Mar 30, 2015

OK, so it looks like the logic is that we call processNode, which calls processTable or other things as appropriate. It's recursive as it moves down a tree, so eventually we get to processLeaf, a table cell appears to be one instance of a leaf.

In processLeaf it builds a line, then iterates building lines until no more lines are left.

When building a line it calls line.hasEnoughSpaceForInline, and uses that information to decide whether to append a word to the line or start a new line (essentially). hasEnoughSpaceForInline has a condition right at the start that if the existing line length is zero then there must be enough space for the word - so effectively the first word always gets put into the line.

I can see the outlines of a solution. I would modify hasEnoughSpaceForInline so that it doesn't automatically assume an inline will fit:

Line.prototype.hasEnoughSpaceForInline = function(inline) {
//  if (this.inlines.length === 0) return true;
    if (this.newLineForced) return false;

    return this.inlineWidths + inline.width - this.leadingCut - (inline.trailingCut || 0) <= this.maxWidth;
};

I would then modify buildNextLine so that it notices if the first inline is too long to fit into the cell:

LayoutBuilder.prototype.buildNextLine = function(textNode) {
    if (!textNode._inlines || textNode._inlines.length === 0) return null;

    var line = new Line(this.writer.context().availableWidth);

    while(textNode._inlines && textNode._inlines.length > 0 && line.hasEnoughSpaceForInline(textNode._inlines[0])) {
        line.addInline(textNode._inlines.shift());
    }

    if(line.inlines.length === 0){
      // the first word was too long to fit into the line, grab as much as can fit and leave the rest in the node
      line.addPartInline(textNode._inlines[0]);
    }

    line.lastLineInParagraph = textNode._inlines.length === 0;
    return line;
};

Finally, we need a new line.addPartInline method. The trick is that this is going to have to clone the existing inline, and split the text into two. That means that it'll have to be measured again. And it'll have to guess (or measure) how much of the word it needs to put in.

My stab at that doesn't measure properly, but is:

Line.prototype.addPartInline = function(inline) {
  // guess number of characters ignoring kerning.  This could be better done by measuring perhaps
  var avgLengthPerChar = (inline.width - inline.leadingCut - inline.trailingCut) / inline.text.length;
  var useChars = Math.ceil(this.maxWidth / avgLengthPerChar, 0);

  // there should be a better way to clone an inline than this
  var newInline = {
    alignment: inline.alignment,
    background: inline.background,
    color: inline.color,
    decoration: inline.decoration,
    decorationColor: inline.decorationColor,
    decorationStyle: inline.decorationStyle,
    font: inline.font,
    fontSize: inline.fontSize,
    height: inline.height,
    leadingCut: inline.leadingCut,
    trailingCut: inline.trailingCut
  };

  // divide the width between the two - also should really be measured properly if I knew how
  newInline.width = inline.width * useChars / inline.text.length;
  inline.width = inline.width - newInline.width;

  newInline.text = inline.text.substr(0,useChars);
  inline.text = inline.text.substr(useChars);

  // write the newly cloned inline to the line, leave the old inline to get picked up next iteration
  this.inlines.push(newInline);
  this.inlineWidths += inline.width;
};

It doesn't work, which doesn't help much. I'm ending up with a couple of largely blank pages, and no errors anywhere in the javascript console. My first guess would be that the code doesn't like my butchering of the inlines to clone one. Perhaps someone who knows the pdfMake internals better could use this as inspiration to do something?

@jthoenes
Copy link
Contributor

Thanks for the analysis.

The cloning is probably the issue - because pdfmake keeps references at some places.

@RedDroid
Copy link

Can you add " _ " (underscore) also as a valid delimiter for long text. Looks like the cell content does not wrap even when the text has underscores in them.

@ui-mumtaz
Copy link

Yes I am also facing same issue..
I am using angular UI Grid with PDFMake export
The text is overlapping inside cell.
CSS file not working...
so how to wrap the text inside the table cell?

@einfallstoll
Copy link
Contributor

We recently came across this problem and found a workaround that works in some cases. I'll just leave this here as an input if someone experiences the same.

Here's a content which is too big for its cell:

bildschirmfoto 2015-11-11 um 10 19 02

As a workaround we added a row and used colSpan and rowSpan to make a slightly better layout:

bildschirmfoto 2015-11-11 um 10 21 15

_Note: This does not solve the problem, but may work in some cases with the right content._

@Sampath-Lokuge
Copy link

Hi @jthoenes

Any update about this feature ? Thanks.

@codeuniquely
Copy link

+1 for adding '_' to the list of delimiting / breakable characters

@cvanlabe
Copy link

+1 as well for the '_' wrapping!

@mhamzeh
Copy link

mhamzeh commented Jul 15, 2016

+1 for adding '_' to wrap text

@AndrewPreciado
Copy link

I am also having the same issue since I am exporting a UI Grid. Has this been resolved?

"Yes I am also facing same issue..
I am using angular UI Grid with PDFMake export
The text is overlapping inside cell.
CSS file not working...
so how to wrap the text inside the table cell?"

@jostang
Copy link

jostang commented Apr 17, 2017

@jthoenes Could you add the zero-width space to the list of breaking characters? This way, I could tell pdfmake where to break my words by adding an invisible zero-width space (\u200b) at this position. Or if I don't care at which position it breaks words (which is my case), I could use text.split('').join('\u200b') to allow breaking after any character

@liborm85
Copy link
Collaborator

@jostang Unicode character "ZERO WIDTH SPACE" is breaking character in version 0.1.23 and above.

@jostang
Copy link

jostang commented Apr 17, 2017

@liborm85 Great, thank you! I was still using 0.1.20

@liborm85
Copy link
Collaborator

Support for wrapping non-wrappable long words implemented by commit: 4c5599d

@liborm85
Copy link
Collaborator

New version pdfmake released.

@aa-telali
Copy link

aa-telali commented Jun 6, 2017

Has this broken the newline (\n) processing, I just can't get it to recognise newline characters in a table cell, tough all the examples say it should work?

@Wabbala
Copy link

Wabbala commented Jun 7, 2017 via email

@ttristan
Copy link

ttristan commented Sep 5, 2017

I am having the same issue with words not wrapping when setting all table widths to "auto".

I have dynamic tables passed from an UI and therefore I do this:
let widths = [] for (var i = 0; i < columnCount; i++) { widths.push("auto") }

It is no problem when I give a fixed width for each column like this:
let widths = [] for (var i = 0; i < columnCount; i++) { widths.push(450/columnCount) }

But I want the "auto" so that I get the maximum space for each column if needed. With the fixed width words do wrap like they should but I sometimes have unwanted white space and therefore wasted space for other columns.

Does anyone know a fix?

@sayjeyhi
Copy link

@jthoenes could we modify https://github.com/bpampuch/pdfmake/blob/master/src/textTools.js to work with nbspace char , i use it instead of space for RTL support .
(you can add nbspace with alt+255)

@AvinashChandanshive
Copy link

Hello,
I tried one solution and its work.
Check this.
To break word:
function breakword(number) {
var index = 8;
if (number.length > 10) {

            return number.substring(0, index) + "\n" + number.substring(index);

        } else {
            return number;
        }

    }

var c2 = { text: breakword(value), noWrap: false };

ima

@BunnyApocalypse
Copy link

Sorry for bumping a dead issue, but I'm still getting this problem when using large filenames in a table, for example: this_is_a_very_long_file_name_separated_by_underscores.csv. I have not set nowrap to false. Additionally, putting no-width spaces in between all of the underscores does solve the problem, but that's less than optimal.

I am using pdfmake 0.1.71.

@owen-soak
Copy link

I'm bumping this issue, I have a long string that i can't break manually (think JWT or similar in length) and i need to render this in a table so i can provide a background color and some padding (well the illusion of padding).

Any help greatly apreciated.

@italomandara
Copy link

For whom is still interested I found a quick workaround:
split the long text with a zero width space every n characters, this way pdfmake will go on a newline every n character if it doesn't fit in the cell.
example every 10 characters string.match(/.{1,10}/g).join('\u{200B}')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests