diff --git a/README.md b/README.md index ec05c72d1..23a3c60dd 100644 --- a/README.md +++ b/README.md @@ -48,11 +48,11 @@ Installation uses the [npm](http://npmjs.org/) package manager. Just type the f * Highlights * Underlines * etc. +* Outlines ## Coming soon! * Patterns fills -* Outlines * PDF Security * Higher level APIs for creating tables and laying out content * More performance optimizations diff --git a/docs/generate.coffee b/docs/generate.coffee index 7dae10d86..888ef0eae 100644 --- a/docs/generate.coffee +++ b/docs/generate.coffee @@ -190,6 +190,11 @@ class Node if @type in ['h1', 'h2'] and lastType? and lastType isnt 'h1' doc.addPage() + if @type == 'h1' + doc.h1Outline = doc.outline.addItem(fragment.text) + else if @type == 'h2' && doc.h1Outline != null + doc.h1Outline.addItem(fragment.text) + # set styles and whether this fragment is continued (for rich text wrapping) options = @setStyle doc options.continued ?= continued or index < @content.length - 1 @@ -227,6 +232,7 @@ renderTitlePage = (doc) -> doc.y = doc.page.height / 2 - doc.currentLineHeight() doc.text title, align: 'center' w = doc.widthOfString(title) + doc.h1Outline = doc.outline.addItem(title) doc.fontSize 20 doc.y -= 10 @@ -250,5 +256,6 @@ do -> render doc, 'vector.coffee.md' render doc, 'text.coffee.md' render doc, 'images.coffee.md' + render doc, 'outline.coffee.md' render doc, 'annotations.coffee.md' doc.end() diff --git a/docs/guide.pdf b/docs/guide.pdf index b4b0bff21..373dda3fd 100644 Binary files a/docs/guide.pdf and b/docs/guide.pdf differ diff --git a/docs/outline.coffee.md b/docs/outline.coffee.md new file mode 100644 index 000000000..f1e6b8f15 --- /dev/null +++ b/docs/outline.coffee.md @@ -0,0 +1,28 @@ +# Outlines in PDFKit + +Outlines are the heirachical bookmarks that display in some PDF readers. Currently only page bookmarks are supported, but more may be added in the future. They are simple to add and only require a single method: + +* `addItem(title, options)` + +Here is an example of adding a bookmark with a single child bookmark. + + # Get a reference to the Outline root + outline = doc.outline + + # Add a top-level bookmark + top = outline.addItem('Top Level') + + # Add a sub-section + top.addItem('Sub-section') + +## Options + +The `options` parameter currently only has one property: `expanded`. If this value is set to `true` then all of that section's children will be visible by default. This value defaults to `false`. + +In this example the 'Top Level' section will be expanded to show 'Sub-section'. + + # Add a top-level bookmark + top = outline.addItem('Top Level', { expanded: true }) + + # Add a sub-section + top.addItem('Sub-section') \ No newline at end of file diff --git a/lib/document.coffee b/lib/document.coffee index c0db7268b..5ce9a5166 100644 --- a/lib/document.coffee +++ b/lib/document.coffee @@ -44,6 +44,7 @@ class PDFDocument extends stream.Readable @initFonts() @initText() @initImages() + @initOutline() # Initialize the metadata @info = @@ -77,6 +78,7 @@ class PDFDocument extends stream.Readable mixin require './mixins/text' mixin require './mixins/images' mixin require './mixins/annotations' + mixin require './mixins/outline' addPage: (options = @options) -> # end the current page if needed @@ -184,6 +186,8 @@ class PDFDocument extends stream.Readable for name, font of @_fontFamilies font.finalize() + @endOutline() + @_root.end() @_root.data.Pages.end() diff --git a/lib/mixins/outline.coffee b/lib/mixins/outline.coffee new file mode 100644 index 000000000..c3c83f55f --- /dev/null +++ b/lib/mixins/outline.coffee @@ -0,0 +1,11 @@ +PDFOutline = require '../outline' + +module.exports = + initOutline: () -> + @outline = new PDFOutline(this, null, null, null) + + endOutline: () -> + @outline.endOutline() + if @outline.children.length > 0 + @_root.data.Outlines = @outline.dictionary + @_root.data.PageMode = 'UseOutlines' diff --git a/lib/outline.coffee b/lib/outline.coffee new file mode 100644 index 000000000..b16264521 --- /dev/null +++ b/lib/outline.coffee @@ -0,0 +1,47 @@ +PDFObject = require './object' +PDFPage = require './page' + +class PDFOutline + constructor: (@document, parent, title, dest, @options = { expanded: false }) -> + + @outlineData = {} + + if dest != null + @outlineData['Dest'] = [dest.dictionary, 'Fit'] + + if parent != null + @outlineData['Parent'] = parent + + if title != null + @outlineData['Title'] = new String(title) + + @dictionary = @document.ref @outlineData + @children = [] + + addItem: (title, options = { expanded: false }) -> + result = new PDFOutline(@document, @dictionary, title, @document.page, options) + @children.push(result) + + return result + + endOutline: () -> + if @children.length > 0 + if @options.expanded + @outlineData.Count = @children.length + + [first, ..., last] = @children + @outlineData.First = first.dictionary + @outlineData.Last = last.dictionary + + for i in [0...@children.length] + child = @children[i] + if i > 0 + child.outlineData.Prev = @children[i-1].dictionary + if i < @children.length - 1 + child.outlineData.Next = @children[i+1].dictionary + child.endOutline() + + @dictionary.end() + + +module.exports = PDFOutline \ No newline at end of file