|
165 | 165 | return UnitedNumberMap[css_line_height_string] = 1; |
166 | 166 | }; |
167 | 167 | GetCSS = function (element) { |
168 | | - var css, |
169 | | - tmp, |
170 | | - computedCSSElement; |
| 168 | + var css,tmp,computedCSSElement; |
171 | 169 | computedCSSElement = (function (el) { |
172 | 170 | var compCSS; |
173 | 171 | compCSS = (function (el) { |
|
202 | 200 | css["font-size"] = ResolveUnitedNumber(computedCSSElement("font-size")) || 1; |
203 | 201 | css["line-height"] = ResolveUnitedNumber(computedCSSElement("line-height")) || 1; |
204 | 202 | css["display"] = (computedCSSElement("display") === "inline" ? "inline" : "block"); |
205 | | - if (css["display"] === "block") { |
206 | | - css["margin-top"] = ResolveUnitedNumber(computedCSSElement("margin-top")) || 0; |
207 | | - css["margin-bottom"] = ResolveUnitedNumber(computedCSSElement("margin-bottom")) || 0; |
208 | | - css["padding-top"] = ResolveUnitedNumber(computedCSSElement("padding-top")) || 0; |
209 | | - css["padding-bottom"] = ResolveUnitedNumber(computedCSSElement("padding-bottom")) || 0; |
210 | | - css["margin-left"] = ResolveUnitedNumber(computedCSSElement("margin-left")) || 0; |
211 | | - css["margin-right"] = ResolveUnitedNumber(computedCSSElement("margin-right")) || 0; |
212 | | - css["padding-left"] = ResolveUnitedNumber(computedCSSElement("padding-left")) || 0; |
213 | | - css["padding-right"] = ResolveUnitedNumber(computedCSSElement("padding-right")) || 0; |
214 | | - } |
| 203 | + |
| 204 | + tmp = (css["display"] === "block"); |
| 205 | + css["margin-top"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-top")) || 0; |
| 206 | + css["margin-bottom"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-bottom")) || 0; |
| 207 | + css["padding-top"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-top")) || 0; |
| 208 | + css["padding-bottom"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-bottom")) || 0; |
| 209 | + css["margin-left"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-left")) || 0; |
| 210 | + css["margin-right"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-right")) || 0; |
| 211 | + css["padding-left"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-left")) || 0; |
| 212 | + css["padding-right"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-right")) || 0; |
| 213 | + |
215 | 214 | //float and clearing of floats |
216 | 215 | css["float"] = FloatMap[computedCSSElement("cssFloat")] || "none"; |
217 | 216 | css["clear"] = ClearMap[computedCSSElement("clear")] || "none"; |
|
330 | 329 | while (i < l) { |
331 | 330 | cn = cns[i]; |
332 | 331 | if (typeof cn === "object") { |
333 | | - |
| 332 | + |
334 | 333 | //execute all watcher functions to e.g. reset floating |
335 | 334 | renderer.executeWatchFunctions(cn); |
336 | 335 |
|
|
357 | 356 | renderer.pdf.addPage(); |
358 | 357 | renderer.y = renderer.pdf.margins_doc.top; |
359 | 358 | } |
360 | | - |
| 359 | + |
361 | 360 | } else if (cn.nodeType === 1 && !SkipNode[cn.nodeName]) { |
362 | 361 | /*** IMAGE RENDERING ***/ |
363 | | - if (cn.nodeName === "IMG" && images[cn.getAttribute("src")]) { |
| 362 | + var cached_image; |
| 363 | + if (cn.nodeName === "IMG") { |
| 364 | + var url = cn.getAttribute("src"); |
| 365 | + cached_image = images[renderer.pdf.sHashCode(url) || url]; |
| 366 | + } |
| 367 | + if (cached_image) { |
364 | 368 | if ((renderer.pdf.internal.pageSize.height - renderer.pdf.margins_doc.bottom < renderer.y + cn.height) && (renderer.y > renderer.pdf.margins_doc.top)) { |
365 | 369 | renderer.pdf.addPage(); |
366 | 370 | renderer.y = renderer.pdf.margins_doc.top; |
367 | 371 | //check if we have to set back some values due to e.g. header rendering for new page |
368 | 372 | renderer.executeWatchFunctions(cn); |
369 | | - } |
370 | | - |
| 373 | + } |
| 374 | + |
371 | 375 | var imagesCSS = GetCSS(cn); |
372 | 376 | var imageX = renderer.x; |
373 | 377 | var fontToUnitRatio = 12 / renderer.pdf.internal.scaleFactor; |
374 | | - |
| 378 | + |
375 | 379 | //define additional paddings, margins which have to be taken into account for margin calculations |
376 | 380 | var additionalSpaceLeft = (imagesCSS["margin-left"] + imagesCSS["padding-left"])*fontToUnitRatio; |
377 | 381 | var additionalSpaceRight = (imagesCSS["margin-right"] + imagesCSS["padding-right"])*fontToUnitRatio; |
378 | 382 | var additionalSpaceTop = (imagesCSS["margin-top"] + imagesCSS["padding-top"])*fontToUnitRatio; |
379 | 383 | var additionalSpaceBottom = (imagesCSS["margin-bottom"] + imagesCSS["padding-bottom"])*fontToUnitRatio; |
380 | | - |
| 384 | + |
381 | 385 | //if float is set to right, move the image to the right border |
382 | 386 | //add space if margin is set |
383 | 387 | if (imagesCSS['float'] !== undefined && imagesCSS['float'] === 'right') { |
|
386 | 390 | imageX += additionalSpaceLeft; |
387 | 391 | } |
388 | 392 |
|
389 | | - renderer.pdf.addImage(images[cn.getAttribute("src")], imageX, renderer.y + additionalSpaceTop, cn.width, cn.height); |
| 393 | + renderer.pdf.addImage(cached_image, imageX, renderer.y + additionalSpaceTop, cn.width, cn.height); |
| 394 | + cached_image = undefined; |
390 | 395 | //if the float prop is specified we have to float the text around the image |
391 | | - if (imagesCSS['float'] !== undefined) { |
392 | | - if (imagesCSS['float'] === 'right' || imagesCSS['float'] === 'left') { |
393 | | - |
394 | | - //add functiont to set back coordinates after image rendering |
395 | | - renderer.watchFunctions.push((function(diffX , thresholdY, diffWidth, el) { |
396 | | - //undo drawing box adaptions which were set by floating |
397 | | - if (renderer.y >= thresholdY) { |
398 | | - renderer.x += diffX; |
399 | | - renderer.settings.width += diffWidth; |
400 | | - return true; |
401 | | - } else if(el && el.nodeType === 1 && !SkipNode[el.nodeName] && renderer.x+el.width > (renderer.pdf.margins_doc.left + renderer.pdf.margins_doc.width)) { |
402 | | - renderer.x += diffX; |
403 | | - renderer.y = thresholdY; |
404 | | - renderer.settings.width += diffWidth; |
| 396 | + if (imagesCSS['float'] === 'right' || imagesCSS['float'] === 'left') { |
| 397 | + //add functiont to set back coordinates after image rendering |
| 398 | + renderer.watchFunctions.push((function(diffX , thresholdY, diffWidth, el) { |
| 399 | + //undo drawing box adaptions which were set by floating |
| 400 | + if (renderer.y >= thresholdY) { |
| 401 | + renderer.x += diffX; |
| 402 | + renderer.settings.width += diffWidth; |
| 403 | + return true; |
| 404 | + } else if(el && el.nodeType === 1 && !SkipNode[el.nodeName] && renderer.x+el.width > (renderer.pdf.margins_doc.left + renderer.pdf.margins_doc.width)) { |
| 405 | + renderer.x += diffX; |
| 406 | + renderer.y = thresholdY; |
| 407 | + renderer.settings.width += diffWidth; |
| 408 | + return true; |
| 409 | + } else { |
| 410 | + return false; |
| 411 | + } |
| 412 | + }).bind(this, (imagesCSS['float'] === 'left') ? -cn.width-additionalSpaceLeft-additionalSpaceRight : 0, renderer.y+cn.height+additionalSpaceTop+additionalSpaceBottom, cn.width)); |
| 413 | + //reset floating by clear:both divs |
| 414 | + //just set cursorY after the floating element |
| 415 | + renderer.watchFunctions.push((function(yPositionAfterFloating, pages, el) { |
| 416 | + if (renderer.y < yPositionAfterFloating && pages === renderer.pdf.internal.getNumberOfPages()) { |
| 417 | + if (el.nodeType === 1 && GetCSS(el).clear === 'both') { |
| 418 | + renderer.y = yPositionAfterFloating; |
405 | 419 | return true; |
406 | 420 | } else { |
407 | 421 | return false; |
408 | 422 | } |
409 | | - }).bind(this, (imagesCSS['float'] === 'left') ? -cn.width-additionalSpaceLeft-additionalSpaceRight : 0, renderer.y+cn.height+additionalSpaceTop+additionalSpaceBottom, cn.width)); |
410 | | - |
411 | | - //reset floating by clear:both divs |
412 | | - //just set cursorY after the floating element |
413 | | - renderer.watchFunctions.push((function(yPositionAfterFloating, pages, el) { |
414 | | - if (renderer.y < yPositionAfterFloating && pages === renderer.pdf.internal.getNumberOfPages()) { |
415 | | - if (el.nodeType === 1 && GetCSS(el).clear === 'both') { |
416 | | - renderer.y = yPositionAfterFloating; |
417 | | - return true; |
418 | | - } else { |
419 | | - return false; |
420 | | - } |
421 | | - } else { |
422 | | - return true; |
423 | | - } |
424 | | - }).bind(this, renderer.y+cn.height, renderer.pdf.internal.getNumberOfPages())); |
425 | | - |
426 | | - //if floating is set we decrease the available width by the image width |
427 | | - renderer.settings.width -= cn.width+additionalSpaceLeft+additionalSpaceRight; |
428 | | - //if left just add the image width to the X coordinate |
429 | | - if (imagesCSS['float'] === 'left') { |
430 | | - renderer.x += cn.width+additionalSpaceLeft+additionalSpaceRight; |
| 423 | + } else { |
| 424 | + return true; |
431 | 425 | } |
| 426 | + }).bind(this, renderer.y+cn.height, renderer.pdf.internal.getNumberOfPages())); |
| 427 | + |
| 428 | + //if floating is set we decrease the available width by the image width |
| 429 | + renderer.settings.width -= cn.width+additionalSpaceLeft+additionalSpaceRight; |
| 430 | + //if left just add the image width to the X coordinate |
| 431 | + if (imagesCSS['float'] === 'left') { |
| 432 | + renderer.x += cn.width+additionalSpaceLeft+additionalSpaceRight; |
432 | 433 | } |
433 | | - //if no floating is set, move the rendering cursor after the image height |
434 | 434 | } else { |
| 435 | + //if no floating is set, move the rendering cursor after the image height |
435 | 436 | renderer.y += cn.height + additionalSpaceBottom; |
436 | | - } |
437 | | - |
438 | | - /*** TABLE RENDERING ***/ |
| 437 | + } |
| 438 | + |
| 439 | + /*** TABLE RENDERING ***/ |
439 | 440 | } else if (cn.nodeName === "TABLE") { |
440 | 441 | table2json = tableToJson(cn, renderer); |
441 | 442 | renderer.y += 10; |
|
495 | 496 | images = {}; |
496 | 497 | loadImgs = function (element, renderer, elementHandlers, cb) { |
497 | 498 | var imgs = element.getElementsByTagName('img'), |
498 | | - l = imgs.length, |
| 499 | + l = imgs.length, found_images, |
499 | 500 | x = 0; |
500 | 501 | function done() { |
501 | 502 | renderer.pdf.internal.events.publish('imagesLoaded'); |
502 | | - cb(); |
| 503 | + cb(found_images); |
503 | 504 | } |
504 | 505 | function loadImage(url, width, height) { |
505 | 506 | if (!url) |
506 | 507 | return; |
507 | 508 | var img = new Image(); |
508 | | - ++x; |
| 509 | + found_images = ++x; |
509 | 510 | img.crossOrigin = ''; |
510 | 511 | img.onerror = img.onload = function () { |
511 | 512 | if(img.complete) { |
|
517 | 518 | } |
518 | 519 | //if valid image add to known images array |
519 | 520 | if (img.width + img.height) { |
520 | | - //TODO: use a hash since data URIs could greatly increase the memory usage |
521 | | - images[url] = images[url] || img; |
| 521 | + var hash = renderer.pdf.sHashCode(url) || url; |
| 522 | + images[hash] = images[hash] || img; |
522 | 523 | } |
523 | 524 | } |
524 | 525 | if(!--x) { |
|
531 | 532 | loadImage(imgs[l].getAttribute("src"),imgs[l].width,imgs[l].height); |
532 | 533 | return x || done(); |
533 | 534 | }; |
534 | | - checkForFooter = function (elem, renderer, elementHandlers, callback) { |
| 535 | + checkForFooter = function (elem, renderer, elementHandlers) { |
535 | 536 | //check if we can found a <footer> element |
536 | 537 | var footer = elem.getElementsByTagName("footer"); |
537 | 538 | if (footer.length > 0) { |
|
596 | 597 |
|
597 | 598 | //prevent footer rendering |
598 | 599 | SkipNode['FOOTER'] = 1; |
599 | | - |
600 | 600 | } |
601 | | - |
602 | | - //footer preparation finished |
603 | | - callback(); |
604 | 601 | }; |
605 | 602 | process = function (pdf, element, x, y, settings, callback) { |
606 | 603 | if (!element) |
|
625 | 622 | })(element.replace(/<\/?script[^>]*?>/gi, '')); |
626 | 623 | } |
627 | 624 | var r = new Renderer(pdf, x, y, settings); |
628 | | - callback = callback || function () {}; |
629 | 625 |
|
630 | 626 | // 1. load images |
631 | 627 | // 2. prepare optional footer elements |
632 | 628 | // 3. render content |
633 | | - loadImgs.call(this, element, r, settings.elementHandlers, function () { |
634 | | - checkForFooter.call(this, element, r, settings.elementHandlers, function () { |
635 | | - DrillForContent(element, r, settings.elementHandlers); |
636 | | - //send event dispose for final taks (e.g. footer totalpage replacement) |
637 | | - r.pdf.internal.events.publish('htmlRenderingFinished'); |
638 | | - callback(r.dispose()); |
639 | | - }); |
| 629 | + loadImgs.call(this, element, r, settings.elementHandlers, function (found_images) { |
| 630 | + checkForFooter( element, r, settings.elementHandlers); |
| 631 | + DrillForContent(element, r, settings.elementHandlers); |
| 632 | + //send event dispose for final taks (e.g. footer totalpage replacement) |
| 633 | + r.pdf.internal.events.publish('htmlRenderingFinished'); |
| 634 | + r = r.dispose(); |
| 635 | + if (typeof callback === 'function') callback(r); |
| 636 | + else if (found_images) console.error('jsPDF Warning: rendering issues? provide a callback to fromHTML!'); |
640 | 637 | }); |
641 | | - |
642 | 638 | return pdf; |
643 | 639 | }; |
644 | 640 | Renderer.prototype.init = function () { |
|
655 | 651 | y : this.y |
656 | 652 | }; |
657 | 653 | }; |
658 | | - |
| 654 | + |
659 | 655 | //Checks if we have to execute some watcher functions |
660 | 656 | //e.g. to end text floating around an image |
661 | 657 | Renderer.prototype.executeWatchFunctions = function(el) { |
|
672 | 668 | this.watchFunctions = narray; |
673 | 669 | } |
674 | 670 | return ret; |
675 | | - }; |
| 671 | + }; |
676 | 672 |
|
677 | 673 | Renderer.prototype.splitFragmentsIntoLines = function (fragments, styles) { |
678 | 674 | var currentLineLength, |
|
843 | 839 |
|
844 | 840 | //stores the current indent of cursor position |
845 | 841 | var currentIndent = 0; |
846 | | - |
| 842 | + |
847 | 843 | while (lines.length) { |
848 | 844 | line = lines.shift(); |
849 | 845 | maxLineHeight = 0; |
|
875 | 871 | i++; |
876 | 872 | } |
877 | 873 | this.y += maxLineHeight * fontToUnitRatio; |
878 | | - |
| 874 | + |
879 | 875 | //if some watcher function was executed sucessful, so e.g. margin and widths were changed, |
880 | 876 | //reset line drawing and calculate position and lines again |
881 | 877 | //e.g. to stop text floating around an image |
882 | 878 | if (this.executeWatchFunctions(line[0][1]) && lines.length > 0) { |
883 | 879 | var localFragments = []; |
884 | 880 | var localStyles = []; |
885 | | - //create fragement array of |
| 881 | + //create fragement array of |
886 | 882 | lines.forEach(function(localLine) { |
887 | 883 | var i = 0; |
888 | 884 | var l = localLine.length; |
889 | 885 | while (i !== l) { |
890 | 886 | if (localLine[i][0]) { |
891 | | - localFragments.push(localLine[i][0]+' '); |
| 887 | + localFragments.push(localLine[i][0]+' '); |
892 | 888 | localStyles.push(localLine[i][1]); |
893 | 889 | } |
894 | 890 | ++i; |
|
897 | 893 | //split lines again due to possible coordinate changes |
898 | 894 | lines = this.splitFragmentsIntoLines(PurgeWhiteSpace(localFragments), localStyles); |
899 | 895 | //reposition the current cursor |
900 | | - out("ET", "Q"); |
| 896 | + out("ET", "Q"); |
901 | 897 | out("q", "BT", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td"); |
902 | | - } |
903 | | - |
| 898 | + } |
| 899 | + |
904 | 900 | } |
905 | 901 | if (cb && typeof cb === "function") { |
906 | 902 | cb.call(this, this.x - 9, this.y - fontSize / 2); |
|
961 | 957 | ClearMap = { |
962 | 958 | none : 'none', |
963 | 959 | both : 'both' |
964 | | - }; |
| 960 | + }; |
965 | 961 | UnitedNumberMap = { |
966 | 962 | normal : 1 |
967 | 963 | }; |
|
0 commit comments