Skip to content

Commit 133a321

Browse files
committed
Add support to scale text horizontally
inspired by #445
1 parent 42172c5 commit 133a321

File tree

6 files changed

+59
-20
lines changed

6 files changed

+59
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Update fontkit to 2.0
66
- Update linebreak to 1.1
77
- Add support for spot colors
8+
- Add support to scale text horizontally
89
- Fix sets tab order to "Structure" when a document is tagged
910
- Fix font cache collision for fonts with missing postscript name or bad TTF metadata
1011
- Fix measuring text when OpenType features are passed in to .text()

docs/text.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ below.
9494
* `lineGap` - the amount of space between each line of text
9595
* `wordSpacing` - the amount of space between each word in the text
9696
* `characterSpacing` - the amount of space between each character in the text
97+
* `horizontalScaling` - ability to scale text horizontally (`100` percent by default)
9798
* `fill` - whether to fill the text (`true` by default)
9899
* `stroke` - whether to stroke the text
99100
* `link` - a URL to link this text to (shortcut to create an annotation)

examples/kitchen-sink.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,36 @@ doc.moveDown()
118118
.fillColor('PANTONE185C')
119119
.text('This text uses spot color!');
120120

121+
doc.moveDown();
122+
123+
doc
124+
.font('Helvetica')
125+
.fillColor('#000')
126+
.text('Horizontal scaling support:');
127+
128+
doc.moveDown();
129+
130+
doc
131+
.text(loremIpsum, {
132+
height: 100,
133+
width: 300,
134+
align: 'justify',
135+
});
136+
137+
doc
138+
.text(loremIpsum, {
139+
height: 100,
140+
width: 300,
141+
align: 'justify',
142+
horizontalScaling: 75
143+
});
144+
145+
doc
146+
.text(loremIpsum, {
147+
height: 100,
148+
width: 300,
149+
align: 'justify',
150+
horizontalScaling: 130
151+
});
152+
121153
doc.end();

examples/kitchen-sink.pdf

726 Bytes
Binary file not shown.

lib/line_wrapper.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ class LineWrapper extends EventEmitter {
88
constructor(document, options) {
99
super();
1010
this.document = document;
11-
this.indent = options.indent || 0;
12-
this.characterSpacing = options.characterSpacing || 0;
13-
this.wordSpacing = options.wordSpacing === 0;
11+
this.horizontalScaling = options.horizontalScaling || 100;
12+
this.indent = ((options.indent || 0) * this.horizontalScaling) / 100;
13+
this.characterSpacing = ((options.characterSpacing || 0) * this.horizontalScaling) / 100;
14+
this.wordSpacing = ((options.wordSpacing === 0) * this.horizontalScaling) / 100;
1415
this.columns = options.columns || 1;
15-
this.columnGap = options.columnGap != null ? options.columnGap : 18; // 1/4 inch
16-
this.lineWidth =
17-
(options.width - this.columnGap * (this.columns - 1)) / this.columns;
16+
this.columnGap = ((options.columnGap != null ? options.columnGap : 18) * this.horizontalScaling) / 100; // 1/4 inch
17+
this.lineWidth = (((options.width * this.horizontalScaling) / 100) - (this.columnGap * (this.columns - 1))) / this.columns;
1818
this.spaceLeft = this.lineWidth;
1919
this.startX = this.document.x;
2020
this.startY = this.document.y;
@@ -165,14 +165,15 @@ class LineWrapper extends EventEmitter {
165165

166166
wrap(text, options) {
167167
// override options from previous continued fragments
168+
this.horizontalScaling = options.horizontalScaling || 100;
168169
if (options.indent != null) {
169-
this.indent = options.indent;
170+
this.indent = (options.indent * this.horizontalScaling) / 100;
170171
}
171172
if (options.characterSpacing != null) {
172-
this.characterSpacing = options.characterSpacing;
173+
this.characterSpacing = (options.characterSpacing * this.horizontalScaling) / 100;
173174
}
174175
if (options.wordSpacing != null) {
175-
this.wordSpacing = options.wordSpacing;
176+
this.wordSpacing = (options.wordSpacing * this.horizontalScaling) / 100;
176177
}
177178
if (options.ellipsis != null) {
178179
this.ellipsis = options.ellipsis;

lib/mixins/text.js

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export default {
4747
const addStructure = () => {
4848
if (options.structParent) {
4949
options.structParent.add(this.struct(options.structType || 'P',
50-
[ this.markStructureContent(options.structType || 'P') ]));
50+
[this.markStructureContent(options.structType || 'P')]));
5151
}
5252
};
5353

@@ -80,10 +80,8 @@ export default {
8080
},
8181

8282
widthOfString(string, options = {}) {
83-
return (
84-
this._font.widthOfString(string, this._fontSize, options.features) +
85-
(options.characterSpacing || 0) * (string.length - 1)
86-
);
83+
const horizontalScaling = options.horizontalScaling || 100;
84+
return ((this._font.widthOfString(string, this._fontSize, options.features) + (options.characterSpacing || 0) * (string.length - 1)) * horizontalScaling) / 100;
8785
},
8886

8987
heightOfString(text, options) {
@@ -121,7 +119,7 @@ export default {
121119
const levels = [];
122120
const numbers = [];
123121

124-
var flatten = function(list) {
122+
var flatten = function (list) {
125123
let n = 1;
126124
for (let i = 0; i < list.length; i++) {
127125
const item = list[i];
@@ -141,7 +139,7 @@ export default {
141139

142140
flatten(list);
143141

144-
const label = function(n) {
142+
const label = function (n) {
145143
switch (listType) {
146144
case 'numbered':
147145
return `${n}.`;
@@ -153,7 +151,7 @@ export default {
153151
}
154152
};
155153

156-
const drawListItem = function(listItem, i) {
154+
const drawListItem = function (listItem, i) {
157155
wrapper = new LineWrapper(this, options);
158156
wrapper.on('line', this._line);
159157

@@ -299,6 +297,7 @@ export default {
299297
const align = options.align || 'left';
300298
let wordSpacing = options.wordSpacing || 0;
301299
const characterSpacing = options.characterSpacing || 0;
300+
const horizontalScaling = options.horizontalScaling || 100;
302301

303302
// text alignments
304303
if (options.width) {
@@ -320,7 +319,7 @@ export default {
320319
wordSpacing = Math.max(
321320
0,
322321
(options.lineWidth - textWidth) / Math.max(1, words.length - 1) -
323-
spaceWidth
322+
spaceWidth
324323
);
325324
break;
326325
}
@@ -388,13 +387,13 @@ export default {
388387
this._fontSize < 10 ? 0.5 : Math.floor(this._fontSize / 10);
389388
this.lineWidth(lineWidth);
390389

391-
let lineY = (y + this.currentLineHeight()) - lineWidth
390+
let lineY = (y + this.currentLineHeight()) - lineWidth
392391
this.moveTo(x, lineY);
393392
this.lineTo(x + renderedWidth, lineY);
394393
this.stroke();
395394
this.restore();
396395
}
397-
396+
398397
// create strikethrough line
399398
if (options.strike) {
400399
this.save();
@@ -457,6 +456,11 @@ export default {
457456
this.addContent(`${number(characterSpacing)} Tc`);
458457
}
459458

459+
// Horizontal scaling
460+
if (horizontalScaling !== 100) {
461+
this.addContent(`${horizontalScaling} Tz`);
462+
}
463+
460464
// Add the actual text
461465
// If we have a word spacing value, we need to encode each word separately
462466
// since the normal Tw operator only works on character code 32, which isn't

0 commit comments

Comments
 (0)