start test11

main
Matthew Butterick 8 years ago
parent 5a06b48f20
commit 7dba1d4c71

@ -0,0 +1,338 @@
// Generated by CoffeeScript 1.12.5
(function() {
var LineWrapper, number;
LineWrapper = require('../line_wrapper');
number = require('../object').number;
module.exports = {
initText: function() {
this.x = 0;
this.y = 0;
return this._lineGap = 0;
},
lineGap: function(_lineGap) {
this._lineGap = _lineGap;
return this;
},
moveDown: function(lines) {
if (lines == null) {
lines = 1;
}
this.y += this.currentLineHeight(true) * lines + this._lineGap;
return this;
},
moveUp: function(lines) {
if (lines == null) {
lines = 1;
}
this.y -= this.currentLineHeight(true) * lines + this._lineGap;
return this;
},
_text: function(text, x, y, options, lineCallback) {
var j, len, line, ref, wrapper;
options = this._initOptions(x, y, options);
text = '' + text;
if (options.wordSpacing) {
text = text.replace(/\s{2,}/g, ' ');
}
if (options.width) {
wrapper = this._wrapper;
if (!wrapper) {
wrapper = new LineWrapper(this, options);
wrapper.on('line', lineCallback);
}
this._wrapper = options.continued ? wrapper : null;
this._textOptions = options.continued ? options : null;
wrapper.wrap(text, options);
} else {
ref = text.split('\n');
for (j = 0, len = ref.length; j < len; j++) {
line = ref[j];
lineCallback(line, options);
}
}
return this;
},
text: function(text, x, y, options) {
return this._text(text, x, y, options, this._line.bind(this));
},
widthOfString: function(string, options) {
if (options == null) {
options = {};
}
return this._font.widthOfString(string, this._fontSize, options.features) + (options.characterSpacing || 0) * (string.length - 1);
},
heightOfString: function(text, options) {
var height, lineGap, ref, x, y;
if (options == null) {
options = {};
}
ref = this, x = ref.x, y = ref.y;
options = this._initOptions(options);
options.height = 2e308;
lineGap = options.lineGap || this._lineGap || 0;
this._text(text, this.x, this.y, options, (function(_this) {
return function(line, options) {
return _this.y += _this.currentLineHeight(true) + lineGap;
};
})(this));
height = this.y - y;
this.x = x;
this.y = y;
return height;
},
list: function(list, x, y, options, wrapper) {
var flatten, i, indent, itemIndent, items, level, levels, midLine, r;
options = this._initOptions(x, y, options);
midLine = Math.round((this._font.ascender / 1000 * this._fontSize) / 2);
r = options.bulletRadius || Math.round((this._font.ascender / 1000 * this._fontSize) / 3);
indent = options.textIndent || r * 5;
itemIndent = options.bulletIndent || r * 8;
level = 1;
items = [];
levels = [];
flatten = function(list) {
var i, item, j, len, results;
results = [];
for (i = j = 0, len = list.length; j < len; i = ++j) {
item = list[i];
if (Array.isArray(item)) {
level++;
flatten(item);
results.push(level--);
} else {
items.push(item);
results.push(levels.push(level));
}
}
return results;
};
flatten(list);
wrapper = new LineWrapper(this, options);
wrapper.on('line', this._line.bind(this));
level = 1;
i = 0;
wrapper.on('firstLine', (function(_this) {
return function() {
var diff, l;
if ((l = levels[i++]) !== level) {
diff = itemIndent * (l - level);
_this.x += diff;
wrapper.lineWidth -= diff;
level = l;
}
_this.circle(_this.x - indent + r, _this.y + midLine, r);
return _this.fill();
};
})(this));
wrapper.on('sectionStart', (function(_this) {
return function() {
var pos;
pos = indent + itemIndent * (level - 1);
_this.x += pos;
return wrapper.lineWidth -= pos;
};
})(this));
wrapper.on('sectionEnd', (function(_this) {
return function() {
var pos;
pos = indent + itemIndent * (level - 1);
_this.x -= pos;
return wrapper.lineWidth += pos;
};
})(this));
wrapper.wrap(items.join('\n'), options);
return this;
},
_initOptions: function(x, y, options) {
var key, margins, ref, val;
if (x == null) {
x = {};
}
if (options == null) {
options = {};
}
if (typeof x === 'object') {
options = x;
x = null;
}
options = (function() {
var k, opts, v;
opts = {};
for (k in options) {
v = options[k];
opts[k] = v;
}
return opts;
})();
if (this._textOptions) {
ref = this._textOptions;
for (key in ref) {
val = ref[key];
if (key !== 'continued') {
if (options[key] == null) {
options[key] = val;
}
}
}
}
if (x != null) {
this.x = x;
}
if (y != null) {
this.y = y;
}
if (options.lineBreak !== false) {
margins = this.page.margins;
if (options.width == null) {
options.width = this.page.width - this.x - margins.right;
}
}
options.columns || (options.columns = 0);
if (options.columnGap == null) {
options.columnGap = 18;
}
return options;
},
_line: function(text, options, wrapper) {
var lineGap;
if (options == null) {
options = {};
}
this._fragment(text, this.x, this.y, options);
lineGap = options.lineGap || this._lineGap || 0;
if (!wrapper) {
return this.x += this.widthOfString(text);
} else {
return this.y += this.currentLineHeight(true) + lineGap;
}
},
_fragment: function(text, x, y, options) {
var addSegment, align, base, characterSpacing, commands, d, encoded, encodedWord, flush, hadOffset, i, j, last, len, len1, lineWidth, lineY, m, mode, name, pos, positions, positionsWord, ref, ref1, renderedWidth, scale, spaceWidth, textWidth, word, wordSpacing, words;
text = ('' + text).replace(/\n/g, '');
if (text.length === 0) {
return;
}
align = options.align || 'left';
wordSpacing = options.wordSpacing || 0;
characterSpacing = options.characterSpacing || 0;
if (options.width) {
switch (align) {
case 'right':
textWidth = this.widthOfString(text.replace(/\s+$/, ''), options);
x += options.lineWidth - textWidth;
break;
case 'center':
x += options.lineWidth / 2 - options.textWidth / 2;
break;
case 'justify':
words = text.trim().split(/\s+/);
textWidth = this.widthOfString(text.replace(/\s+/g, ''), options);
spaceWidth = this.widthOfString(' ') + characterSpacing;
wordSpacing = Math.max(0, (options.lineWidth - textWidth) / Math.max(1, words.length - 1) - spaceWidth);
}
}
renderedWidth = options.textWidth + (wordSpacing * (options.wordCount - 1)) + (characterSpacing * (text.length - 1));
if (options.link) {
this.link(x, y, renderedWidth, this.currentLineHeight(), options.link);
}
if (options.underline || options.strike) {
this.save();
if (!options.stroke) {
this.strokeColor.apply(this, this._fillColor);
}
lineWidth = this._fontSize < 10 ? 0.5 : Math.floor(this._fontSize / 10);
this.lineWidth(lineWidth);
d = options.underline ? 1 : 2;
lineY = y + this.currentLineHeight() / d;
if (options.underline) {
lineY -= lineWidth;
}
this.moveTo(x, lineY);
this.lineTo(x + renderedWidth, lineY);
this.stroke();
this.restore();
}
this.save();
this.transform(1, 0, 0, -1, 0, this.page.height);
y = this.page.height - y - (this._font.ascender / 1000 * this._fontSize);
if ((base = this.page.fonts)[name = this._font.id] == null) {
base[name] = this._font.ref();
}
this.addContent("BT");
this.addContent("1 0 0 1 " + (number(x)) + " " + (number(y)) + " Tm");
this.addContent("/" + this._font.id + " " + (number(this._fontSize)) + " Tf");
mode = options.fill && options.stroke ? 2 : options.stroke ? 1 : 0;
if (mode) {
this.addContent(mode + " Tr");
}
if (characterSpacing) {
this.addContent((number(characterSpacing)) + " Tc");
}
if (wordSpacing) {
words = text.trim().split(/\s+/);
wordSpacing += this.widthOfString(' ') + characterSpacing;
wordSpacing *= 1000 / this._fontSize;
encoded = [];
positions = [];
for (j = 0, len = words.length; j < len; j++) {
word = words[j];
ref = this._font.encode(word, options.features), encodedWord = ref[0], positionsWord = ref[1];
encoded.push.apply(encoded, encodedWord);
positions.push.apply(positions, positionsWord);
positions[positions.length - 1].xAdvance += wordSpacing;
}
} else {
ref1 = this._font.encode(text, options.features), encoded = ref1[0], positions = ref1[1];
}
scale = this._fontSize / 1000;
commands = [];
last = 0;
hadOffset = false;
addSegment = (function(_this) {
return function(cur) {
var advance, hex;
if (last < cur) {
hex = encoded.slice(last, cur).join('');
advance = positions[cur - 1].xAdvance - positions[cur - 1].advanceWidth;
commands.push("<" + hex + "> " + (number(-advance)));
}
return last = cur;
};
})(this);
flush = (function(_this) {
return function(i) {
addSegment(i);
if (commands.length > 0) {
_this.addContent("[" + (commands.join(' ')) + "] TJ");
return commands.length = 0;
}
};
})(this);
for (i = m = 0, len1 = positions.length; m < len1; i = ++m) {
pos = positions[i];
if (pos.xOffset || pos.yOffset) {
flush(i);
this.addContent("1 0 0 1 " + (number(x + pos.xOffset * scale)) + " " + (number(y + pos.yOffset * scale)) + " Tm");
flush(i + 1);
hadOffset = true;
} else {
if (hadOffset) {
this.addContent("1 0 0 1 " + (number(x)) + " " + (number(y)) + " Tm");
hadOffset = false;
}
if (pos.xAdvance - pos.advanceWidth !== 0) {
addSegment(i + 1);
}
}
x += pos.xAdvance * scale;
}
flush(i);
this.addContent("ET");
return this.restore();
}
};
}).call(this);

