mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-04 01:47:32 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			298 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			298 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/** @preserve 
 | 
						|
jsPDF split_text_to_size plugin
 | 
						|
Copyright (c) 2012 Willow Systems Corporation, willow-systems.com
 | 
						|
MIT license.
 | 
						|
*/
 | 
						|
/**
 | 
						|
 * Permission is hereby granted, free of charge, to any person obtaining
 | 
						|
 * a copy of this software and associated documentation files (the
 | 
						|
 * "Software"), to deal in the Software without restriction, including
 | 
						|
 * without limitation the rights to use, copy, modify, merge, publish,
 | 
						|
 * distribute, sublicense, and/or sell copies of the Software, and to
 | 
						|
 * permit persons to whom the Software is furnished to do so, subject to
 | 
						|
 * the following conditions:
 | 
						|
 * 
 | 
						|
 * The above copyright notice and this permission notice shall be
 | 
						|
 * included in all copies or substantial portions of the Software.
 | 
						|
 * 
 | 
						|
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
						|
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | 
						|
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | 
						|
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 | 
						|
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 | 
						|
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 | 
						|
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
						|
 * ====================================================================
 | 
						|
 */
 | 
						|
 | 
						|
;(function(API) {
 | 
						|
'use strict'
 | 
						|
 | 
						|
/**
 | 
						|
Returns an array of length matching length of the 'word' string, with each
 | 
						|
cell ocupied by the width of the char in that position.
 | 
						|
 | 
						|
@function
 | 
						|
@param word {String}
 | 
						|
@param widths {Object}
 | 
						|
@param kerning {Object}
 | 
						|
@returns {Array}
 | 
						|
*/
 | 
						|
var getCharWidthsArray = API.getCharWidthsArray = function(text, options){
 | 
						|
	if (!options) {
 | 
						|
		options = {}
 | 
						|
	}
 | 
						|
 | 
						|
	var widths = options.widths ? options.widths : this.internal.getFont().metadata.Unicode.widths
 | 
						|
	, widthsFractionOf = widths.fof ? widths.fof : 1
 | 
						|
	, kerning = options.kerning ? options.kerning : this.internal.getFont().metadata.Unicode.kerning
 | 
						|
	, kerningFractionOf = kerning.fof ? kerning.fof : 1
 | 
						|
	
 | 
						|
	// console.log("widths, kergnings", widths, kerning)
 | 
						|
 | 
						|
	var i, l
 | 
						|
	, char_code
 | 
						|
	, char_width
 | 
						|
	, prior_char_code = 0 // for kerning
 | 
						|
	, default_char_width = widths[0] || widthsFractionOf
 | 
						|
	, output = []
 | 
						|
 | 
						|
	for (i = 0, l = text.length; i < l; i++) {
 | 
						|
		char_code = text.charCodeAt(i)
 | 
						|
		output.push(
 | 
						|
			( widths[char_code] || default_char_width ) / widthsFractionOf + 
 | 
						|
			( kerning[char_code] && kerning[char_code][prior_char_code] || 0 ) / kerningFractionOf
 | 
						|
		)
 | 
						|
		prior_char_code = char_code
 | 
						|
	}
 | 
						|
 | 
						|
	return output
 | 
						|
}
 | 
						|
var getArraySum = function(array){
 | 
						|
	var i = array.length
 | 
						|
	, output = 0
 | 
						|
	while(i){
 | 
						|
		;i--;
 | 
						|
		output += array[i]
 | 
						|
	}
 | 
						|
	return output
 | 
						|
}
 | 
						|
/**
 | 
						|
Returns a widths of string in a given font, if the font size is set as 1 point.
 | 
						|
 | 
						|
In other words, this is "proportional" value. For 1 unit of font size, the length
 | 
						|
of the string will be that much.
 | 
						|
 | 
						|
Multiply by font size to get actual width in *points*
 | 
						|
Then divide by 72 to get inches or divide by (72/25.6) to get 'mm' etc.
 | 
						|
 | 
						|
@public
 | 
						|
@function
 | 
						|
@param
 | 
						|
@returns {Type}
 | 
						|
*/
 | 
						|
var getStringUnitWidth = API.getStringUnitWidth = function(text, options) {
 | 
						|
	return getArraySum(getCharWidthsArray.call(this, text, options))
 | 
						|
}
 | 
						|
 | 
						|
/** 
 | 
						|
returns array of lines
 | 
						|
*/
 | 
						|
var splitLongWord = function(word, widths_array, firstLineMaxLen, maxLen){
 | 
						|
	var answer = []
 | 
						|
 | 
						|
	// 1st, chop off the piece that can fit on the hanging line.
 | 
						|
	var i = 0
 | 
						|
	, l = word.length
 | 
						|
	, workingLen = 0
 | 
						|
	while (i !== l && workingLen + widths_array[i] < firstLineMaxLen){
 | 
						|
		workingLen += widths_array[i]
 | 
						|
		;i++;
 | 
						|
	}
 | 
						|
	// this is first line.
 | 
						|
	answer.push(word.slice(0, i))
 | 
						|
 | 
						|
	// 2nd. Split the rest into maxLen pieces.
 | 
						|
	var startOfLine = i
 | 
						|
	workingLen = 0
 | 
						|
	while (i !== l){
 | 
						|
		if (workingLen + widths_array[i] > maxLen) {
 | 
						|
			answer.push(word.slice(startOfLine, i))
 | 
						|
			workingLen = 0
 | 
						|
			startOfLine = i
 | 
						|
		}
 | 
						|
		workingLen += widths_array[i]
 | 
						|
		;i++;
 | 
						|
	}
 | 
						|
	if (startOfLine !== i) {
 | 
						|
		answer.push(word.slice(startOfLine, i))
 | 
						|
	}
 | 
						|
 | 
						|
	return answer
 | 
						|
}
 | 
						|
 | 
						|
// Note, all sizing inputs for this function must be in "font measurement units"
 | 
						|
// By default, for PDF, it's "point".
 | 
						|
var splitParagraphIntoLines = function(text, maxlen, options){
 | 
						|
	// at this time works only on Western scripts, ones with space char
 | 
						|
	// separating the words. Feel free to expand.
 | 
						|
 | 
						|
	if (!options) {
 | 
						|
		options = {}
 | 
						|
	}
 | 
						|
 | 
						|
	var spaceCharWidth = getCharWidthsArray(' ', options)[0]
 | 
						|
 | 
						|
	var words = text.split(' ')
 | 
						|
 | 
						|
	var line = []
 | 
						|
	, lines = [line]
 | 
						|
	, line_length = options.textIndent || 0
 | 
						|
	, separator_length = 0
 | 
						|
	, current_word_length = 0
 | 
						|
	, word
 | 
						|
	, widths_array
 | 
						|
 | 
						|
	var i, l, tmp
 | 
						|
	for (i = 0, l = words.length; i < l; i++) {
 | 
						|
		word = words[i]
 | 
						|
		widths_array = getCharWidthsArray(word, options)
 | 
						|
		current_word_length = getArraySum(widths_array)
 | 
						|
 | 
						|
		if (line_length + separator_length + current_word_length > maxlen) {
 | 
						|
			if (current_word_length > maxlen) {
 | 
						|
				// this happens when you have space-less long URLs for example.
 | 
						|
				// we just chop these to size. We do NOT insert hiphens
 | 
						|
				tmp = splitLongWord(word, widths_array, maxlen - (line_length + separator_length), maxlen)
 | 
						|
				// first line we add to existing line object
 | 
						|
				line.push(tmp.shift()) // it's ok to have extra space indicator there
 | 
						|
				// last line we make into new line object
 | 
						|
				line = [tmp.pop()]
 | 
						|
				// lines in the middle we apped to lines object as whole lines
 | 
						|
				while(tmp.length){
 | 
						|
					lines.push([tmp.shift()]) // single fragment occupies whole line
 | 
						|
				}
 | 
						|
				current_word_length = getArraySum( widths_array.slice(word.length - line[0].length) )
 | 
						|
			} else {
 | 
						|
				// just put it on a new line
 | 
						|
				line = [word]
 | 
						|
			}
 | 
						|
 | 
						|
			// now we attach new line to lines
 | 
						|
			lines.push(line)
 | 
						|
 | 
						|
			line_length = current_word_length
 | 
						|
			separator_length = spaceCharWidth
 | 
						|
 | 
						|
		} else {
 | 
						|
			line.push(word)
 | 
						|
 | 
						|
			line_length += separator_length + current_word_length
 | 
						|
			separator_length = spaceCharWidth
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	var output = []
 | 
						|
	for (i = 0, l = lines.length; i < l; i++) {
 | 
						|
		output.push( lines[i].join(' ') )
 | 
						|
	}
 | 
						|
	return output
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
Splits a given string into an array of strings. Uses 'size' value
 | 
						|
(in measurement units declared as default for the jsPDF instance)
 | 
						|
and the font's "widths" and "Kerning" tables, where availabe, to
 | 
						|
determine display length of a given string for a given font.
 | 
						|
 | 
						|
We use character's 100% of unit size (height) as width when Width
 | 
						|
table or other default width is not available.
 | 
						|
 | 
						|
@public
 | 
						|
@function
 | 
						|
@param text {String} Unencoded, regular JavaScript (Unicode, UTF-16 / UCS-2) string.
 | 
						|
@param size {Number} Nominal number, measured in units default to this instance of jsPDF.
 | 
						|
@param options {Object} Optional flags needed for chopper to do the right thing.
 | 
						|
@returns {Array} with strings chopped to size.
 | 
						|
*/
 | 
						|
API.splitTextToSize = function(text, maxlen, options) {
 | 
						|
	'use strict'
 | 
						|
 | 
						|
	if (!options) {
 | 
						|
		options = {}
 | 
						|
	}
 | 
						|
 | 
						|
	var fsize = options.fontSize || this.internal.getFontSize()
 | 
						|
	, newOptions = (function(options){
 | 
						|
		var widths = {0:1}
 | 
						|
		, kerning = {}
 | 
						|
 | 
						|
		if (!options.widths || !options.kerning) {
 | 
						|
			var f = this.internal.getFont(options.fontName, options.fontStyle)
 | 
						|
			, encoding = 'Unicode'
 | 
						|
			// NOT UTF8, NOT UTF16BE/LE, NOT UCS2BE/LE
 | 
						|
			// Actual JavaScript-native String's 16bit char codes used.
 | 
						|
			// no multi-byte logic here
 | 
						|
 | 
						|
			if (f.metadata[encoding]) {
 | 
						|
				return {
 | 
						|
					widths: f.metadata[encoding].widths || widths
 | 
						|
					, kerning: f.metadata[encoding].kerning || kerning
 | 
						|
				}
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			return 	{
 | 
						|
				widths: options.widths
 | 
						|
				, kerning: options.kerning
 | 
						|
			}			
 | 
						|
		}
 | 
						|
 | 
						|
		// then use default values
 | 
						|
		return 	{
 | 
						|
			widths: widths
 | 
						|
			, kerning: kerning
 | 
						|
		}
 | 
						|
	}).call(this, options)
 | 
						|
 | 
						|
	// first we split on end-of-line chars
 | 
						|
	var paragraphs 
 | 
						|
	if (text.match(/[\n\r]/)) {
 | 
						|
		paragraphs = text.split(/\r\n|\r|\n/g)
 | 
						|
	} else {
 | 
						|
		paragraphs = [text]
 | 
						|
	}
 | 
						|
 | 
						|
	// now we convert size (max length of line) into "font size units"
 | 
						|
	// at present time, the "font size unit" is always 'point'
 | 
						|
	// 'proportional' means, "in proportion to font size"
 | 
						|
	var fontUnit_maxLen = 1.0 * this.internal.scaleFactor * maxlen / fsize
 | 
						|
	// at this time, fsize is always in "points" regardless of the default measurement unit of the doc.
 | 
						|
	// this may change in the future?
 | 
						|
	// until then, proportional_maxlen is likely to be in 'points'
 | 
						|
 | 
						|
	// If first line is to be indented (shorter or longer) than maxLen 
 | 
						|
	// we indicate that by using CSS-style "text-indent" option.
 | 
						|
	// here it's in font units too (which is likely 'points')
 | 
						|
	// it can be negative (which makes the first line longer than maxLen)
 | 
						|
	newOptions.textIndent = options.textIndent ? 
 | 
						|
		options.textIndent * 1.0 * this.internal.scaleFactor / fsize : 
 | 
						|
		0
 | 
						|
 | 
						|
	var i, l
 | 
						|
	, output = []
 | 
						|
	for (i = 0, l = paragraphs.length; i < l; i++) {
 | 
						|
		output = output.concat(
 | 
						|
			splitParagraphIntoLines(
 | 
						|
				paragraphs[i]
 | 
						|
				, fontUnit_maxLen
 | 
						|
				, newOptions
 | 
						|
			)
 | 
						|
		)
 | 
						|
	}
 | 
						|
 | 
						|
	return output 
 | 
						|
}
 | 
						|
 | 
						|
})(jsPDF.API);
 |