mirror of
https://github.com/foliojs/pdfkit.git
synced 2025-12-08 20:15:54 +00:00
Switch to using Node's built in zlib bindings. Fixes #48.
This commit is contained in:
parent
a39c7d0995
commit
b9da8ebedd
@ -15,7 +15,7 @@ class PDFDocument
|
||||
@version = 1.3
|
||||
|
||||
# Whether streams should be compressed
|
||||
@compress = true
|
||||
@compress = yes
|
||||
|
||||
# The PDF object store
|
||||
@store = new PDFObjectStore
|
||||
@ -80,30 +80,34 @@ class PDFDocument
|
||||
@page.content.add str
|
||||
return this # make chaining possible
|
||||
|
||||
write: (filename, callback) ->
|
||||
fs.writeFile filename, @output(), 'binary', callback
|
||||
write: (filename, fn) ->
|
||||
@output (out) ->
|
||||
fs.writeFile filename, out, 'binary', fn
|
||||
|
||||
output: ->
|
||||
out = []
|
||||
@finalize()
|
||||
@generateHeader out
|
||||
@generateBody out
|
||||
@generateXRef out
|
||||
@generateTrailer out
|
||||
return out.join('\n')
|
||||
output: (fn) ->
|
||||
@finalize =>
|
||||
out = []
|
||||
@generateHeader out
|
||||
@generateBody out, =>
|
||||
@generateXRef out
|
||||
@generateTrailer out
|
||||
fn out.join('\n')
|
||||
|
||||
finalize: ->
|
||||
finalize: (fn) ->
|
||||
# convert strings in the info dictionary to literals
|
||||
for key, val of @info when typeof val is 'string'
|
||||
@info[key] = PDFObject.s val
|
||||
|
||||
# embed the subsetted fonts
|
||||
for family, font of @_fontFamilies
|
||||
font.embed()
|
||||
|
||||
# finalize each page
|
||||
for page in @pages
|
||||
page.finalize()
|
||||
@embedFonts =>
|
||||
# embed the images
|
||||
@embedImages =>
|
||||
done = 0
|
||||
cb = => fn() if ++done is @pages.length
|
||||
|
||||
# finalize each page
|
||||
for page in @pages
|
||||
page.finalize(cb)
|
||||
|
||||
generateHeader: (out) ->
|
||||
# PDF version
|
||||
@ -113,17 +117,20 @@ class PDFDocument
|
||||
out.push "%\xFF\xFF\xFF\xFF\n"
|
||||
return out
|
||||
|
||||
generateBody: (out) ->
|
||||
generateBody: (out, fn) ->
|
||||
offset = out.join('\n').length
|
||||
|
||||
for id, ref of @store.objects
|
||||
object = ref.object()
|
||||
ref.offset = offset
|
||||
out.push object
|
||||
|
||||
offset += object.length + 1
|
||||
|
||||
@xref_offset = offset
|
||||
refs = (ref for id, ref of @store.objects)
|
||||
do proceed = =>
|
||||
if ref = refs.shift()
|
||||
ref.object @compress, (object) ->
|
||||
ref.offset = offset
|
||||
out.push object
|
||||
offset += object.length + 1
|
||||
proceed()
|
||||
else
|
||||
@xref_offset = offset
|
||||
fn()
|
||||
|
||||
generateXRef: (out) ->
|
||||
len = @store.length + 1
|
||||
|
||||
@ -6,7 +6,7 @@ By Devon Govett
|
||||
TTFFont = require './font/ttf'
|
||||
AFMFont = require './font/afm'
|
||||
Subset = require './font/subset'
|
||||
zlib = require 'flate'
|
||||
zlib = require 'zlib'
|
||||
|
||||
class PDFFont
|
||||
constructor: (@document, @filename, @family, @id) ->
|
||||
@ -29,8 +29,9 @@ class PDFFont
|
||||
use: (characters) ->
|
||||
@subset?.use characters
|
||||
|
||||
embed: ->
|
||||
@embedTTF() unless @isAFM
|
||||
embed: (fn) ->
|
||||
return fn() if @isAFM
|
||||
@embedTTF fn
|
||||
|
||||
encode: (text) ->
|
||||
@subset?.encodeText(text) or text
|
||||
@ -78,52 +79,54 @@ class PDFFont
|
||||
Type: 'Font'
|
||||
Subtype: 'TrueType'
|
||||
|
||||
embedTTF: ->
|
||||
embedTTF: (fn) ->
|
||||
data = @subset.encode()
|
||||
compressedData = zlib.deflate(data)
|
||||
zlib.deflate data, (err, compressedData) =>
|
||||
throw err if err
|
||||
|
||||
@fontfile = @document.ref
|
||||
Length: compressedData.length
|
||||
Length1: data.length
|
||||
Filter: 'FlateDecode'
|
||||
@fontfile = @document.ref
|
||||
Length: compressedData.length
|
||||
Length1: data.length
|
||||
Filter: 'FlateDecode'
|
||||
|
||||
@fontfile.add compressedData
|
||||
@fontfile.add compressedData
|
||||
|
||||
@descriptor = @document.ref
|
||||
Type: 'FontDescriptor'
|
||||
FontName: @subset.postscriptName
|
||||
FontFile2: @fontfile
|
||||
FontBBox: @bbox
|
||||
Flags: @flags
|
||||
StemV: @stemV
|
||||
ItalicAngle: @italicAngle
|
||||
Ascent: @ascender
|
||||
Descent: @decender
|
||||
CapHeight: @capHeight
|
||||
XHeight: @xHeight
|
||||
@descriptor = @document.ref
|
||||
Type: 'FontDescriptor'
|
||||
FontName: @subset.postscriptName
|
||||
FontFile2: @fontfile
|
||||
FontBBox: @bbox
|
||||
Flags: @flags
|
||||
StemV: @stemV
|
||||
ItalicAngle: @italicAngle
|
||||
Ascent: @ascender
|
||||
Descent: @decender
|
||||
CapHeight: @capHeight
|
||||
XHeight: @xHeight
|
||||
|
||||
firstChar = +Object.keys(@subset.cmap)[0]
|
||||
charWidths = for code, glyph of @subset.cmap
|
||||
Math.round @ttf.hmtx.forGlyph(glyph).advance * @scaleFactor
|
||||
firstChar = +Object.keys(@subset.cmap)[0]
|
||||
charWidths = for code, glyph of @subset.cmap
|
||||
Math.round @ttf.hmtx.forGlyph(glyph).advance * @scaleFactor
|
||||
|
||||
|
||||
cmap = @document.ref()
|
||||
cmap.add toUnicodeCmap(@subset.subset)
|
||||
cmap.finalize(true) # compress it
|
||||
cmap = @document.ref()
|
||||
cmap.add toUnicodeCmap(@subset.subset)
|
||||
|
||||
ref =
|
||||
Type: 'Font'
|
||||
BaseFont: @subset.postscriptName
|
||||
Subtype: 'TrueType'
|
||||
FontDescriptor: @descriptor
|
||||
FirstChar: firstChar
|
||||
LastChar: firstChar + charWidths.length - 1
|
||||
Widths: @document.ref charWidths
|
||||
Encoding: 'MacRomanEncoding'
|
||||
ToUnicode: cmap
|
||||
ref =
|
||||
Type: 'Font'
|
||||
BaseFont: @subset.postscriptName
|
||||
Subtype: 'TrueType'
|
||||
FontDescriptor: @descriptor
|
||||
FirstChar: firstChar
|
||||
LastChar: firstChar + charWidths.length - 1
|
||||
Widths: @document.ref charWidths
|
||||
Encoding: 'MacRomanEncoding'
|
||||
ToUnicode: cmap
|
||||
|
||||
for key, val of ref
|
||||
@ref.data[key] = val
|
||||
for key, val of ref
|
||||
@ref.data[key] = val
|
||||
|
||||
cmap.finalize(@document.compress, fn) # compress it
|
||||
|
||||
toUnicodeCmap = (map) ->
|
||||
unicodeMap = '''
|
||||
|
||||
@ -2,11 +2,6 @@ fs = require 'fs'
|
||||
Data = '../data'
|
||||
|
||||
class JPEG
|
||||
@open: (filename) ->
|
||||
contents = fs.readFileSync filename
|
||||
data = new Data(contents)
|
||||
new JPEG(data)
|
||||
|
||||
constructor: (@data) ->
|
||||
len = data.length
|
||||
|
||||
@ -36,7 +31,7 @@ class JPEG
|
||||
|
||||
@imgData = @data
|
||||
|
||||
object: (document) ->
|
||||
object: (document, fn) ->
|
||||
obj = document.ref
|
||||
Type: 'XObject'
|
||||
Subtype: 'Image'
|
||||
@ -54,6 +49,6 @@ class JPEG
|
||||
obj.data['Decode'] = [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0]
|
||||
|
||||
obj.add @data.data
|
||||
return obj
|
||||
fn obj
|
||||
|
||||
module.exports = JPEG
|
||||
@ -1,13 +1,8 @@
|
||||
fs = require 'fs'
|
||||
Data = '../data'
|
||||
zlib = require 'flate'
|
||||
zlib = require 'zlib'
|
||||
|
||||
class PNG
|
||||
@open: (filename) ->
|
||||
contents = fs.readFileSync filename
|
||||
data = new Data(contents)
|
||||
new PNG(data)
|
||||
|
||||
constructor: (@data) ->
|
||||
data.pos = 8 # Skip the default header
|
||||
|
||||
@ -84,7 +79,22 @@ class PNG
|
||||
|
||||
return
|
||||
|
||||
object: (document) ->
|
||||
object: (document, fn) ->
|
||||
# get the async stuff out of the way first
|
||||
if not @alphaChannel
|
||||
if @transparency.indexed
|
||||
# Create a transparency SMask for the image based on the data
|
||||
# in the PLTE and tRNS sections. See below for details on SMasks.
|
||||
@loadIndexedAlphaChannel => @object document, fn
|
||||
return
|
||||
|
||||
else if @hasAlphaChannel
|
||||
# For PNG color types 4 and 6, the transparency data is stored as a alpha
|
||||
# channel mixed in with the main image data. Separate this data out into an
|
||||
# SMask object and store it separately in the PDF.
|
||||
@splitAlphaChannel => @object document, fn
|
||||
return
|
||||
|
||||
obj = document.ref
|
||||
Type: 'XObject'
|
||||
Subtype: 'Image'
|
||||
@ -130,18 +140,6 @@ class PNG
|
||||
mask.push x, x
|
||||
|
||||
obj.data['Mask'] = mask
|
||||
|
||||
else if @transparency.indexed
|
||||
# Create a transparency SMask for the image based on the data
|
||||
# in the PLTE and tRNS sections. See below for details on SMasks.
|
||||
@loadIndexedAlphaChannel()
|
||||
|
||||
# For PNG color types 4 and 6, the transparency data is stored as a alpha
|
||||
# channel mixed in with the main image data. Separate this data out into an
|
||||
# SMask object and store it separately in the PDF.
|
||||
if @hasAlphaChannel
|
||||
@splitAlphaChannel()
|
||||
obj.data['Length'] = @imgData.length
|
||||
|
||||
if @alphaChannel
|
||||
sMask = document.ref
|
||||
@ -160,105 +158,112 @@ class PNG
|
||||
|
||||
# add the actual image data
|
||||
obj.add @imgData
|
||||
return obj
|
||||
fn obj
|
||||
|
||||
decodePixels: ->
|
||||
data = zlib.inflate @imgData
|
||||
pixelBytes = @pixelBitlength / 8
|
||||
scanlineLength = pixelBytes * @width
|
||||
|
||||
row = 0
|
||||
pixels = []
|
||||
length = data.length
|
||||
pos = 0
|
||||
|
||||
while pos < length
|
||||
filter = data[pos++]
|
||||
i = 0
|
||||
rowData = []
|
||||
|
||||
switch filter
|
||||
when 0 # None
|
||||
while i < scanlineLength
|
||||
rowData[i++] = data[pos++]
|
||||
|
||||
when 1 # Sub
|
||||
while i < scanlineLength
|
||||
byte = data[pos++]
|
||||
left = if i < pixelBytes then 0 else rowData[i - pixelBytes]
|
||||
rowData[i++] = (byte + left) % 256
|
||||
|
||||
when 2 # Up
|
||||
while i < scanlineLength
|
||||
byte = data[pos++]
|
||||
col = (i - (i % pixelBytes)) / pixelBytes
|
||||
upper = if row is 0 then 0 else pixels[row - 1][col][i % pixelBytes]
|
||||
rowData[i++] = (upper + byte) % 256
|
||||
|
||||
when 3 # Average
|
||||
while i < scanlineLength
|
||||
byte = data[pos++]
|
||||
col = (i - (i % pixelBytes)) / pixelBytes
|
||||
left = if i < pixelBytes then 0 else rowData[i - pixelBytes]
|
||||
upper = if row is 0 then 0 else pixels[row - 1][col][i % pixelBytes]
|
||||
rowData[i++] = (byte + Math.floor((left + upper) / 2)) % 256
|
||||
|
||||
when 4 # Paeth
|
||||
while i < scanlineLength
|
||||
byte = data[pos++]
|
||||
col = (i - (i % pixelBytes)) / pixelBytes
|
||||
left = if i < pixelBytes then 0 else rowData[i - pixelBytes]
|
||||
|
||||
if row is 0
|
||||
upper = upperLeft = 0
|
||||
else
|
||||
upper = pixels[row - 1][col][i % pixelBytes]
|
||||
upperLeft = if col is 0 then 0 else pixels[row - 1][col - 1][i % pixelBytes]
|
||||
|
||||
p = left + upper - upperLeft
|
||||
pa = Math.abs(p - left)
|
||||
pb = Math.abs(p - upper)
|
||||
pc = Math.abs(p - upperLeft)
|
||||
|
||||
if pa <= pb and pa <= pc
|
||||
paeth = left
|
||||
else if pb <= pc
|
||||
paeth = upper
|
||||
else
|
||||
paeth = upperLeft
|
||||
|
||||
rowData[i++] = (byte + paeth) % 256
|
||||
|
||||
else
|
||||
throw new Error "Invalid filter algorithm: " + filter
|
||||
|
||||
s = []
|
||||
for i in [0...rowData.length] by pixelBytes
|
||||
s.push rowData.slice(i, i + pixelBytes)
|
||||
|
||||
pixels.push(s)
|
||||
row += 1
|
||||
decodePixels: (fn) ->
|
||||
zlib.inflate @imgData, (err, data) =>
|
||||
throw err if err
|
||||
|
||||
return pixels
|
||||
|
||||
splitAlphaChannel: ->
|
||||
pixels = @decodePixels()
|
||||
pixelBytes = @pixelBitlength / 8
|
||||
scanlineLength = pixelBytes * @width
|
||||
|
||||
colorByteSize = @colors * @bits / 8
|
||||
alphaByteSize = 1
|
||||
row = 0
|
||||
pixels = []
|
||||
length = data.length
|
||||
pos = 0
|
||||
|
||||
pixelCount = @width * @height
|
||||
imgData = new Buffer(pixelCount * colorByteSize)
|
||||
alphaChannel = new Buffer(pixelCount)
|
||||
while pos < length
|
||||
filter = data[pos++]
|
||||
i = 0
|
||||
rowData = []
|
||||
|
||||
switch filter
|
||||
when 0 # None
|
||||
while i < scanlineLength
|
||||
rowData[i++] = data[pos++]
|
||||
|
||||
when 1 # Sub
|
||||
while i < scanlineLength
|
||||
byte = data[pos++]
|
||||
left = if i < pixelBytes then 0 else rowData[i - pixelBytes]
|
||||
rowData[i++] = (byte + left) % 256
|
||||
|
||||
when 2 # Up
|
||||
while i < scanlineLength
|
||||
byte = data[pos++]
|
||||
col = (i - (i % pixelBytes)) / pixelBytes
|
||||
upper = if row is 0 then 0 else pixels[row - 1][col][i % pixelBytes]
|
||||
rowData[i++] = (upper + byte) % 256
|
||||
|
||||
when 3 # Average
|
||||
while i < scanlineLength
|
||||
byte = data[pos++]
|
||||
col = (i - (i % pixelBytes)) / pixelBytes
|
||||
left = if i < pixelBytes then 0 else rowData[i - pixelBytes]
|
||||
upper = if row is 0 then 0 else pixels[row - 1][col][i % pixelBytes]
|
||||
rowData[i++] = (byte + Math.floor((left + upper) / 2)) % 256
|
||||
|
||||
when 4 # Paeth
|
||||
while i < scanlineLength
|
||||
byte = data[pos++]
|
||||
col = (i - (i % pixelBytes)) / pixelBytes
|
||||
left = if i < pixelBytes then 0 else rowData[i - pixelBytes]
|
||||
|
||||
if row is 0
|
||||
upper = upperLeft = 0
|
||||
else
|
||||
upper = pixels[row - 1][col][i % pixelBytes]
|
||||
upperLeft = if col is 0 then 0 else pixels[row - 1][col - 1][i % pixelBytes]
|
||||
|
||||
p = left + upper - upperLeft
|
||||
pa = Math.abs(p - left)
|
||||
pb = Math.abs(p - upper)
|
||||
pc = Math.abs(p - upperLeft)
|
||||
|
||||
if pa <= pb and pa <= pc
|
||||
paeth = left
|
||||
else if pb <= pc
|
||||
paeth = upper
|
||||
else
|
||||
paeth = upperLeft
|
||||
|
||||
rowData[i++] = (byte + paeth) % 256
|
||||
|
||||
else
|
||||
throw new Error "Invalid filter algorithm: " + filter
|
||||
|
||||
s = []
|
||||
for i in [0...rowData.length] by pixelBytes
|
||||
s.push rowData.slice(i, i + pixelBytes)
|
||||
|
||||
pixels.push(s)
|
||||
row += 1
|
||||
|
||||
fn pixels
|
||||
|
||||
p = a = 0
|
||||
for row in pixels
|
||||
for pixel in row
|
||||
imgData[p++] = pixel[i] for i in [0...colorByteSize]
|
||||
alphaChannel[a++] = pixel[colorByteSize]
|
||||
splitAlphaChannel: (fn) ->
|
||||
@decodePixels (pixels) =>
|
||||
colorByteSize = @colors * @bits / 8
|
||||
alphaByteSize = 1
|
||||
|
||||
pixelCount = @width * @height
|
||||
imgData = new Buffer(pixelCount * colorByteSize)
|
||||
alphaChannel = new Buffer(pixelCount)
|
||||
|
||||
@imgData = zlib.deflate imgData
|
||||
@alphaChannel = zlib.deflate alphaChannel
|
||||
p = a = 0
|
||||
for row in pixels
|
||||
for pixel in row
|
||||
imgData[p++] = pixel[i] for i in [0...colorByteSize]
|
||||
alphaChannel[a++] = pixel[colorByteSize]
|
||||
|
||||
done = 0
|
||||
zlib.deflate imgData, (err, @imgData) =>
|
||||
throw err if err
|
||||
fn() if ++done is 2
|
||||
|
||||
zlib.deflate alphaChannel, (err, @alphaChannel) =>
|
||||
throw err if err
|
||||
fn() if ++done is 2
|
||||
|
||||
decodePalette: ->
|
||||
palette = @palette
|
||||
@ -273,19 +278,20 @@ class PNG
|
||||
|
||||
return decodingMap
|
||||
|
||||
loadIndexedAlphaChannel: ->
|
||||
loadIndexedAlphaChannel: (fn) ->
|
||||
palette = @decodePalette()
|
||||
pixels = @decodePixels()
|
||||
@decodePixels (pixels) =>
|
||||
pixelCount = @width * @height
|
||||
alphaChannel = new Buffer(pixelCount)
|
||||
|
||||
pixelCount = @width * @height
|
||||
alphaChannel = new Buffer(pixelCount)
|
||||
|
||||
i = 0
|
||||
for row in pixels
|
||||
for pixel in row
|
||||
pixel = pixel[0]
|
||||
alphaChannel[i++] = palette[pixel][3]
|
||||
i = 0
|
||||
for row in pixels
|
||||
for pixel in row
|
||||
pixel = pixel[0]
|
||||
alphaChannel[i++] = palette[pixel][3]
|
||||
|
||||
@alphaChannel = zlib.deflate alphaChannel
|
||||
zlib.deflate alphaChannel, (err, @alphaChannel) =>
|
||||
throw err if err
|
||||
fn()
|
||||
|
||||
module.exports = PNG
|
||||
@ -48,4 +48,10 @@ module.exports =
|
||||
registerFont: (name, path, family) ->
|
||||
@_registeredFonts[name] =
|
||||
filename: path
|
||||
family: family
|
||||
family: family
|
||||
|
||||
embedFonts: (fn) ->
|
||||
fonts = (font for family, font of @_fontFamilies)
|
||||
do proceed = =>
|
||||
return fn() if fonts.length is 0
|
||||
fonts.shift().embed(proceed)
|
||||
@ -14,13 +14,12 @@ module.exports =
|
||||
y = y ? options.y ? @y
|
||||
|
||||
if @_imageRegistry[src]
|
||||
[image, obj, label] = @_imageRegistry[src]
|
||||
[image, @page, label] = @_imageRegistry[src]
|
||||
|
||||
else
|
||||
image = PDFImage.open(src)
|
||||
obj = image.object(this)
|
||||
label = "I" + (++@_imageCount)
|
||||
@_imageRegistry[src] = [image, obj, label]
|
||||
@_imageRegistry[src] = [image, @page, label]
|
||||
|
||||
w = options.width or image.width
|
||||
h = options.height or image.height
|
||||
@ -52,13 +51,22 @@ module.exports =
|
||||
|
||||
# Set the current y position to below the image if it is in the document flow
|
||||
@y += h if @y is y
|
||||
|
||||
y = @page.height - y - h
|
||||
@page.xobjects[label] ?= obj
|
||||
|
||||
@save()
|
||||
@addContent "#{w} 0 0 #{h} #{x} #{y} cm"
|
||||
@addContent "/#{label} Do"
|
||||
@restore()
|
||||
|
||||
return this
|
||||
return this
|
||||
|
||||
embedImages: (fn) ->
|
||||
images = (item for src, item of @_imageRegistry)
|
||||
do proceed = =>
|
||||
if images.length
|
||||
[image, page, label] = images.shift()
|
||||
image.object this, (obj) ->
|
||||
page.xobjects[label] ?= obj
|
||||
proceed()
|
||||
else
|
||||
fn()
|
||||
@ -3,10 +3,10 @@ PDFObject - converts JavaScript types into their corrisponding PDF types.
|
||||
By Devon Govett
|
||||
###
|
||||
|
||||
pad = (str, length) ->
|
||||
(Array(length + 1).join('0') + str).slice(-length)
|
||||
|
||||
class PDFObject
|
||||
pad = (str, length) ->
|
||||
(Array(length + 1).join('0') + str).slice(-length)
|
||||
|
||||
@convert: (object) ->
|
||||
if Array.isArray object
|
||||
items = (PDFObject.convert e for e in object).join(' ')
|
||||
|
||||
@ -5,8 +5,8 @@ By Devon Govett
|
||||
|
||||
class PDFPage
|
||||
constructor: (@document, options = {}) ->
|
||||
@size = options.size or "letter"
|
||||
@layout = options.layout or "portrait"
|
||||
@size = options.size or 'letter'
|
||||
@layout = options.layout or 'portrait'
|
||||
|
||||
# if margin was passed as a single number
|
||||
if typeof options.margin is 'number'
|
||||
@ -55,8 +55,8 @@ class PDFPage
|
||||
maxY: ->
|
||||
@height - @margins.bottom
|
||||
|
||||
finalize: ->
|
||||
@content.finalize(@document.compress)
|
||||
finalize: (fn) ->
|
||||
@content.finalize(@document.compress, fn)
|
||||
|
||||
DEFAULT_MARGINS =
|
||||
top: 72
|
||||
|
||||
@ -3,7 +3,7 @@ PDFReference - represents a reference to another object in the PDF object heirar
|
||||
By Devon Govett
|
||||
###
|
||||
|
||||
zlib = require 'flate'
|
||||
zlib = require 'zlib'
|
||||
|
||||
class PDFReference
|
||||
constructor: (@id, @data = {}) ->
|
||||
@ -11,8 +11,10 @@ class PDFReference
|
||||
@stream = null
|
||||
@finalizedStream = null
|
||||
|
||||
object: ->
|
||||
@finalize() if not @finalizedStream
|
||||
object: (compress, fn) ->
|
||||
unless @finalizedStream?
|
||||
return @finalize compress, => @object compress, fn
|
||||
|
||||
out = ["#{@id} #{@gen} obj"]
|
||||
out.push PDFObject.convert(@data)
|
||||
|
||||
@ -22,30 +24,33 @@ class PDFReference
|
||||
out.push "endstream"
|
||||
|
||||
out.push "endobj"
|
||||
return out.join '\n'
|
||||
fn out.join '\n'
|
||||
|
||||
add: (s) ->
|
||||
@stream ?= []
|
||||
@stream.push if Buffer.isBuffer(s) then s.toString('binary') else s
|
||||
|
||||
finalize: (compress = false) ->
|
||||
finalize: (compress = false, fn) ->
|
||||
# cache the finalized stream
|
||||
if @stream
|
||||
data = @stream.join '\n'
|
||||
if compress
|
||||
if compress and not @data.Filter
|
||||
# create a byte array instead of passing a string to the Buffer
|
||||
# fixes a weird unicode bug.
|
||||
data = new Buffer(data.charCodeAt(i) for i in [0...data.length])
|
||||
compressedData = zlib.deflate(data)
|
||||
@finalizedStream = compressedData.toString 'binary'
|
||||
|
||||
@data.Filter = 'FlateDecode'
|
||||
zlib.deflate data, (err, compressedData) =>
|
||||
throw err if err
|
||||
@finalizedStream = compressedData.toString 'binary'
|
||||
@data.Filter = 'FlateDecode'
|
||||
@data.Length = @finalizedStream.length
|
||||
fn()
|
||||
else
|
||||
@finalizedStream = data
|
||||
|
||||
@data.Length ?= @finalizedStream.length
|
||||
@data.Length = @finalizedStream.length
|
||||
fn()
|
||||
else
|
||||
@finalizedStream = ''
|
||||
fn()
|
||||
|
||||
toString: ->
|
||||
"#{@id} #{@gen} R"
|
||||
|
||||
@ -9,14 +9,13 @@ class PDFObjectStore
|
||||
constructor: ->
|
||||
@objects = {}
|
||||
@length = 0
|
||||
|
||||
|
||||
@root = @ref
|
||||
Type: 'Catalog'
|
||||
|
||||
@root.data['Pages'] = @ref
|
||||
Type: 'Pages'
|
||||
Count: 0
|
||||
Kids: []
|
||||
Pages: @ref
|
||||
Type: 'Pages'
|
||||
Count: 0
|
||||
Kids: []
|
||||
|
||||
@pages = @root.data['Pages']
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user