Subversion-Projekte lars-tiefland.ci

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
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);