fix transformation composition & annotations

main
Matthew Butterick 7 years ago
parent a99be67345
commit 0c441b2478

@ -1,5 +1,6 @@
module.exports =
annotate: (x, y, w, h, options) ->
#console.log(@_ctm)
options.Type = 'Annot'
options.Rect = @_convertRect x, y, w, h
options.Border = [0, 0, 0]

@ -205,7 +205,7 @@ module.exports =
# create underline or strikethrough line
if options.underline or options.strike
console.log("enter underline")
#console.log("enter underline")
@save()
@strokeColor @_fillColor... unless options.stroke

@ -1,265 +0,0 @@
// 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);

@ -50,10 +50,8 @@
[y1 (+ y1 h)]
[x2 (+ x1 w)])
(match-define (list m0 m1 m2 m3 m4 m5) (· this _ctm))
;; original code mutates x1 and y1 during transformation
;; I think this is wrong
(let* ([x1a (+ (* x1 m0) (* y1 m2) m4)]
[y1a (+ (* x1 m1) (* y1 m3) m5)]
[x2a (+ (* x2 m0) (* y2 m2) m4)]
[y2a (+ (* x2 m1) (* y2 m3) m5)])
(list x1a y1a x2a y2a))))
(let* ([x1 (+ (* x1 m0) (* y1 m2) m4)]
[y1 (+ (* x1 m1) (* y1 m3) m5)]
[x2 (+ (* x2 m0) (* y2 m2) m4)]
[y2 (+ (* x2 m1) (* y2 m3) m5)])
(list x1 y1 x2 y2))))

@ -4,11 +4,16 @@ fs = require 'fs'
make = (doc) ->
# Add some text with annotations
doc.fillColor("blue")
.translate(50,50)
.font('Helvetica', 30)
.text('Here is a link!', 100, 100, { link: 'http://google.com/', underline: true, width: false})
doc.end()
doc = new PDFDocument({compress: no})
doc.pipe(fs.createWriteStream('test11.pdf'))
make doc
doc = new PDFDocument({compress: yes})
doc.pipe(fs.createWriteStream('test11c.pdf'))
make doc

