mirror of
https://github.com/foliojs/pdfkit.git
synced 2025-12-08 20:15:54 +00:00
148 lines
5.2 KiB
CoffeeScript
148 lines
5.2 KiB
CoffeeScript
{EventEmitter} = require 'events'
|
|
LineBreaker = require 'linebreak'
|
|
|
|
class LineWrapper extends EventEmitter
|
|
constructor: (@document, options) ->
|
|
@indent = options.indent or 0
|
|
@charSpacing = options.characterSpacing or 0
|
|
@wordSpacing = options.wordSpacing is 0
|
|
@columns = options.columns or 1
|
|
@columnGap = options.columnGap ? 18 # 1/4 inch
|
|
@lineWidth = (options.width - (@columnGap * (@columns - 1))) / @columns
|
|
@startX = @document.x
|
|
@startY = @document.y
|
|
@column = 1
|
|
|
|
# calculate the maximum Y position the text can appear at
|
|
@maxY = @startY + options.height
|
|
|
|
# handle paragraph indents
|
|
@on 'firstLine', (options) =>
|
|
# if this is the first line of the text segment, and
|
|
# we're continuing where we left off, indent that much
|
|
# otherwise use the user specified indent option
|
|
indent = @continuedX ? @indent
|
|
@document.x += indent
|
|
@lineWidth -= indent
|
|
|
|
@once 'line', =>
|
|
@document.x -= indent
|
|
@lineWidth += indent
|
|
@continuedX = null
|
|
|
|
# handle left aligning last lines of paragraphs
|
|
@on 'lastLine', (options) =>
|
|
align = options.align
|
|
options.align = 'left' if align is 'justify'
|
|
@lastLine = true
|
|
|
|
@once 'line', =>
|
|
@document.y += options.paragraphGap or 0
|
|
options.align = align
|
|
@lastLine = false
|
|
|
|
wrap: (text, options) ->
|
|
# override options from previous continued fragments
|
|
@indent = options.indent if options.indent?
|
|
@charSpacing = options.characterSpacing if options.characterSpacing?
|
|
@wordSpacing = options.wordSpacing if options.wordSpacing?
|
|
|
|
# make sure we're actually on the page
|
|
# and that the first line of is never by
|
|
# itself at the bottom of a page (orphans)
|
|
nextY = @document.y + @document.currentLineHeight(true)
|
|
if @document.y > @maxY or nextY > @maxY
|
|
@nextSection()
|
|
|
|
# word width cache
|
|
wordWidths = {}
|
|
@emit 'sectionStart', options, this
|
|
|
|
breaker = new LineBreaker(text)
|
|
last = null
|
|
buffer = ''
|
|
textWidth = 0
|
|
wc = 0
|
|
|
|
y = @document.y # used to reset Y pos if options.continued (below)
|
|
emitLine = =>
|
|
options.textWidth = textWidth + @wordSpacing * (wc - 1)
|
|
options.wordCount = wc
|
|
options.lineWidth = @lineWidth
|
|
y = @document.y
|
|
@emit 'line', buffer, options, this
|
|
|
|
while bk = breaker.nextBreak()
|
|
if not last? or last.required
|
|
@emit 'firstLine', options, this
|
|
spaceLeft = @lineWidth
|
|
|
|
word = text.slice(last?.position or 0, bk.position)
|
|
w = wordWidths[word] ?= @document.widthOfString(word, this) + @charSpacing + @wordSpacing
|
|
|
|
if w <= spaceLeft
|
|
buffer += word
|
|
textWidth += w
|
|
wc++
|
|
|
|
if bk.required or w > spaceLeft
|
|
if bk.required
|
|
@emit 'lastLine', options, this
|
|
|
|
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()
|
|
|
|
# reset the space left and buffer
|
|
if bk.required
|
|
buffer = ''
|
|
textWidth = 0
|
|
wc = 0
|
|
else
|
|
# reset the space left and buffer
|
|
spaceLeft = @lineWidth - w
|
|
buffer = word
|
|
textWidth = w
|
|
wc = 1
|
|
else
|
|
spaceLeft -= w
|
|
|
|
last = bk
|
|
|
|
if wc > 0
|
|
@emit 'lastLine', options, this
|
|
emitLine()
|
|
|
|
@emit 'sectionEnd', options, this
|
|
|
|
# if the wrap is set to be continued, save the X position
|
|
# to start the first line of the next segment at, and reset
|
|
# the y position
|
|
if options.continued is yes
|
|
@continuedX = textWidth
|
|
@document.y = y
|
|
|
|
nextSection: (options) ->
|
|
@emit 'sectionEnd', options, this
|
|
|
|
if ++@column > @columns
|
|
@document.addPage()
|
|
@column = 1
|
|
@startY = @document.page.margins.top
|
|
@maxY = @document.page.maxY()
|
|
@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
|
|
|
|
module.exports = LineWrapper
|