|
33 | 33 | FontNameDB,
|
34 | 34 | FontStyleMap,
|
35 | 35 | FontWeightMap,
|
| 36 | + FloatMap, |
| 37 | + ClearMap, |
36 | 38 | GetCSS,
|
37 | 39 | PurgeWhiteSpace,
|
38 | 40 | Renderer,
|
|
101 | 103 | this.x = x;
|
102 | 104 | this.y = y;
|
103 | 105 | this.settings = settings;
|
| 106 | + //list of functions which are called after each element-rendering process |
| 107 | + this.watchFunctions = []; |
104 | 108 | this.init();
|
105 | 109 | return this;
|
106 | 110 | };
|
|
208 | 212 | css["padding-left"] = ResolveUnitedNumber(computedCSSElement("padding-left")) || 0;
|
209 | 213 | css["padding-right"] = ResolveUnitedNumber(computedCSSElement("padding-right")) || 0;
|
210 | 214 | }
|
| 215 | + //float and clearing of floats |
| 216 | + css["float"] = FloatMap[computedCSSElement("cssFloat")] || "none"; |
| 217 | + css["clear"] = ClearMap[computedCSSElement("clear")] || "none"; |
211 | 218 | return css;
|
212 | 219 | };
|
213 | 220 | elementHandledElsewhere = function (element, renderer, elementHandlers) {
|
|
323 | 330 | while (i < l) {
|
324 | 331 | cn = cns[i];
|
325 | 332 | if (typeof cn === "object") {
|
| 333 | + |
| 334 | + //execute all watcher functions to e.g. reset floating |
| 335 | + renderer.executeWatchFunctions(cn); |
326 | 336 |
|
327 | 337 | /*** HEADER rendering **/
|
328 | 338 | if (cn.nodeType === 1 && cn.nodeName === 'HEADER') {
|
|
347 | 357 | renderer.pdf.addPage();
|
348 | 358 | renderer.y = renderer.pdf.margins_doc.top;
|
349 | 359 | }
|
| 360 | + |
350 | 361 | } else if (cn.nodeType === 1 && !SkipNode[cn.nodeName]) {
|
| 362 | + /*** IMAGE RENDERING ***/ |
351 | 363 | if (cn.nodeName === "IMG" && images[cn.getAttribute("src")]) {
|
352 | 364 | if ((renderer.pdf.internal.pageSize.height - renderer.pdf.margins_doc.bottom < renderer.y + cn.height) && (renderer.y > renderer.pdf.margins_doc.top)) {
|
353 | 365 | renderer.pdf.addPage();
|
354 | 366 | renderer.y = renderer.pdf.margins_doc.top;
|
| 367 | + //check if we have to set back some values due to e.g. header rendering for new page |
| 368 | + renderer.executeWatchFunctions(cn); |
| 369 | + } |
| 370 | + |
| 371 | + var imagesCSS = GetCSS(cn); |
| 372 | + var imageX = renderer.x; |
| 373 | + //if float is set to right, move the image to the right border |
| 374 | + if (imagesCSS['float'] !== undefined && imagesCSS['float'] === 'right') { |
| 375 | + imageX += renderer.settings.width-cn.width; |
355 | 376 | }
|
356 |
| - renderer.pdf.addImage(images[cn.getAttribute("src")], renderer.x, renderer.y, cn.width, cn.height); |
357 |
| - renderer.y += cn.height; |
| 377 | + |
| 378 | + renderer.pdf.addImage(images[cn.getAttribute("src")], imageX, renderer.y, cn.width, cn.height); |
| 379 | + //if the float prop is specified we have to float the text around the image |
| 380 | + if (imagesCSS['float'] !== undefined) { |
| 381 | + if (imagesCSS['float'] === 'right' || imagesCSS['float'] === 'left') { |
| 382 | + |
| 383 | + //add functiont to set back coordinates after image rendering |
| 384 | + renderer.watchFunctions.push((function(diffX , thresholdY, diffWidth, el) { |
| 385 | + //undo drawing box adaptions which were set by floating |
| 386 | + if (renderer.y >= thresholdY) { |
| 387 | + renderer.x += diffX; |
| 388 | + renderer.settings.width += diffWidth; |
| 389 | + return true; |
| 390 | + } else if(el && el.nodeType === 1 && !SkipNode[el.nodeName] && renderer.x+el.width > (renderer.pdf.margins_doc.left + renderer.pdf.margins_doc.width)) { |
| 391 | + renderer.x += diffX; |
| 392 | + renderer.y = thresholdY; |
| 393 | + renderer.settings.width += diffWidth; |
| 394 | + return true; |
| 395 | + } else { |
| 396 | + return false; |
| 397 | + } |
| 398 | + }).bind(this, (imagesCSS['float'] === 'left') ? -cn.width : 0, renderer.y+cn.height, cn.width)); |
| 399 | + |
| 400 | + //reset floating by clear:both divs |
| 401 | + //just set cursorY after the floating element |
| 402 | + renderer.watchFunctions.push((function(yPositionAfterFloating, pages, el) { |
| 403 | + if (renderer.y < yPositionAfterFloating && pages === renderer.pdf.internal.getNumberOfPages()) { |
| 404 | + if (el.nodeType === 1 && GetCSS(el).clear === 'both') { |
| 405 | + renderer.y = yPositionAfterFloating; |
| 406 | + return true; |
| 407 | + } else { |
| 408 | + return false; |
| 409 | + } |
| 410 | + } else { |
| 411 | + return true; |
| 412 | + } |
| 413 | + }).bind(this, renderer.y+cn.height, renderer.pdf.internal.getNumberOfPages())); |
| 414 | + |
| 415 | + //if floating is set we decrease the available width by the image width |
| 416 | + renderer.settings.width -= cn.width; |
| 417 | + //if left just add the image width to the X coordinate |
| 418 | + if (imagesCSS['float'] === 'left') { |
| 419 | + renderer.x += cn.width; |
| 420 | + } |
| 421 | + } |
| 422 | + //if no floating is set, move the rendering cursor after the image height |
| 423 | + } else { |
| 424 | + renderer.y += cn.height; |
| 425 | + } |
| 426 | + |
| 427 | + /*** TABLE RENDERING ***/ |
358 | 428 | } else if (cn.nodeName === "TABLE") {
|
359 | 429 | table2json = tableToJson(cn, renderer);
|
360 | 430 | renderer.y += 10;
|
|
574 | 644 | y : this.y
|
575 | 645 | };
|
576 | 646 | };
|
| 647 | + |
| 648 | + //Checks if we have to execute some watcher functions |
| 649 | + //e.g. to end text floating around an image |
| 650 | + Renderer.prototype.executeWatchFunctions = function(el) { |
| 651 | + var ret = false; |
| 652 | + var narray = []; |
| 653 | + if (this.watchFunctions.length > 0) { |
| 654 | + for(var i=0; i< this.watchFunctions.length; ++i) { |
| 655 | + if (this.watchFunctions[i](el) === true) { |
| 656 | + ret = true; |
| 657 | + } else { |
| 658 | + narray.push(this.watchFunctions[i]); |
| 659 | + } |
| 660 | + } |
| 661 | + this.watchFunctions = narray; |
| 662 | + } |
| 663 | + return ret; |
| 664 | + }; |
| 665 | + |
577 | 666 | Renderer.prototype.splitFragmentsIntoLines = function (fragments, styles) {
|
578 | 667 | var currentLineLength,
|
579 | 668 | defaultFontSize,
|
|
669 | 758 | };
|
670 | 759 | Renderer.prototype.RenderTextFragment = function (text, style) {
|
671 | 760 | var defaultFontSize,
|
672 |
| - font; |
| 761 | + font, |
| 762 | + maxLineHeight; |
| 763 | + |
| 764 | + maxLineHeight = 0; |
| 765 | + defaultFontSize = 12; |
| 766 | + |
673 | 767 | if (this.pdf.internal.pageSize.height - this.pdf.margins_doc.bottom < this.y + this.pdf.internal.getFontSize()) {
|
674 | 768 | this.pdf.internal.write("ET", "Q");
|
675 | 769 | this.pdf.addPage();
|
676 | 770 | this.y = this.pdf.margins_doc.top;
|
677 | 771 | this.pdf.internal.write("q", "BT", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td");
|
| 772 | + //move cursor by one line on new page |
| 773 | + maxLineHeight = Math.max(maxLineHeight, style["line-height"], style["font-size"]); |
| 774 | + this.pdf.internal.write(0, (-1 * defaultFontSize * maxLineHeight).toFixed(2), "Td"); |
678 | 775 | }
|
679 |
| - defaultFontSize = 12; |
| 776 | + |
680 | 777 | font = this.pdf.internal.getFont(style["font-family"], style["font-style"]);
|
681 | 778 |
|
682 | 779 | //set the word spacing for e.g. justify style
|
|
735 | 832 |
|
736 | 833 | //stores the current indent of cursor position
|
737 | 834 | var currentIndent = 0;
|
| 835 | + |
738 | 836 | while (lines.length) {
|
739 | 837 | line = lines.shift();
|
740 | 838 | maxLineHeight = 0;
|
|
766 | 864 | i++;
|
767 | 865 | }
|
768 | 866 | this.y += maxLineHeight * fontToUnitRatio;
|
| 867 | + |
| 868 | + //if some watcher function was executed sucessful, so e.g. margin and widths were changed, |
| 869 | + //reset line drawing and calculate position and lines again |
| 870 | + //e.g. to stop text floating around an image |
| 871 | + if (this.executeWatchFunctions(line[0][1]) && lines.length > 0) { |
| 872 | + var localFragments = []; |
| 873 | + var localStyles = []; |
| 874 | + //create fragement array of |
| 875 | + lines.forEach(function(localLine) { |
| 876 | + var i = 0; |
| 877 | + var l = localLine.length; |
| 878 | + while (i !== l) { |
| 879 | + if (localLine[i][0]) { |
| 880 | + localFragments.push(localLine[i][0]+' '); |
| 881 | + localStyles.push(localLine[i][1]); |
| 882 | + } |
| 883 | + ++i; |
| 884 | + } |
| 885 | + }); |
| 886 | + //split lines again due to possible coordinate changes |
| 887 | + lines = this.splitFragmentsIntoLines(PurgeWhiteSpace(localFragments), localStyles); |
| 888 | + //reposition the current cursor |
| 889 | + out("ET", "Q"); |
| 890 | + out("q", "BT", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td"); |
| 891 | + } |
| 892 | + |
769 | 893 | }
|
770 | 894 | if (cb && typeof cb === "function") {
|
771 | 895 | cb.call(this, this.x - 9, this.y - fontSize / 2);
|
|
818 | 942 | center : "center",
|
819 | 943 | justify : "justify"
|
820 | 944 | };
|
| 945 | + FloatMap = { |
| 946 | + none : 'none', |
| 947 | + right: 'right', |
| 948 | + left: 'left' |
| 949 | + }; |
| 950 | + ClearMap = { |
| 951 | + none : 'none', |
| 952 | + both : 'both' |
| 953 | + }; |
821 | 954 | UnitedNumberMap = {
|
822 | 955 | normal : 1
|
823 | 956 | };
|
|
0 commit comments