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 odf, runtime, xmldom, core, document*/ 26 27 /** 28 * @constructor 29 */ 30 odf.Style2CSS = function Style2CSS() { 31 "use strict"; 32 var // helper constants 33 /**@const 34 @type{!string}*/ 35 drawns = odf.Namespaces.drawns, 36 /**@const 37 @type{!string}*/ 38 fons = odf.Namespaces.fons, 39 /**@const 40 @type{!string}*/ 41 officens = odf.Namespaces.officens, 42 /**@const 43 @type{!string}*/ 44 stylens = odf.Namespaces.stylens, 45 /**@const 46 @type{!string}*/ 47 svgns = odf.Namespaces.svgns, 48 /**@const 49 @type{!string}*/ 50 tablens = odf.Namespaces.tablens, 51 /**@const 52 @type{!string}*/ 53 xlinkns = odf.Namespaces.xlinkns, 54 /**@const 55 @type{!string}*/ 56 presentationns = odf.Namespaces.presentationns, 57 domUtils = new core.DomUtils(), 58 59 /**@const 60 @type{!Object.<string,string>}*/ 61 familynamespaceprefixes = { 62 'graphic': 'draw', 63 'drawing-page': 'draw', 64 'paragraph': 'text', 65 'presentation': 'presentation', 66 'ruby': 'text', 67 'section': 'text', 68 'table': 'table', 69 'table-cell': 'table', 70 'table-column': 'table', 71 'table-row': 'table', 72 'text': 'text', 73 'list': 'text', 74 'page': 'office' 75 }, 76 77 /**@const 78 @type{!Object.<string,!Array.<!string>>}*/ 79 familytagnames = { 80 'graphic': ['circle', 'connected', 'control', 'custom-shape', 81 'ellipse', 'frame', 'g', 'line', 'measure', 'page', 82 'page-thumbnail', 'path', 'polygon', 'polyline', 'rect', 83 'regular-polygon' ], 84 'paragraph': ['alphabetical-index-entry-template', 'h', 85 'illustration-index-entry-template', 'index-source-style', 86 'object-index-entry-template', 'p', 87 'table-index-entry-template', 'table-of-content-entry-template', 88 'user-index-entry-template'], 89 'presentation': ['caption', 'circle', 'connector', 'control', 90 'custom-shape', 'ellipse', 'frame', 'g', 'line', 'measure', 91 'page-thumbnail', 'path', 'polygon', 'polyline', 'rect', 92 'regular-polygon'], 93 'drawing-page': ['caption', 'circle', 'connector', 'control', 'page', 94 'custom-shape', 'ellipse', 'frame', 'g', 'line', 'measure', 95 'page-thumbnail', 'path', 'polygon', 'polyline', 'rect', 96 'regular-polygon'], 97 'ruby': ['ruby', 'ruby-text'], 98 'section': ['alphabetical-index', 'bibliography', 99 'illustration-index', 'index-title', 'object-index', 'section', 100 'table-of-content', 'table-index', 'user-index'], 101 'table': ['background', 'table'], 102 'table-cell': ['body', 'covered-table-cell', 'even-columns', 103 'even-rows', 'first-column', 'first-row', 'last-column', 104 'last-row', 'odd-columns', 'odd-rows', 'table-cell'], 105 'table-column': ['table-column'], 106 'table-row': ['table-row'], 107 'text': ['a', 'index-entry-chapter', 'index-entry-link-end', 108 'index-entry-link-start', 'index-entry-page-number', 109 'index-entry-span', 'index-entry-tab-stop', 'index-entry-text', 110 'index-title-template', 'linenumbering-configuration', 111 'list-level-style-number', 'list-level-style-bullet', 112 'outline-level-style', 'span'], 113 'list': ['list-item'] 114 }, 115 116 /**@const 117 @type{!Array.<!Array.<!string>>}*/ 118 textPropertySimpleMapping = [ 119 [ fons, 'color', 'color' ], 120 // this sets the element background, not just the text background 121 [ fons, 'background-color', 'background-color' ], 122 [ fons, 'font-weight', 'font-weight' ], 123 [ fons, 'font-style', 'font-style' ] 124 ], 125 126 /**@const 127 @type{!Array.<!Array.<!string>>}*/ 128 bgImageSimpleMapping = [ 129 [ stylens, 'repeat', 'background-repeat' ] 130 ], 131 132 /**@const 133 @type{!Array.<!Array.<!string>>}*/ 134 paragraphPropertySimpleMapping = [ 135 [ fons, 'background-color', 'background-color' ], 136 [ fons, 'text-align', 'text-align' ], 137 [ fons, 'text-indent', 'text-indent' ], 138 [ fons, 'padding', 'padding' ], 139 [ fons, 'padding-left', 'padding-left' ], 140 [ fons, 'padding-right', 'padding-right' ], 141 [ fons, 'padding-top', 'padding-top' ], 142 [ fons, 'padding-bottom', 'padding-bottom' ], 143 [ fons, 'border-left', 'border-left' ], 144 [ fons, 'border-right', 'border-right' ], 145 [ fons, 'border-top', 'border-top' ], 146 [ fons, 'border-bottom', 'border-bottom' ], 147 [ fons, 'margin', 'margin' ], 148 [ fons, 'margin-left', 'margin-left' ], 149 [ fons, 'margin-right', 'margin-right' ], 150 [ fons, 'margin-top', 'margin-top' ], 151 [ fons, 'margin-bottom', 'margin-bottom' ], 152 [ fons, 'border', 'border' ] 153 ], 154 155 /**@const 156 @type{!Array.<!Array.<!string>>}*/ 157 graphicPropertySimpleMapping = [ 158 [ fons, 'background-color', 'background-color'], 159 [ fons, 'min-height', 'min-height' ], 160 [ drawns, 'stroke', 'border' ], 161 [ svgns, 'stroke-color', 'border-color' ], 162 [ svgns, 'stroke-width', 'border-width' ], 163 [ fons, 'border', 'border' ], 164 [ fons, 'border-left', 'border-left' ], 165 [ fons, 'border-right', 'border-right' ], 166 [ fons, 'border-top', 'border-top' ], 167 [ fons, 'border-bottom', 'border-bottom' ] 168 ], 169 170 /**@const 171 @type{!Array.<!Array.<!string>>}*/ 172 tablecellPropertySimpleMapping = [ 173 [ fons, 'background-color', 'background-color' ], 174 [ fons, 'border-left', 'border-left' ], 175 [ fons, 'border-right', 'border-right' ], 176 [ fons, 'border-top', 'border-top' ], 177 [ fons, 'border-bottom', 'border-bottom' ], 178 [ fons, 'border', 'border' ] 179 ], 180 181 /**@const 182 @type{!Array.<!Array.<!string>>}*/ 183 tablecolumnPropertySimpleMapping = [ 184 [ stylens, 'column-width', 'width' ] 185 ], 186 187 /**@const 188 @type{!Array.<!Array.<!string>>}*/ 189 tablerowPropertySimpleMapping = [ 190 [ stylens, 'row-height', 'height' ], 191 [ fons, 'keep-together', null ] 192 ], 193 194 /**@const 195 @type{!Array.<!Array.<!string>>}*/ 196 tablePropertySimpleMapping = [ 197 [ stylens, 'width', 'width' ], 198 [ fons, 'margin-left', 'margin-left' ], 199 [ fons, 'margin-right', 'margin-right' ], 200 [ fons, 'margin-top', 'margin-top' ], 201 [ fons, 'margin-bottom', 'margin-bottom' ] 202 ], 203 204 /**@const 205 @type{!Array.<!Array.<!string>>}*/ 206 pageContentPropertySimpleMapping = [ 207 [ fons, 'background-color', 'background-color' ], 208 [ fons, 'padding', 'padding' ], 209 [ fons, 'padding-left', 'padding-left' ], 210 [ fons, 'padding-right', 'padding-right' ], 211 [ fons, 'padding-top', 'padding-top' ], 212 [ fons, 'padding-bottom', 'padding-bottom' ], 213 [ fons, 'border', 'border' ], 214 [ fons, 'border-left', 'border-left' ], 215 [ fons, 'border-right', 'border-right' ], 216 [ fons, 'border-top', 'border-top' ], 217 [ fons, 'border-bottom', 'border-bottom' ], 218 [ fons, 'margin', 'margin' ], 219 [ fons, 'margin-left', 'margin-left' ], 220 [ fons, 'margin-right', 'margin-right' ], 221 [ fons, 'margin-top', 'margin-top' ], 222 [ fons, 'margin-bottom', 'margin-bottom' ] 223 ], 224 225 /**@const 226 @type{!Array.<!Array.<!string>>}*/ 227 pageSizePropertySimpleMapping = [ 228 [ fons, 'page-width', 'width' ], 229 [ fons, 'page-height', 'height' ] 230 ], 231 232 /**@const 233 @type{!Object.<!boolean>}*/ 234 borderPropertyMap = { 235 'border': true, 236 'border-left': true, 237 'border-right': true, 238 'border-top': true, 239 'border-bottom': true, 240 'stroke-width': true 241 }, 242 243 // A font-face declaration map, to be populated once style2css is called. 244 /**@type{!Object.<string,string>}*/ 245 fontFaceDeclsMap = {}, 246 utils = new odf.OdfUtils(), 247 documentType, 248 odfRoot, 249 defaultFontSize, 250 xpath = xmldom.XPath, 251 cssUnits = new core.CSSUnits(); 252 253 /** 254 * @param {!string} family 255 * @param {!string} name 256 * @return {?string} 257 */ 258 function createSelector(family, name) { 259 var prefix = familynamespaceprefixes[family], 260 namepart, 261 selector; 262 if (prefix === undefined) { 263 return null; 264 } 265 266 // If there is no name, it is a default style, in which case style-name shall be used without a value 267 if (name) { 268 namepart = '[' + prefix + '|style-name="' + name + '"]'; 269 } else { 270 namepart = ''; 271 } 272 if (prefix === 'presentation') { 273 prefix = 'draw'; 274 if (name) { 275 namepart = '[presentation|style-name="' + name + '"]'; 276 } else { 277 namepart = ''; 278 } 279 } 280 selector = prefix + '|' + familytagnames[family].join( 281 namepart + ',' + prefix + '|' 282 ) + namepart; 283 return selector; 284 } 285 /** 286 * @param {!string} family 287 * @param {!string} name 288 * @param {!odf.StyleTreeNode} node 289 * @return {!Array.<string>} 290 */ 291 function getSelectors(family, name, node) { 292 var selectors = [], ss, 293 derivedStyles = node.derivedStyles, 294 /**@type{string}*/ 295 n; 296 ss = createSelector(family, name); 297 if (ss !== null) { 298 selectors.push(ss); 299 } 300 for (n in derivedStyles) { 301 if (derivedStyles.hasOwnProperty(n)) { 302 ss = getSelectors(family, n, derivedStyles[n]); 303 selectors = selectors.concat(ss); 304 } 305 } 306 return selectors; 307 } 308 /** 309 * Make sure border width is no less than 1px wide; otherwise border is not rendered. 310 * Only have problems with point unit at the moment. Please add more rule if needed. 311 * @param {!string} value a string contains border attributes eg. 1pt solid black or 1px 312 * @return {!string} 313 */ 314 function fixBorderWidth(value) { 315 var index = value.indexOf(' '), 316 width, theRestOfBorderAttributes; 317 318 if (index !== -1) { 319 width = value.substring(0, index); 320 theRestOfBorderAttributes = value.substring(index); // everything after the width attribute 321 } else { 322 width = value; 323 theRestOfBorderAttributes = ''; 324 } 325 326 width = utils.parseLength(width); 327 // According to CSS 2.1, 1px is equal to 0.75pt http://www.w3.org/TR/CSS2/syndata.html#length-units 328 if (width && width.unit === 'pt' && width.value < 0.75) { 329 value = '0.75pt' + theRestOfBorderAttributes; 330 } 331 return value; 332 } 333 /** 334 * @param {!Element} props 335 * @param {!Array.<!Array.<!string>>} mapping 336 * @return {!string} 337 */ 338 function applySimpleMapping(props, mapping) { 339 var rule = '', i, r, value; 340 for (i = 0; i < mapping.length; i += 1) { 341 r = mapping[i]; 342 value = props.getAttributeNS(r[0], r[1]); 343 344 if (value) { 345 value = value.trim(); 346 347 if (borderPropertyMap.hasOwnProperty(r[1])) { 348 value = fixBorderWidth(value); 349 } 350 if (r[2]) { 351 rule += r[2] + ':' + value + ';'; 352 } 353 } 354 } 355 return rule; 356 } 357 358 /** 359 * Returns the font size attribute value from the text properties of a style node 360 * @param {?Element} styleNode 361 * @return {?{value: !number, unit: !string}} 362 */ 363 function getFontSize(styleNode) { 364 var props = domUtils.getDirectChild(styleNode, stylens, 'text-properties'); 365 if (props) { 366 return utils.parseFoFontSize(props.getAttributeNS(fons, 'font-size')); 367 } 368 return null; 369 } 370 371 /** 372 * Returns the parent style node of a given style node 373 * @param {!Element} styleNode 374 * @return {Element} 375 */ 376 function getParentStyleNode(styleNode) { 377 var parentStyleName = '', 378 parentStyleFamily = '', 379 parentStyleNode = null, 380 xp; 381 382 if (styleNode.localName === 'default-style') { 383 return null; 384 } 385 386 parentStyleName = styleNode.getAttributeNS(stylens, 'parent-style-name'); 387 parentStyleFamily = styleNode.getAttributeNS(stylens, 'family'); 388 389 if (parentStyleName) { 390 xp = "//style:*[@style:name='" + parentStyleName + "'][@style:family='" + parentStyleFamily + "']"; 391 } else { 392 xp = "//style:default-style[@style:family='" + parentStyleFamily + "']"; 393 } 394 parentStyleNode = xpath.getODFElementsWithXPath(/**@type{!Element}*/(odfRoot), xp, odf.Namespaces.lookupNamespaceURI)[0]; 395 return parentStyleNode; 396 } 397 /** 398 * @param {!Element} props 399 * @return {!string} 400 */ 401 function getTextProperties(props) { 402 var rule = '', fontName, fontSize, value, textDecoration = '', 403 fontSizeRule = '', 404 sizeMultiplier = 1, 405 parentStyle; 406 407 rule += applySimpleMapping(props, textPropertySimpleMapping); 408 409 value = props.getAttributeNS(stylens, 'text-underline-style'); 410 if (value === 'solid') { 411 textDecoration += ' underline'; 412 } 413 value = props.getAttributeNS(stylens, 'text-line-through-style'); 414 if (value === 'solid') { 415 textDecoration += ' line-through'; 416 } 417 418 if (textDecoration.length) { 419 textDecoration = 'text-decoration:' + textDecoration + ';'; 420 rule += textDecoration; 421 } 422 423 fontName = props.getAttributeNS(stylens, 'font-name') 424 || props.getAttributeNS(fons, 'font-family'); 425 if (fontName) { 426 value = fontFaceDeclsMap[fontName]; 427 // TODO: use other information from style:font-face, like style:font-family-generic 428 rule += 'font-family: ' + (value || fontName) + ';'; 429 } 430 431 parentStyle = /**@type{!Element}*/(props.parentNode); 432 fontSize = getFontSize(parentStyle); 433 // This is actually the font size of the current style. 434 if (!fontSize) { 435 return rule; 436 } 437 438 while (parentStyle) { 439 fontSize = getFontSize(parentStyle); 440 if (fontSize) { 441 // If the current style's font size is a non-% value, then apply the multiplier to get the child style (with the %)'s 442 // actual font size. And now we can stop crawling up the style ancestry since we have a concrete font size. 443 if (fontSize.unit !== '%') { 444 fontSizeRule = 'font-size: ' + (fontSize.value * sizeMultiplier) + fontSize.unit + ';'; 445 break; 446 } 447 // If we got a % font size for the current style, then update the multiplier with it's 'normalized' multiplier 448 sizeMultiplier *= (fontSize.value / 100); 449 } 450 // Crawl up the style ancestry 451 parentStyle = getParentStyleNode(parentStyle); 452 } 453 454 // If there was nothing in the ancestry that specified a concrete font size, just apply the multiplier onto the page's default font size. 455 if (!fontSizeRule) { 456 fontSizeRule = 'font-size: ' + parseFloat(defaultFontSize) * sizeMultiplier + cssUnits.getUnits(defaultFontSize) + ';'; 457 } 458 459 rule += fontSizeRule; 460 return rule; 461 } 462 /** 463 * @param {!Element} props <style:paragraph-properties/> 464 * @return {!string} 465 */ 466 function getParagraphProperties(props) { 467 var rule = '', bgimage, url, lineHeight; 468 rule += applySimpleMapping(props, paragraphPropertySimpleMapping); 469 bgimage = domUtils.getDirectChild(props, stylens, 'background-image'); 470 if (bgimage) { 471 url = bgimage.getAttributeNS(xlinkns, 'href'); 472 if (url) { 473 rule += "background-image: url('odfkit:" + url + "');"; 474 //rule += "background-repeat: repeat;"; //FIXME test 475 rule += applySimpleMapping(bgimage, bgImageSimpleMapping); 476 } 477 } 478 479 lineHeight = props.getAttributeNS(fons, 'line-height'); 480 if (lineHeight && lineHeight !== 'normal') { 481 lineHeight = utils.parseFoLineHeight(lineHeight); 482 if (lineHeight.unit !== '%') { 483 rule += 'line-height: ' + lineHeight.value + lineHeight.unit + ';'; 484 } else { 485 rule += 'line-height: ' + lineHeight.value / 100 + ';'; 486 } 487 } 488 489 return rule; 490 } 491 492 /*jslint unparam: true*/ 493 /** 494 * @param {*} m 495 * @param {string} r 496 * @param {string} g 497 * @param {string} b 498 * @return {string} 499 */ 500 function matchToRgb(m, r, g, b) { 501 return r + r + g + g + b + b; 502 } 503 /*jslint unparam: false*/ 504 505 /** 506 * @param {!string} hex 507 * @return {?{ r: number, g: number, b: number}} 508 */ 509 function hexToRgb(hex) { 510 // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") 511 var result, 512 shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; 513 hex = hex.replace(shorthandRegex, matchToRgb); 514 515 result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); 516 return result ? { 517 r: parseInt(result[1], 16), 518 g: parseInt(result[2], 16), 519 b: parseInt(result[3], 16) 520 } : null; 521 } 522 523 /** 524 * @param {string} n 525 * @return {boolean} 526 */ 527 function isNumber(n) { 528 return !isNaN(parseFloat(n)); 529 } 530 531 /** 532 * @param {!Element} props 533 * @return {string} 534 */ 535 function getGraphicProperties(props) { 536 var rule = '', alpha, bgcolor, fill; 537 538 rule += applySimpleMapping(props, graphicPropertySimpleMapping); 539 alpha = props.getAttributeNS(drawns, 'opacity'); 540 fill = props.getAttributeNS(drawns, 'fill'); 541 bgcolor = props.getAttributeNS(drawns, 'fill-color'); 542 543 if (fill === 'solid' || fill === 'hatch') { 544 if (bgcolor && bgcolor !== 'none') { 545 alpha = isNumber(alpha) ? parseFloat(alpha) / 100 : 1; 546 bgcolor = hexToRgb(bgcolor); 547 if (bgcolor) { 548 rule += "background-color: rgba(" 549 + bgcolor.r + "," 550 + bgcolor.g + "," 551 + bgcolor.b + "," 552 + alpha + ");"; 553 } 554 } else { 555 rule += "background: none;"; 556 } 557 } else if (fill === "none") { 558 rule += "background: none;"; 559 } 560 561 return rule; 562 } 563 /** 564 * @param {!Element} props 565 * @return {string} 566 */ 567 function getDrawingPageProperties(props) { 568 var rule = ''; 569 570 rule += applySimpleMapping(props, graphicPropertySimpleMapping); 571 if (props.getAttributeNS(presentationns, 'background-visible') === 'true') { 572 rule += "background: none;"; 573 } 574 return rule; 575 } 576 /** 577 * @param {!Element} props 578 * @return {string} 579 */ 580 function getTableCellProperties(props) { 581 var rule = ''; 582 rule += applySimpleMapping(props, tablecellPropertySimpleMapping); 583 return rule; 584 } 585 /** 586 * @param {!Element} props 587 * @return {string} 588 */ 589 function getTableRowProperties(props) { 590 var rule = ''; 591 rule += applySimpleMapping(props, tablerowPropertySimpleMapping); 592 return rule; 593 } 594 /** 595 * @param {!Element} props 596 * @return {string} 597 */ 598 function getTableColumnProperties(props) { 599 var rule = ''; 600 rule += applySimpleMapping(props, tablecolumnPropertySimpleMapping); 601 return rule; 602 } 603 /** 604 * @param {!Element} props 605 * @return {string} 606 */ 607 function getTableProperties(props) { 608 var rule = '', borderModel; 609 rule += applySimpleMapping(props, tablePropertySimpleMapping); 610 borderModel = props.getAttributeNS(tablens, 'border-model'); 611 612 if (borderModel === 'collapsing') { 613 rule += 'border-collapse:collapse;'; 614 } else if (borderModel === 'separating') { 615 rule += 'border-collapse:separate;'; 616 } 617 618 return rule; 619 } 620 /** 621 * @param {!CSSStyleSheet} sheet 622 * @param {string} family 623 * @param {string} name 624 * @param {!odf.StyleTreeNode} node 625 * @return {undefined} 626 */ 627 function addStyleRule(sheet, family, name, node) { 628 var selectors = getSelectors(family, name, node), 629 selector = selectors.join(','), 630 rule = '', 631 properties; 632 properties = domUtils.getDirectChild(node.element, stylens, 'text-properties'); 633 if (properties) { 634 rule += getTextProperties(properties); 635 } 636 properties = domUtils.getDirectChild(node.element, 637 stylens, 'paragraph-properties'); 638 if (properties) { 639 rule += getParagraphProperties(properties); 640 } 641 properties = domUtils.getDirectChild(node.element, 642 stylens, 'graphic-properties'); 643 if (properties) { 644 rule += getGraphicProperties(properties); 645 } 646 properties = domUtils.getDirectChild(node.element, 647 stylens, 'drawing-page-properties'); 648 if (properties) { 649 rule += getDrawingPageProperties(properties); 650 } 651 properties = domUtils.getDirectChild(node.element, 652 stylens, 'table-cell-properties'); 653 if (properties) { 654 rule += getTableCellProperties(properties); 655 } 656 properties = domUtils.getDirectChild(node.element, 657 stylens, 'table-row-properties'); 658 if (properties) { 659 rule += getTableRowProperties(properties); 660 } 661 properties = domUtils.getDirectChild(node.element, 662 stylens, 'table-column-properties'); 663 if (properties) { 664 rule += getTableColumnProperties(properties); 665 } 666 properties = domUtils.getDirectChild(node.element, 667 stylens, 'table-properties'); 668 if (properties) { 669 rule += getTableProperties(properties); 670 } 671 if (rule.length === 0) { 672 return; 673 } 674 rule = selector + '{' + rule + '}'; 675 sheet.insertRule(rule, sheet.cssRules.length); 676 } 677 678 /** 679 * @param {!CSSStyleSheet} sheet 680 * @param {!Element} node <style:page-layout/>/<style:default-page-layout/> 681 * @return {undefined} 682 */ 683 function addPageStyleRules(sheet, node) { 684 var rule = '', imageProps, url, 685 contentLayoutRule = '', 686 pageSizeRule = '', 687 props = domUtils.getDirectChild(node, stylens, 'page-layout-properties'), 688 stylename, 689 masterStyles, 690 e, 691 masterStyleName; 692 if (!props) { 693 return; 694 } 695 stylename = node.getAttributeNS(stylens, 'name'); 696 697 rule += applySimpleMapping(props, pageContentPropertySimpleMapping); 698 imageProps = domUtils.getDirectChild(props, stylens, 'background-image'); 699 if (imageProps) { 700 url = imageProps.getAttributeNS(xlinkns, 'href'); 701 if (url) { 702 rule += "background-image: url('odfkit:" + url + "');"; 703 //rule += "background-repeat: repeat;"; //FIXME test 704 rule += applySimpleMapping(imageProps, bgImageSimpleMapping); 705 } 706 } 707 708 if (documentType === 'presentation') { 709 masterStyles = domUtils.getDirectChild(/**@type{!Element}*/(node.parentNode.parentNode), officens, 'master-styles'); 710 e = masterStyles && masterStyles.firstElementChild; 711 while (e) { 712 // Generate CSS for all the pages that use the master page that use this page-layout 713 if (e.namespaceURI === stylens && e.localName === "master-page" 714 && e.getAttributeNS(stylens, 'page-layout-name') 715 === stylename) { 716 masterStyleName = e.getAttributeNS(stylens, 'name'); 717 718 contentLayoutRule = 'draw|page[draw|master-page-name=' + masterStyleName + '] {' + rule + '}'; 719 pageSizeRule = 'office|body, draw|page[draw|master-page-name=' + masterStyleName + '] {' 720 + applySimpleMapping(props, pageSizePropertySimpleMapping) 721 + ' }'; 722 723 sheet.insertRule(contentLayoutRule, sheet.cssRules.length); 724 sheet.insertRule(pageSizeRule, sheet.cssRules.length); 725 } 726 e = e.nextElementSibling; 727 } 728 729 } else if (documentType === 'text') { 730 contentLayoutRule = 'office|text {' + rule + '}'; 731 rule = ''; 732 733 // TODO: We want to use the simpleMapping for ODTs, but not until we have pagination. 734 // So till then, set only the width. 735 //rule += applySimpleMapping(props, pageSizePropertySimpleMapping); 736 pageSizeRule = 'office|body {' 737 + 'width: ' + props.getAttributeNS(fons, 'page-width') + ';' 738 + '}'; 739 740 sheet.insertRule(contentLayoutRule, sheet.cssRules.length); 741 sheet.insertRule(pageSizeRule, sheet.cssRules.length); 742 } 743 744 } 745 746 /** 747 * @param {!CSSStyleSheet} sheet 748 * @param {string} family 749 * @param {string} name 750 * @param {!odf.StyleTreeNode} node 751 * @return {undefined} 752 */ 753 function addRule(sheet, family, name, node) { 754 if (family === "page") { 755 addPageStyleRules(sheet, node.element); 756 } else { 757 addStyleRule(sheet, family, name, node); 758 } 759 } 760 /** 761 * @param {!CSSStyleSheet} sheet 762 * @param {string} family 763 * @param {string} name 764 * @param {!odf.StyleTreeNode} node 765 * @return {undefined} 766 */ 767 function addRules(sheet, family, name, node) { 768 addRule(sheet, family, name, node); 769 var /**@type{string}*/ 770 n; 771 for (n in node.derivedStyles) { 772 if (node.derivedStyles.hasOwnProperty(n)) { 773 addRules(sheet, family, n, node.derivedStyles[n]); 774 } 775 } 776 } 777 778 // css vs odf styles 779 // ODF styles occur in families. A family is a group of odf elements to 780 // which an element applies. ODF families can be mapped to a group of css 781 // elements 782 783 /** 784 * @param {!string} doctype 785 * @param {!Element} rootNode 786 * @param {!CSSStyleSheet} stylesheet 787 * @param {!Object.<string,string>} fontFaceMap 788 * @param {!Object.<string,!Object.<string,!odf.StyleTreeNode>>} styleTree 789 * @return {undefined} 790 */ 791 this.style2css = function (doctype, rootNode, stylesheet, fontFaceMap, styleTree) { 792 var tree, rule, 793 /**@type{string}*/ 794 name, 795 /**@type{string}*/ 796 family; 797 798 odfRoot = rootNode; 799 800 // make stylesheet empty 801 while (stylesheet.cssRules.length) { 802 stylesheet.deleteRule(stylesheet.cssRules.length - 1); 803 } 804 805 // add @odfRoot namespace rules 806 odf.Namespaces.forEachPrefix(function (prefix, ns) { 807 rule = '@namespace ' + prefix + ' url(' + ns + ');'; 808 try { 809 stylesheet.insertRule(rule, stylesheet.cssRules.length); 810 } catch (/**@type{!DOMException}*/ignore) { 811 // WebKit can throw an exception here, but it will have 812 // retained the namespace declarations anyway. 813 } 814 }); 815 816 fontFaceDeclsMap = fontFaceMap; 817 documentType = doctype; 818 defaultFontSize = runtime.getWindow().getComputedStyle(document.body, null).getPropertyValue('font-size') || '12pt'; 819 820 // add the various styles 821 for (family in familynamespaceprefixes) { 822 if (familynamespaceprefixes.hasOwnProperty(family)) { 823 tree = styleTree[family]; 824 for (name in tree) { 825 if (tree.hasOwnProperty(name)) { 826 addRules(stylesheet, family, name, tree[name]); 827 } 828 } 829 } 830 } 831 }; 832 }; 833