1 /** 2 * Copyright (C) 2012-2013 KO GmbH <copyright@kogmbh.com> 3 * 4 * @licstart 5 * This file is part of WebODF. 6 * 7 * WebODF is free software: you can redistribute it and/or modify it 8 * under the terms of the GNU Affero General Public License (GNU AGPL) 9 * as published by the Free Software Foundation, either version 3 of 10 * the License, or (at your option) any later version. 11 * 12 * WebODF is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU Affero General Public License for more details. 16 * 17 * You should have received a copy of the GNU Affero General Public License 18 * along with WebODF. If not, see <http://www.gnu.org/licenses/>. 19 * @licend 20 * 21 * @source: http://www.webodf.org/ 22 * @source: https://github.com/kogmbh/WebODF/ 23 */ 24 25 /*global runtime, odf, core, document, xmldom*/ 26 /*jslint sub: true*/ 27 28 29 (function () { 30 "use strict"; 31 var xpath = xmldom.XPath, 32 odfUtils = odf.OdfUtils, 33 base64 = new core.Base64(); 34 35 /** 36 * @param {!Element} fontFaceDecls 37 * @return {!Object.<string,{href:string,family:string}>} 38 */ 39 function getEmbeddedFontDeclarations(fontFaceDecls) { 40 var decls = {}, fonts, i, font, name, uris, href, family; 41 if (!fontFaceDecls) { 42 return decls; 43 } 44 fonts = xpath.getODFElementsWithXPath(fontFaceDecls, 45 "style:font-face[svg:font-face-src]", 46 odf.Namespaces.lookupNamespaceURI); 47 for (i = 0; i < fonts.length; i += 1) { 48 font = fonts[i]; 49 name = font.getAttributeNS(odf.Namespaces.stylens, "name"); 50 family = odfUtils.getNormalizedFontFamilyName(font.getAttributeNS(odf.Namespaces.svgns, "font-family")); 51 uris = xpath.getODFElementsWithXPath(font, 52 "svg:font-face-src/svg:font-face-uri", 53 odf.Namespaces.lookupNamespaceURI); 54 if (uris.length > 0) { 55 href = uris[0].getAttributeNS(odf.Namespaces.xlinkns, "href"); 56 decls[name] = {href: href, family: family}; 57 } 58 } 59 return decls; 60 } 61 /** 62 * @param {!string} name 63 * @param {!{href:string,family:string}} font 64 * @param {!Uint8Array} fontdata 65 * @param {!CSSStyleSheet} stylesheet 66 * @return {undefined} 67 */ 68 function addFontToCSS(name, font, fontdata, stylesheet) { 69 var cssFamily = font.family || name, 70 // font-family already has a quotation in the name if needed, as required by 71 // ODF 1.2 §19.528 svg:font-family, which points to SVG 1.1 §20.8.3, which points to 72 // @font-face facility in CSS2 73 // wrapping again with ' and ' only result in problems with font-family names 74 // that are quoted with ' and ' itself 75 rule = "@font-face { font-family: " + cssFamily + "; src: " + 76 "url(data:application/x-font-ttf;charset=binary;base64," + 77 base64.convertUTF8ArrayToBase64(fontdata) + 78 ") format(\"truetype\"); }"; 79 try { 80 stylesheet.insertRule(rule, stylesheet.cssRules.length); 81 } catch (/**@type{!DOMException}*/e) { 82 runtime.log("Problem inserting rule in CSS: " + runtime.toJson(e) + "\nRule: " + rule); 83 } 84 } 85 /** 86 * @param {!Object.<string,{href:string,family:string}>} embeddedFontDeclarations 87 * @param {!odf.OdfContainer} odfContainer 88 * @param {!number} pos 89 * @param {!CSSStyleSheet} stylesheet 90 * @param {!function():undefined=} callback 91 * @return {undefined} 92 */ 93 function loadFontIntoCSS(embeddedFontDeclarations, odfContainer, pos, 94 stylesheet, callback) { 95 var name, i = 0, 96 /**@type{string}*/ 97 n; 98 for (n in embeddedFontDeclarations) { 99 if (embeddedFontDeclarations.hasOwnProperty(n)) { 100 if (i === pos) { 101 name = n; 102 break; 103 } 104 i += 1; 105 } 106 } 107 if (!name) { 108 if (callback) { 109 callback(); 110 } 111 return; 112 } 113 odfContainer.getPartData(embeddedFontDeclarations[name].href, function (err, fontdata) { 114 if (err) { 115 runtime.log(err); 116 } else if (!fontdata) { 117 runtime.log("missing font data for " 118 + embeddedFontDeclarations[name].href); 119 } else { 120 addFontToCSS(name, embeddedFontDeclarations[name], fontdata, 121 stylesheet); 122 } 123 loadFontIntoCSS(embeddedFontDeclarations, odfContainer, pos + 1, 124 stylesheet, callback); 125 }); 126 } 127 /** 128 * @param {!Object.<string,{href:string,family:string}>} embeddedFontDeclarations 129 * @param {!odf.OdfContainer} odfContainer 130 * @param {!CSSStyleSheet} stylesheet 131 * @return {undefined} 132 */ 133 function loadFontsIntoCSS(embeddedFontDeclarations, odfContainer, 134 stylesheet) { 135 loadFontIntoCSS(embeddedFontDeclarations, odfContainer, 0, stylesheet); 136 } 137 /** 138 * This class loads embedded fonts into the CSS 139 * @constructor 140 * @return {?} 141 */ 142 odf.FontLoader = function FontLoader() { 143 /** 144 * @param {!odf.OdfContainer} odfContainer 145 * @param {!CSSStyleSheet} stylesheet Will be cleaned and filled with rules for the fonts 146 * @return {undefined} 147 */ 148 this.loadFonts = function (odfContainer, stylesheet) { 149 var embeddedFontDeclarations, 150 /** @type {?Element}*/fontFaceDecls = odfContainer.rootElement.fontFaceDecls; 151 152 // make stylesheet empty 153 while (stylesheet.cssRules.length) { 154 stylesheet.deleteRule(stylesheet.cssRules.length - 1); 155 } 156 157 if (fontFaceDecls) { 158 embeddedFontDeclarations = getEmbeddedFontDeclarations( 159 fontFaceDecls 160 ); 161 loadFontsIntoCSS(embeddedFontDeclarations, odfContainer, stylesheet); 162 } 163 }; 164 }; 165 }()); 166