Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Nested lists #116

Closed
wants to merge 2 commits into from

2 participants

@booo

This adds basic support for nested lists. Usage:

doc.list(['One', ['Two', ['foo', 'bar'], 'Three'], 'four'])

Relevant patch starts here: https://github.com/booo/pdfkit/pull/new/nested_lists#L0R78

Best Regards
Philipp

booo added some commits
@booo booo remove trailing whitespaces b18c9b1
@booo booo add support for nested lists
Usage:

    doc.list(['One', ['Two', ['foo', 'bar'], 'Three'], 'four'])
aa2c302
@devongovett
Owner

Nested lists are already supported. Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 8, 2013
  1. @booo

    remove trailing whitespaces

    booo authored
Commits on Mar 11, 2013
  1. @booo

    add support for nested lists

    booo authored
    Usage:
    
        doc.list(['One', ['Two', ['foo', 'bar'], 'Three'], 'four'])
This page is out of date. Refresh to see the latest.
Showing with 71 additions and 68 deletions.
  1. +71 −68 lib/mixins/text.coffee
View
139 lib/mixins/text.coffee
@@ -1,66 +1,66 @@
# This regular expression is used for splitting a string into wrappable words
WORD_RE = /([^ ,\/!.?:;\-\n]+[ ,\/!.?:;\-]*)|\n/g
-module.exports =
+module.exports =
initText: ->
# Current coordinates
@x = 0
@y = 0
@_lineGap = 0
-
+
# Keeps track of what has been set in the document
- @_textState =
+ @_textState =
mode: 0
wordSpacing: 0
characterSpacing: 0
-
+
# state of the wrapping algorithm
@_wrapState = {}
-
+
lineGap: (@_lineGap) ->
return this
-
+
text: (text, x = {}, y, options = {}) ->
if typeof x is 'object'
options = x
x = null
-
+
# Convert text to a string
text = '' + text
-
+
# Update the current position
if x? or y?
@x = x or @x
@y = y or @y
-
+
# wrap to margins if no x or y position passed
else
margins = @page.margins
options.width ?= @page.width - @x - margins.right
options.height ?= @page.height - @y - margins.bottom
-
+
# wrap to columns
options.columns ||= 1
options.columnGap ?= 18 # 1/4 inch
-
+
# if the wordSpacing option is specified, remove multiple consecutive spaces
if options.wordSpacing
text = text.replace(/\s+/g, ' ')
-
+
# word wrapping
if options.width
@_wrap text, options
-
- # newlines
+
+ # newlines
else if (matches = text.split '\n').length > 1
@_line match, options for match in matches
-
- # single line
+
+ # single line
else
@_line text, options
-
+
return this
-
+
moveDown: (lines = 1) ->
@y += @currentLineHeight(true) * lines + @_lineGap
return this
@@ -75,99 +75,102 @@ module.exports =
@y = y = oy or @y
for item in array
- @circle x + 3, @y + gap + 3, 3
- @text item, x + 15
- @y += 3
-
+ if item instanceof Array
+ @list item, x + 15
+ else
+ @circle x + 3, @y + gap + 3, 3
+ @text item, x + 15
+ @y += 3
+
@x = x
@fill()
-
+
_line: (text, options) ->
wrap = @_wrapState
paragraphGap = (wrap.firstLine and @y isnt wrap.startY and options.paragraphGap) or 0
lineGap = options.lineGap or @_lineGap or 0
-
+
@_fragment text, @x, @y + paragraphGap, options
@y += @currentLineHeight(true) + lineGap + paragraphGap
-
+
_fragment: (text, x, y, options = {}) ->
text = '' + text
return if text.length is 0
-
+
state = @_textState
wrap = @_wrapState
-
+
# handle options
align = options.align or 'left'
indent = (wrap.firstLine and options.indent) or 0
wordSpacing = options.wordSpacing or 0
characterSpacing = options.characterSpacing or 0
-
+
# text alignments
if options.width
lineWidth = wrap.lineWidth - indent - wrap.extraSpace
-
+
switch align
when 'right'
x += lineWidth - @widthOfString(text)
-
+
when 'center'
x += lineWidth / 2 - @widthOfString(text) / 2
-
+
when 'justify'
# don't justify the last line of paragraphs
break if wrap.lastLine
-
+
# split the line into words
words = text.match(WORD_RE)
break unless words
-
- # calculate the word spacing value
+
+ # calculate the word spacing value
textWidth = @widthOfString text.replace(/\s+/g, '')
wordSpacing = (lineWidth - textWidth) / (words.length - 1) - @widthOfString(' ')
-
+
# indentation support
x += indent
-
+
# flip coordinate system
y = @page.height - y - (@_font.ascender / 1000 * @_fontSize)
-
+
# add current font to page if necessary
@page.fonts[@_font.id] ?= @_font.ref
-
+
# tell the font subset to use the characters
@_font.use text
-
+
# encode the text based on the font subset,
# and then convert it to hex
text = @_font.encode text
text = (text.charCodeAt(i).toString(16) for i in [0...text.length]).join('')
-
+
# begin the text object
@addContent "BT"
-
+
# text position
@addContent "#{x} #{y} Td"
-
+
# font and font size
@addContent "/#{@_font.id} #{@_fontSize} Tf"
-
+
# rendering mode
mode = if options.fill and options.stroke then 2 else if options.stroke then 1 else 0
@addContent "#{mode} Tr" unless mode is state.mode
-
+
# Word spacing
@addContent wordSpacing + ' Tw' unless wordSpacing is state.wordSpacing
-
+
# Character spacing
@addContent characterSpacing + ' Tc' unless characterSpacing is state.characterSpacing
-
+
# add the actual text
@addContent "<#{text}> Tj"
-
+
# end the text object
@addContent "ET"
-
+
# keep track of text states
state.mode = mode
state.wordSpacing = wordSpacing
@@ -177,62 +180,62 @@ module.exports =
width = @widthOfString.bind this
indent = options.indent or 0
lineWidth = (options.width - (options.columnGap * (options.columns - 1))) / options.columns
-
+
# initial settings
wrap.column = 1 # the current column
wrap.startY = @y # the initial Y position
wrap.lineWidth = lineWidth # the maximum width of each line
wrap.firstLine = true # whether we are on the first line of a paragraph
wrap.lastLine = false # whether we are on the last line of a paragraph
-
+
# calculate the maximum Y position the text can appear at
wrap.maxY = @y + options.height - @currentLineHeight()
-
+
# split the line into words
words = text.match(WORD_RE)
-
+
# calculate the extra width
wrap.extraSpace = (options.wordSpacing or 0) * (words.length - 1) + # wordSpacing
(options.characterSpacing or 0) * (text.length - 1) # characterSpacing
-
+
# space left on the line to fill with words
spaceLeft = lineWidth - indent - wrap.extraSpace
-
+
# word width cache
wordWidths = {}
len = words.length
buffer = ''
-
+
for word, i in words
w = wordWidths[word] ?= width(word)
-
+
if w > spaceLeft or word is '\n'
# keep track of the wrapping state
if wrap.lastLine
wrap.firstLine = true
wrap.lastLine = false
-
+
# if we've got a newline, mark it
if word is '\n'
wrap.lastLine = true
w += indent
-
+
# render the line
lastLine = buffer.trim()
@_line lastLine, options
-
+
# we're no longer on the first line...
wrap.firstLine = false
-
+
# if we've reached the maximum height, make sure
- # that the first line of a paragraph is never by
+ # that the first line of a paragraph is never by
# itself at the bottom of a page
nextY = @y + @currentLineHeight(true)
if @y > wrap.maxY or (wrap.lastLine and nextY > wrap.maxY)
- # if we've reached the edge of the page,
+ # if we've reached the edge of the page,
# continue on a new page or column
@_nextSection options
-
+
# reset the space left and buffer
spaceLeft = lineWidth - w - wrap.extraSpace
buffer = if word is '\n' then '' else word
@@ -245,19 +248,19 @@ module.exports =
# add the last line
wrap.lastLine = true
@_line buffer.trim(), options
-
+
# reset wrap state
@_wrapState = {}
-
+
_nextSection: (options) ->
wrap = @_wrapState
-
+
if ++wrap.column > options.columns
@addPage()
wrap.column = 1
wrap.startY = @page.margins.top
wrap.maxY = @page.maxY()
-
+
else
@x += wrap.lineWidth + options.columnGap
- @y = wrap.startY
+ @y = wrap.startY
Something went wrong with that request. Please try again.