pdfkit/lib/document.coffee
2014-03-23 17:11:48 -07:00

198 lines
4.2 KiB
CoffeeScript

###
PDFDocument - represents an entire PDF document
By Devon Govett
###
stream = require 'stream'
fs = require 'fs'
PDFObject = require './object'
PDFReference = require './reference'
PDFPage = require './page'
class PDFDocument extends stream.Readable
constructor: (@options = {}) ->
super
# PDF version
@version = 1.3
# Whether streams should be compressed
@compress = yes
# The PDF object store
@_objects = []
@_waiting = 0
@_ended = false
@_offset = 0
@_root = @ref
Type: 'Catalog'
Pages: @ref
Type: 'Pages'
Count: 0
Kids: []
# The current page
@page = null
# Initialize mixins
@initColor()
@initVector()
@initFonts()
@initText()
@initImages()
# Initialize the metadata
@info =
Producer: 'PDFKit'
Creator: 'PDFKit'
CreationDate: new Date()
if @options.info
for key, val of @options.info
@info[key] = val
# Write the header
# PDF version
@_write "%PDF-#{@version}"
# 4 binary chars, as recommended by the spec
@_write "%\xFF\xFF\xFF\xFF"
# Add the first page
@addPage()
mixin = (name) =>
methods = require './mixins/' + name
for name, method of methods
this::[name] = method
# Load mixins
mixin 'color'
mixin 'vector'
mixin 'fonts'
mixin 'text'
mixin 'images'
mixin 'annotations'
addPage: (options = @options) ->
# end the current page if needed
@page?.end()
# create a page object
@page = new PDFPage(this, options)
# add the page to the object store
pages = @_root.data.Pages.data
pages.Kids.push @page.dictionary
pages.Count++
# reset x and y coordinates
@x = @page.margins.left
@y = @page.margins.top
# flip PDF coordinate system so that the origin is in
# the top left rather than the bottom left
@_ctm = [1, 0, 0, 1, 0, 0]
@transform 1, 0, 0, -1, 0, @page.height
return this
ref: (data) ->
ref = new PDFReference(this, @_objects.length + 1, data)
@_objects.push ref
@_waiting++
return ref
_read: ->
# do nothing, but this method is required by node
_write: (data) ->
unless Buffer.isBuffer(data)
data = new Buffer(data + '\n', 'binary')
@push data
@_offset += data.length
addContent: (data) ->
@page.write data
return this
_refEnd: ->
if --@_waiting is 0 and @_ended
@_finalize()
@_ended = false
write: (filename, fn) ->
# print a deprecation warning with a stacktrace
err = new Error '
PDFDocument#write is deprecated, and will be removed in a future version of PDFKit.
Please pipe the document into a Node stream.
'
console.warn err.stack
@pipe fs.createWriteStream(filename)
@end()
@once 'end', fn
output: (fn) ->
# more difficult to support this. It would involve concatenating all the buffers together
throw new Error '
PDFDocument#output is deprecated, and has been removed from PDFKit.
Please pipe the document into a Node stream.
'
end: ->
@page.end()
@_info = @ref()
for key, val of @info
if typeof val is 'string'
val = PDFObject.s val, true
@_info.data[key] = val
@_info.end()
for name, font of @_fontFamilies
font.embed()
@_root.end()
@_root.data.Pages.end()
if @_waiting is 0
@_finalize()
else
@_ended = true
_finalize: (fn) ->
# generate xref
xRefOffset = @_offset
@_write "xref"
@_write "0 #{@_objects.length + 1}"
@_write "0000000000 65535 f "
for ref in @_objects
offset = ('0000000000' + ref.offset).slice(-10)
@_write offset + ' 00000 n '
# trailer
@_write 'trailer'
@_write PDFObject.convert
Size: @_objects.length
Root: @_root
Info: @_info
@_write 'startxref'
@_write "#{xRefOffset}"
@_write '%%EOF'
# end the stream
@push null
toString: ->
"[object PDFDocument]"
module.exports = PDFDocument