diff --git a/pitfall/pdfkit/lib/gradient.js b/pitfall/pdfkit/lib/gradient.js new file mode 100644 index 00000000..06a04606 --- /dev/null +++ b/pitfall/pdfkit/lib/gradient.js @@ -0,0 +1,240 @@ +// Generated by CoffeeScript 1.12.5 +(function() { + var PDFGradient, PDFLinearGradient, PDFRadialGradient, + 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; + + PDFGradient = (function() { + function PDFGradient(doc) { + this.doc = doc; + this.stops = []; + this.embedded = false; + this.transform = [1, 0, 0, 1, 0, 0]; + this._colorSpace = 'DeviceRGB'; + } + + PDFGradient.prototype.stop = function(pos, color, opacity) { + if (opacity == null) { + opacity = 1; + } + opacity = Math.max(0, Math.min(1, opacity)); + this.stops.push([pos, this.doc._normalizeColor(color), opacity]); + return this; + }; + + PDFGradient.prototype.setTransform = function(m11, m12, m21, m22, dx, dy) { + this.transform = [m11, m12, m21, m22, dx, dy]; + return this; + }; + + PDFGradient.prototype.embed = function(m) { + var bounds, encode, fn, form, grad, gstate, i, j, k, last, len, opacityPattern, pageBBox, pattern, ref, ref1, shader, stop, stops, v; + if (this.stops.length === 0) { + return; + } + this.embedded = true; + this.matrix = m; + last = this.stops[this.stops.length - 1]; + if (last[0] < 1) { + this.stops.push([1, last[1], last[2]]); + } + bounds = []; + encode = []; + stops = []; + for (i = j = 0, ref = this.stops.length - 1; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) { + encode.push(0, 1); + if (i + 2 !== this.stops.length) { + bounds.push(this.stops[i + 1][0]); + } + fn = this.doc.ref({ + FunctionType: 2, + Domain: [0, 1], + C0: this.stops[i + 0][1], + C1: this.stops[i + 1][1], + N: 1 + }); + stops.push(fn); + fn.end(); + } + if (stops.length === 1) { + fn = stops[0]; + } else { + fn = this.doc.ref({ + FunctionType: 3, + Domain: [0, 1], + Functions: stops, + Bounds: bounds, + Encode: encode + }); + fn.end(); + } + this.id = 'Sh' + (++this.doc._gradCount); + shader = this.shader(fn); + shader.end(); + pattern = this.doc.ref({ + Type: 'Pattern', + PatternType: 2, + Shading: shader, + Matrix: (function() { + var k, len, ref1, results; + ref1 = this.matrix; + results = []; + for (k = 0, len = ref1.length; k < len; k++) { + v = ref1[k]; + results.push(+v.toFixed(5)); + } + return results; + }).call(this) + }); + pattern.end(); + if (this.stops.some(function(stop) { + return stop[2] < 1; + })) { + grad = this.opacityGradient(); + grad._colorSpace = 'DeviceGray'; + ref1 = this.stops; + for (k = 0, len = ref1.length; k < len; k++) { + stop = ref1[k]; + grad.stop(stop[0], [stop[2]]); + } + grad = grad.embed(this.matrix); + pageBBox = [0, 0, this.doc.page.width, this.doc.page.height]; + form = this.doc.ref({ + Type: 'XObject', + Subtype: 'Form', + FormType: 1, + BBox: pageBBox, + Group: { + Type: 'Group', + S: 'Transparency', + CS: 'DeviceGray' + }, + Resources: { + ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI'], + Pattern: { + Sh1: grad + } + } + }); + form.write("/Pattern cs /Sh1 scn"); + form.end((pageBBox.join(" ")) + " re f"); + gstate = this.doc.ref({ + Type: 'ExtGState', + SMask: { + Type: 'Mask', + S: 'Luminosity', + G: form + } + }); + gstate.end(); + opacityPattern = this.doc.ref({ + Type: 'Pattern', + PatternType: 1, + PaintType: 1, + TilingType: 2, + BBox: pageBBox, + XStep: pageBBox[2], + YStep: pageBBox[3], + Resources: { + ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI'], + Pattern: { + Sh1: pattern + }, + ExtGState: { + Gs1: gstate + } + } + }); + opacityPattern.write("/Gs1 gs /Pattern cs /Sh1 scn"); + opacityPattern.end((pageBBox.join(" ")) + " re f"); + this.doc.page.patterns[this.id] = opacityPattern; + } else { + this.doc.page.patterns[this.id] = pattern; + } + return pattern; + }; + + PDFGradient.prototype.apply = function(op) { + var dx, dy, m, m0, m1, m11, m12, m2, m21, m22, m3, m4, m5, ref, ref1; + ref = this.doc._ctm.slice(), m0 = ref[0], m1 = ref[1], m2 = ref[2], m3 = ref[3], m4 = ref[4], m5 = ref[5]; + ref1 = this.transform, m11 = ref1[0], m12 = ref1[1], m21 = ref1[2], m22 = ref1[3], dx = ref1[4], dy = ref1[5]; + m = [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]; + if (!(this.embedded && m.join(" ") === this.matrix.join(" "))) { + this.embed(m); + } + return this.doc.addContent("/" + this.id + " " + op); + }; + + return PDFGradient; + + })(); + + PDFLinearGradient = (function(superClass) { + extend(PDFLinearGradient, superClass); + + function PDFLinearGradient(doc, x1, y1, x2, y2) { + this.doc = doc; + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + PDFLinearGradient.__super__.constructor.apply(this, arguments); + } + + PDFLinearGradient.prototype.shader = function(fn) { + return this.doc.ref({ + ShadingType: 2, + ColorSpace: this._colorSpace, + Coords: [this.x1, this.y1, this.x2, this.y2], + Function: fn, + Extend: [true, true] + }); + }; + + PDFLinearGradient.prototype.opacityGradient = function() { + return new PDFLinearGradient(this.doc, this.x1, this.y1, this.x2, this.y2); + }; + + return PDFLinearGradient; + + })(PDFGradient); + + PDFRadialGradient = (function(superClass) { + extend(PDFRadialGradient, superClass); + + function PDFRadialGradient(doc, x1, y1, r1, x2, y2, r2) { + this.doc = doc; + this.x1 = x1; + this.y1 = y1; + this.r1 = r1; + this.x2 = x2; + this.y2 = y2; + this.r2 = r2; + PDFRadialGradient.__super__.constructor.apply(this, arguments); + } + + PDFRadialGradient.prototype.shader = function(fn) { + return this.doc.ref({ + ShadingType: 3, + ColorSpace: this._colorSpace, + Coords: [this.x1, this.y1, this.r1, this.x2, this.y2, this.r2], + Function: fn, + Extend: [true, true] + }); + }; + + PDFRadialGradient.prototype.opacityGradient = function() { + return new PDFRadialGradient(this.doc, this.x1, this.y1, this.r1, this.x2, this.y2, this.r2); + }; + + return PDFRadialGradient; + + })(PDFGradient); + + module.exports = { + PDFGradient: PDFGradient, + PDFLinearGradient: PDFLinearGradient, + PDFRadialGradient: PDFRadialGradient + }; + +}).call(this); diff --git a/pitfall/pdfkit/lib/mixins/color.js b/pitfall/pdfkit/lib/mixins/color.js new file mode 100644 index 00000000..5dd8bc4e --- /dev/null +++ b/pitfall/pdfkit/lib/mixins/color.js @@ -0,0 +1,304 @@ +// Generated by CoffeeScript 1.12.5 +(function() { + var PDFGradient, PDFLinearGradient, PDFRadialGradient, namedColors, ref; + + ref = require('../gradient'), PDFGradient = ref.PDFGradient, PDFLinearGradient = ref.PDFLinearGradient, PDFRadialGradient = ref.PDFRadialGradient; + + module.exports = { + initColor: function() { + this._opacityRegistry = {}; + this._opacityCount = 0; + return this._gradCount = 0; + }, + _normalizeColor: function(color) { + var hex, part; + if (color instanceof PDFGradient) { + return color; + } + if (typeof color === 'string') { + if (color.charAt(0) === '#') { + if (color.length === 4) { + color = color.replace(/#([0-9A-F])([0-9A-F])([0-9A-F])/i, "#$1$1$2$2$3$3"); + } + hex = parseInt(color.slice(1), 16); + color = [hex >> 16, hex >> 8 & 0xff, hex & 0xff]; + } else if (namedColors[color]) { + color = namedColors[color]; + } + } + if (Array.isArray(color)) { + if (color.length === 3) { + color = (function() { + var i, len, results; + results = []; + for (i = 0, len = color.length; i < len; i++) { + part = color[i]; + results.push(part / 255); + } + return results; + })(); + } else if (color.length === 4) { + color = (function() { + var i, len, results; + results = []; + for (i = 0, len = color.length; i < len; i++) { + part = color[i]; + results.push(part / 100); + } + return results; + })(); + } + return color; + } + return null; + }, + _setColor: function(color, stroke) { + var op, space; + color = this._normalizeColor(color); + if (!color) { + return false; + } + op = stroke ? 'SCN' : 'scn'; + if (color instanceof PDFGradient) { + this._setColorSpace('Pattern', stroke); + color.apply(op); + } else { + space = color.length === 4 ? 'DeviceCMYK' : 'DeviceRGB'; + this._setColorSpace(space, stroke); + color = color.join(' '); + this.addContent(color + " " + op); + } + return true; + }, + _setColorSpace: function(space, stroke) { + var op; + op = stroke ? 'CS' : 'cs'; + return this.addContent("/" + space + " " + op); + }, + fillColor: function(color, opacity) { + var set; + if (opacity == null) { + opacity = 1; + } + set = this._setColor(color, false); + if (set) { + this.fillOpacity(opacity); + } + this._fillColor = [color, opacity]; + return this; + }, + strokeColor: function(color, opacity) { + var set; + if (opacity == null) { + opacity = 1; + } + set = this._setColor(color, true); + if (set) { + this.strokeOpacity(opacity); + } + return this; + }, + opacity: function(opacity) { + this._doOpacity(opacity, opacity); + return this; + }, + fillOpacity: function(opacity) { + this._doOpacity(opacity, null); + return this; + }, + strokeOpacity: function(opacity) { + this._doOpacity(null, opacity); + return this; + }, + _doOpacity: function(fillOpacity, strokeOpacity) { + var dictionary, id, key, name, ref1; + if (!((fillOpacity != null) || (strokeOpacity != null))) { + return; + } + if (fillOpacity != null) { + fillOpacity = Math.max(0, Math.min(1, fillOpacity)); + } + if (strokeOpacity != null) { + strokeOpacity = Math.max(0, Math.min(1, strokeOpacity)); + } + key = fillOpacity + "_" + strokeOpacity; + if (this._opacityRegistry[key]) { + ref1 = this._opacityRegistry[key], dictionary = ref1[0], name = ref1[1]; + } else { + dictionary = { + Type: 'ExtGState' + }; + if (fillOpacity != null) { + dictionary.ca = fillOpacity; + } + if (strokeOpacity != null) { + dictionary.CA = strokeOpacity; + } + dictionary = this.ref(dictionary); + dictionary.end(); + id = ++this._opacityCount; + name = "Gs" + id; + this._opacityRegistry[key] = [dictionary, name]; + } + this.page.ext_gstates[name] = dictionary; + return this.addContent("/" + name + " gs"); + }, + linearGradient: function(x1, y1, x2, y2) { + return new PDFLinearGradient(this, x1, y1, x2, y2); + }, + radialGradient: function(x1, y1, r1, x2, y2, r2) { + return new PDFRadialGradient(this, x1, y1, r1, x2, y2, r2); + } + }; + + namedColors = { + aliceblue: [240, 248, 255], + antiquewhite: [250, 235, 215], + aqua: [0, 255, 255], + aquamarine: [127, 255, 212], + azure: [240, 255, 255], + beige: [245, 245, 220], + bisque: [255, 228, 196], + black: [0, 0, 0], + blanchedalmond: [255, 235, 205], + blue: [0, 0, 255], + blueviolet: [138, 43, 226], + brown: [165, 42, 42], + burlywood: [222, 184, 135], + cadetblue: [95, 158, 160], + chartreuse: [127, 255, 0], + chocolate: [210, 105, 30], + coral: [255, 127, 80], + cornflowerblue: [100, 149, 237], + cornsilk: [255, 248, 220], + crimson: [220, 20, 60], + cyan: [0, 255, 255], + darkblue: [0, 0, 139], + darkcyan: [0, 139, 139], + darkgoldenrod: [184, 134, 11], + darkgray: [169, 169, 169], + darkgreen: [0, 100, 0], + darkgrey: [169, 169, 169], + darkkhaki: [189, 183, 107], + darkmagenta: [139, 0, 139], + darkolivegreen: [85, 107, 47], + darkorange: [255, 140, 0], + darkorchid: [153, 50, 204], + darkred: [139, 0, 0], + darksalmon: [233, 150, 122], + darkseagreen: [143, 188, 143], + darkslateblue: [72, 61, 139], + darkslategray: [47, 79, 79], + darkslategrey: [47, 79, 79], + darkturquoise: [0, 206, 209], + darkviolet: [148, 0, 211], + deeppink: [255, 20, 147], + deepskyblue: [0, 191, 255], + dimgray: [105, 105, 105], + dimgrey: [105, 105, 105], + dodgerblue: [30, 144, 255], + firebrick: [178, 34, 34], + floralwhite: [255, 250, 240], + forestgreen: [34, 139, 34], + fuchsia: [255, 0, 255], + gainsboro: [220, 220, 220], + ghostwhite: [248, 248, 255], + gold: [255, 215, 0], + goldenrod: [218, 165, 32], + gray: [128, 128, 128], + grey: [128, 128, 128], + green: [0, 128, 0], + greenyellow: [173, 255, 47], + honeydew: [240, 255, 240], + hotpink: [255, 105, 180], + indianred: [205, 92, 92], + indigo: [75, 0, 130], + ivory: [255, 255, 240], + khaki: [240, 230, 140], + lavender: [230, 230, 250], + lavenderblush: [255, 240, 245], + lawngreen: [124, 252, 0], + lemonchiffon: [255, 250, 205], + lightblue: [173, 216, 230], + lightcoral: [240, 128, 128], + lightcyan: [224, 255, 255], + lightgoldenrodyellow: [250, 250, 210], + lightgray: [211, 211, 211], + lightgreen: [144, 238, 144], + lightgrey: [211, 211, 211], + lightpink: [255, 182, 193], + lightsalmon: [255, 160, 122], + lightseagreen: [32, 178, 170], + lightskyblue: [135, 206, 250], + lightslategray: [119, 136, 153], + lightslategrey: [119, 136, 153], + lightsteelblue: [176, 196, 222], + lightyellow: [255, 255, 224], + lime: [0, 255, 0], + limegreen: [50, 205, 50], + linen: [250, 240, 230], + magenta: [255, 0, 255], + maroon: [128, 0, 0], + mediumaquamarine: [102, 205, 170], + mediumblue: [0, 0, 205], + mediumorchid: [186, 85, 211], + mediumpurple: [147, 112, 219], + mediumseagreen: [60, 179, 113], + mediumslateblue: [123, 104, 238], + mediumspringgreen: [0, 250, 154], + mediumturquoise: [72, 209, 204], + mediumvioletred: [199, 21, 133], + midnightblue: [25, 25, 112], + mintcream: [245, 255, 250], + mistyrose: [255, 228, 225], + moccasin: [255, 228, 181], + navajowhite: [255, 222, 173], + navy: [0, 0, 128], + oldlace: [253, 245, 230], + olive: [128, 128, 0], + olivedrab: [107, 142, 35], + orange: [255, 165, 0], + orangered: [255, 69, 0], + orchid: [218, 112, 214], + palegoldenrod: [238, 232, 170], + palegreen: [152, 251, 152], + paleturquoise: [175, 238, 238], + palevioletred: [219, 112, 147], + papayawhip: [255, 239, 213], + peachpuff: [255, 218, 185], + peru: [205, 133, 63], + pink: [255, 192, 203], + plum: [221, 160, 221], + powderblue: [176, 224, 230], + purple: [128, 0, 128], + red: [255, 0, 0], + rosybrown: [188, 143, 143], + royalblue: [65, 105, 225], + saddlebrown: [139, 69, 19], + salmon: [250, 128, 114], + sandybrown: [244, 164, 96], + seagreen: [46, 139, 87], + seashell: [255, 245, 238], + sienna: [160, 82, 45], + silver: [192, 192, 192], + skyblue: [135, 206, 235], + slateblue: [106, 90, 205], + slategray: [112, 128, 144], + slategrey: [112, 128, 144], + snow: [255, 250, 250], + springgreen: [0, 255, 127], + steelblue: [70, 130, 180], + tan: [210, 180, 140], + teal: [0, 128, 128], + thistle: [216, 191, 216], + tomato: [255, 99, 71], + turquoise: [64, 224, 208], + violet: [238, 130, 238], + wheat: [245, 222, 179], + white: [255, 255, 255], + whitesmoke: [245, 245, 245], + yellow: [255, 255, 0], + yellowgreen: [154, 205, 50] + }; + +}).call(this); diff --git a/pitfall/pdfkit/lib/reference.js b/pitfall/pdfkit/lib/reference.js deleted file mode 100644 index cf745f25..00000000 --- a/pitfall/pdfkit/lib/reference.js +++ /dev/null @@ -1,117 +0,0 @@ -// 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 new file mode 100644 index 00000000..806aefa6 --- /dev/null +++ b/pitfall/pitfall/kit/color.rkt @@ -0,0 +1,256 @@ +#lang racket/base +(require racket/class racket/string) +(provide color-mixin) +(require "helper.rkt") + +(define (color-mixin %) + (class % + (super-new) + (field [(@_opacityRegistry _opacityRegistry) #f] + [(@_opacityCount _opacityCount) #f] + [(@_gradCount _gradCount) #f]) + + (define/public (initColor) + (set! @_opacityRegistry (mhash)) + (set! @_opacityCount 0) + (set! @_gradCount 0)) + + ;; parses color string into list of values + (public [@_normalizeColor _normalizeColor]) + (define (@_normalizeColor color) + (cond + #;[(is-a? color PDFGradient) color] ; todo + ;; 3-digit hex becomes 6-digit hex + [(and (string? color) + (regexp-match #px"^#(?i:[0-9A-F]){3}$" color)) + (@_normalizeColor + (list->string (cdr (apply append + (for/list ([c (in-string color)]) + (list c c))))))] + ;; 6-digit hexish string becomes list of hex numbers and maybe #f vals + [(and (string? color) (= 7 (string-length color)) (string-prefix? color "#")) + (@_normalizeColor + (for/list ([str (in-list (regexp-match* #rx".." (string-trim color "#")))]) + (string->number str 16)))] + ;; named color + [(and (string? color) (hash-ref namedColors color #f)) => @_normalizeColor] + ;; array of numbers + [(and (list? color) (andmap number? color)) + (for/list ([i (in-list color)]) + (define x (/ i (case (length color) + [(3) 255.0] ; RGB + [(4) 100.0] ; CMYK + [else 1]))) + (if (integer? x) (inexact->exact x) x))] + [else #f])) + + (public [@_setColor _setColor]) + (define (@_setColor color stroke) + (set! color (@_normalizeColor color)) + (define op (if stroke "SCN" "scn")) + (cond + [(not color)] + #;[(is-a? color PDFGradient) + (@_setColorSpace "Pattern" stroke) + (send color apply op) + #t] ; todo + [else + (define space (if (= (length color) 4) + "DeviceCMYK" + "DeviceRGB")) + (@_setColorSpace space stroke) + (set! color (string-join (map number color) " ")) + (send this addContent (format "~a ~a" color op)) + #t])) + + + (public [@_setColorSpace _setColorSpace]) + (define (@_setColorSpace space stroke) + (define op (if stroke "CS" "cs")) + (send this addContent (format "/~a ~a" space op))) + + + (field ([@_fillColor _fillColor] #f)) + (public [@fillColor fillColor]) + (define (@fillColor color [opacity 1]) + (define set (@_setColor color #f)) + (when set (@fillOpacity opacity)) + + ;; save this for text wrapper, which needs to reset + ;; the fill color on new pages + (set! @_fillColor (list color opacity)) + this) + + + (public [@fillOpacity fillOpacity]) + (define (@fillOpacity opacity) + (@_doOpacity opacity #f) + this) + + + (public [@_doOpacity doOpacity]) + (define (@_doOpacity fillOpacity strokeOpacity) + (error 'bonk)) + + )) + +(define namedColors + (mhash "aliceblue" '(240 248 255) + "antiquewhite" '(250 235 215) + "aqua" '(0 255 255) + "aquamarine" '(127 255 212) + "azure" '(240 255 255) + "beige" '(245 245 220) + "bisque" '(255 228 196) + "black" '(0 0 0) + "blanchedalmond" '(255 235 205) + "blue" '(0 0 255) + "blueviolet" '(138 43 226) + "brown" '(165 42 42) + "burlywood" '(222 184 135) + "cadetblue" '(95 158 160) + "chartreuse" '(127 255 0) + "chocolate" '(210 105 30) + "coral" '(255 127 80) + "cornflowerblue" '(100 149 237) + "cornsilk" '(255 248 220) + "crimson" '(220 20 60) + "cyan" '(0 255 255) + "darkblue" '(0 0 139) + "darkcyan" '(0 139 139) + "darkgoldenrod" '(184 134 11) + "darkgray" '(169 169 169) + "darkgreen" '(0 100 0) + "darkgrey" '(169 169 169) + "darkkhaki" '(189 183 107) + "darkmagenta" '(139 0 139) + "darkolivegreen" '(85 107 47) + "darkorange" '(255 140 0) + "darkorchid" '(153 50 204) + "darkred" '(139 0 0) + "darksalmon" '(233 150 122) + "darkseagreen" '(143 188 143) + "darkslateblue" '(72 61 139) + "darkslategray" '(47 79 79) + "darkslategrey" '(47 79 79) + "darkturquoise" '(0 206 209) + "darkviolet" '(148 0 211) + "deeppink" '(255 20 147) + "deepskyblue" '(0 191 255) + "dimgray" '(105 105 105) + "dimgrey" '(105 105 105) + "dodgerblue" '(30 144 255) + "firebrick" '(178 34 34) + "floralwhite" '(255 250 240) + "forestgreen" '(34 139 34) + "fuchsia" '(255 0 255) + "gainsboro" '(220 220 220) + "ghostwhite" '(248 248 255) + "gold" '(255 215 0) + "goldenrod" '(218 165 32) + "gray" '(128 128 128) + "grey" '(128 128 128) + "green" '(0 128 0) + "greenyellow" '(173 255 47) + "honeydew" '(240 255 240) + "hotpink" '(255 105 180) + "indianred" '(205 92 92) + "indigo" '(75 0 130) + "ivory" '(255 255 240) + "khaki" '(240 230 140) + "lavender" '(230 230 250) + "lavenderblush" '(255 240 245) + "lawngreen" '(124 252 0) + "lemonchiffon" '(255 250 205) + "lightblue" '(173 216 230) + "lightcoral" '(240 128 128) + "lightcyan" '(224 255 255) + "lightgoldenrodyellow" '(250 250 210) + "lightgray" '(211 211 211) + "lightgreen" '(144 238 144) + "lightgrey" '(211 211 211) + "lightpink" '(255 182 193) + "lightsalmon" '(255 160 122) + "lightseagreen" '(32 178 170) + "lightskyblue" '(135 206 250) + "lightslategray" '(119 136 153) + "lightslategrey" '(119 136 153) + "lightsteelblue" '(176 196 222) + "lightyellow" '(255 255 224) + "lime" '(0 255 0) + "limegreen" '(50 205 50) + "linen" '(250 240 230) + "magenta" '(255 0 255) + "maroon" '(128 0 0) + "mediumaquamarine" '(102 205 170) + "mediumblue" '(0 0 205) + "mediumorchid" '(186 85 211) + "mediumpurple" '(147 112 219) + "mediumseagreen" '(60 179 113) + "mediumslateblue" '(123 104 238) + "mediumspringgreen" '(0 250 154) + "mediumturquoise" '(72 209 204) + "mediumvioletred" '(199 21 133) + "midnightblue" '(25 25 112) + "mintcream" '(245 255 250) + "mistyrose" '(255 228 225) + "moccasin" '(255 228 181) + "navajowhite" '(255 222 173) + "navy" '(0 0 128) + "oldlace" '(253 245 230) + "olive" '(128 128 0) + "olivedrab" '(107 142 35) + "orange" '(255 165 0) + "orangered" '(255 69 0) + "orchid" '(218 112 214) + "palegoldenrod" '(238 232 170) + "palegreen" '(152 251 152) + "paleturquoise" '(175 238 238) + "palevioletred" '(219 112 147) + "papayawhip" '(255 239 213) + "peachpuff" '(255 218 185) + "peru" '(205 133 63) + "pink" '(255 192 203) + "plum" '(221 160 221) + "powderblue" '(176 224 230) + "purple" '(128 0 128) + "red" '(255 0 0) + "rosybrown" '(188 143 143) + "royalblue" '(65 105 225) + "saddlebrown" '(139 69 19) + "salmon" '(250 128 114) + "sandybrown" '(244 164 96) + "seagreen" '(46 139 87) + "seashell" '(255 245 238) + "sienna" '(160 82 45) + "silver" '(192 192 192) + "skyblue" '(135 206 235) + "slateblue" '(106 90 205) + "slategray" '(112 128 144) + "slategrey" '(112 128 144) + "snow" '(255 250 250) + "springgreen" '(0 255 127) + "steelblue" '(70 130 180) + "tan" '(210 180 140) + "teal" '(0 128 128) + "thistle" '(216 191 216) + "tomato" '(255 99 71) + "turquoise" '(64 224 208) + "violet" '(238 130 238) + "wheat" '(245 222 179) + "white" '(255 255 255) + "whitesmoke" '(245 245 245) + "yellow" '(255 255 0) + "yellowgreen" '(154 205 50))) + + +(define c (new (color-mixin object%))) + +(module+ test + (require rackunit) + (check-equal? (send c _normalizeColor "#6699Cc") '(0.4 0.6 0.8)) + (check-false (send c _normalizeColor "#88aaCCC")) + (check-equal? (send c _normalizeColor "#69C") '(0.4 0.6 0.8)) + (check-equal? (send c _normalizeColor "#69c") '(0.4 0.6 0.8)) + (check-false (send c _normalizeColor "#8aCC")) + (check-equal? (send c _normalizeColor "aqua") '(0 1 1))) \ No newline at end of file diff --git a/pitfall/pitfall/kit/document.rkt b/pitfall/pitfall/kit/document.rkt index c5dc604d..4fa2b28b 100644 --- a/pitfall/pitfall/kit/document.rkt +++ b/pitfall/pitfall/kit/document.rkt @@ -1,14 +1,14 @@ #lang sugar/debug racket/base -(require racket/class racket/draw br/list racket/list racket/format racket/port) +(require racket/class racket/draw racket/list racket/format racket/port) (require sugar/debug) (provide PDFDocument) (require "reference.rkt" "struct.rkt" "object.rkt" "page.rkt" "helper.rkt" "params.rkt") -(require "vector.rkt") +(require "vector.rkt" "color.rkt") (define PDFDocument ;; actually is an instance of readable.Stream, which is an input port - (class (vector-mixin object%) + (class (color-mixin (vector-mixin object%)) (init-field [(@options options) (mhash)]) (let ([output-file (hash-ref @options 'out "outrkt.pdf")]) (super-new)) @@ -48,7 +48,7 @@ ;; todo ;; Initialize mixins - #;(@initColor) + (· this initColor) (· this initVector) #;(@initFonts) #;(@initText) @@ -102,7 +102,7 @@ ;; create a page object (set! @page (make-object PDFPage this options)) - (push-end! @_pageBuffer @page) + (push! @_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) @@ -235,4 +235,4 @@ (with-output-to-file (string-append fn ".pdf") (λ () (display result-str)) #:exists 'replace) (check-equal? (file->bytes (string-append fn ".pdf")) (file->bytes (string-append fn " copy.pdf"))) - #;(display (bytes->string/latin-1 result-str))) \ No newline at end of file + (display (bytes->string/latin-1 result-str))) \ No newline at end of file diff --git a/pitfall/pitfall/kit/helper.rkt b/pitfall/pitfall/kit/helper.rkt index d2be36fb..0f852296 100644 --- a/pitfall/pitfall/kit/helper.rkt +++ b/pitfall/pitfall/kit/helper.rkt @@ -1,6 +1,6 @@ #lang racket/base -(require (for-syntax racket/base) racket/class sugar/list racket/list) -(provide (all-defined-out)) +(require (for-syntax racket/base) racket/class sugar/list racket/list (only-in br/list push! pop!)) +(provide (all-defined-out) push! pop!) (define-syntax (· stx) (syntax-case stx () @@ -56,10 +56,25 @@ (define (newBuffer x) (string->bytes/latin-1 (format "~a" x))) (define buffer-length bytes-length) + ;; js-style `push`, which appends to end of list (define-syntax-rule (push-end! id thing) (set! id (append id (list thing)))) (module+ test (define xs '(1 2 3)) (push-end! xs 4) - (check-equal? xs '(1 2 3 4))) \ No newline at end of file + (check-equal? xs '(1 2 3 4))) + +;; fancy number->string. bounds are checked, inexact integers are coerced. +(define (number x) + (unless (< -1e21 x 1e21) + (raise-argument-error 'number "valid number" x)) + (let ([x (/ (round (* x 1e6)) 1e6)]) + (number->string (if (integer? x) + (inexact->exact x) + x)))) + +(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 diff --git a/pitfall/pitfall/kit/object.rkt b/pitfall/pitfall/kit/object.rkt index 3c630571..e8b1c1ab 100644 --- a/pitfall/pitfall/kit/object.rkt +++ b/pitfall/pitfall/kit/object.rkt @@ -1,6 +1,6 @@ #lang at-exp br (require racket/class racket/string racket/list srfi/19) -(require "struct.rkt") +(require "struct.rkt" "helper.rkt") (provide PDFObject) (define PDFObject @@ -45,12 +45,8 @@ (bytes-set! newbuff (add1 bidx) (bytes-ref buff bidx)) newbuff)) - (define/public (number n) - (unless (< -1e21 n 1e21) - (raise-argument-error 'number "valid number" n)) - (define result (/ (round (* n 1e6)) 1e6)) - (if (integer? result) (inexact->exact result) result)) - + ;; moved this to helper module + #;(define/public (number n) ···) (define/public (convert object) (let loop ([x object]) diff --git a/pitfall/pitfall/kit/vector.rkt b/pitfall/pitfall/kit/vector.rkt index 137ce2c1..fdfee094 100644 --- a/pitfall/pitfall/kit/vector.rkt +++ b/pitfall/pitfall/kit/vector.rkt @@ -1,14 +1,71 @@ #lang racket/base -(require racket/class) +(require racket/class racket/match racket/string racket/format) (provide vector-mixin) +(require "helper.rkt") + +(define default-ctm-value '(1 0 0 1 0 0)) (define (vector-mixin %) (class % (super-new) - (field [(@_ctm _ctm) '(1 0 0 1 0 0)] + (field [(@_ctm _ctm) default-ctm-value] [(@_ctmStack _ctmStack) null]) - (define/public (initVector) (void)) + (define/public (initVector) + (set! @_ctm default-ctm-value) + (set! @_ctmStack null)) + + (define/public (save) + (push! @_ctmStack @_ctm) + (send this addContent "q")) + + (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) - (send this addContent "1 0 0 -1 0 792 cm")))) ; todo debug \ No newline at end of file + (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) + (send this addContent (format "~a ~a m" x y))) + + (define/public (lineTo x y) + (send this addContent (format "~a ~a l" x y))) + + (public [@_windingRule _windingRule]) + (define (@_windingRule rule) + (if (and (string? rule) (regexp-match #rx"^even-?odd$" rule)) + "*" + "")) + + (define/public (fill color [rule #f]) + (when (regexp-match #rx"^(even-?odd)|(non-?zero)$" color) + (set! rule color) + (set! color #f)) + (when color (send this fillColor color)) ;; fillColor method is from color mixin + (send this addContent (format "f~a" (@_windingRule rule)))) + + )) + +(module+ test + (require rackunit) + (define v (new (vector-mixin object%))) + (check-equal? (· v _ctm) default-ctm-value) + (check-equal? (send v transform 1 2 3 4 5 6 #:debug #t) "1 2 3 4 5 6 cm") + (check-equal? (· v _ctm) '(1 2 3 4 5 6)) + (check-equal? (send v transform 1 2 3 4 5 6 #:debug #t) "1 2 3 4 5 6 cm") + (check-equal? (· v _ctm) '(7 10 15 22 28 40)) + (check-equal? (send v transform 1 2 3 4 5 6 #:debug #t) "1 2 3 4 5 6 cm") + (check-equal? (· v _ctm) '(37 54 81 118 153 222))) \ No newline at end of file diff --git a/pitfall/pktest/test.pdf b/pitfall/pktest/test.pdf index dfeb8076..dcccbd0d 100644 Binary files a/pitfall/pktest/test.pdf and b/pitfall/pktest/test.pdf differ diff --git a/pitfall/pktest/test0.coffee b/pitfall/pktest/test0.coffee index f068cb68..44ac70db 100644 --- a/pitfall/pktest/test0.coffee +++ b/pitfall/pktest/test0.coffee @@ -2,7 +2,7 @@ PDFDocument = require 'pdfkit' fs = require 'fs' # Create a new PDFDocument -doc = new PDFDocument +doc = new PDFDocument({compress: no}) doc.pipe(fs.createWriteStream('test0.pdf')) doc.end() \ No newline at end of file diff --git a/pitfall/pktest/test0.pdf b/pitfall/pktest/test0.pdf index 7597a018..c79a55b0 100644 Binary files a/pitfall/pktest/test0.pdf and b/pitfall/pktest/test0.pdf differ diff --git a/pitfall/pktest/test0.rkt b/pitfall/pktest/test0.rkt new file mode 100644 index 00000000..1df60153 --- /dev/null +++ b/pitfall/pktest/test0.rkt @@ -0,0 +1,7 @@ +#lang br + +(require pitfall/kit/document) + +(define doc (new PDFDocument)) +(send doc pipe (open-output-file "test0rkt.pdf" #:exists 'replace)) +(send doc end) \ No newline at end of file diff --git a/pitfall/pktest/test0rkt.pdf b/pitfall/pktest/test0rkt.pdf new file mode 100644 index 00000000..f84357ef --- /dev/null +++ b/pitfall/pktest/test0rkt.pdf @@ -0,0 +1,63 @@ +%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 18 +>> +stream +1 0 0 -1 0 792 cm + +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 +0000000403 00000 n +0000000354 00000 n +0000000186 00000 n +0000000119 00000 n +0000000015 00000 n +0000000254 00000 n +trailer +<< +/Info 6 0 R +/Root 2 0 R +/Size 7 +>> +startxref +460 +%%EOF diff --git a/pitfall/pktest/test1.coffee b/pitfall/pktest/test1.coffee new file mode 100644 index 00000000..48b758a1 --- /dev/null +++ b/pitfall/pktest/test1.coffee @@ -0,0 +1,24 @@ +PDFDocument = require 'pdfkit' +fs = require 'fs' + +# Create a new PDFDocument +doc = new PDFDocument({compress: no}) +doc.pipe(fs.createWriteStream('test1.pdf')) + +# Draw a triangle and a circle +doc.save() + .moveTo(100, 150) + .lineTo(100, 250) + .lineTo(200, 250) + .fill("#FF3300") + +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 new file mode 100644 index 00000000..3abf80fb --- /dev/null +++ b/pitfall/pktest/test1.pdf @@ -0,0 +1,104 @@ +%PDF-1.3 +%ÿÿÿÿ +6 0 obj +<< +/Type /ExtGState +/ca 1 +>> +endobj +5 0 obj +<< +/Type /Page +/Parent 1 0 R +/MediaBox [0 0 612 792] +/Contents 3 0 R +/Resources 4 0 R +>> +endobj +4 0 obj +<< +/ProcSet [/PDF /Text /ImageB /ImageC /ImageI] +/ExtGState << +/Gs1 6 0 R +>> +>> +endobj +3 0 obj +<< +/Length 421 +>> +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 +/Gs1 gs +f +230 200 m +230 172.385763 252.385763 150 280 150 c +307.614237 150 330 172.385763 330 200 c +330 227.614237 307.614237 250 280 250 c +252.385763 250 230 227.614237 230 200 c +h +/DeviceRGB cs +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 +7 0 obj +<< +/Producer (PDFKit) +/Creator (PDFKit) +/CreationDate (D:20170513192922Z) +>> +endobj +2 0 obj +<< +/Type /Catalog +/Pages 1 0 R +>> +endobj +1 0 obj +<< +/Type /Pages +/Count 1 +/Kids [5 0 R] +>> +endobj +xref +0 8 +0000000000 65535 f +0000000871 00000 n +0000000822 00000 n +0000000258 00000 n +0000000163 00000 n +0000000059 00000 n +0000000015 00000 n +0000000730 00000 n +trailer +<< +/Size 8 +/Root 2 0 R +/Info 7 0 R +>> +startxref +928 +%%EOF diff --git a/pitfall/pktest/test1.rkt b/pitfall/pktest/test1.rkt new file mode 100644 index 00000000..2bfcc677 --- /dev/null +++ b/pitfall/pktest/test1.rkt @@ -0,0 +1,18 @@ +#lang br + +(require pitfall/kit/document) + +;; 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 (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 doc end) \ No newline at end of file diff --git a/pitfall/pktest/test1rkt.pdf b/pitfall/pktest/test1rkt.pdf new file mode 100644 index 00000000..e69de29b diff --git a/pitfall/pktest/testrkt0.rkt b/pitfall/pktest/testrkt0.rkt deleted file mode 100644 index 1bdd8054..00000000 --- a/pitfall/pktest/testrkt0.rkt +++ /dev/null @@ -1,21 +0,0 @@ -#lang br - -(require pitfall/kit/document) - -(define doc (new PDFDocument)) -(send doc pipe (open-output-file "testrkt0.pdf" #:exists 'replace)) -(send doc end) - -#| - var PDFDocument, doc, fs; - - PDFDocument = require('pdfkit'); - - fs = require('fs'); - - doc = new PDFDocument; - - doc.pipe(fs.createWriteStream('out.pdf')); - - doc.end(); -|# \ No newline at end of file