mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-18 00:53:10 +01:00
299 lines
8.5 KiB
JavaScript
Executable File
299 lines
8.5 KiB
JavaScript
Executable File
/** @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);
|