Skip to content

Commit 78eac71

Browse files
committed
Merge pull request #262 from woolfg/imagefloating
support of css style float and clear
2 parents 22e4ddc + 7ab98a3 commit 78eac71

File tree

1 file changed

+137
-4
lines changed

1 file changed

+137
-4
lines changed

jspdf.plugin.from_html.js

+137-4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
FontNameDB,
3434
FontStyleMap,
3535
FontWeightMap,
36+
FloatMap,
37+
ClearMap,
3638
GetCSS,
3739
PurgeWhiteSpace,
3840
Renderer,
@@ -101,6 +103,8 @@
101103
this.x = x;
102104
this.y = y;
103105
this.settings = settings;
106+
//list of functions which are called after each element-rendering process
107+
this.watchFunctions = [];
104108
this.init();
105109
return this;
106110
};
@@ -208,6 +212,9 @@
208212
css["padding-left"] = ResolveUnitedNumber(computedCSSElement("padding-left")) || 0;
209213
css["padding-right"] = ResolveUnitedNumber(computedCSSElement("padding-right")) || 0;
210214
}
215+
//float and clearing of floats
216+
css["float"] = FloatMap[computedCSSElement("cssFloat")] || "none";
217+
css["clear"] = ClearMap[computedCSSElement("clear")] || "none";
211218
return css;
212219
};
213220
elementHandledElsewhere = function (element, renderer, elementHandlers) {
@@ -323,6 +330,9 @@
323330
while (i < l) {
324331
cn = cns[i];
325332
if (typeof cn === "object") {
333+
334+
//execute all watcher functions to e.g. reset floating
335+
renderer.executeWatchFunctions(cn);
326336

327337
/*** HEADER rendering **/
328338
if (cn.nodeType === 1 && cn.nodeName === 'HEADER') {
@@ -347,14 +357,74 @@
347357
renderer.pdf.addPage();
348358
renderer.y = renderer.pdf.margins_doc.top;
349359
}
360+
350361
} else if (cn.nodeType === 1 && !SkipNode[cn.nodeName]) {
362+
/*** IMAGE RENDERING ***/
351363
if (cn.nodeName === "IMG" && images[cn.getAttribute("src")]) {
352364
if ((renderer.pdf.internal.pageSize.height - renderer.pdf.margins_doc.bottom < renderer.y + cn.height) && (renderer.y > renderer.pdf.margins_doc.top)) {
353365
renderer.pdf.addPage();
354366
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;
355376
}
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 ***/
358428
} else if (cn.nodeName === "TABLE") {
359429
table2json = tableToJson(cn, renderer);
360430
renderer.y += 10;
@@ -574,6 +644,25 @@
574644
y : this.y
575645
};
576646
};
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+
577666
Renderer.prototype.splitFragmentsIntoLines = function (fragments, styles) {
578667
var currentLineLength,
579668
defaultFontSize,
@@ -669,14 +758,22 @@
669758
};
670759
Renderer.prototype.RenderTextFragment = function (text, style) {
671760
var defaultFontSize,
672-
font;
761+
font,
762+
maxLineHeight;
763+
764+
maxLineHeight = 0;
765+
defaultFontSize = 12;
766+
673767
if (this.pdf.internal.pageSize.height - this.pdf.margins_doc.bottom < this.y + this.pdf.internal.getFontSize()) {
674768
this.pdf.internal.write("ET", "Q");
675769
this.pdf.addPage();
676770
this.y = this.pdf.margins_doc.top;
677771
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");
678775
}
679-
defaultFontSize = 12;
776+
680777
font = this.pdf.internal.getFont(style["font-family"], style["font-style"]);
681778

682779
//set the word spacing for e.g. justify style
@@ -735,6 +832,7 @@
735832

736833
//stores the current indent of cursor position
737834
var currentIndent = 0;
835+
738836
while (lines.length) {
739837
line = lines.shift();
740838
maxLineHeight = 0;
@@ -766,6 +864,32 @@
766864
i++;
767865
}
768866
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+
769893
}
770894
if (cb && typeof cb === "function") {
771895
cb.call(this, this.x - 9, this.y - fontSize / 2);
@@ -818,6 +942,15 @@
818942
center : "center",
819943
justify : "justify"
820944
};
945+
FloatMap = {
946+
none : 'none',
947+
right: 'right',
948+
left: 'left'
949+
};
950+
ClearMap = {
951+
none : 'none',
952+
both : 'both'
953+
};
821954
UnitedNumberMap = {
822955
normal : 1
823956
};

0 commit comments

Comments
 (0)