diff --git a/CHANGELOG.md b/CHANGELOG.md index 14e2bb7f..bcb0ccb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Update fontkit to 2.0 - Update linebreak to 1.1 - Add support for spot colors +- Add support to scale text horizontally - Fix sets tab order to "Structure" when a document is tagged - Fix font cache collision for fonts with missing postscript name or bad TTF metadata - Fix measuring text when OpenType features are passed in to .text() diff --git a/docs/text.md b/docs/text.md index ed442c57..ca40f5b6 100644 --- a/docs/text.md +++ b/docs/text.md @@ -94,6 +94,7 @@ below. * `lineGap` - the amount of space between each line of text * `wordSpacing` - the amount of space between each word in the text * `characterSpacing` - the amount of space between each character in the text +* `horizontalScaling` - ability to scale text horizontally (`100` percent by default) * `fill` - whether to fill the text (`true` by default) * `stroke` - whether to stroke the text * `link` - a URL to link this text to (shortcut to create an annotation) diff --git a/examples/kitchen-sink.js b/examples/kitchen-sink.js index 1ad43271..c00dde45 100644 --- a/examples/kitchen-sink.js +++ b/examples/kitchen-sink.js @@ -118,4 +118,36 @@ doc.moveDown() .fillColor('PANTONE185C') .text('This text uses spot color!'); +doc.moveDown(); + +doc + .font('Helvetica') + .fillColor('#000') + .text('Horizontal scaling support:'); + +doc.moveDown(); + +doc + .text(loremIpsum, { + height: 100, + width: 300, + align: 'justify', + }); + +doc + .text(loremIpsum, { + height: 100, + width: 300, + align: 'justify', + horizontalScaling: 75 + }); + +doc + .text(loremIpsum, { + height: 100, + width: 300, + align: 'justify', + horizontalScaling: 130 + }); + doc.end(); diff --git a/examples/kitchen-sink.pdf b/examples/kitchen-sink.pdf index a8e1c467..4fa70c52 100644 Binary files a/examples/kitchen-sink.pdf and b/examples/kitchen-sink.pdf differ diff --git a/lib/line_wrapper.js b/lib/line_wrapper.js index dfc019dc..cc737360 100644 --- a/lib/line_wrapper.js +++ b/lib/line_wrapper.js @@ -8,13 +8,13 @@ class LineWrapper extends EventEmitter { constructor(document, options) { super(); this.document = document; - this.indent = options.indent || 0; - this.characterSpacing = options.characterSpacing || 0; - this.wordSpacing = options.wordSpacing === 0; + this.horizontalScaling = options.horizontalScaling || 100; + this.indent = ((options.indent || 0) * this.horizontalScaling) / 100; + this.characterSpacing = ((options.characterSpacing || 0) * this.horizontalScaling) / 100; + this.wordSpacing = ((options.wordSpacing === 0) * this.horizontalScaling) / 100; this.columns = options.columns || 1; - this.columnGap = options.columnGap != null ? options.columnGap : 18; // 1/4 inch - this.lineWidth = - (options.width - this.columnGap * (this.columns - 1)) / this.columns; + this.columnGap = ((options.columnGap != null ? options.columnGap : 18) * this.horizontalScaling) / 100; // 1/4 inch + this.lineWidth = (((options.width * this.horizontalScaling) / 100) - (this.columnGap * (this.columns - 1))) / this.columns; this.spaceLeft = this.lineWidth; this.startX = this.document.x; this.startY = this.document.y; @@ -165,14 +165,15 @@ class LineWrapper extends EventEmitter { wrap(text, options) { // override options from previous continued fragments + this.horizontalScaling = options.horizontalScaling || 100; if (options.indent != null) { - this.indent = options.indent; + this.indent = (options.indent * this.horizontalScaling) / 100; } if (options.characterSpacing != null) { - this.characterSpacing = options.characterSpacing; + this.characterSpacing = (options.characterSpacing * this.horizontalScaling) / 100; } if (options.wordSpacing != null) { - this.wordSpacing = options.wordSpacing; + this.wordSpacing = (options.wordSpacing * this.horizontalScaling) / 100; } if (options.ellipsis != null) { this.ellipsis = options.ellipsis; diff --git a/lib/mixins/text.js b/lib/mixins/text.js index 1ea320f1..9ee6d28c 100644 --- a/lib/mixins/text.js +++ b/lib/mixins/text.js @@ -47,7 +47,7 @@ export default { const addStructure = () => { if (options.structParent) { options.structParent.add(this.struct(options.structType || 'P', - [ this.markStructureContent(options.structType || 'P') ])); + [this.markStructureContent(options.structType || 'P')])); } }; @@ -80,10 +80,8 @@ export default { }, widthOfString(string, options = {}) { - return ( - this._font.widthOfString(string, this._fontSize, options.features) + - (options.characterSpacing || 0) * (string.length - 1) - ); + const horizontalScaling = options.horizontalScaling || 100; + return ((this._font.widthOfString(string, this._fontSize, options.features) + (options.characterSpacing || 0) * (string.length - 1)) * horizontalScaling) / 100; }, heightOfString(text, options) { @@ -121,7 +119,7 @@ export default { const levels = []; const numbers = []; - var flatten = function(list) { + var flatten = function (list) { let n = 1; for (let i = 0; i < list.length; i++) { const item = list[i]; @@ -141,7 +139,7 @@ export default { flatten(list); - const label = function(n) { + const label = function (n) { switch (listType) { case 'numbered': return `${n}.`; @@ -153,7 +151,7 @@ export default { } }; - const drawListItem = function(listItem, i) { + const drawListItem = function (listItem, i) { wrapper = new LineWrapper(this, options); wrapper.on('line', this._line); @@ -299,6 +297,7 @@ export default { const align = options.align || 'left'; let wordSpacing = options.wordSpacing || 0; const characterSpacing = options.characterSpacing || 0; + const horizontalScaling = options.horizontalScaling || 100; // text alignments if (options.width) { @@ -320,7 +319,7 @@ export default { wordSpacing = Math.max( 0, (options.lineWidth - textWidth) / Math.max(1, words.length - 1) - - spaceWidth + spaceWidth ); break; } @@ -388,13 +387,13 @@ export default { this._fontSize < 10 ? 0.5 : Math.floor(this._fontSize / 10); this.lineWidth(lineWidth); - let lineY = (y + this.currentLineHeight()) - lineWidth + let lineY = (y + this.currentLineHeight()) - lineWidth this.moveTo(x, lineY); this.lineTo(x + renderedWidth, lineY); this.stroke(); this.restore(); } - + // create strikethrough line if (options.strike) { this.save(); @@ -457,6 +456,11 @@ export default { this.addContent(`${number(characterSpacing)} Tc`); } + // Horizontal scaling + if (horizontalScaling !== 100) { + this.addContent(`${horizontalScaling} Tz`); + } + // Add the actual text // If we have a word spacing value, we need to encode each word separately // since the normal Tw operator only works on character code 32, which isn't