@ -8,5 +8,8 @@
pitfall/test/test5
pitfall/test/test6
pitfall/test/test7
pitfall/test/test8
pitfall/test/test09
pitfall/test/test10
pitfall/page-test
(submod pitfall/zlib test)))

@ -0,0 +1,58 @@
#lang pitfall/racket
(provide annotation-mixin)
(define (annotation-mixin [% mixin-tester%])
(class %
(super-new)
(as-methods
annotate
link
_convertRect)))
(define/contract (annotate this x y w h options)
(number? number? number? number? hash? . ->m . object?)
(hash-set*! options
'Type "Annot"
'Rect (send this _convertRect x y w h)
'Border '(0 0 0))
(hash-ref! options 'C
(λ ()
(unless (equal? (· options Subtype) "Link")
(send this _normalizeColor (or (· options color) '(0 0 0))))))
(hash-remove! options 'color)
(when (string? (· options Dest))
(hash-update! options 'Dest (λ (val) (String val))))
(for ([(k v) (in-hash options)])
(hash-set! options (string->symbol (string-titlecase (symbol->string k))) v))
(define ref (· this ref options))
(push-field! annotations this ref)
(· ref end)
this)
(define/contract (link this x y w h url [options (mhash)])
((number? number? number? number? string?) (hash?) . ->*m . object?)
(hash-set! options 'Subtype "Link")
(hash-set! options 'A (send this ref (mhash 'S "URI"
'URI (String url))))
(send (· options A) end)
(send this annotate x y w h options))
(define/contract (_convertRect this x1 y1 w h)
(number? number? number? number? . ->m . (list/c number? number? number? number?))
;; flip y1 and y2
(let ([y2 y1]
[y1 (+ y1 h)]
[x2 (+ x1 w)])
(match-define (list m0 m1 m2 m3 m4 m5) (· this _ctm))
(list
(+ (* x1 m0) (* y1 m2) m4)
(+ (* x1 m1) (* y1 m3) m5)
(+ (* x2 m0) (* y2 m2) m4)
(+ (* x2 m1) (* y2 m3) m5))))

@ -1,9 +1,9 @@
#lang pitfall/racket
(require "reference.rkt" "object.rkt" "page.rkt")
(require "vector.rkt" "color.rkt" "fonts.rkt" "text.rkt" "images.rkt")
(require "vector.rkt" "color.rkt" "fonts.rkt" "text.rkt" "images.rkt" "annotations.rkt")
(provide PDFDocument)
(define mixed% (image-mixin (text-mixin (fonts-mixin (color-mixin (vector-mixin object%))))))
(define mixed% (annotation-mixin (image-mixin (text-mixin (fonts-mixin (color-mixin (vector-mixin object%)))))))
(define-subclass mixed% (PDFDocument [options (mhash)])
(super-new)

@ -10,7 +10,8 @@
(as-methods
ref
finalize)
finalize
lineHeight)
))
(define/contract (PDFFont-open document src family id)
@ -34,6 +35,11 @@
(· this embed)
(set-field! embedded this #t)))
(define/contract (lineHeight this size [includeGap #f])
((number?)(boolean?) . ->*m . number?)
(define gap (if includeGap (· this lineGap) 0))
(* (/ (+ (· this ascender) gap (- (· this descender))) 1000.0) size))
(define StandardFont
(class PDFFont

@ -17,7 +17,8 @@
(as-methods
initFonts
font
fontSize)))
fontSize
currentLineHeight)))
(define/contract (initFonts this)
@ -53,7 +54,7 @@
[else (let ([ck (or family src)])
(and (string? ck) ck))])))
(when size (set-field! fontSize this size))
(when size (fontSize this size))
;; fast path: check if the font is already in the PDF
(cond
@ -84,5 +85,9 @@
(set-field! _fontSize this size)
this)
(define/contract (currentLineHeight this [includeGap #f])
(() (boolean?) . ->*m . number?)
(send (· this _font) lineHeight (· this _fontSize) includeGap))
(module+ test
(define fo (new (fonts-mixin))))

@ -8,7 +8,7 @@
#'(cond
[(object? x) (with-handlers ([exn:fail:object? (λ (exn) (send x ref))])
(get-field ref x))]
[(hash? x) (hash-ref x 'ref)]
[(hash? x) (hash-ref x 'ref #f)]
[else (raise-argument-error '· (format "~a must be object or hash" 'x) x)])]
[(_ x ref0 . refs) #'(· (· x ref0) . refs)]))

@ -0,0 +1,14 @@
PDFDocument = require 'pdfkit'
fs = require 'fs'
make = (doc) ->
# Add some text with annotations
doc.fillColor("blue")
.font('Helvetica', 30)
.text('Here is a link!', 100, 100, { link: 'http://google.com/', underline: true })
doc.end()
doc = new PDFDocument({compress: yes})
doc.pipe(fs.createWriteStream('test11c.pdf'))
make doc

@ -1,15 +1,12 @@
#lang pitfall/pdftest
(define (proc doc)
(send doc translate 200 300)
(send* doc [path "M 0 0 v 100 h 100 v -100 h -100"]
[stroke])
(send doc translate 0 150)
(send* doc [path "M 0 0 l 0 100 l 100 0 l 0 -100 l -100 0"]
[stroke]))
(send* doc
[fillColor "blue"]
[font "Helvetica" 30]
[text "Here is a link!" 100 100 (hash
'link "http://google.com/"
'underline #t)]))
(define-runtime-path that "test11crkt.pdf")
(make-doc that #t proc #:test #f)

@ -0,0 +1,112 @@
%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 [100 664.25 281.71 692]
/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
11 0 obj
<<
/Producer (PDFKit)
/Creator (PDFKit)
/CreationDate (D:20170527044501Z)
>>
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
3 0 obj
<<
/Length 161
/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
endstream
endobj
xref
0 12
0000000000 65535 f
0000000751 00000 n
0000000702 00000 n
0000000808 00000 n
0000000382 00000 n
0000000262 00000 n
0000000015 00000 n
0000000059 00000 n
0000000114 00000 n
0000000218 00000 n
0000000604 00000 n
0000000511 00000 n
trailer
<<
/Size 12
/Root 2 0 R
/Info 11 0 R
>>
startxref
1041
%%EOF

@ -1,83 +0,0 @@
%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
<<
/Font <<
/F1 6 0 R
>>
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
>>
endobj
3 0 obj
<<
/Length 123
>>
stream
1 0 0 -1 0 792 cm
q
1 0 0 -1 0 792 cm
BT
1 0 0 1 72 711.384 Tm
/F1 12 Tf
[<48656c6c6f2077> 10 <6f72> -15 <6c64> 0] TJ
ET
Q
endstream
endobj
7 0 obj
<<
/CreationDate (D:19700101000000Z)
/Creator (PITKIT)
/Producer (PITKIT)
>>
endobj
6 0 obj
<<
/BaseFont /Helvetica
/Encoding /WinAnsiEncoding
/Subtype /Type1
/Type /Font
>>
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 8
0000000000 65535 f
0000000620 00000 n
0000000571 00000 n
0000000208 00000 n
0000000119 00000 n
0000000015 00000 n
0000000474 00000 n
0000000382 00000 n
trailer
<<
/Info 7 0 R
/Root 2 0 R
/Size 8
>>
startxref
677
%%EOF

@ -117,11 +117,36 @@
;; text alignments ; todo
;; calculate the actual rendered width of the string after word and character spacing ; todo
;; calculate the actual rendered width of the string after word and character spacing
(define renderedWidth
(+ (or (· options textWidth) 0)
(* wordSpacing (sub1 (or (· options wordCount) 0)))
(* characterSpacing (sub1 (string-length text)))))
;; create link annotations if the link option is given
(when (· options link)
(report 'zing)
(send this link x y-in renderedWidth (· this currentLineHeight) (hash-ref options 'link)))
(error 'froom)
;; create link annotations if the link option is given ; todo
;; create underline or strikethrough line ; todo
;; create underline or strikethrough line
(when (or (· options underline) (· options strike))
(send this save)
(unless (· options stroke)
(send this strokeColor (· this _fillColor)))
(define lineWidth (if (< (· this _fontSize) 10)
0.5
(floor (/ (· this _fontSize) 10) 10)))
(define d (if (· options underline) 1 2))
(define lineY (+ y (/ (· this currentLineHeight) d)))
(when (· options underline)
(increment! lineY (- lineWidth)))
(send this moveTo x lineY)
(send this lineTo (+ x renderedWidth lineY))
(send this stroke)
(send this restore))
;; flip coordinate system
(send this save)

Loading…
Cancel
Save