From 00c6dabf3c5dbd34b2f569e764baadf6aee4effe Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Sun, 16 Feb 2014 20:08:19 -0800 Subject: [PATCH] Stop wrapping text after filling specified height, and add ellipsis option. Fixes #149. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No longer makes new pages after user specified heights, but still does so if no height is given (page edge by default). This works with multiple columns too. It will fill the columns in the horizontal and vertical space provided and then stop, rather than going to a new page. Also adds the `ellipsis` option which can be used to append an ellipsis character like `…` to the end of the cut off text. If you set `ellipsis: true`, it will use the default ellipsis character, but you can also set the option to any string you want to use. --- lib/line_wrapper.coffee | 47 +++++++++++++++++++++++++++++++++++------ lib/mixins/text.coffee | 1 - 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/lib/line_wrapper.coffee b/lib/line_wrapper.coffee index 65c7cd8..f78f7fc 100644 --- a/lib/line_wrapper.coffee +++ b/lib/line_wrapper.coffee @@ -12,9 +12,14 @@ class LineWrapper extends EventEmitter @startX = @document.x @startY = @document.y @column = 1 + @ellipsis = options.ellipsis # calculate the maximum Y position the text can appear at - @maxY = @startY + options.height + if options.height? + @height = options.height + @maxY = @startY + options.height + else + @maxY = @document.page.maxY() # handle paragraph indents @on 'firstLine', (options) => @@ -55,6 +60,7 @@ class LineWrapper extends EventEmitter w = wordWidths[word] ?= @wordWidth word # if the word is longer than the whole line, chop it up + # TODO: break by grapheme clusters, not JS string characters if w > @lineWidth # make some fake break objects lbk = last @@ -68,16 +74,19 @@ class LineWrapper extends EventEmitter # send a required break unless this is the last piece fbk.required = l < word.length - fn word.slice(0, l), w, fbk, lbk + shouldContinue = fn word.slice(0, l), w, fbk, lbk lbk = required: false # get the remaining piece of the word word = word.slice(l) w = @wordWidth word + + break if shouldContinue is no else # otherwise just emit the break as it was given to us - fn word, w, bk, last + shouldContinue = fn word, w, bk, last + break if shouldContinue is no last = bk return @@ -87,6 +96,7 @@ class LineWrapper extends EventEmitter @indent = options.indent if options.indent? @charSpacing = options.characterSpacing if options.characterSpacing? @wordSpacing = options.wordSpacing if options.wordSpacing? + @ellipsis = options.ellipsis if options.ellipsis? # make sure we're actually on the page # and that the first line of is never by @@ -122,13 +132,34 @@ class LineWrapper extends EventEmitter if bk.required or w > @spaceLeft if bk.required @emit 'lastLine', options, this + + # if the user specified a max height and an ellipsis, and is about to pass the + # max height and max columns after the next line, append the ellipsis + lh = @document.currentLineHeight(true) + if @height? and @ellipsis and @document.y + lh * 2 > @maxY and @column >= @columns + @ellipsis = '…' if @ellipsis is true # map default ellipsis character + buffer = buffer.trimRight() + textWidth = @wordWidth buffer + @ellipsis + + # remove characters from the buffer until the ellipsis fits + while w > @lineWidth + buffer = buffer.slice(0, -1).trimRight() + textWidth = @wordWidth buffer + @ellipsis + buffer = buffer + @ellipsis + emitLine() # if we've reached the edge of the page, # continue on a new page or column - if @document.y + @document.currentLineHeight(true) > @maxY - @nextSection() + if @document.y + lh > @maxY + shouldContinue = @nextSection() + + # stop if we reached the maximum height + unless shouldContinue + wc = 0 + buffer = '' + return no # reset the space left and buffer if bk.required @@ -164,6 +195,10 @@ class LineWrapper extends EventEmitter @emit 'sectionEnd', options, this if ++@column > @columns + # if a max height was specified by the user, we're done. + # otherwise, the default is to make a new page at the bottom. + return false if @height? + @document.addPage() @column = 1 @startY = @document.page.margins.top @@ -171,12 +206,12 @@ class LineWrapper extends EventEmitter @document.x = @startX @document.fillColor @document._fillColor... if @document._fillColor @emit 'pageBreak', options, this - else @document.x += @lineWidth + @columnGap @document.y = @startY @emit 'columnBreak', options, this @emit 'sectionStart', options, this + return true module.exports = LineWrapper diff --git a/lib/mixins/text.coffee b/lib/mixins/text.coffee index f55ec8f..a756fbe 100644 --- a/lib/mixins/text.coffee +++ b/lib/mixins/text.coffee @@ -124,7 +124,6 @@ module.exports = unless options.lineBreak is false margins = @page.margins options.width ?= @page.width - @x - margins.right - options.height ?= @page.height - @y - margins.bottom options.columns ||= 0 options.columnGap ?= 18 # 1/4 inch