@ -0,0 +1,132 @@
%PDF-1.3
%ÿÿÿÿ
6 0 obj
<<
/Type /ExtGState
/ca 1
>>
endobj
7 0 obj
<<
/S /URI
/URI (http://google.com/)
>>
endobj
8 0 obj
<<
/Subtype /Link
/A 7 0 R
/Type /Annot
/Rect [150 614.25 331.71 642]
/Border [0 0 0]
>>
endobj
9 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
/Annots [8 0 R]
>>
endobj
4 0 obj
<<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
/ExtGState <<
/Gs1 6 0 R
/Gs2 9 0 R
>>
/Font <<
/F1 10 0 R
>>
>>
endobj
3 0 obj
<<
/Length 238
>>
stream
1 0 0 -1 0 792 cm
/DeviceRGB cs
0 0 1 scn
/Gs1 gs
1 0 0 1 50 50 cm
q
/DeviceRGB CS
0 0 1 SCN
/Gs2 gs
3 w
100 124.75 m
281.71 124.75 l
S
Q
q
1 0 0 -1 0 792 cm
BT
1 0 0 1 100 670.46 Tm
/F1 30 Tf
[<486572652069732061206c696e6b21> 0] TJ
ET
Q
endstream
endobj
11 0 obj
<<
/Producer (PDFKit)
/Creator (PDFKit)
/CreationDate (D:20170824194220Z)
>>
endobj
10 0 obj
<<
/Type /Font
/BaseFont /Helvetica
/Subtype /Type1
/Encoding /WinAnsiEncoding
>>
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 12
0000000000 65535 f
0000001040 00000 n
0000000991 00000 n
0000000511 00000 n
0000000382 00000 n
0000000262 00000 n
0000000015 00000 n
0000000059 00000 n
0000000114 00000 n
0000000218 00000 n
0000000893 00000 n
0000000800 00000 n
trailer
<<
/Size 12
/Root 2 0 R
/Info 11 0 R
>>
startxref
1097
%%EOF

@ -4,6 +4,7 @@
(send* doc
[fillColor "blue"]
[font "Helvetica" 30]
[translate 50 50]
[text "Here is a link!" 100 100 (hash
'link "http://google.com/"
'underline #t

@ -17,7 +17,7 @@ endobj
/Subtype /Link
/A 7 0 R
/Type /Annot
/Rect [100 664.25 281.71 692]
/Rect [150 614.25 331.71 642]
/Border [0 0 0]
>>
endobj
@ -53,7 +53,7 @@ endobj
<<
/Producer (PDFKit)
/Creator (PDFKit)
/CreationDate (D:20170529031624Z)
/CreationDate (D:20170824194220Z)
>>
endobj
10 0 obj
@ -79,12 +79,12 @@ endobj
endobj
3 0 obj
<<
/Length 161
/Length 167
/Filter /FlateDecode
>>
stream
xœeŽÁ
Â0 †ïyŠ¼€[“µéâaS]oâeeŠà)èë›ÁAB~Hò%<08>ÆT|Å×Ãë‡c[cL0M S|@Þ&Âkç/Ñt3Ñ5û‰à‰(ð d´Ë6ó2Oßúô ýY×an*«ûâMfƒþ´%, œ–¶çY©|¡JšQ*¤gZ¡9cØÁ&¨Ç<07>'0P
xœeŽË
Â@ E÷ùŠü€m2ídZ­Zp!hg'nªV<>ý}3ø@<40>< 7œä2Æ„µ¹Ú`÷s¶]ƒ!BÚ2Æp…¼Œ§Œ/ÍRJn¿HÛ¿¾]'Ä$¤À0©jÊÌYÁTœ9þÌèa£gøÏKã¿ï/Ž²RЫÉ%cAè<41>°•XgÄ’ÚÚY+H-ƒ Ï<>öèW°ðúã Ÿ 3v
endstream
endobj
xref
@ -108,5 +108,5 @@ trailer
/Info 11 0 R
>>
startxref
1041
1047
%%EOF

@ -17,7 +17,7 @@ endobj
/A 7 0 R
/Border [0 0 0]
/Subtype /Link
/Rect [100 664.25 281.71 692]
/Rect [150 614.25 331.71 642]
/Type /Annot
>>
endobj
@ -51,12 +51,12 @@ endobj
endobj
3 0 obj
<<
/Length 161
/Length 167
/Filter /FlateDecode
>>
stream
xœeŽÁ
Â0 †ïyŠ¼€[“µéâaS]oâeeŠà)èë›ÁAB~Hò%<08>ÆT|Å×Ãë‡c[cL0M S|@Þ&Âkç/Ñt3Ñ5û‰à‰(ð d´Ë6ó2Oßúô ýY×an*«ûâMfƒþ´%, œ–¶çY©|¡JšQ*¤gZ¡9cØÁ&¨Ç<07>'0P
xœeŽË
Â@ E÷ùŠü€m2ídZ­Zp!hg'nªV<>ý}3ø@<40>< 7œä2Æ„µ¹Ú`÷s¶]ƒ!BÚ2Æp…¼Œ§Œ/ÍRJn¿HÛ¿¾]'Ä$¤À0©jÊÌYÁTœ9þÌèa£gøÏKã¿ï/Ž²RЫÉ%cAè<41>°•XgÄ’ÚÚY+H-ƒ Ï<>öèW°ðúã Ÿ 3v
endstream
endobj
11 0 obj
@ -90,8 +90,8 @@ endobj
xref
0 12
0000000000 65535 f
0000000984 00000 n
0000000935 00000 n
0000000990 00000 n
0000000941 00000 n
0000000511 00000 n
0000000382 00000 n
0000000262 00000 n
@ -99,8 +99,8 @@ xref
0000000059 00000 n
0000000114 00000 n
0000000218 00000 n
0000000837 00000 n
0000000744 00000 n
0000000843 00000 n
0000000750 00000 n
trailer
<<
/Info 11 0 R
@ -108,5 +108,5 @@ trailer
/Size 12
>>
startxref
1041
1047
%%EOF

@ -17,7 +17,7 @@ endobj
/A 7 0 R
/Border [0 0 0]
/Subtype /Link
/Rect [100 664.25 281.71 692]
/Rect [150 614.25 331.71 642]
/Type /Annot
>>
endobj
@ -51,12 +51,12 @@ endobj
endobj
3 0 obj
<<
/Length 161
/Length 167
/Filter /FlateDecode
>>
stream
xœeŽÁ
Â0 †ïyŠ¼€[“µéâaS]oâeeŠà)èë›ÁAB~Hò%<08>ÆT|Å×Ãë‡c[cL0M S|@Þ&Âkç/Ñt3Ñ5û‰à‰(ð d´Ë6ó2Oßúô ýY×an*«ûâMfƒþ´%, œ–¶çY©|¡JšQ*¤gZ¡9cØÁ&¨Ç<07>'0P
xœeŽË
Â@ E÷ùŠü€m2ídZ­Zp!hg'nªV<>ý}3ø@<40>< 7œä2Æ„µ¹Ú`÷s¶]ƒ!BÚ2Æp…¼Œ§Œ/ÍRJn¿HÛ¿¾]'Ä$¤À0©jÊÌYÁTœ9þÌèa£gøÏKã¿ï/Ž²RЫÉ%cAè<41>°•XgÄ’ÚÚY+H-ƒ Ï<>öèW°ðúã Ÿ 3v
endstream
endobj
11 0 obj
@ -90,8 +90,8 @@ endobj
xref
0 12
0000000000 65535 f
0000000984 00000 n
0000000935 00000 n
0000000990 00000 n
0000000941 00000 n
0000000511 00000 n
0000000382 00000 n
0000000262 00000 n
@ -99,8 +99,8 @@ xref
0000000059 00000 n
0000000114 00000 n
0000000218 00000 n
0000000837 00000 n
0000000744 00000 n
0000000843 00000 n
0000000750 00000 n
trailer
<<
/Info 11 0 R
@ -108,5 +108,5 @@ trailer
/Size 12
>>
startxref
1041
1047
%%EOF

@ -17,7 +17,7 @@ endobj
/A 7 0 R
/Border [0 0 0]
/Subtype /Link
/Rect [100 664.25 281.71 692]
/Rect [150 614.25 331.71 642]
/Type /Annot
>>
endobj
@ -51,13 +51,14 @@ endobj
endobj
3 0 obj
<<
/Length 221
/Length 238
>>
stream
1 0 0 -1 0 792 cm
/DeviceRGB cs
0 0 1 scn
/Gs1 gs
1 0 0 1 50 50 cm
q
/DeviceRGB CS
0 0 1 SCN
@ -109,8 +110,8 @@ endobj
xref
0 12
0000000000 65535 f
0000001023 00000 n
0000000974 00000 n
0000001040 00000 n
0000000991 00000 n
0000000511 00000 n
0000000382 00000 n
0000000262 00000 n
@ -118,8 +119,8 @@ xref
0000000059 00000 n
0000000114 00000 n
0000000218 00000 n
0000000876 00000 n
0000000783 00000 n
0000000893 00000 n
0000000800 00000 n
trailer
<<
/Info 11 0 R
@ -127,5 +128,5 @@ trailer
/Size 12
>>
startxref
1080
1097
%%EOF

@ -17,7 +17,7 @@ endobj
/A 7 0 R
/Border [0 0 0]
/Subtype /Link
/Rect [100 664.25 281.71 692]
/Rect [150 614.25 331.71 642]
/Type /Annot
>>
endobj
@ -51,13 +51,14 @@ endobj
endobj
3 0 obj
<<
/Length 221
/Length 238
>>
stream
1 0 0 -1 0 792 cm
/DeviceRGB cs
0 0 1 scn
/Gs1 gs
1 0 0 1 50 50 cm
q
/DeviceRGB CS
0 0 1 SCN
@ -109,8 +110,8 @@ endobj
xref
0 12
0000000000 65535 f
0000001023 00000 n
0000000974 00000 n
0000001040 00000 n
0000000991 00000 n
0000000511 00000 n
0000000382 00000 n
0000000262 00000 n
@ -118,8 +119,8 @@ xref
0000000059 00000 n
0000000114 00000 n
0000000218 00000 n
0000000876 00000 n
0000000783 00000 n
0000000893 00000 n
0000000800 00000 n
trailer
<<
/Info 11 0 R
@ -127,5 +128,5 @@ trailer
/Size 12
>>
startxref
1080
1097
%%EOF

@ -160,7 +160,7 @@
[(pair? points)
(apply moveTo this (car points))
(for ([pt (in-list (cdr points))])
(apply lineTo this pt))
(apply lineTo this pt))
(closePath this)]
[else this]))
@ -200,16 +200,16 @@
(format "~a cm" (string-join (map number ctm) " ")))
(define/contract (combine-transforms m-transform n-transform)
(define/contract (combine-transforms m new-ctm)
(tm/c tm/c . -> . tm/c)
(match-define (list m11 m12 m21 m22 mdx mdy) m-transform)
(match-define (list n11 n12 n21 n22 ndx ndy) n-transform)
(list (+ (* n11 m11) (* n21 m12))
(+ (* n12 m11) (* n22 m12))
(+ (* n11 m21) (* n21 m22))
(+ (* n12 m21) (* n22 m22))
(+ (* n11 mdx) (* n21 mdy) ndx)
(+ (* n12 mdx) (* n22 mdy) ndy)))
(match-define (list m0 m1 m2 m3 m4 m5) m)
(match-define (list m11 m12 m21 m22 dx dy) new-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/contract (clip this [rule #f])
@ -261,4 +261,11 @@
(set! ctm (combine-transforms ctm ctm2))
(check-equal? ctm '(7 10 15 22 28 40))
(set! ctm (combine-transforms ctm ctm2))
(check-equal? ctm '(37 54 81 118 153 222)))
(check-equal? ctm '(37 54 81 118 153 222))
(check-equal? (combine-transforms '(1 0 0 -1 0 792.0) '(1 0 0 1 50 50))
'(1 0 0 -1 50 742.0))
(check-equal? (combine-transforms '(1 0 0 -1 50 742.0) '(1 0 0 -1 0 792))
'(1 0 0 1 50 -50.0))
)
Loading…
Cancel
Save