From 1b4968d10796a5a659af822ddea8972e8388ef59 Mon Sep 17 00:00:00 2001 From: Matthew Butterick Date: Sat, 13 May 2017 18:39:34 -0700 Subject: [PATCH] test1 successful: draw triangle & circle --- pitfall/pdfkit/lib/data.js | 192 +++++++++++++++ pitfall/pdfkit/lib/document.js | 247 +++++++++++++++++++ pitfall/pdfkit/lib/font.js | 75 ++++++ pitfall/pdfkit/lib/image.js | 53 ++++ pitfall/pdfkit/lib/line_wrapper.js | 252 +++++++++++++++++++ pitfall/pdfkit/lib/mixins/vector.js | 265 ++++++++++++++++++++ pitfall/pdfkit/lib/object.js | 122 ++++++++++ pitfall/pdfkit/lib/page.js | 170 +++++++++++++ pitfall/pdfkit/lib/path.js | 366 ++++++++++++++++++++++++++++ pitfall/pdfkit/lib/reference.js | 117 +++++++++ pitfall/pitfall/kit/color.rkt | 2 +- pitfall/pitfall/kit/document.rkt | 2 +- pitfall/pitfall/kit/helper.rkt | 17 +- pitfall/pitfall/kit/reference.rkt | 2 +- pitfall/pitfall/kit/vector.rkt | 101 ++++++-- pitfall/pktest/test1.coffee | 6 - pitfall/pktest/test1.pdf | 25 +- pitfall/pktest/test1.rkt | 14 +- pitfall/pktest/test1rkt.pdf | 81 ++++++ 19 files changed, 2056 insertions(+), 53 deletions(-) create mode 100644 pitfall/pdfkit/lib/data.js create mode 100644 pitfall/pdfkit/lib/document.js create mode 100644 pitfall/pdfkit/lib/font.js create mode 100644 pitfall/pdfkit/lib/image.js create mode 100644 pitfall/pdfkit/lib/line_wrapper.js create mode 100644 pitfall/pdfkit/lib/mixins/vector.js create mode 100644 pitfall/pdfkit/lib/object.js create mode 100644 pitfall/pdfkit/lib/page.js create mode 100644 pitfall/pdfkit/lib/path.js create mode 100644 pitfall/pdfkit/lib/reference.js diff --git a/pitfall/pdfkit/lib/data.js b/pitfall/pdfkit/lib/data.js new file mode 100644 index 00000000..b02dbbdd --- /dev/null +++ b/pitfall/pdfkit/lib/data.js @@ -0,0 +1,192 @@ +// Generated by CoffeeScript 1.12.5 +(function() { + var Data; + + Data = (function() { + function Data(data) { + this.data = data != null ? data : []; + this.pos = 0; + this.length = this.data.length; + } + + Data.prototype.readByte = function() { + return this.data[this.pos++]; + }; + + Data.prototype.writeByte = function(byte) { + return this.data[this.pos++] = byte; + }; + + Data.prototype.byteAt = function(index) { + return this.data[index]; + }; + + Data.prototype.readBool = function() { + return !!this.readByte(); + }; + + Data.prototype.writeBool = function(val) { + return this.writeByte(val ? 1 : 0); + }; + + Data.prototype.readUInt32 = function() { + var b1, b2, b3, b4; + b1 = this.readByte() * 0x1000000; + b2 = this.readByte() << 16; + b3 = this.readByte() << 8; + b4 = this.readByte(); + return b1 + b2 + b3 + b4; + }; + + Data.prototype.writeUInt32 = function(val) { + this.writeByte((val >>> 24) & 0xff); + this.writeByte((val >> 16) & 0xff); + this.writeByte((val >> 8) & 0xff); + return this.writeByte(val & 0xff); + }; + + Data.prototype.readInt32 = function() { + var int; + int = this.readUInt32(); + if (int >= 0x80000000) { + return int - 0x100000000; + } else { + return int; + } + }; + + Data.prototype.writeInt32 = function(val) { + if (val < 0) { + val += 0x100000000; + } + return this.writeUInt32(val); + }; + + Data.prototype.readUInt16 = function() { + var b1, b2; + b1 = this.readByte() << 8; + b2 = this.readByte(); + return b1 | b2; + }; + + Data.prototype.writeUInt16 = function(val) { + this.writeByte((val >> 8) & 0xff); + return this.writeByte(val & 0xff); + }; + + Data.prototype.readInt16 = function() { + var int; + int = this.readUInt16(); + if (int >= 0x8000) { + return int - 0x10000; + } else { + return int; + } + }; + + Data.prototype.writeInt16 = function(val) { + if (val < 0) { + val += 0x10000; + } + return this.writeUInt16(val); + }; + + Data.prototype.readString = function(length) { + var i, j, ref, ret; + ret = []; + for (i = j = 0, ref = length; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) { + ret[i] = String.fromCharCode(this.readByte()); + } + return ret.join(''); + }; + + Data.prototype.writeString = function(val) { + var i, j, ref, results; + results = []; + for (i = j = 0, ref = val.length; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) { + results.push(this.writeByte(val.charCodeAt(i))); + } + return results; + }; + + Data.prototype.stringAt = function(pos, length) { + this.pos = pos; + return this.readString(length); + }; + + Data.prototype.readShort = function() { + return this.readInt16(); + }; + + Data.prototype.writeShort = function(val) { + return this.writeInt16(val); + }; + + Data.prototype.readLongLong = function() { + var b1, b2, b3, b4, b5, b6, b7, b8; + b1 = this.readByte(); + b2 = this.readByte(); + b3 = this.readByte(); + b4 = this.readByte(); + b5 = this.readByte(); + b6 = this.readByte(); + b7 = this.readByte(); + b8 = this.readByte(); + if (b1 & 0x80) { + return ((b1 ^ 0xff) * 0x100000000000000 + (b2 ^ 0xff) * 0x1000000000000 + (b3 ^ 0xff) * 0x10000000000 + (b4 ^ 0xff) * 0x100000000 + (b5 ^ 0xff) * 0x1000000 + (b6 ^ 0xff) * 0x10000 + (b7 ^ 0xff) * 0x100 + (b8 ^ 0xff) + 1) * -1; + } + return b1 * 0x100000000000000 + b2 * 0x1000000000000 + b3 * 0x10000000000 + b4 * 0x100000000 + b5 * 0x1000000 + b6 * 0x10000 + b7 * 0x100 + b8; + }; + + Data.prototype.writeLongLong = function(val) { + var high, low; + high = Math.floor(val / 0x100000000); + low = val & 0xffffffff; + this.writeByte((high >> 24) & 0xff); + this.writeByte((high >> 16) & 0xff); + this.writeByte((high >> 8) & 0xff); + this.writeByte(high & 0xff); + this.writeByte((low >> 24) & 0xff); + this.writeByte((low >> 16) & 0xff); + this.writeByte((low >> 8) & 0xff); + return this.writeByte(low & 0xff); + }; + + Data.prototype.readInt = function() { + return this.readInt32(); + }; + + Data.prototype.writeInt = function(val) { + return this.writeInt32(val); + }; + + Data.prototype.slice = function(start, end) { + return this.data.slice(start, end); + }; + + Data.prototype.read = function(bytes) { + var buf, i, j, ref; + buf = []; + for (i = j = 0, ref = bytes; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) { + buf.push(this.readByte()); + } + return buf; + }; + + Data.prototype.write = function(bytes) { + var byte, j, len, results; + results = []; + for (j = 0, len = bytes.length; j < len; j++) { + byte = bytes[j]; + results.push(this.writeByte(byte)); + } + return results; + }; + + return Data; + + })(); + + module.exports = Data; + +}).call(this); diff --git a/pitfall/pdfkit/lib/document.js b/pitfall/pdfkit/lib/document.js new file mode 100644 index 00000000..691a56f7 --- /dev/null +++ b/pitfall/pdfkit/lib/document.js @@ -0,0 +1,247 @@ +// Generated by CoffeeScript 1.12.5 + +/* +PDFDocument - represents an entire PDF document +By Devon Govett + */ + +(function() { + var PDFDocument, PDFObject, PDFPage, PDFReference, fs, stream, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + + stream = require('stream'); + + fs = require('fs'); + + PDFObject = require('./object'); + + PDFReference = require('./reference'); + + PDFPage = require('./page'); + + PDFDocument = (function(superClass) { + var mixin; + + extend(PDFDocument, superClass); + + function PDFDocument(options1) { + var key, ref1, ref2, val; + this.options = options1 != null ? options1 : {}; + PDFDocument.__super__.constructor.apply(this, arguments); + this.version = 1.3; + this.compress = (ref1 = this.options.compress) != null ? ref1 : true; + this._pageBuffer = []; + this._pageBufferStart = 0; + this._offsets = []; + this._waiting = 0; + this._ended = false; + this._offset = 0; + this._root = this.ref({ + Type: 'Catalog', + Pages: this.ref({ + Type: 'Pages', + Count: 0, + Kids: [] + }) + }); + this.page = null; + this.initColor(); + this.initVector(); + this.initFonts(); + this.initText(); + this.initImages(); + this.info = { + Producer: 'PDFKit', + Creator: 'PDFKit', + CreationDate: new Date() + }; + if (this.options.info) { + ref2 = this.options.info; + for (key in ref2) { + val = ref2[key]; + this.info[key] = val; + } + } + this._write("%PDF-" + this.version); + this._write("%\xFF\xFF\xFF\xFF"); + if (this.options.autoFirstPage !== false) { + this.addPage(); + } + } + + mixin = function(methods) { + var method, name, results; + results = []; + for (name in methods) { + method = methods[name]; + results.push(PDFDocument.prototype[name] = method); + } + return results; + }; + + mixin(require('./mixins/color')); + + mixin(require('./mixins/vector')); + + mixin(require('./mixins/fonts')); + + mixin(require('./mixins/text')); + + mixin(require('./mixins/images')); + + mixin(require('./mixins/annotations')); + + PDFDocument.prototype.addPage = function(options) { + var pages; + if (options == null) { + options = this.options; + } + if (!this.options.bufferPages) { + this.flushPages(); + } + this.page = new PDFPage(this, options); + this._pageBuffer.push(this.page); + pages = this._root.data.Pages.data; + pages.Kids.push(this.page.dictionary); + pages.Count++; + this.x = this.page.margins.left; + this.y = this.page.margins.top; + this._ctm = [1, 0, 0, 1, 0, 0]; + this.transform(1, 0, 0, -1, 0, this.page.height); + this.emit('pageAdded'); + return this; + }; + + PDFDocument.prototype.bufferedPageRange = function() { + return { + start: this._pageBufferStart, + count: this._pageBuffer.length + }; + }; + + PDFDocument.prototype.switchToPage = function(n) { + var page; + if (!(page = this._pageBuffer[n - this._pageBufferStart])) { + throw new Error("switchToPage(" + n + ") out of bounds, current buffer covers pages " + this._pageBufferStart + " to " + (this._pageBufferStart + this._pageBuffer.length - 1)); + } + return this.page = page; + }; + + PDFDocument.prototype.flushPages = function() { + var i, len, page, pages; + pages = this._pageBuffer; + this._pageBuffer = []; + this._pageBufferStart += pages.length; + for (i = 0, len = pages.length; i < len; i++) { + page = pages[i]; + page.end(); + } + }; + + PDFDocument.prototype.ref = function(data) { + var ref; + ref = new PDFReference(this, this._offsets.length + 1, data); + this._offsets.push(null); + this._waiting++; + return ref; + }; + + PDFDocument.prototype._read = function() {}; + + PDFDocument.prototype._write = function(data) { + if (!Buffer.isBuffer(data)) { + data = new Buffer(data + '\n', 'binary'); + } + this.push(data); + return this._offset += data.length; + }; + + PDFDocument.prototype.addContent = function(data) { + this.page.write(data); + return this; + }; + + PDFDocument.prototype._refEnd = function(ref) { + this._offsets[ref.id - 1] = ref.offset; + if (--this._waiting === 0 && this._ended) { + this._finalize(); + return this._ended = false; + } + }; + + PDFDocument.prototype.write = function(filename, fn) { + var err; + 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); + this.pipe(fs.createWriteStream(filename)); + this.end(); + return this.once('end', fn); + }; + + PDFDocument.prototype.output = function(fn) { + throw new Error('PDFDocument#output is deprecated, and has been removed from PDFKit. Please pipe the document into a Node stream.'); + }; + + PDFDocument.prototype.end = function() { + var font, key, name, ref1, ref2, val; + this.flushPages(); + this._info = this.ref(); + ref1 = this.info; + for (key in ref1) { + val = ref1[key]; + if (typeof val === 'string') { + val = new String(val); + } + this._info.data[key] = val; + } + this._info.end(); + ref2 = this._fontFamilies; + for (name in ref2) { + font = ref2[name]; + font.finalize(); + } + this._root.end(); + this._root.data.Pages.end(); + if (this._waiting === 0) { + return this._finalize(); + } else { + return this._ended = true; + } + }; + + PDFDocument.prototype._finalize = function(fn) { + var i, len, offset, ref1, xRefOffset; + xRefOffset = this._offset; + this._write("xref"); + this._write("0 " + (this._offsets.length + 1)); + this._write("0000000000 65535 f "); + ref1 = this._offsets; + for (i = 0, len = ref1.length; i < len; i++) { + offset = ref1[i]; + offset = ('0000000000' + offset).slice(-10); + this._write(offset + ' 00000 n '); + } + this._write('trailer'); + this._write(PDFObject.convert({ + Size: this._offsets.length + 1, + Root: this._root, + Info: this._info + })); + this._write('startxref'); + this._write("" + xRefOffset); + this._write('%%EOF'); + return this.push(null); + }; + + PDFDocument.prototype.toString = function() { + return "[object PDFDocument]"; + }; + + return PDFDocument; + + })(stream.Readable); + + module.exports = PDFDocument; + +}).call(this); diff --git a/pitfall/pdfkit/lib/font.js b/pitfall/pdfkit/lib/font.js new file mode 100644 index 00000000..ac48b465 --- /dev/null +++ b/pitfall/pdfkit/lib/font.js @@ -0,0 +1,75 @@ +// Generated by CoffeeScript 1.12.5 +(function() { + var EmbeddedFont, PDFFont, StandardFont, fontkit; + + fontkit = require('fontkit'); + + PDFFont = (function() { + PDFFont.open = function(document, src, family, id) { + var font; + if (typeof src === 'string') { + if (StandardFont.isStandardFont(src)) { + return new StandardFont(document, src, id); + } + font = fontkit.openSync(src, family); + } else if (Buffer.isBuffer(src)) { + font = fontkit.create(src, family); + } else if (src instanceof Uint8Array) { + font = fontkit.create(new Buffer(src), family); + } else if (src instanceof ArrayBuffer) { + font = fontkit.create(new Buffer(new Uint8Array(src)), family); + } + if (font == null) { + throw new Error('Not a supported font format or standard PDF font.'); + } + return new EmbeddedFont(document, font, id); + }; + + function PDFFont() { + throw new Error('Cannot construct a PDFFont directly.'); + } + + PDFFont.prototype.encode = function(text) { + throw new Error('Must be implemented by subclasses'); + }; + + PDFFont.prototype.widthOfString = function(text) { + throw new Error('Must be implemented by subclasses'); + }; + + PDFFont.prototype.ref = function() { + return this.dictionary != null ? this.dictionary : this.dictionary = this.document.ref(); + }; + + PDFFont.prototype.finalize = function() { + if (this.embedded || (this.dictionary == null)) { + return; + } + this.embed(); + return this.embedded = true; + }; + + PDFFont.prototype.embed = function() { + throw new Error('Must be implemented by subclasses'); + }; + + PDFFont.prototype.lineHeight = function(size, includeGap) { + var gap; + if (includeGap == null) { + includeGap = false; + } + gap = includeGap ? this.lineGap : 0; + return (this.ascender + gap - this.descender) / 1000 * size; + }; + + return PDFFont; + + })(); + + module.exports = PDFFont; + + StandardFont = require('./font/standard'); + + EmbeddedFont = require('./font/embedded'); + +}).call(this); diff --git a/pitfall/pdfkit/lib/image.js b/pitfall/pdfkit/lib/image.js new file mode 100644 index 00000000..04281434 --- /dev/null +++ b/pitfall/pdfkit/lib/image.js @@ -0,0 +1,53 @@ +// Generated by CoffeeScript 1.12.5 + +/* +PDFImage - embeds images in PDF documents +By Devon Govett + */ + +(function() { + var Data, JPEG, PDFImage, PNG, fs; + + fs = require('fs'); + + Data = require('./data'); + + JPEG = require('./image/jpeg'); + + PNG = require('./image/png'); + + PDFImage = (function() { + function PDFImage() {} + + PDFImage.open = function(src, label) { + var data, match; + if (Buffer.isBuffer(src)) { + data = src; + } else if (src instanceof ArrayBuffer) { + data = new Buffer(new Uint8Array(src)); + } else { + if (match = /^data:.+;base64,(.*)$/.exec(src)) { + data = new Buffer(match[1], 'base64'); + } else { + data = fs.readFileSync(src); + if (!data) { + return; + } + } + } + if (data[0] === 0xff && data[1] === 0xd8) { + return new JPEG(data, label); + } else if (data[0] === 0x89 && data.toString('ascii', 1, 4) === 'PNG') { + return new PNG(data, label); + } else { + throw new Error('Unknown image format.'); + } + }; + + return PDFImage; + + })(); + + module.exports = PDFImage; + +}).call(this); diff --git a/pitfall/pdfkit/lib/line_wrapper.js b/pitfall/pdfkit/lib/line_wrapper.js new file mode 100644 index 00000000..d0209174 --- /dev/null +++ b/pitfall/pdfkit/lib/line_wrapper.js @@ -0,0 +1,252 @@ +// Generated by CoffeeScript 1.12.5 +(function() { + var EventEmitter, LineBreaker, LineWrapper, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + + EventEmitter = require('events').EventEmitter; + + LineBreaker = require('linebreak'); + + LineWrapper = (function(superClass) { + extend(LineWrapper, superClass); + + function LineWrapper(document, options) { + var ref; + this.document = document; + this.indent = options.indent || 0; + this.characterSpacing = options.characterSpacing || 0; + this.wordSpacing = options.wordSpacing === 0; + this.columns = options.columns || 1; + this.columnGap = (ref = options.columnGap) != null ? ref : 18; + this.lineWidth = (options.width - (this.columnGap * (this.columns - 1))) / this.columns; + this.spaceLeft = this.lineWidth; + this.startX = this.document.x; + this.startY = this.document.y; + this.column = 1; + this.ellipsis = options.ellipsis; + this.continuedX = 0; + this.features = options.features; + if (options.height != null) { + this.height = options.height; + this.maxY = this.startY + options.height; + } else { + this.maxY = this.document.page.maxY(); + } + this.on('firstLine', (function(_this) { + return function(options) { + var indent; + indent = _this.continuedX || _this.indent; + _this.document.x += indent; + _this.lineWidth -= indent; + return _this.once('line', function() { + _this.document.x -= indent; + _this.lineWidth += indent; + if (options.continued && !_this.continuedX) { + _this.continuedX = _this.indent; + } + if (!options.continued) { + return _this.continuedX = 0; + } + }); + }; + })(this)); + this.on('lastLine', (function(_this) { + return function(options) { + var align; + align = options.align; + if (align === 'justify') { + options.align = 'left'; + } + _this.lastLine = true; + return _this.once('line', function() { + _this.document.y += options.paragraphGap || 0; + options.align = align; + return _this.lastLine = false; + }); + }; + })(this)); + } + + LineWrapper.prototype.wordWidth = function(word) { + return this.document.widthOfString(word, this) + this.characterSpacing + this.wordSpacing; + }; + + LineWrapper.prototype.eachWord = function(text, fn) { + var bk, breaker, fbk, l, last, lbk, shouldContinue, w, word, wordWidths; + breaker = new LineBreaker(text); + last = null; + wordWidths = Object.create(null); + while (bk = breaker.nextBreak()) { + word = text.slice((last != null ? last.position : void 0) || 0, bk.position); + w = wordWidths[word] != null ? wordWidths[word] : wordWidths[word] = this.wordWidth(word); + if (w > this.lineWidth + this.continuedX) { + lbk = last; + fbk = {}; + while (word.length) { + l = word.length; + while (w > this.spaceLeft) { + w = this.wordWidth(word.slice(0, --l)); + } + fbk.required = l < word.length; + shouldContinue = fn(word.slice(0, l), w, fbk, lbk); + lbk = { + required: false + }; + word = word.slice(l); + w = this.wordWidth(word); + if (shouldContinue === false) { + break; + } + } + } else { + shouldContinue = fn(word, w, bk, last); + } + if (shouldContinue === false) { + break; + } + last = bk; + } + }; + + LineWrapper.prototype.wrap = function(text, options) { + var buffer, emitLine, lc, nextY, textWidth, wc, y; + if (options.indent != null) { + this.indent = options.indent; + } + if (options.characterSpacing != null) { + this.characterSpacing = options.characterSpacing; + } + if (options.wordSpacing != null) { + this.wordSpacing = options.wordSpacing; + } + if (options.ellipsis != null) { + this.ellipsis = options.ellipsis; + } + nextY = this.document.y + this.document.currentLineHeight(true); + if (this.document.y > this.maxY || nextY > this.maxY) { + this.nextSection(); + } + buffer = ''; + textWidth = 0; + wc = 0; + lc = 0; + y = this.document.y; + emitLine = (function(_this) { + return function() { + options.textWidth = textWidth + _this.wordSpacing * (wc - 1); + options.wordCount = wc; + options.lineWidth = _this.lineWidth; + y = _this.document.y; + _this.emit('line', buffer, options, _this); + return lc++; + }; + })(this); + this.emit('sectionStart', options, this); + this.eachWord(text, (function(_this) { + return function(word, w, bk, last) { + var lh, shouldContinue; + if ((last == null) || last.required) { + _this.emit('firstLine', options, _this); + _this.spaceLeft = _this.lineWidth; + } + if (w <= _this.spaceLeft) { + buffer += word; + textWidth += w; + wc++; + } + if (bk.required || w > _this.spaceLeft) { + if (bk.required) { + _this.emit('lastLine', options, _this); + } + lh = _this.document.currentLineHeight(true); + if ((_this.height != null) && _this.ellipsis && _this.document.y + lh * 2 > _this.maxY && _this.column >= _this.columns) { + if (_this.ellipsis === true) { + _this.ellipsis = '…'; + } + buffer = buffer.replace(/\s+$/, ''); + textWidth = _this.wordWidth(buffer + _this.ellipsis); + while (textWidth > _this.lineWidth) { + buffer = buffer.slice(0, -1).replace(/\s+$/, ''); + textWidth = _this.wordWidth(buffer + _this.ellipsis); + } + buffer = buffer + _this.ellipsis; + } + if (bk.required && w > _this.spaceLeft) { + buffer = word; + textWidth = w; + wc = 1; + } + emitLine(); + if (_this.document.y + lh > _this.maxY) { + shouldContinue = _this.nextSection(); + if (!shouldContinue) { + wc = 0; + buffer = ''; + return false; + } + } + if (bk.required) { + _this.spaceLeft = _this.lineWidth; + buffer = ''; + textWidth = 0; + return wc = 0; + } else { + _this.spaceLeft = _this.lineWidth - w; + buffer = word; + textWidth = w; + return wc = 1; + } + } else { + return _this.spaceLeft -= w; + } + }; + })(this)); + if (wc > 0) { + this.emit('lastLine', options, this); + emitLine(); + } + this.emit('sectionEnd', options, this); + if (options.continued === true) { + if (lc > 1) { + this.continuedX = 0; + } + this.continuedX += options.textWidth; + return this.document.y = y; + } else { + return this.document.x = this.startX; + } + }; + + LineWrapper.prototype.nextSection = function(options) { + var ref; + this.emit('sectionEnd', options, this); + if (++this.column > this.columns) { + if (this.height != null) { + return false; + } + this.document.addPage(); + this.column = 1; + this.startY = this.document.page.margins.top; + this.maxY = this.document.page.maxY(); + this.document.x = this.startX; + if (this.document._fillColor) { + (ref = this.document).fillColor.apply(ref, this.document._fillColor); + } + this.emit('pageBreak', options, this); + } else { + this.document.x += this.lineWidth + this.columnGap; + this.document.y = this.startY; + this.emit('columnBreak', options, this); + } + this.emit('sectionStart', options, this); + return true; + }; + + return LineWrapper; + + })(EventEmitter); + + module.exports = LineWrapper; + +}).call(this); diff --git a/pitfall/pdfkit/lib/mixins/vector.js b/pitfall/pdfkit/lib/mixins/vector.js new file mode 100644 index 00000000..a7beed7e --- /dev/null +++ b/pitfall/pdfkit/lib/mixins/vector.js @@ -0,0 +1,265 @@ +// Generated by CoffeeScript 1.12.5 +(function() { + var KAPPA, SVGPath, number, + slice = [].slice; + + SVGPath = require('../path'); + + number = require('../object').number; + + KAPPA = 4.0 * ((Math.sqrt(2) - 1.0) / 3.0); + + module.exports = { + initVector: function() { + this._ctm = [1, 0, 0, 1, 0, 0]; + return this._ctmStack = []; + }, + save: function() { + this._ctmStack.push(this._ctm.slice()); + return this.addContent('q'); + }, + restore: function() { + this._ctm = this._ctmStack.pop() || [1, 0, 0, 1, 0, 0]; + return this.addContent('Q'); + }, + closePath: function() { + return this.addContent('h'); + }, + lineWidth: function(w) { + return this.addContent((number(w)) + " w"); + }, + _CAP_STYLES: { + BUTT: 0, + ROUND: 1, + SQUARE: 2 + }, + lineCap: function(c) { + if (typeof c === 'string') { + c = this._CAP_STYLES[c.toUpperCase()]; + } + return this.addContent(c + " J"); + }, + _JOIN_STYLES: { + MITER: 0, + ROUND: 1, + BEVEL: 2 + }, + lineJoin: function(j) { + if (typeof j === 'string') { + j = this._JOIN_STYLES[j.toUpperCase()]; + } + return this.addContent(j + " j"); + }, + miterLimit: function(m) { + return this.addContent((number(m)) + " M"); + }, + dash: function(length, options) { + var phase, ref, space, v; + if (options == null) { + options = {}; + } + if (length == null) { + return this; + } + if (Array.isArray(length)) { + length = ((function() { + var i, len, results; + results = []; + for (i = 0, len = length.length; i < len; i++) { + v = length[i]; + results.push(number(v)); + } + return results; + })()).join(' '); + phase = options.phase || 0; + return this.addContent("[" + length + "] " + (number(phase)) + " d"); + } else { + space = (ref = options.space) != null ? ref : length; + phase = options.phase || 0; + return this.addContent("[" + (number(length)) + " " + (number(space)) + "] " + (number(phase)) + " d"); + } + }, + undash: function() { + return this.addContent("[] 0 d"); + }, + moveTo: function(x, y) { + return this.addContent((number(x)) + " " + (number(y)) + " m"); + }, + lineTo: function(x, y) { + return this.addContent((number(x)) + " " + (number(y)) + " l"); + }, + bezierCurveTo: function(cp1x, cp1y, cp2x, cp2y, x, y) { + return this.addContent((number(cp1x)) + " " + (number(cp1y)) + " " + (number(cp2x)) + " " + (number(cp2y)) + " " + (number(x)) + " " + (number(y)) + " c"); + }, + quadraticCurveTo: function(cpx, cpy, x, y) { + return this.addContent((number(cpx)) + " " + (number(cpy)) + " " + (number(x)) + " " + (number(y)) + " v"); + }, + rect: function(x, y, w, h) { + return this.addContent((number(x)) + " " + (number(y)) + " " + (number(w)) + " " + (number(h)) + " re"); + }, + roundedRect: function(x, y, w, h, r) { + var c; + if (r == null) { + r = 0; + } + r = Math.min(r, 0.5 * w, 0.5 * h); + c = r * (1.0 - KAPPA); + this.moveTo(x + r, y); + this.lineTo(x + w - r, y); + this.bezierCurveTo(x + w - c, y, x + w, y + c, x + w, y + r); + this.lineTo(x + w, y + h - r); + this.bezierCurveTo(x + w, y + h - c, x + w - c, y + h, x + w - r, y + h); + this.lineTo(x + r, y + h); + this.bezierCurveTo(x + c, y + h, x, y + h - c, x, y + h - r); + this.lineTo(x, y + r); + this.bezierCurveTo(x, y + c, x + c, y, x + r, y); + return this.closePath(); + }, + ellipse: function(x, y, r1, r2) { + var ox, oy, xe, xm, ye, ym; + if (r2 == null) { + r2 = r1; + } + x -= r1; + y -= r2; + ox = r1 * KAPPA; + oy = r2 * KAPPA; + xe = x + r1 * 2; + ye = y + r2 * 2; + xm = x + r1; + ym = y + r2; + this.moveTo(x, ym); + this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + return this.closePath(); + }, + circle: function(x, y, radius) { + return this.ellipse(x, y, radius); + }, + polygon: function() { + var i, len, point, points; + points = 1 <= arguments.length ? slice.call(arguments, 0) : []; + this.moveTo.apply(this, points.shift()); + for (i = 0, len = points.length; i < len; i++) { + point = points[i]; + this.lineTo.apply(this, point); + } + return this.closePath(); + }, + path: function(path) { + SVGPath.apply(this, path); + return this; + }, + _windingRule: function(rule) { + if (/even-?odd/.test(rule)) { + return '*'; + } + return ''; + }, + fill: function(color, rule) { + if (/(even-?odd)|(non-?zero)/.test(color)) { + rule = color; + color = null; + } + if (color) { + this.fillColor(color); + } + return this.addContent('f' + this._windingRule(rule)); + }, + stroke: function(color) { + if (color) { + this.strokeColor(color); + } + return this.addContent('S'); + }, + fillAndStroke: function(fillColor, strokeColor, rule) { + var isFillRule; + if (strokeColor == null) { + strokeColor = fillColor; + } + isFillRule = /(even-?odd)|(non-?zero)/; + if (isFillRule.test(fillColor)) { + rule = fillColor; + fillColor = null; + } + if (isFillRule.test(strokeColor)) { + rule = strokeColor; + strokeColor = fillColor; + } + if (fillColor) { + this.fillColor(fillColor); + this.strokeColor(strokeColor); + } + return this.addContent('B' + this._windingRule(rule)); + }, + clip: function(rule) { + return this.addContent('W' + this._windingRule(rule) + ' n'); + }, + transform: function(m11, m12, m21, m22, dx, dy) { + var m, m0, m1, m2, m3, m4, m5, v, values; + m = this._ctm; + m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3], m4 = m[4], m5 = m[5]; + m[0] = m0 * m11 + m2 * m12; + m[1] = m1 * m11 + m3 * m12; + m[2] = m0 * m21 + m2 * m22; + m[3] = m1 * m21 + m3 * m22; + m[4] = m0 * dx + m2 * dy + m4; + m[5] = m1 * dx + m3 * dy + m5; + values = ((function() { + var i, len, ref, results; + ref = [m11, m12, m21, m22, dx, dy]; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + v = ref[i]; + results.push(number(v)); + } + return results; + })()).join(' '); + return this.addContent(values + " cm"); + }, + translate: function(x, y) { + return this.transform(1, 0, 0, 1, x, y); + }, + rotate: function(angle, options) { + var cos, rad, ref, sin, x, x1, y, y1; + if (options == null) { + options = {}; + } + rad = angle * Math.PI / 180; + cos = Math.cos(rad); + sin = Math.sin(rad); + x = y = 0; + if (options.origin != null) { + ref = options.origin, x = ref[0], y = ref[1]; + x1 = x * cos - y * sin; + y1 = x * sin + y * cos; + x -= x1; + y -= y1; + } + return this.transform(cos, sin, -sin, cos, x, y); + }, + scale: function(xFactor, yFactor, options) { + var ref, x, y; + if (yFactor == null) { + yFactor = xFactor; + } + if (options == null) { + options = {}; + } + if (arguments.length === 2) { + yFactor = xFactor; + options = yFactor; + } + x = y = 0; + if (options.origin != null) { + ref = options.origin, x = ref[0], y = ref[1]; + x -= xFactor * x; + y -= yFactor * y; + } + return this.transform(xFactor, 0, 0, yFactor, x, y); + } + }; + +}).call(this); diff --git a/pitfall/pdfkit/lib/object.js b/pitfall/pdfkit/lib/object.js new file mode 100644 index 00000000..d7ad884b --- /dev/null +++ b/pitfall/pdfkit/lib/object.js @@ -0,0 +1,122 @@ +// Generated by CoffeeScript 1.12.5 + +/* +PDFObject - converts JavaScript types into their corrisponding PDF types. +By Devon Govett + */ + +(function() { + var PDFObject, PDFReference; + + PDFObject = (function() { + var escapable, escapableRe, pad, swapBytes; + + function PDFObject() {} + + pad = function(str, length) { + return (Array(length + 1).join('0') + str).slice(-length); + }; + + PDFObject.pad = pad; + + escapableRe = /[\n\r\t\b\f\(\)\\]/g; + + PDFObject.escapableRe = escapableRe; + + escapable = { + '\n': '\\n', + '\r': '\\r', + '\t': '\\t', + '\b': '\\b', + '\f': '\\f', + '\\': '\\\\', + '(': '\\(', + ')': '\\)' + }; + + PDFObject.escapable = escapable; + + swapBytes = function(buff) { + var a, i, j, l, ref; + l = buff.length; + if (l & 0x01) { + throw new Error("Buffer length must be even"); + } else { + for (i = j = 0, ref = l - 1; j < ref; i = j += 2) { + a = buff[i]; + buff[i] = buff[i + 1]; + buff[i + 1] = a; + } + } + return buff; + }; + + PDFObject.swapBytes = swapBytes; + + PDFObject.convert = function(object) { + var e, i, isUnicode, items, j, key, out, ref, string, val; + if (typeof object === 'string') { + return '/' + object; + } else if (object instanceof String) { + string = object.replace(escapableRe, function(c) { + return escapable[c]; + }); + isUnicode = false; + for (i = j = 0, ref = string.length; j < ref; i = j += 1) { + if (string.charCodeAt(i) > 0x7f) { + isUnicode = true; + break; + } + } + if (isUnicode) { + string = swapBytes(new Buffer('\ufeff' + string, 'utf16le')).toString('binary'); + } + return '(' + string + ')'; + } else if (Buffer.isBuffer(object)) { + return '<' + object.toString('hex') + '>'; + } else if (object instanceof PDFReference) { + return object.toString(); + } else if (object instanceof Date) { + return '(D:' + pad(object.getUTCFullYear(), 4) + pad(object.getUTCMonth() + 1, 2) + pad(object.getUTCDate(), 2) + pad(object.getUTCHours(), 2) + pad(object.getUTCMinutes(), 2) + pad(object.getUTCSeconds(), 2) + 'Z)'; + } else if (Array.isArray(object)) { + items = ((function() { + var k, len, results; + results = []; + for (k = 0, len = object.length; k < len; k++) { + e = object[k]; + results.push(PDFObject.convert(e)); + } + return results; + })()).join(' '); + return '[' + items + ']'; + } else if ({}.toString.call(object) === '[object Object]') { + out = ['<<']; + for (key in object) { + val = object[key]; + out.push('/' + key + ' ' + PDFObject.convert(val)); + } + out.push('>>'); + return out.join('\n'); + } else if (typeof object === 'number') { + return PDFObject.number(object); + } else { + return '' + object; + } + }; + + PDFObject.number = function(n) { + if (n > -1e21 && n < 1e21) { + return Math.round(n * 1e6) / 1e6; + } + throw new Error("unsupported number: " + n); + }; + + return PDFObject; + + })(); + + module.exports = PDFObject; + + PDFReference = require('./reference'); + +}).call(this); diff --git a/pitfall/pdfkit/lib/page.js b/pitfall/pdfkit/lib/page.js new file mode 100644 index 00000000..e0a21534 --- /dev/null +++ b/pitfall/pdfkit/lib/page.js @@ -0,0 +1,170 @@ +// Generated by CoffeeScript 1.12.5 + +/* +PDFPage - represents a single page in the PDF document +By Devon Govett + */ + +(function() { + var PDFPage; + + PDFPage = (function() { + var DEFAULT_MARGINS, SIZES; + + function PDFPage(document, options) { + var dimensions; + this.document = document; + if (options == null) { + options = {}; + } + this.size = options.size || 'letter'; + this.layout = options.layout || 'portrait'; + if (typeof options.margin === 'number') { + this.margins = { + top: options.margin, + left: options.margin, + bottom: options.margin, + right: options.margin + }; + } else { + this.margins = options.margins || DEFAULT_MARGINS; + } + dimensions = Array.isArray(this.size) ? this.size : SIZES[this.size.toUpperCase()]; + this.width = dimensions[this.layout === 'portrait' ? 0 : 1]; + this.height = dimensions[this.layout === 'portrait' ? 1 : 0]; + this.content = this.document.ref(); + this.resources = this.document.ref({ + ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI'] + }); + Object.defineProperties(this, { + fonts: { + get: (function(_this) { + return function() { + var base; + return (base = _this.resources.data).Font != null ? base.Font : base.Font = {}; + }; + })(this) + }, + xobjects: { + get: (function(_this) { + return function() { + var base; + return (base = _this.resources.data).XObject != null ? base.XObject : base.XObject = {}; + }; + })(this) + }, + ext_gstates: { + get: (function(_this) { + return function() { + var base; + return (base = _this.resources.data).ExtGState != null ? base.ExtGState : base.ExtGState = {}; + }; + })(this) + }, + patterns: { + get: (function(_this) { + return function() { + var base; + return (base = _this.resources.data).Pattern != null ? base.Pattern : base.Pattern = {}; + }; + })(this) + }, + annotations: { + get: (function(_this) { + return function() { + var base; + return (base = _this.dictionary.data).Annots != null ? base.Annots : base.Annots = []; + }; + })(this) + } + }); + this.dictionary = this.document.ref({ + Type: 'Page', + Parent: this.document._root.data.Pages, + MediaBox: [0, 0, this.width, this.height], + Contents: this.content, + Resources: this.resources + }); + } + + PDFPage.prototype.maxY = function() { + return this.height - this.margins.bottom; + }; + + PDFPage.prototype.write = function(chunk) { + return this.content.write(chunk); + }; + + PDFPage.prototype.end = function() { + this.dictionary.end(); + this.resources.end(); + return this.content.end(); + }; + + DEFAULT_MARGINS = { + top: 72, + left: 72, + bottom: 72, + right: 72 + }; + + SIZES = { + '4A0': [4767.87, 6740.79], + '2A0': [3370.39, 4767.87], + A0: [2383.94, 3370.39], + A1: [1683.78, 2383.94], + A2: [1190.55, 1683.78], + A3: [841.89, 1190.55], + A4: [595.28, 841.89], + A5: [419.53, 595.28], + A6: [297.64, 419.53], + A7: [209.76, 297.64], + A8: [147.40, 209.76], + A9: [104.88, 147.40], + A10: [73.70, 104.88], + B0: [2834.65, 4008.19], + B1: [2004.09, 2834.65], + B2: [1417.32, 2004.09], + B3: [1000.63, 1417.32], + B4: [708.66, 1000.63], + B5: [498.90, 708.66], + B6: [354.33, 498.90], + B7: [249.45, 354.33], + B8: [175.75, 249.45], + B9: [124.72, 175.75], + B10: [87.87, 124.72], + C0: [2599.37, 3676.54], + C1: [1836.85, 2599.37], + C2: [1298.27, 1836.85], + C3: [918.43, 1298.27], + C4: [649.13, 918.43], + C5: [459.21, 649.13], + C6: [323.15, 459.21], + C7: [229.61, 323.15], + C8: [161.57, 229.61], + C9: [113.39, 161.57], + C10: [79.37, 113.39], + RA0: [2437.80, 3458.27], + RA1: [1729.13, 2437.80], + RA2: [1218.90, 1729.13], + RA3: [864.57, 1218.90], + RA4: [609.45, 864.57], + SRA0: [2551.18, 3628.35], + SRA1: [1814.17, 2551.18], + SRA2: [1275.59, 1814.17], + SRA3: [907.09, 1275.59], + SRA4: [637.80, 907.09], + EXECUTIVE: [521.86, 756.00], + FOLIO: [612.00, 936.00], + LEGAL: [612.00, 1008.00], + LETTER: [612.00, 792.00], + TABLOID: [792.00, 1224.00] + }; + + return PDFPage; + + })(); + + module.exports = PDFPage; + +}).call(this); diff --git a/pitfall/pdfkit/lib/path.js b/pitfall/pdfkit/lib/path.js new file mode 100644 index 00000000..b91648b8 --- /dev/null +++ b/pitfall/pdfkit/lib/path.js @@ -0,0 +1,366 @@ +// Generated by CoffeeScript 1.12.5 +(function() { + var SVGPath; + + SVGPath = (function() { + var apply, arcToSegments, cx, cy, parameters, parse, px, py, runners, segmentToBezier, solveArc, sx, sy; + + function SVGPath() {} + + SVGPath.apply = function(doc, path) { + var commands; + commands = parse(path); + return apply(commands, doc); + }; + + parameters = { + A: 7, + a: 7, + C: 6, + c: 6, + H: 1, + h: 1, + L: 2, + l: 2, + M: 2, + m: 2, + Q: 4, + q: 4, + S: 4, + s: 4, + T: 2, + t: 2, + V: 1, + v: 1, + Z: 0, + z: 0 + }; + + parse = function(path) { + var args, c, cmd, curArg, foundDecimal, j, len, params, ret; + ret = []; + args = []; + curArg = ""; + foundDecimal = false; + params = 0; + for (j = 0, len = path.length; j < len; j++) { + c = path[j]; + if (parameters[c] != null) { + params = parameters[c]; + if (cmd) { + if (curArg.length > 0) { + args[args.length] = +curArg; + } + ret[ret.length] = { + cmd: cmd, + args: args + }; + args = []; + curArg = ""; + foundDecimal = false; + } + cmd = c; + } else if ((c === " " || c === ",") || (c === "-" && curArg.length > 0 && curArg[curArg.length - 1] !== 'e') || (c === "." && foundDecimal)) { + if (curArg.length === 0) { + continue; + } + if (args.length === params) { + ret[ret.length] = { + cmd: cmd, + args: args + }; + args = [+curArg]; + if (cmd === "M") { + cmd = "L"; + } + if (cmd === "m") { + cmd = "l"; + } + } else { + args[args.length] = +curArg; + } + foundDecimal = c === "."; + curArg = c === '-' || c === '.' ? c : ''; + } else { + curArg += c; + if (c === '.') { + foundDecimal = true; + } + } + } + if (curArg.length > 0) { + if (args.length === params) { + ret[ret.length] = { + cmd: cmd, + args: args + }; + args = [+curArg]; + if (cmd === "M") { + cmd = "L"; + } + if (cmd === "m") { + cmd = "l"; + } + } else { + args[args.length] = +curArg; + } + } + ret[ret.length] = { + cmd: cmd, + args: args + }; + return ret; + }; + + cx = cy = px = py = sx = sy = 0; + + apply = function(commands, doc) { + var c, i, j, len, name; + cx = cy = px = py = sx = sy = 0; + for (i = j = 0, len = commands.length; j < len; i = ++j) { + c = commands[i]; + if (typeof runners[name = c.cmd] === "function") { + runners[name](doc, c.args); + } + } + return cx = cy = px = py = 0; + }; + + runners = { + M: function(doc, a) { + cx = a[0]; + cy = a[1]; + px = py = null; + sx = cx; + sy = cy; + return doc.moveTo(cx, cy); + }, + m: function(doc, a) { + cx += a[0]; + cy += a[1]; + px = py = null; + sx = cx; + sy = cy; + return doc.moveTo(cx, cy); + }, + C: function(doc, a) { + cx = a[4]; + cy = a[5]; + px = a[2]; + py = a[3]; + return doc.bezierCurveTo.apply(doc, a); + }, + c: function(doc, a) { + doc.bezierCurveTo(a[0] + cx, a[1] + cy, a[2] + cx, a[3] + cy, a[4] + cx, a[5] + cy); + px = cx + a[2]; + py = cy + a[3]; + cx += a[4]; + return cy += a[5]; + }, + S: function(doc, a) { + if (px === null) { + px = cx; + py = cy; + } + doc.bezierCurveTo(cx - (px - cx), cy - (py - cy), a[0], a[1], a[2], a[3]); + px = a[0]; + py = a[1]; + cx = a[2]; + return cy = a[3]; + }, + s: function(doc, a) { + if (px === null) { + px = cx; + py = cy; + } + doc.bezierCurveTo(cx - (px - cx), cy - (py - cy), cx + a[0], cy + a[1], cx + a[2], cy + a[3]); + px = cx + a[0]; + py = cy + a[1]; + cx += a[2]; + return cy += a[3]; + }, + Q: function(doc, a) { + px = a[0]; + py = a[1]; + cx = a[2]; + cy = a[3]; + return doc.quadraticCurveTo(a[0], a[1], cx, cy); + }, + q: function(doc, a) { + doc.quadraticCurveTo(a[0] + cx, a[1] + cy, a[2] + cx, a[3] + cy); + px = cx + a[0]; + py = cy + a[1]; + cx += a[2]; + return cy += a[3]; + }, + T: function(doc, a) { + if (px === null) { + px = cx; + py = cy; + } else { + px = cx - (px - cx); + py = cy - (py - cy); + } + doc.quadraticCurveTo(px, py, a[0], a[1]); + px = cx - (px - cx); + py = cy - (py - cy); + cx = a[0]; + return cy = a[1]; + }, + t: function(doc, a) { + if (px === null) { + px = cx; + py = cy; + } else { + px = cx - (px - cx); + py = cy - (py - cy); + } + doc.quadraticCurveTo(px, py, cx + a[0], cy + a[1]); + cx += a[0]; + return cy += a[1]; + }, + A: function(doc, a) { + solveArc(doc, cx, cy, a); + cx = a[5]; + return cy = a[6]; + }, + a: function(doc, a) { + a[5] += cx; + a[6] += cy; + solveArc(doc, cx, cy, a); + cx = a[5]; + return cy = a[6]; + }, + L: function(doc, a) { + cx = a[0]; + cy = a[1]; + px = py = null; + return doc.lineTo(cx, cy); + }, + l: function(doc, a) { + cx += a[0]; + cy += a[1]; + px = py = null; + return doc.lineTo(cx, cy); + }, + H: function(doc, a) { + cx = a[0]; + px = py = null; + return doc.lineTo(cx, cy); + }, + h: function(doc, a) { + cx += a[0]; + px = py = null; + return doc.lineTo(cx, cy); + }, + V: function(doc, a) { + cy = a[0]; + px = py = null; + return doc.lineTo(cx, cy); + }, + v: function(doc, a) { + cy += a[0]; + px = py = null; + return doc.lineTo(cx, cy); + }, + Z: function(doc) { + doc.closePath(); + cx = sx; + return cy = sy; + }, + z: function(doc) { + doc.closePath(); + cx = sx; + return cy = sy; + } + }; + + solveArc = function(doc, x, y, coords) { + var bez, ex, ey, j, large, len, results, rot, rx, ry, seg, segs, sweep; + rx = coords[0], ry = coords[1], rot = coords[2], large = coords[3], sweep = coords[4], ex = coords[5], ey = coords[6]; + segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y); + results = []; + for (j = 0, len = segs.length; j < len; j++) { + seg = segs[j]; + bez = segmentToBezier.apply(null, seg); + results.push(doc.bezierCurveTo.apply(doc, bez)); + } + return results; + }; + + arcToSegments = function(x, y, rx, ry, large, sweep, rotateX, ox, oy) { + var a00, a01, a10, a11, cos_th, d, i, j, pl, ref, result, segments, sfactor, sfactor_sq, sin_th, th, th0, th1, th2, th3, th_arc, x0, x1, xc, y0, y1, yc; + th = rotateX * (Math.PI / 180); + sin_th = Math.sin(th); + cos_th = Math.cos(th); + rx = Math.abs(rx); + ry = Math.abs(ry); + px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5; + py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5; + pl = (px * px) / (rx * rx) + (py * py) / (ry * ry); + if (pl > 1) { + pl = Math.sqrt(pl); + rx *= pl; + ry *= pl; + } + a00 = cos_th / rx; + a01 = sin_th / rx; + a10 = (-sin_th) / ry; + a11 = cos_th / ry; + x0 = a00 * ox + a01 * oy; + y0 = a10 * ox + a11 * oy; + x1 = a00 * x + a01 * y; + y1 = a10 * x + a11 * y; + d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0); + sfactor_sq = 1 / d - 0.25; + if (sfactor_sq < 0) { + sfactor_sq = 0; + } + sfactor = Math.sqrt(sfactor_sq); + if (sweep === large) { + sfactor = -sfactor; + } + xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0); + yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0); + th0 = Math.atan2(y0 - yc, x0 - xc); + th1 = Math.atan2(y1 - yc, x1 - xc); + th_arc = th1 - th0; + if (th_arc < 0 && sweep === 1) { + th_arc += 2 * Math.PI; + } else if (th_arc > 0 && sweep === 0) { + th_arc -= 2 * Math.PI; + } + segments = Math.ceil(Math.abs(th_arc / (Math.PI * 0.5 + 0.001))); + result = []; + for (i = j = 0, ref = segments; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) { + th2 = th0 + i * th_arc / segments; + th3 = th0 + (i + 1) * th_arc / segments; + result[i] = [xc, yc, th2, th3, rx, ry, sin_th, cos_th]; + } + return result; + }; + + segmentToBezier = function(cx, cy, th0, th1, rx, ry, sin_th, cos_th) { + var a00, a01, a10, a11, t, th_half, x1, x2, x3, y1, y2, y3; + a00 = cos_th * rx; + a01 = -sin_th * ry; + a10 = sin_th * rx; + a11 = cos_th * ry; + th_half = 0.5 * (th1 - th0); + t = (8 / 3) * Math.sin(th_half * 0.5) * Math.sin(th_half * 0.5) / Math.sin(th_half); + x1 = cx + Math.cos(th0) - t * Math.sin(th0); + y1 = cy + Math.sin(th0) + t * Math.cos(th0); + x3 = cx + Math.cos(th1); + y3 = cy + Math.sin(th1); + x2 = x3 + t * Math.sin(th1); + y2 = y3 - t * Math.cos(th1); + return [a00 * x1 + a01 * y1, a10 * x1 + a11 * y1, a00 * x2 + a01 * y2, a10 * x2 + a11 * y2, a00 * x3 + a01 * y3, a10 * x3 + a11 * y3]; + }; + + return SVGPath; + + })(); + + module.exports = SVGPath; + +}).call(this); diff --git a/pitfall/pdfkit/lib/reference.js b/pitfall/pdfkit/lib/reference.js new file mode 100644 index 00000000..cf745f25 --- /dev/null +++ b/pitfall/pdfkit/lib/reference.js @@ -0,0 +1,117 @@ +// Generated by CoffeeScript 1.12.5 + +/* +PDFReference - represents a reference to another object in the PDF object heirarchy +By Devon Govett + */ + +(function() { + var PDFObject, PDFReference, stream, zlib, + bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + + zlib = require('zlib'); + + stream = require('stream'); + + PDFReference = (function(superClass) { + extend(PDFReference, superClass); + + function PDFReference(document, id, data) { + this.document = document; + this.id = id; + this.data = data != null ? data : {}; + this.finalize = bind(this.finalize, this); + PDFReference.__super__.constructor.call(this, { + decodeStrings: false + }); + this.gen = 0; + this.deflate = null; + this.compress = false; + this.uncompressedLength = 0; + this.chunks = []; + } + + PDFReference.prototype.initDeflate = function() { + this.data.Filter = 'FlateDecode'; + this.deflate = zlib.createDeflate(); + this.deflate.on('data', (function(_this) { + return function(chunk) { + console.log("got data event for ref " + _this.id + " from " + _this.toString()); + _this.chunks.push(chunk); + return _this.data.Length += chunk.length; + }; + })(this)); + return this.deflate.on('end', (function(_this) { + return function() { + console.log("got end event for ref " + _this.id + " from " + _this.toString()); + return _this.finalize(); + }; + })(this)); + }; + + PDFReference.prototype._write = function(chunk, encoding, callback) { + var base; + if (!Buffer.isBuffer(chunk)) { + chunk = new Buffer(chunk + '\n', 'binary'); + } + this.uncompressedLength += chunk.length; + if ((base = this.data).Length == null) { + base.Length = 0; + } + if (this.compress) { + if (!this.deflate) { + this.initDeflate(); + } + console.log("chunk = " + chunk); + this.deflate.write(chunk); + console.log("wrote chunk"); + } else { + this.chunks.push(chunk); + this.data.Length += chunk.length; + } + return callback(); + }; + + PDFReference.prototype.end = function(chunk) { + PDFReference.__super__.end.apply(this, arguments); + if (this.deflate) { + return this.deflate.end(); + } else { + return this.finalize(); + } + }; + + PDFReference.prototype.finalize = function() { + var chunk, i, len, ref; + this.offset = this.document._offset; + this.document._write(this.id + " " + this.gen + " obj"); + this.document._write(PDFObject.convert(this.data)); + if (this.chunks.length) { + this.document._write('stream'); + ref = this.chunks; + for (i = 0, len = ref.length; i < len; i++) { + chunk = ref[i]; + this.document._write(chunk); + } + this.chunks.length = 0; + this.document._write('\nendstream'); + } + this.document._write('endobj'); + return this.document._refEnd(this); + }; + + PDFReference.prototype.toString = function() { + return this.id + " " + this.gen + " R"; + }; + + return PDFReference; + + })(stream.Writable); + + module.exports = PDFReference; + + PDFObject = require('./object'); + +}).call(this); diff --git a/pitfall/pitfall/kit/color.rkt b/pitfall/pitfall/kit/color.rkt index 806aefa6..faa99245 100644 --- a/pitfall/pitfall/kit/color.rkt +++ b/pitfall/pitfall/kit/color.rkt @@ -90,7 +90,7 @@ (public [@_doOpacity doOpacity]) (define (@_doOpacity fillOpacity strokeOpacity) - (error 'bonk)) + (send this addContent "")) ; todo )) diff --git a/pitfall/pitfall/kit/document.rkt b/pitfall/pitfall/kit/document.rkt index 4fa2b28b..b51ee880 100644 --- a/pitfall/pitfall/kit/document.rkt +++ b/pitfall/pitfall/kit/document.rkt @@ -102,7 +102,7 @@ ;; create a page object (set! @page (make-object PDFPage this options)) - (push! @_pageBuffer @page) + (push-end! @_pageBuffer @page) ;; add the page to the object store (define pages (· @_root data Pages data)) (hash-update! pages 'Kids (λ (val) (cons (· @page dictionary) val)) null) diff --git a/pitfall/pitfall/kit/helper.rkt b/pitfall/pitfall/kit/helper.rkt index 0f852296..025fd062 100644 --- a/pitfall/pitfall/kit/helper.rkt +++ b/pitfall/pitfall/kit/helper.rkt @@ -77,4 +77,19 @@ (module+ test (check-equal? (number 4.5) "4.5") (check-equal? (number 4.0) "4") - (check-equal? (number 4) "4")) \ No newline at end of file + (check-equal? (number 4) "4")) + + +(define-syntax (send*/fold stx) + (syntax-case stx () + [(_ o) #'o] + [(_ o [m0 . args0] [m . args] ...) + #'(send*/fold (send o m0 . args0) [m . args] ...)])) + +(module+ test + (define SFC (class object% + (super-new) + (field [sum 0]) + (define/public (add x) (set! sum (+ sum x)) this))) + (define sfo (new SFC)) + (check-equal? (get-field sum (send*/fold sfo [add 1] [add 2] [add 3])) 6)) \ No newline at end of file diff --git a/pitfall/pitfall/kit/reference.rkt b/pitfall/pitfall/kit/reference.rkt index f64ce32c..9d023ead 100644 --- a/pitfall/pitfall/kit/reference.rkt +++ b/pitfall/pitfall/kit/reference.rkt @@ -33,7 +33,7 @@ (cond [@compress (when (not @deflate) (@initDeflate)) (send @deflate write chunk)] - [else (push! @chunks chunk) + [else (push-end! @chunks chunk) (hash-update! @data 'Length (λ (len) (+ len (buffer-length chunk))))]) (callback)) diff --git a/pitfall/pitfall/kit/vector.rkt b/pitfall/pitfall/kit/vector.rkt index fdfee094..f99c6c01 100644 --- a/pitfall/pitfall/kit/vector.rkt +++ b/pitfall/pitfall/kit/vector.rkt @@ -3,6 +3,10 @@ (provide vector-mixin) (require "helper.rkt") +;; This constant is used to approximate a symmetrical arc using a cubic +;; Bezier curve. +(define KAPPA (* 4 (/ (- (sqrt 2) 1) 3.0))) + (define default-ctm-value '(1 0 0 1 0 0)) (define (vector-mixin %) @@ -22,28 +26,54 @@ (define/public (restore) (set! @_ctm (if (pair? @_ctmStack) (pop! @_ctmStack) default-ctm-value)) (send this addContent "Q")) - - (define/public (transform m11 m12 m21 m22 dx dy #:debug [debug #f]) - ;; keep track of the current transformation matrix - (match-define (list m0 m1 m2 m3 m4 m5) @_ctm) - (set! @_ctm (list (+ (* m0 m11) (* m2 m12)) - (+ (* m1 m11) (* m3 m12)) - (+ (* m0 m21) (* m2 m22)) - (+ (* m1 m21) (* m3 m22)) - (+ (* m0 dx) (* m2 dy) m4) - (+ (* m1 dx) (* m3 dy) m5))) - (define values (string-join (map number (list m11 m12 m21 m22 dx dy)) " ")) - (define result (format "~a cm" values)) - (if debug - result - (send this addContent result))) - (define/public (moveTo x y) + (define (@closePath) + (send this addContent "h")) + + (public [@moveTo moveTo]) + (define (@moveTo x y) (send this addContent (format "~a ~a m" x y))) - (define/public (lineTo x y) + + (public [@lineTo lineTo]) + (define (@lineTo x y) (send this addContent (format "~a ~a l" x y))) + + (public [@bezierCurveTo bezierCurveTo]) + (define (@bezierCurveTo cp1x cp1y cp2x cp2y x y) + (send this addContent (format "~a ~a ~a ~a ~a ~a c" cp1x cp1y cp2x cp2y x y))) + + + (public [@ellipse ellipse]) + (define (@ellipse x y r1 [r2 r1]) + ;; based on http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas/2173084#2173084 + (-= x r1) + (-= y r2) + (define ox (* r1 KAPPA)) + (define oy (* r2 KAPPA)) + (define xe (+ x (* r1 2))) + (define ye (+ y (* r2 2))) + (define xm (+ x r1)) + (define ym (+ y r2)) + (@moveTo x ym) + (@bezierCurveTo x (- ym oy) (- xm ox) y xm y) + (@bezierCurveTo (+ xm ox) y xe (- ym oy) xe ym) + (@bezierCurveTo xe (+ ym oy) (+ xm ox) ye xm ye) + (@bezierCurveTo (- xm ox) ye x (+ ym oy) x ym) + (@closePath)) + + + (public [@circle circle]) + (define (@circle x y radius) + (@ellipse x y radius)) + + + (public [@path path]) + (define (@path path) + ;; SVGPath.apply this, path ; todo + this) + (public [@_windingRule _windingRule]) (define (@_windingRule rule) (if (and (string? rule) (regexp-match #rx"^even-?odd$" rule)) @@ -57,6 +87,43 @@ (when color (send this fillColor color)) ;; fillColor method is from color mixin (send this addContent (format "f~a" (@_windingRule rule)))) + + (public [@transform transform]) + (define (@transform m11 m12 m21 m22 dx dy #:debug [debug #f]) + ;; keep track of the current transformation matrix + (match-define (list m0 m1 m2 m3 m4 m5) @_ctm) + (set! @_ctm (list (+ (* m0 m11) (* m2 m12)) + (+ (* m1 m11) (* m3 m12)) + (+ (* m0 m21) (* m2 m22)) + (+ (* m1 m21) (* m3 m22)) + (+ (* m0 dx) (* m2 dy) m4) + (+ (* m1 dx) (* m3 dy) m5))) + (define values (string-join (map number (list m11 m12 m21 m22 dx dy)) " ")) + (define result (format "~a cm" values)) + (if debug + result + (send this addContent result))) + + + (public [@translate translate]) + (define (@translate x y) + (@transform 1 0 0 1 x y)) + + (public [@scale scale]) + (define @scale + (match-lambda* + [(list (? number? xFactor)) (@scale xFactor (mhash))] + [(list (? number? xFactor) (? hash? options)) (@scale xFactor xFactor options)] + [(list (? number? xFactor) (? number? yFactor)) (@scale xFactor yFactor (mhash))] + [(list (? number? xFactor) (? number? yFactor) (? hash? options)) + (define x 0) (define y 0) + (when (hash-ref options 'origin #f) + (match-define (list x y) (hash-ref options 'origin)) + (-= x (* x xFactor)) + (-= y (* y yFactor))) + (@transform xFactor 0 0 yFactor x y)])) + + )) (module+ test diff --git a/pitfall/pktest/test1.coffee b/pitfall/pktest/test1.coffee index 48b758a1..dd54a40d 100644 --- a/pitfall/pktest/test1.coffee +++ b/pitfall/pktest/test1.coffee @@ -15,10 +15,4 @@ doc.save() doc.circle(280, 200, 50) .fill("#6600FF") -doc.scale(0.6) - .translate(470, -380) - .path('M 250,75 L 323,301 131,161 369,161 177,301 z') # render an SVG path - .fill('red', 'even-odd') # fill using the even-odd winding rule - .restore() - doc.end() \ No newline at end of file diff --git a/pitfall/pktest/test1.pdf b/pitfall/pktest/test1.pdf index 3abf80fb..d1aaa358 100644 --- a/pitfall/pktest/test1.pdf +++ b/pitfall/pktest/test1.pdf @@ -25,7 +25,7 @@ endobj endobj 3 0 obj << -/Length 421 +/Length 294 >> stream 1 0 0 -1 0 792 cm @@ -47,19 +47,6 @@ h 0.4 0 1 scn /Gs1 gs f -0.6 0 0 0.6 0 0 cm -1 0 0 1 470 -380 cm -250 75 m -323 301 l -131 161 l -369 161 l -177 301 l -h -/DeviceRGB cs -1 0 0 scn -/Gs1 gs -f* -Q endstream endobj @@ -67,7 +54,7 @@ endobj << /Producer (PDFKit) /Creator (PDFKit) -/CreationDate (D:20170513192922Z) +/CreationDate (D:20170514013102Z) >> endobj 2 0 obj @@ -86,13 +73,13 @@ endobj xref 0 8 0000000000 65535 f -0000000871 00000 n -0000000822 00000 n +0000000744 00000 n +0000000695 00000 n 0000000258 00000 n 0000000163 00000 n 0000000059 00000 n 0000000015 00000 n -0000000730 00000 n +0000000603 00000 n trailer << /Size 8 @@ -100,5 +87,5 @@ trailer /Info 7 0 R >> startxref -928 +801 %%EOF diff --git a/pitfall/pktest/test1.rkt b/pitfall/pktest/test1.rkt index 2bfcc677..5829a16a 100644 --- a/pitfall/pktest/test1.rkt +++ b/pitfall/pktest/test1.rkt @@ -1,18 +1,18 @@ #lang br -(require pitfall/kit/document) +(require pitfall/kit/document pitfall/kit/helper) ;; Create a new PDFDocument (define doc (new PDFDocument)) (send doc pipe (open-output-file "test1rkt.pdf" #:exists 'replace)) ;; Draw a triangle and a circle -(send (send (send (send (send doc save) moveTo 100 150) lineTo 100 250) lineTo 200 250) fill "#FF3300") +(send*/fold doc [save] + [moveTo 100 150] + [lineTo 100 250] + [lineTo 200 250] + [fill "#FF3300"]) -(send (send doc circle 280 200 50) fill "#6600FF") - -;; render an SVG path -;; fill using the even-odd winding rule -(send (send (send (send (send doc scale 0.6) translate 470 -380) path "M 250,75 L 323,301 131,161 369,161 177,301 z") fill "red" "even-odd") restore) +(send*/fold doc [circle 280 200 50] [fill "#6600FF"]) (send doc end) \ No newline at end of file diff --git a/pitfall/pktest/test1rkt.pdf b/pitfall/pktest/test1rkt.pdf index e69de29b..203d2f78 100644 --- a/pitfall/pktest/test1rkt.pdf +++ b/pitfall/pktest/test1rkt.pdf @@ -0,0 +1,81 @@ +%PDF-1.3 +%ÿÿÿÿ +5 0 obj +<< +/Parent 1 0 R +/Resources 4 0 R +/Contents 3 0 R +/MediaBox [0 0 612 792] +/Type /Page +>> +endobj +4 0 obj +<< +/ProcSet [/PDF /Text /ImageB /ImageC /ImageI] +>> +endobj +3 0 obj +<< +/Length 344 +>> +stream +1 0 0 -1 0 792 cm +q +100 150 m +100 250 l +200 250 l +/DeviceRGB cs +1 0.2 0 scn + +f +230 200 m +230 172.38576250846032 252.38576250846032 150 280 150 c +307.61423749153965 150 330 172.38576250846032 330 200 c +330 227.61423749153968 307.61423749153965 250 280 250 c +252.38576250846032 250 230 227.61423749153968 230 200 c +h +/DeviceRGB cs +0.4 0 1 scn + +f + +endstream +endobj +6 0 obj +<< +/CreationDate (D:19700101000000Z) +/Creator (PitfallKit) +/Producer (PitfallKit) +>> +endobj +2 0 obj +<< +/Pages 1 0 R +/Type /Catalog +>> +endobj +1 0 obj +<< +/Kids [5 0 R] +/Count 1 +/Type /Pages +>> +endobj +xref +0 7 +0000000000 65535 f +0000000730 00000 n +0000000681 00000 n +0000000186 00000 n +0000000119 00000 n +0000000015 00000 n +0000000581 00000 n +trailer +<< +/Info 6 0 R +/Root 2 0 R +/Size 7 +>> +startxref +787 +%%EOF