| 875 |
lars |
1 |
/** @preserve
|
|
|
2 |
jsPDF split_text_to_size plugin
|
|
|
3 |
Copyright (c) 2012 Willow Systems Corporation, willow-systems.com
|
|
|
4 |
MIT license.
|
|
|
5 |
*/
|
|
|
6 |
/**
|
|
|
7 |
* Permission is hereby granted, free of charge, to any person obtaining
|
|
|
8 |
* a copy of this software and associated documentation files (the
|
|
|
9 |
* "Software"), to deal in the Software without restriction, including
|
|
|
10 |
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
11 |
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
12 |
* permit persons to whom the Software is furnished to do so, subject to
|
|
|
13 |
* the following conditions:
|
|
|
14 |
*
|
|
|
15 |
* The above copyright notice and this permission notice shall be
|
|
|
16 |
* included in all copies or substantial portions of the Software.
|
|
|
17 |
*
|
|
|
18 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
19 |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
20 |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
21 |
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
|
22 |
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
|
23 |
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
24 |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
25 |
* ====================================================================
|
|
|
26 |
*/
|
|
|
27 |
|
|
|
28 |
;(function(API) {
|
|
|
29 |
'use strict'
|
|
|
30 |
|
|
|
31 |
/**
|
|
|
32 |
Returns an array of length matching length of the 'word' string, with each
|
|
|
33 |
cell ocupied by the width of the char in that position.
|
|
|
34 |
|
|
|
35 |
@function
|
|
|
36 |
@param word {String}
|
|
|
37 |
@param widths {Object}
|
|
|
38 |
@param kerning {Object}
|
|
|
39 |
@returns {Array}
|
|
|
40 |
*/
|
|
|
41 |
var getCharWidthsArray = API.getCharWidthsArray = function(text, options){
|
|
|
42 |
|
|
|
43 |
if (!options) {
|
|
|
44 |
options = {}
|
|
|
45 |
}
|
|
|
46 |
|
|
|
47 |
var widths = options.widths ? options.widths : this.internal.getFont().metadata.Unicode.widths
|
|
|
48 |
, widthsFractionOf = widths.fof ? widths.fof : 1
|
|
|
49 |
, kerning = options.kerning ? options.kerning : this.internal.getFont().metadata.Unicode.kerning
|
|
|
50 |
, kerningFractionOf = kerning.fof ? kerning.fof : 1
|
|
|
51 |
|
|
|
52 |
// console.log("widths, kergnings", widths, kerning)
|
|
|
53 |
|
|
|
54 |
var i, l
|
|
|
55 |
, char_code
|
|
|
56 |
, char_width
|
|
|
57 |
, prior_char_code = 0 // for kerning
|
|
|
58 |
, default_char_width = widths[0] || widthsFractionOf
|
|
|
59 |
, output = []
|
|
|
60 |
|
|
|
61 |
for (i = 0, l = text.length; i < l; i++) {
|
|
|
62 |
char_code = text.charCodeAt(i)
|
|
|
63 |
output.push(
|
|
|
64 |
( widths[char_code] || default_char_width ) / widthsFractionOf +
|
|
|
65 |
( kerning[char_code] && kerning[char_code][prior_char_code] || 0 ) / kerningFractionOf
|
|
|
66 |
)
|
|
|
67 |
prior_char_code = char_code
|
|
|
68 |
}
|
|
|
69 |
|
|
|
70 |
return output
|
|
|
71 |
}
|
|
|
72 |
var getArraySum = function(array){
|
|
|
73 |
var i = array.length
|
|
|
74 |
, output = 0
|
|
|
75 |
while(i){
|
|
|
76 |
;i--;
|
|
|
77 |
output += array[i]
|
|
|
78 |
}
|
|
|
79 |
return output
|
|
|
80 |
}
|
|
|
81 |
/**
|
|
|
82 |
Returns a widths of string in a given font, if the font size is set as 1 point.
|
|
|
83 |
|
|
|
84 |
In other words, this is "proportional" value. For 1 unit of font size, the length
|
|
|
85 |
of the string will be that much.
|
|
|
86 |
|
|
|
87 |
Multiply by font size to get actual width in *points*
|
|
|
88 |
Then divide by 72 to get inches or divide by (72/25.6) to get 'mm' etc.
|
|
|
89 |
|
|
|
90 |
@public
|
|
|
91 |
@function
|
|
|
92 |
@param
|
|
|
93 |
@returns {Type}
|
|
|
94 |
*/
|
|
|
95 |
var getStringUnitWidth = API.getStringUnitWidth = function(text, options) {
|
|
|
96 |
return getArraySum(getCharWidthsArray.call(this, text, options))
|
|
|
97 |
}
|
|
|
98 |
|
|
|
99 |
/**
|
|
|
100 |
returns array of lines
|
|
|
101 |
*/
|
|
|
102 |
var splitLongWord = function(word, widths_array, firstLineMaxLen, maxLen){
|
|
|
103 |
var answer = []
|
|
|
104 |
|
|
|
105 |
// 1st, chop off the piece that can fit on the hanging line.
|
|
|
106 |
var i = 0
|
|
|
107 |
, l = word.length
|
|
|
108 |
, workingLen = 0
|
|
|
109 |
while (i !== l && workingLen + widths_array[i] < firstLineMaxLen){
|
|
|
110 |
workingLen += widths_array[i]
|
|
|
111 |
;i++;
|
|
|
112 |
}
|
|
|
113 |
// this is first line.
|
|
|
114 |
answer.push(word.slice(0, i))
|
|
|
115 |
|
|
|
116 |
// 2nd. Split the rest into maxLen pieces.
|
|
|
117 |
var startOfLine = i
|
|
|
118 |
workingLen = 0
|
|
|
119 |
while (i !== l){
|
|
|
120 |
if (workingLen + widths_array[i] > maxLen) {
|
|
|
121 |
answer.push(word.slice(startOfLine, i))
|
|
|
122 |
workingLen = 0
|
|
|
123 |
startOfLine = i
|
|
|
124 |
}
|
|
|
125 |
workingLen += widths_array[i]
|
|
|
126 |
;i++;
|
|
|
127 |
}
|
|
|
128 |
if (startOfLine !== i) {
|
|
|
129 |
answer.push(word.slice(startOfLine, i))
|
|
|
130 |
}
|
|
|
131 |
|
|
|
132 |
return answer
|
|
|
133 |
}
|
|
|
134 |
|
|
|
135 |
// Note, all sizing inputs for this function must be in "font measurement units"
|
|
|
136 |
// By default, for PDF, it's "point".
|
|
|
137 |
var splitParagraphIntoLines = function(text, maxlen, options){
|
|
|
138 |
// at this time works only on Western scripts, ones with space char
|
|
|
139 |
// separating the words. Feel free to expand.
|
|
|
140 |
|
|
|
141 |
if (!options) {
|
|
|
142 |
options = {}
|
|
|
143 |
}
|
|
|
144 |
|
|
|
145 |
var spaceCharWidth = getCharWidthsArray(' ', options)[0]
|
|
|
146 |
|
|
|
147 |
var words = text.split(' ')
|
|
|
148 |
|
|
|
149 |
var line = []
|
|
|
150 |
, lines = [line]
|
|
|
151 |
, line_length = options.textIndent || 0
|
|
|
152 |
, separator_length = 0
|
|
|
153 |
, current_word_length = 0
|
|
|
154 |
, word
|
|
|
155 |
, widths_array
|
|
|
156 |
|
|
|
157 |
var i, l, tmp
|
|
|
158 |
for (i = 0, l = words.length; i < l; i++) {
|
|
|
159 |
word = words[i]
|
|
|
160 |
widths_array = getCharWidthsArray(word, options)
|
|
|
161 |
current_word_length = getArraySum(widths_array)
|
|
|
162 |
|
|
|
163 |
if (line_length + separator_length + current_word_length > maxlen) {
|
|
|
164 |
if (current_word_length > maxlen) {
|
|
|
165 |
// this happens when you have space-less long URLs for example.
|
|
|
166 |
// we just chop these to size. We do NOT insert hiphens
|
|
|
167 |
tmp = splitLongWord(word, widths_array, maxlen - (line_length + separator_length), maxlen)
|
|
|
168 |
// first line we add to existing line object
|
|
|
169 |
line.push(tmp.shift()) // it's ok to have extra space indicator there
|
|
|
170 |
// last line we make into new line object
|
|
|
171 |
line = [tmp.pop()]
|
|
|
172 |
// lines in the middle we apped to lines object as whole lines
|
|
|
173 |
while(tmp.length){
|
|
|
174 |
lines.push([tmp.shift()]) // single fragment occupies whole line
|
|
|
175 |
}
|
|
|
176 |
current_word_length = getArraySum( widths_array.slice(word.length - line[0].length) )
|
|
|
177 |
} else {
|
|
|
178 |
// just put it on a new line
|
|
|
179 |
line = [word]
|
|
|
180 |
}
|
|
|
181 |
|
|
|
182 |
// now we attach new line to lines
|
|
|
183 |
lines.push(line)
|
|
|
184 |
|
|
|
185 |
line_length = current_word_length
|
|
|
186 |
separator_length = spaceCharWidth
|
|
|
187 |
|
|
|
188 |
} else {
|
|
|
189 |
line.push(word)
|
|
|
190 |
|
|
|
191 |
line_length += separator_length + current_word_length
|
|
|
192 |
separator_length = spaceCharWidth
|
|
|
193 |
}
|
|
|
194 |
}
|
|
|
195 |
|
|
|
196 |
var output = []
|
|
|
197 |
for (i = 0, l = lines.length; i < l; i++) {
|
|
|
198 |
output.push( lines[i].join(' ') )
|
|
|
199 |
}
|
|
|
200 |
return output
|
|
|
201 |
|
|
|
202 |
}
|
|
|
203 |
|
|
|
204 |
/**
|
|
|
205 |
Splits a given string into an array of strings. Uses 'size' value
|
|
|
206 |
(in measurement units declared as default for the jsPDF instance)
|
|
|
207 |
and the font's "widths" and "Kerning" tables, where availabe, to
|
|
|
208 |
determine display length of a given string for a given font.
|
|
|
209 |
|
|
|
210 |
We use character's 100% of unit size (height) as width when Width
|
|
|
211 |
table or other default width is not available.
|
|
|
212 |
|
|
|
213 |
@public
|
|
|
214 |
@function
|
|
|
215 |
@param text {String} Unencoded, regular JavaScript (Unicode, UTF-16 / UCS-2) string.
|
|
|
216 |
@param size {Number} Nominal number, measured in units default to this instance of jsPDF.
|
|
|
217 |
@param options {Object} Optional flags needed for chopper to do the right thing.
|
|
|
218 |
@returns {Array} with strings chopped to size.
|
|
|
219 |
*/
|
|
|
220 |
API.splitTextToSize = function(text, maxlen, options) {
|
|
|
221 |
'use strict'
|
|
|
222 |
|
|
|
223 |
if (!options) {
|
|
|
224 |
options = {}
|
|
|
225 |
}
|
|
|
226 |
|
|
|
227 |
var fsize = options.fontSize || this.internal.getFontSize()
|
|
|
228 |
, newOptions = (function(options){
|
|
|
229 |
var widths = {0:1}
|
|
|
230 |
, kerning = {}
|
|
|
231 |
|
|
|
232 |
if (!options.widths || !options.kerning) {
|
|
|
233 |
var f = this.internal.getFont(options.fontName, options.fontStyle)
|
|
|
234 |
, encoding = 'Unicode'
|
|
|
235 |
// NOT UTF8, NOT UTF16BE/LE, NOT UCS2BE/LE
|
|
|
236 |
// Actual JavaScript-native String's 16bit char codes used.
|
|
|
237 |
// no multi-byte logic here
|
|
|
238 |
|
|
|
239 |
if (f.metadata[encoding]) {
|
|
|
240 |
return {
|
|
|
241 |
widths: f.metadata[encoding].widths || widths
|
|
|
242 |
, kerning: f.metadata[encoding].kerning || kerning
|
|
|
243 |
}
|
|
|
244 |
}
|
|
|
245 |
} else {
|
|
|
246 |
return {
|
|
|
247 |
widths: options.widths
|
|
|
248 |
, kerning: options.kerning
|
|
|
249 |
}
|
|
|
250 |
}
|
|
|
251 |
|
|
|
252 |
// then use default values
|
|
|
253 |
return {
|
|
|
254 |
widths: widths
|
|
|
255 |
, kerning: kerning
|
|
|
256 |
}
|
|
|
257 |
}).call(this, options)
|
|
|
258 |
|
|
|
259 |
// first we split on end-of-line chars
|
|
|
260 |
var paragraphs
|
|
|
261 |
if (text.match(/[\n\r]/)) {
|
|
|
262 |
paragraphs = text.split(/\r\n|\r|\n/g)
|
|
|
263 |
} else {
|
|
|
264 |
paragraphs = [text]
|
|
|
265 |
}
|
|
|
266 |
|
|
|
267 |
// now we convert size (max length of line) into "font size units"
|
|
|
268 |
// at present time, the "font size unit" is always 'point'
|
|
|
269 |
// 'proportional' means, "in proportion to font size"
|
|
|
270 |
var fontUnit_maxLen = 1.0 * this.internal.scaleFactor * maxlen / fsize
|
|
|
271 |
// at this time, fsize is always in "points" regardless of the default measurement unit of the doc.
|
|
|
272 |
// this may change in the future?
|
|
|
273 |
// until then, proportional_maxlen is likely to be in 'points'
|
|
|
274 |
|
|
|
275 |
// If first line is to be indented (shorter or longer) than maxLen
|
|
|
276 |
// we indicate that by using CSS-style "text-indent" option.
|
|
|
277 |
// here it's in font units too (which is likely 'points')
|
|
|
278 |
// it can be negative (which makes the first line longer than maxLen)
|
|
|
279 |
newOptions.textIndent = options.textIndent ?
|
|
|
280 |
options.textIndent * 1.0 * this.internal.scaleFactor / fsize :
|
|
|
281 |
|
|
|
282 |
|
|
|
283 |
var i, l
|
|
|
284 |
, output = []
|
|
|
285 |
for (i = 0, l = paragraphs.length; i < l; i++) {
|
|
|
286 |
output = output.concat(
|
|
|
287 |
splitParagraphIntoLines(
|
|
|
288 |
paragraphs[i]
|
|
|
289 |
, fontUnit_maxLen
|
|
|
290 |
, newOptions
|
|
|
291 |
)
|
|
|
292 |
)
|
|
|
293 |
}
|
|
|
294 |
|
|
|
295 |
return output
|
|
|
296 |
}
|
|
|
297 |
|
|
|
298 |
})(jsPDF.API);
|