Files
odoo_source/addons/website/static/lib/pdfjs/display/font_loader.js
T
Jérome Maes 6a11f594a8 [IMP] website_slide, website : move the PDFJS library from slides to the website module to allow reusing it in website modules.
This huge library handle PDF file to display them, count their pages, ... Due to its size, the lib is declared in a separated optional bundle who should be called with a 't-call-assets' only on the page it is required.
2015-04-15 14:56:24 +02:00

376 lines
13 KiB
JavaScript

/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* globals PDFJS, shadow, isWorker, assert, warn, bytesToString, string32,
globalScope, FontFace, Promise */
'use strict';
PDFJS.disableFontFace = false;
var FontLoader = {
insertRule: function fontLoaderInsertRule(rule) {
var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG');
if (!styleElement) {
styleElement = document.createElement('style');
styleElement.id = 'PDFJS_FONT_STYLE_TAG';
document.documentElement.getElementsByTagName('head')[0].appendChild(
styleElement);
}
var styleSheet = styleElement.sheet;
styleSheet.insertRule(rule, styleSheet.cssRules.length);
},
clear: function fontLoaderClear() {
var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG');
if (styleElement) {
styleElement.parentNode.removeChild(styleElement);
}
//#if !(MOZCENTRAL)
this.nativeFontFaces.forEach(function(nativeFontFace) {
document.fonts.delete(nativeFontFace);
});
this.nativeFontFaces.length = 0;
//#endif
},
//#if !(MOZCENTRAL)
get loadTestFont() {
// This is a CFF font with 1 glyph for '.' that fills its entire width and
// height.
return shadow(this, 'loadTestFont', atob(
'T1RUTwALAIAAAwAwQ0ZGIDHtZg4AAAOYAAAAgUZGVE1lkzZwAAAEHAAAABxHREVGABQAFQ' +
'AABDgAAAAeT1MvMlYNYwkAAAEgAAAAYGNtYXABDQLUAAACNAAAAUJoZWFk/xVFDQAAALwA' +
'AAA2aGhlYQdkA+oAAAD0AAAAJGhtdHgD6AAAAAAEWAAAAAZtYXhwAAJQAAAAARgAAAAGbm' +
'FtZVjmdH4AAAGAAAAAsXBvc3T/hgAzAAADeAAAACAAAQAAAAEAALZRFsRfDzz1AAsD6AAA' +
'AADOBOTLAAAAAM4KHDwAAAAAA+gDIQAAAAgAAgAAAAAAAAABAAADIQAAAFoD6AAAAAAD6A' +
'ABAAAAAAAAAAAAAAAAAAAAAQAAUAAAAgAAAAQD6AH0AAUAAAKKArwAAACMAooCvAAAAeAA' +
'MQECAAACAAYJAAAAAAAAAAAAAQAAAAAAAAAAAAAAAFBmRWQAwAAuAC4DIP84AFoDIQAAAA' +
'AAAQAAAAAAAAAAACAAIAABAAAADgCuAAEAAAAAAAAAAQAAAAEAAAAAAAEAAQAAAAEAAAAA' +
'AAIAAQAAAAEAAAAAAAMAAQAAAAEAAAAAAAQAAQAAAAEAAAAAAAUAAQAAAAEAAAAAAAYAAQ' +
'AAAAMAAQQJAAAAAgABAAMAAQQJAAEAAgABAAMAAQQJAAIAAgABAAMAAQQJAAMAAgABAAMA' +
'AQQJAAQAAgABAAMAAQQJAAUAAgABAAMAAQQJAAYAAgABWABYAAAAAAAAAwAAAAMAAAAcAA' +
'EAAAAAADwAAwABAAAAHAAEACAAAAAEAAQAAQAAAC7//wAAAC7////TAAEAAAAAAAABBgAA' +
'AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAA' +
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAA' +
'AAAAD/gwAyAAAAAQAAAAAAAAAAAAAAAAAAAAABAAQEAAEBAQJYAAEBASH4DwD4GwHEAvgc' +
'A/gXBIwMAYuL+nz5tQXkD5j3CBLnEQACAQEBIVhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWF' +
'hYWFhYWFhYAAABAQAADwACAQEEE/t3Dov6fAH6fAT+fPp8+nwHDosMCvm1Cvm1DAz6fBQA' +
'AAAAAAABAAAAAMmJbzEAAAAAzgTjFQAAAADOBOQpAAEAAAAAAAAADAAUAAQAAAABAAAAAg' +
'ABAAAAAAAAAAAD6AAAAAAAAA=='
));
},
loadTestFontId: 0,
loadingContext: {
requests: [],
nextRequestId: 0
},
isSyncFontLoadingSupported: (function detectSyncFontLoadingSupport() {
if (isWorker) {
return false;
}
// User agent string sniffing is bad, but there is no reliable way to tell
// if font is fully loaded and ready to be used with canvas.
var userAgent = window.navigator.userAgent;
var m = /Mozilla\/5.0.*?rv:(\d+).*? Gecko/.exec(userAgent);
if (m && m[1] >= 14) {
return true;
}
// TODO other browsers
if (userAgent === 'node') {
return true;
}
return false;
})(),
nativeFontFaces: [],
isFontLoadingAPISupported: !isWorker && !!document.fonts,
addNativeFontFace: function fontLoader_addNativeFontFace(nativeFontFace) {
this.nativeFontFaces.push(nativeFontFace);
document.fonts.add(nativeFontFace);
},
bind: function fontLoaderBind(fonts, callback) {
assert(!isWorker, 'bind() shall be called from main thread');
var rules = [];
var fontsToLoad = [];
var fontLoadPromises = [];
for (var i = 0, ii = fonts.length; i < ii; i++) {
var font = fonts[i];
// Add the font to the DOM only once or skip if the font
// is already loaded.
if (font.attached || font.loading === false) {
continue;
}
font.attached = true;
if (this.isFontLoadingAPISupported) {
var nativeFontFace = font.createNativeFontFace();
if (nativeFontFace) {
fontLoadPromises.push(nativeFontFace.loaded);
}
} else {
var rule = font.bindDOM();
if (rule) {
rules.push(rule);
fontsToLoad.push(font);
}
}
}
var request = FontLoader.queueLoadingCallback(callback);
if (this.isFontLoadingAPISupported) {
Promise.all(fontsToLoad).then(function() {
request.complete();
});
} else if (rules.length > 0 && !this.isSyncFontLoadingSupported) {
FontLoader.prepareFontLoadEvent(rules, fontsToLoad, request);
} else {
request.complete();
}
},
queueLoadingCallback: function FontLoader_queueLoadingCallback(callback) {
function LoadLoader_completeRequest() {
assert(!request.end, 'completeRequest() cannot be called twice');
request.end = Date.now();
// sending all completed requests in order how they were queued
while (context.requests.length > 0 && context.requests[0].end) {
var otherRequest = context.requests.shift();
setTimeout(otherRequest.callback, 0);
}
}
var context = FontLoader.loadingContext;
var requestId = 'pdfjs-font-loading-' + (context.nextRequestId++);
var request = {
id: requestId,
complete: LoadLoader_completeRequest,
callback: callback,
started: Date.now()
};
context.requests.push(request);
return request;
},
prepareFontLoadEvent: function fontLoaderPrepareFontLoadEvent(rules,
fonts,
request) {
/** Hack begin */
// There's currently no event when a font has finished downloading so the
// following code is a dirty hack to 'guess' when a font is
// ready. It's assumed fonts are loaded in order, so add a known test
// font after the desired fonts and then test for the loading of that
// test font.
function int32(data, offset) {
return (data.charCodeAt(offset) << 24) |
(data.charCodeAt(offset + 1) << 16) |
(data.charCodeAt(offset + 2) << 8) |
(data.charCodeAt(offset + 3) & 0xff);
}
function spliceString(s, offset, remove, insert) {
var chunk1 = s.substr(0, offset);
var chunk2 = s.substr(offset + remove);
return chunk1 + insert + chunk2;
}
var i, ii;
var canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = 1;
var ctx = canvas.getContext('2d');
var called = 0;
function isFontReady(name, callback) {
called++;
// With setTimeout clamping this gives the font ~100ms to load.
if(called > 30) {
warn('Load test font never loaded.');
callback();
return;
}
ctx.font = '30px ' + name;
ctx.fillText('.', 0, 20);
var imageData = ctx.getImageData(0, 0, 1, 1);
if (imageData.data[3] > 0) {
callback();
return;
}
setTimeout(isFontReady.bind(null, name, callback));
}
var loadTestFontId = 'lt' + Date.now() + this.loadTestFontId++;
// Chromium seems to cache fonts based on a hash of the actual font data,
// so the font must be modified for each load test else it will appear to
// be loaded already.
// TODO: This could maybe be made faster by avoiding the btoa of the full
// font by splitting it in chunks before hand and padding the font id.
var data = this.loadTestFont;
var COMMENT_OFFSET = 976; // has to be on 4 byte boundary (for checksum)
data = spliceString(data, COMMENT_OFFSET, loadTestFontId.length,
loadTestFontId);
// CFF checksum is important for IE, adjusting it
var CFF_CHECKSUM_OFFSET = 16;
var XXXX_VALUE = 0x58585858; // the "comment" filled with 'X'
var checksum = int32(data, CFF_CHECKSUM_OFFSET);
for (i = 0, ii = loadTestFontId.length - 3; i < ii; i += 4) {
checksum = (checksum - XXXX_VALUE + int32(loadTestFontId, i)) | 0;
}
if (i < loadTestFontId.length) { // align to 4 bytes boundary
checksum = (checksum - XXXX_VALUE +
int32(loadTestFontId + 'XXX', i)) | 0;
}
data = spliceString(data, CFF_CHECKSUM_OFFSET, 4, string32(checksum));
var url = 'url(data:font/opentype;base64,' + btoa(data) + ');';
var rule = '@font-face { font-family:"' + loadTestFontId + '";src:' +
url + '}';
FontLoader.insertRule(rule);
var names = [];
for (i = 0, ii = fonts.length; i < ii; i++) {
names.push(fonts[i].loadedName);
}
names.push(loadTestFontId);
var div = document.createElement('div');
div.setAttribute('style',
'visibility: hidden;' +
'width: 10px; height: 10px;' +
'position: absolute; top: 0px; left: 0px;');
for (i = 0, ii = names.length; i < ii; ++i) {
var span = document.createElement('span');
span.textContent = 'Hi';
span.style.fontFamily = names[i];
div.appendChild(span);
}
document.body.appendChild(div);
isFontReady(loadTestFontId, function() {
document.body.removeChild(div);
request.complete();
});
/** Hack end */
}
//#else
//bind: function fontLoaderBind(fonts, callback) {
// assert(!isWorker, 'bind() shall be called from main thread');
//
// for (var i = 0, ii = fonts.length; i < ii; i++) {
// var font = fonts[i];
// if (font.attached) {
// continue;
// }
//
// font.attached = true;
// font.bindDOM()
// }
//
// setTimeout(callback);
//}
//#endif
};
var FontFaceObject = (function FontFaceObjectClosure() {
function FontFaceObject(name, file, properties) {
this.compiledGlyphs = {};
if (arguments.length === 1) {
// importing translated data
var data = arguments[0];
for (var i in data) {
this[i] = data[i];
}
return;
}
}
FontFaceObject.prototype = {
//#if !(MOZCENTRAL)
createNativeFontFace: function FontFaceObject_createNativeFontFace() {
if (!this.data) {
return null;
}
if (PDFJS.disableFontFace) {
this.disableFontFace = true;
return null;
}
var nativeFontFace = new FontFace(this.loadedName, this.data, {});
FontLoader.addNativeFontFace(nativeFontFace);
if (PDFJS.pdfBug && 'FontInspector' in globalScope &&
globalScope['FontInspector'].enabled) {
globalScope['FontInspector'].fontAdded(this);
}
return nativeFontFace;
},
//#endif
bindDOM: function FontFaceObject_bindDOM() {
if (!this.data) {
return null;
}
if (PDFJS.disableFontFace) {
this.disableFontFace = true;
return null;
}
var data = bytesToString(new Uint8Array(this.data));
var fontName = this.loadedName;
// Add the font-face rule to the document
var url = ('url(data:' + this.mimetype + ';base64,' +
window.btoa(data) + ');');
var rule = '@font-face { font-family:"' + fontName + '";src:' + url + '}';
FontLoader.insertRule(rule);
if (PDFJS.pdfBug && 'FontInspector' in globalScope &&
globalScope['FontInspector'].enabled) {
globalScope['FontInspector'].fontAdded(this, url);
}
return rule;
},
getPathGenerator: function FontLoader_getPathGenerator(objs, character) {
if (!(character in this.compiledGlyphs)) {
var js = objs.get(this.loadedName + '_path_' + character);
/*jshint -W054 */
this.compiledGlyphs[character] = new Function('c', 'size', js);
}
return this.compiledGlyphs[character];
}
};
return FontFaceObject;
})();