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