@@ -37,9 +37,9 @@ function renderDocument(document, options, windowWidth, windowHeight) {
3737 document . querySelector ( selector ) . removeAttribute ( html2canvasNodeAttribute ) ;
3838 var clonedWindow = container . contentWindow ;
3939 var node = clonedWindow . document . querySelector ( selector ) ;
40- var support = new Support ( ) ;
40+ var support = new Support ( clonedWindow . document ) ;
4141 var imageLoader = new ImageLoader ( options , support ) ;
42- var bounds = NodeParser . prototype . getBounds ( node ) ;
42+ var bounds = getBounds ( node ) ;
4343 var width = options . type === "view" ? Math . min ( bounds . width , windowWidth ) : documentWidth ( ) ;
4444 var height = options . type === "view" ? Math . min ( bounds . height , windowHeight ) : documentHeight ( ) ;
4545 var renderer = new CanvasRenderer ( width , height , imageLoader ) ;
@@ -343,6 +343,7 @@ function NodeContainer(node, parent) {
343343 this . parent = parent ;
344344 this . stack = null ;
345345 this . bounds = null ;
346+ this . offsetBounds = null ;
346347 this . visible = null ;
347348 this . computedStyles = null ;
348349 this . styles = { } ;
@@ -500,19 +501,28 @@ NodeContainer.prototype.parseTextShadows = function() {
500501NodeContainer . prototype . parseTransform = function ( ) {
501502 var transformRegExp = / ( m a t r i x ) \( ( .+ ) \) / ;
502503 var transform = this . prefixedCss ( "transform" ) ;
503- if ( transform !== null && transform !== "none" ) {
504- var matrix = parseMatrix ( transform . match ( transformRegExp ) ) ;
505- if ( matrix ) {
506- return {
507- origin : this . prefixedCss ( "transformOrigin" ) ,
508- matrix : matrix
509- } ;
510- }
504+ var matrix = parseMatrix ( transform . match ( transformRegExp ) ) ;
505+ var offset = this . parseBounds ( ) ;
506+ if ( matrix ) {
507+ var origin = this . prefixedCss ( "transformOrigin" ) . split ( " " ) . map ( removePx ) . map ( asFloat ) ;
508+ origin [ 0 ] += offset . left ;
509+ origin [ 1 ] += offset . top ;
510+
511+ return {
512+ origin : origin ,
513+ matrix : matrix
514+ } ;
511515 }
512- return {
513- origin : [ 0 , 0 ] ,
514- matrix : [ 1 , 0 , 0 , 1 , 0 , 0 ]
515- } ;
516+ } ;
517+
518+ NodeContainer . prototype . parseBounds = function ( ) {
519+ return this . bounds || ( this . bounds = this . hasTransform ( ) ? offsetBounds ( this . node ) : getBounds ( this . node ) ) ;
520+ } ;
521+
522+
523+ NodeContainer . prototype . hasTransform = function ( ) {
524+ var transform = this . prefixedCss ( "transform" ) ;
525+ return ( transform !== null && transform !== "none" && transform !== "matrix(1, 0, 0, 1, 0, 0)" ) ;
516526} ;
517527
518528NodeContainer . prototype . TEXT_SHADOW_PROPERTY = / ( ( r g b a | r g b ) \( [ ^ \) ] + \) ( \s - ? \d + p x ) { 0 , } ) / g;
@@ -627,6 +637,44 @@ function parseBackgrounds(backgroundImage) {
627637 return results ;
628638}
629639
640+ function removePx ( str ) {
641+ return str . replace ( "px" , "" ) ;
642+ }
643+
644+ function asFloat ( str ) {
645+ return parseFloat ( str ) ;
646+ }
647+
648+ function getBounds ( node ) {
649+ if ( node . getBoundingClientRect ) {
650+ var clientRect = node . getBoundingClientRect ( ) ;
651+ var isBody = node . nodeName === "BODY" ;
652+ var width = isBody ? node . scrollWidth : node . offsetWidth ;
653+ return {
654+ top : clientRect . top ,
655+ bottom : clientRect . bottom || ( clientRect . top + clientRect . height ) ,
656+ right : clientRect . left + width ,
657+ left : clientRect . left ,
658+ width : width ,
659+ height : isBody ? node . scrollHeight : node . offsetHeight
660+ } ;
661+ }
662+ return { } ;
663+ }
664+
665+ function offsetBounds ( node ) {
666+ var parent = node . offsetParent ? offsetBounds ( node . offsetParent ) : { top : 0 , left : 0 } ;
667+
668+ return {
669+ top : node . offsetTop + parent . top ,
670+ bottom : node . offsetTop + node . offsetHeight + parent . top ,
671+ right : node . offsetLeft + parent . left + node . offsetWidth ,
672+ left : node . offsetLeft + parent . left ,
673+ width : node . offsetWidth ,
674+ height : node . offsetHeight
675+ } ;
676+ }
677+
630678function NodeParser ( element , renderer , support , imageLoader , options ) {
631679 log ( "Starting NodeParser" ) ;
632680 this . renderer = renderer ;
@@ -763,7 +811,7 @@ NodeParser.prototype.newStackingContext = function(container, hasOwnStacking) {
763811
764812NodeParser . prototype . createStackingContexts = function ( ) {
765813 this . nodes . forEach ( function ( container ) {
766- if ( isElement ( container ) && ( this . isRootElement ( container ) || hasOpacity ( container ) || isPositionedForStacking ( container ) || this . isBodyWithTransparentRoot ( container ) || hasTransform ( container ) ) ) {
814+ if ( isElement ( container ) && ( this . isRootElement ( container ) || hasOpacity ( container ) || isPositionedForStacking ( container ) || this . isBodyWithTransparentRoot ( container ) || container . hasTransform ( ) ) ) {
767815 this . newStackingContext ( container , true ) ;
768816 } else if ( isElement ( container ) && ( ( isPositioned ( container ) && zIndex0 ( container ) ) || isInlineBlock ( container ) || isFloating ( container ) ) ) {
769817 this . newStackingContext ( container , false ) ;
@@ -786,55 +834,33 @@ NodeParser.prototype.sortStackingContexts = function(stack) {
786834 stack . contexts . forEach ( this . sortStackingContexts , this ) ;
787835} ;
788836
789- NodeParser . prototype . parseBounds = function ( nodeContainer ) {
790- return nodeContainer . bounds = this . getBounds ( nodeContainer . node ) ;
791- } ;
792-
793- NodeParser . prototype . getBounds = function ( node ) {
794- if ( node . getBoundingClientRect ) {
795- var clientRect = node . getBoundingClientRect ( ) ;
796- var isBody = node . nodeName === "BODY" ;
797- var width = isBody ? node . scrollWidth : node . offsetWidth ;
798- return {
799- top : clientRect . top ,
800- bottom : clientRect . bottom || ( clientRect . top + clientRect . height ) ,
801- right : clientRect . left + width ,
802- left : clientRect . left ,
803- width : width ,
804- height : isBody ? node . scrollHeight : node . offsetHeight
805- } ;
806- }
807- return { } ;
808- } ;
809-
810837NodeParser . prototype . parseTextBounds = function ( container ) {
811838 return function ( text , index , textList ) {
812- if ( container . parent . css ( "textDecoration" ) !== "none" || text . trim ( ) . length !== 0 ) {
813- if ( this . support . rangeBounds ) {
839+ if ( container . parent . css ( "textDecoration" ) . substr ( 0 , 4 ) !== "none" || text . trim ( ) . length !== 0 ) {
840+ if ( this . support . rangeBounds && ! container . parent . hasTransform ( ) ) {
814841 var offset = textList . slice ( 0 , index ) . join ( "" ) . length ;
815842 return this . getRangeBounds ( container . node , offset , text . length ) ;
816843 } else if ( container . node && typeof ( container . node . data ) === "string" ) {
817844 var replacementNode = container . node . splitText ( text . length ) ;
818- var bounds = this . getWrapperBounds ( container . node ) ;
845+ var bounds = this . getWrapperBounds ( container . node , container . parent . hasTransform ( ) ) ;
819846 container . node = replacementNode ;
820847 return bounds ;
821848 }
822- } else if ( ! this . support . rangeBounds ) {
849+ } else if ( ! this . support . rangeBounds || container . parent . hasTransform ( ) ) {
823850 container . node = container . node . splitText ( text . length ) ;
824851 }
825852 return { } ;
826853 } ;
827854} ;
828855
829- NodeParser . prototype . getWrapperBounds = function ( node ) {
830- var wrapper = node . ownerDocument . createElement ( 'wrapper ' ) ;
856+ NodeParser . prototype . getWrapperBounds = function ( node , transform ) {
857+ var wrapper = node . ownerDocument . createElement ( 'html2canvaswrapper ' ) ;
831858 var parent = node . parentNode ,
832859 backupText = node . cloneNode ( true ) ;
833860
834861 wrapper . appendChild ( node . cloneNode ( true ) ) ;
835862 parent . replaceChild ( wrapper , node ) ;
836-
837- var bounds = this . getBounds ( wrapper ) ;
863+ var bounds = transform ? offsetBounds ( wrapper ) : getBounds ( wrapper ) ;
838864 parent . replaceChild ( backupText , wrapper ) ;
839865 return bounds ;
840866} ;
@@ -846,6 +872,8 @@ NodeParser.prototype.getRangeBounds = function(node, offset, length) {
846872 return range . getBoundingClientRect ( ) ;
847873} ;
848874
875+ function ClearTransform ( ) { }
876+
849877NodeParser . prototype . parse = function ( stack ) {
850878 // http://www.w3.org/TR/CSS21/visuren.html#z-index
851879 var negativeZindex = stack . contexts . filter ( negativeZIndex ) ; // 2. the child stacking contexts with negative stack levels (most negative first).
@@ -862,13 +890,16 @@ NodeParser.prototype.parse = function(stack) {
862890 this . renderQueue . push ( container ) ;
863891 if ( isStackingContext ( container ) ) {
864892 this . parse ( container ) ;
893+ this . renderQueue . push ( new ClearTransform ( ) ) ;
865894 }
866895 } , this ) ;
867896} ;
868897
869898NodeParser . prototype . paint = function ( container ) {
870899 try {
871- if ( isTextNode ( container ) ) {
900+ if ( container instanceof ClearTransform ) {
901+ this . renderer . ctx . restore ( ) ;
902+ } else if ( isTextNode ( container ) ) {
872903 this . paintText ( container ) ;
873904 } else {
874905 this . paintNode ( container ) ;
@@ -881,13 +912,12 @@ NodeParser.prototype.paint = function(container) {
881912NodeParser . prototype . paintNode = function ( container ) {
882913 if ( isStackingContext ( container ) ) {
883914 this . renderer . setOpacity ( container . opacity ) ;
884- var transform = container . parseTransform ( ) ;
885- if ( transform ) {
886- this . renderer . setTransform ( transform ) ;
915+ this . renderer . ctx . save ( ) ;
916+ if ( container . hasTransform ( ) ) {
917+ this . renderer . setTransform ( container . parseTransform ( ) ) ;
887918 }
888919 }
889-
890- var bounds = this . parseBounds ( container ) ;
920+ var bounds = container . parseBounds ( ) ;
891921 var borderData = this . parseBorders ( container ) ;
892922 this . renderer . clip ( borderData . clip , function ( ) {
893923 this . renderer . renderBackground ( container , bounds , borderData . borders . map ( getWidth ) ) ;
@@ -1254,11 +1284,6 @@ function hasOpacity(container) {
12541284 return container . css ( "opacity" ) < 1 ;
12551285}
12561286
1257- function hasTransform ( container ) {
1258- var transform = container . prefixedCss ( "transform" ) ;
1259- return transform !== null && transform !== "none" ;
1260- }
1261-
12621287function bind ( callback , context ) {
12631288 return function ( ) {
12641289 return callback . apply ( context , arguments ) ;
@@ -1516,8 +1541,8 @@ CanvasRenderer.prototype.setOpacity = function(opacity) {
15161541
15171542CanvasRenderer . prototype . setTransform = function ( transform ) {
15181543 this . ctx . translate ( transform . origin [ 0 ] , transform . origin [ 1 ] ) ;
1519- this . ctx . setTransform . apply ( this . ctx , transform . matrix ) ;
1520- this . ctx . translate ( transform . origin [ 0 ] , transform . origin [ 1 ] ) ;
1544+ this . ctx . transform . apply ( this . ctx , transform . matrix ) ;
1545+ this . ctx . translate ( - transform . origin [ 0 ] , - transform . origin [ 1 ] ) ;
15211546} ;
15221547
15231548CanvasRenderer . prototype . setVariable = function ( property , value ) {
@@ -1588,12 +1613,12 @@ StackingContext.prototype.getParentStack = function(context) {
15881613 return parentStack ? ( parentStack . ownStacking ? parentStack : parentStack . getParentStack ( context ) ) : context . stack ;
15891614} ;
15901615
1591- function Support ( ) {
1592- this . rangeBounds = this . testRangeBounds ( ) ;
1616+ function Support ( document ) {
1617+ this . rangeBounds = this . testRangeBounds ( document ) ;
15931618 this . cors = this . testCORS ( ) ;
15941619}
15951620
1596- Support . prototype . testRangeBounds = function ( ) {
1621+ Support . prototype . testRangeBounds = function ( document ) {
15971622 var range , testElement , rangeBounds , rangeHeight , support = false ;
15981623
15991624 if ( document . createRange ) {
0 commit comments