@@ -940,7 +940,8 @@ function html2canvas(nodeList, options) {
940940
941941 var node = ( ( nodeList === undefined ) ? [ document . documentElement ] : ( ( nodeList . length ) ? nodeList : [ nodeList ] ) ) [ 0 ] ;
942942 node . setAttribute ( html2canvasNodeAttribute + index , index ) ;
943- return renderDocument ( node . ownerDocument , options , node . ownerDocument . defaultView . innerWidth , node . ownerDocument . defaultView . innerHeight , index ) . then ( function ( canvas ) {
943+ // EK 2017-03-24: Edited from `node.ownerDocument.defaultView.innerWidth, node.ownerDocument.defaultView.innerHeight,`.
944+ return renderDocument ( node . ownerDocument , options , node . clientWidth , node . clientHeight , index ) . then ( function ( canvas ) {
944945 if ( typeof ( options . onrendered ) === "function" ) {
945946 log ( "options.onrendered is deprecated, html2canvas returns a Promise containing the canvas" ) ;
946947 options . onrendered ( canvas ) ;
@@ -998,6 +999,16 @@ function renderWindow(node, container, options, windowWidth, windowHeight) {
998999 canvas = crop ( renderer . canvas , { width : renderer . canvas . width , height : renderer . canvas . height , top : 0 , left : 0 , x : 0 , y : 0 } ) ;
9991000 } else if ( node === clonedWindow . document . body || node === clonedWindow . document . documentElement || options . canvas != null ) {
10001001 canvas = renderer . canvas ;
1002+ } else if ( options . scale ) {
1003+ // EK 2017-03-25: Add dpi/scale.
1004+ var origBounds = { width : options . width != null ? options . width : bounds . width , height : options . height != null ? options . height : bounds . height , top : bounds . top , left : bounds . left , x : 0 , y : 0 } ;
1005+ var cropBounds = { } ;
1006+ for ( var key in origBounds ) {
1007+ if ( origBounds . hasOwnProperty ( key ) ) { cropBounds [ key ] = origBounds [ key ] * options . scale ; }
1008+ }
1009+ canvas = crop ( renderer . canvas , cropBounds ) ;
1010+ canvas . style . width = origBounds . width + 'px' ;
1011+ canvas . style . height = origBounds . height + 'px' ;
10011012 } else {
10021013 canvas = crop ( renderer . canvas , { width : options . width != null ? options . width : bounds . width , height : options . height != null ? options . height : bounds . height , top : bounds . top , left : bounds . left , x : 0 , y : 0 } ) ;
10031014 }
@@ -1740,6 +1751,11 @@ NodeContainer.prototype.parseTransformMatrix = function() {
17401751 return this . transformMatrix ;
17411752} ;
17421753
1754+ NodeContainer . prototype . inverseTransform = function ( ) {
1755+ var transformData = this . parseTransform ( ) ;
1756+ return { origin : transformData . origin , matrix : matrixInverse ( transformData . matrix ) } ;
1757+ }
1758+
17431759NodeContainer . prototype . parseBounds = function ( ) {
17441760 return this . bounds || ( this . bounds = this . hasTransform ( ) ? offsetBounds ( this . node ) : getBounds ( this . node ) ) ;
17451761} ;
@@ -1781,6 +1797,14 @@ function parseMatrix(match) {
17811797 }
17821798}
17831799
1800+ function matrixInverse ( m ) {
1801+ // EK 2017-03-25: This is specifically programmed to work for transform matrices.
1802+ var a = m [ 0 ] , b = m [ 2 ] , c = m [ 4 ] , d = m [ 1 ] , e = m [ 3 ] , f = m [ 5 ] ;
1803+ var det = a * e - b * d ;
1804+ var M = [ e , - d , - b , a , b * f - c * e , c * d - a * f ] . map ( function ( val ) { return val / det } ) ;
1805+ return M ;
1806+ }
1807+
17841808function isPercentage ( value ) {
17851809 return value . toString ( ) . indexOf ( "%" ) !== - 1 ;
17861810}
@@ -2123,14 +2147,20 @@ NodeParser.prototype.paintNode = function(container) {
21232147} ;
21242148
21252149NodeParser . prototype . paintElement = function ( container ) {
2150+ // EK 2017-03-25: Added container to all .clip calls for transform (also in paintCheckbox/paintRadio, basically paintNode descendents).
21262151 var bounds = container . parseBounds ( ) ;
21272152 this . renderer . clip ( container . backgroundClip , function ( ) {
21282153 this . renderer . renderBackground ( container , bounds , container . borders . borders . map ( getWidth ) ) ;
2154+ } , this , container ) ;
2155+
2156+ // EK 2017-03-24: Add mask (opposite of clip) for rendering box-shadows.
2157+ this . renderer . mask ( bounds , function ( ) {
2158+ this . renderer . renderShadows ( container , bounds ) ;
21292159 } , this ) ;
21302160
21312161 this . renderer . clip ( container . clip , function ( ) {
21322162 this . renderer . renderBorders ( container . borders . borders ) ;
2133- } , this ) ;
2163+ } , this , container ) ;
21342164
21352165 this . renderer . clip ( container . backgroundClip , function ( ) {
21362166 switch ( container . node . nodeName ) {
@@ -2160,7 +2190,7 @@ NodeParser.prototype.paintElement = function(container) {
21602190 this . paintFormValue ( container ) ;
21612191 break ;
21622192 }
2163- } , this ) ;
2193+ } , this , container ) ;
21642194} ;
21652195
21662196NodeParser . prototype . paintCheckbox = function ( container ) {
@@ -2183,7 +2213,7 @@ NodeParser.prototype.paintCheckbox = function(container) {
21832213 this . renderer . font ( new Color ( '#424242' ) , 'normal' , 'normal' , 'bold' , ( size - 3 ) + "px" , 'arial' ) ;
21842214 this . renderer . text ( "\u2714" , bounds . left + size / 6 , bounds . top + size - 1 ) ;
21852215 }
2186- } , this ) ;
2216+ } , this , container ) ;
21872217} ;
21882218
21892219NodeParser . prototype . paintRadio = function ( container ) {
@@ -2255,7 +2285,7 @@ NodeParser.prototype.paintText = function(container) {
22552285 this . renderTextDecoration ( container . parent , bounds , this . fontMetrics . getMetrics ( family , size ) ) ;
22562286 }
22572287 } , this ) ;
2258- } , this ) ;
2288+ } , this , container . parent ) ;
22592289} ;
22602290
22612291NodeParser . prototype . renderTextDecoration = function ( container , bounds , metrics ) {
@@ -2873,6 +2903,15 @@ Renderer.prototype.renderBackgroundColor = function(container, bounds) {
28732903 }
28742904} ;
28752905
2906+ // EK 2017-03-24: Add box-shadow rendering.
2907+ Renderer . prototype . renderShadows = function ( container , bounds ) {
2908+ var boxShadow = container . css ( 'boxShadow' ) ;
2909+ if ( boxShadow !== 'none' ) {
2910+ var shadows = boxShadow . split ( / , (? ! [ ^ ( ] * \) ) / ) ;
2911+ this . shadow ( bounds . left , bounds . top , bounds . width , bounds . height , shadows ) ;
2912+ }
2913+ } ;
2914+
28762915Renderer . prototype . renderBorders = function ( borders ) {
28772916 borders . forEach ( this . renderBorder , this ) ;
28782917} ;
@@ -2944,11 +2983,24 @@ var log = _dereq_('../log');
29442983function CanvasRenderer ( width , height ) {
29452984 Renderer . apply ( this , arguments ) ;
29462985 this . canvas = this . options . canvas || this . document . createElement ( "canvas" ) ;
2986+ this . ctx = this . canvas . getContext ( "2d" ) ;
29472987 if ( ! this . options . canvas ) {
2948- this . canvas . width = width ;
2949- this . canvas . height = height ;
2988+ // EK 2017-03-25: Add scale/dpi.
2989+ if ( this . options . dpi ) {
2990+ // Default canvas is 96dpi (1 CSS inch = 96px).
2991+ this . options . scale = this . options . dpi / 96 ;
2992+ }
2993+ if ( this . options . scale ) {
2994+ this . canvas . style . width = width + 'px' ;
2995+ this . canvas . style . height = height + 'px' ;
2996+ this . canvas . width = Math . floor ( width * this . options . scale ) ;
2997+ this . canvas . height = Math . floor ( height * this . options . scale ) ;
2998+ this . ctx . scale ( this . options . scale , this . options . scale ) ;
2999+ } else {
3000+ this . canvas . width = width ;
3001+ this . canvas . height = height ;
3002+ }
29503003 }
2951- this . ctx = this . canvas . getContext ( "2d" ) ;
29523004 this . taintCtx = this . document . createElement ( "canvas" ) . getContext ( "2d" ) ;
29533005 this . ctx . textBaseline = "bottom" ;
29543006 this . variables = { } ;
@@ -2966,6 +3018,49 @@ CanvasRenderer.prototype.rectangle = function(left, top, width, height, color) {
29663018 this . setFillStyle ( color ) . fillRect ( left , top , width , height ) ;
29673019} ;
29683020
3021+ // EK 2017-03-24: Add boxShadow handling.
3022+ CanvasRenderer . prototype . shadow = function ( left , top , width , height , shadows ) {
3023+ var parseShadow = function ( str ) {
3024+ // Setup matching info.
3025+ var propertyFilters = { color : / ^ ( # | r g b | h s l | (? ! ( i n s e t | i n i t i a l | i n h e r i t ) ) \D + ) / i, inset : / ^ i n s e t / i, px : / p x $ / i } ;
3026+ var pxPropertyNames = [ 'x' , 'y' , 'blur' , 'spread' ] ;
3027+
3028+ // Extract info from the string.
3029+ var properties = str . split ( / (? ! [ ^ ( ] * \) ) / ) ;
3030+ var info = { } ;
3031+ for ( var key in propertyFilters ) {
3032+ info [ key ] = properties . filter ( propertyFilters [ key ] . test . bind ( propertyFilters [ key ] ) ) ;
3033+ info [ key ] = info [ key ] . length === 0 ? null : info [ key ] . length === 1 ? info [ key ] [ 0 ] : info [ key ] ;
3034+ }
3035+ for ( var i = 0 ; i < info . px . length ; i ++ ) {
3036+ info [ pxPropertyNames [ i ] ] = parseInt ( info . px [ i ] ) ;
3037+ }
3038+
3039+ // Return the info object.
3040+ return info ;
3041+ }
3042+ var drawShadow = function ( shadow ) {
3043+ var info = parseShadow ( shadow ) ;
3044+ if ( ! info . inset ) {
3045+ context . shadowOffsetX = info . x ;
3046+ context . shadowOffsetY = info . y ;
3047+ context . shadowColor = info . color ;
3048+ context . shadowBlur = info . blur ;
3049+ context . fill ( ) ;
3050+ }
3051+ }
3052+
3053+ // Create a bounding rectangle with arbitrary opaque color.
3054+ var context = this . setFillStyle ( 'white' ) ;
3055+ context . save ( ) ;
3056+ context . beginPath ( ) ;
3057+ context . rect ( left , top , width , height ) ;
3058+
3059+ // Create each shadow and restore the context.
3060+ shadows . forEach ( drawShadow , this ) ;
3061+ context . restore ( ) ;
3062+ } ;
3063+
29693064CanvasRenderer . prototype . circle = function ( left , top , size , color ) {
29703065 this . setFillStyle ( color ) ;
29713066 this . ctx . beginPath ( ) ;
@@ -3006,11 +3101,31 @@ CanvasRenderer.prototype.drawImage = function(imageContainer, sx, sy, sw, sh, dx
30063101 }
30073102} ;
30083103
3009- CanvasRenderer . prototype . clip = function ( shapes , callback , context ) {
3104+ CanvasRenderer . prototype . clip = function ( shapes , callback , context , container ) {
3105+ // EK 2017-03-25: Clip transform fix.
3106+ var hasTransform = container && container . hasTransform ( ) ;
30103107 this . ctx . save ( ) ;
3108+ if ( hasTransform ) {
3109+ this . setTransform ( container . inverseTransform ( ) ) ;
3110+ }
30113111 shapes . filter ( hasEntries ) . forEach ( function ( shape ) {
30123112 this . shape ( shape ) . clip ( ) ;
30133113 } , this ) ;
3114+ if ( hasTransform ) {
3115+ this . setTransform ( container . parseTransform ( ) ) ;
3116+ }
3117+ callback . call ( context ) ;
3118+ this . ctx . restore ( ) ;
3119+ } ;
3120+
3121+ // EK 2017-03-24: Add mask (opposite of clip) for rendering box-shadows.
3122+ CanvasRenderer . prototype . mask = function ( bounds , callback , context ) {
3123+ var canvasBorderCCW = [ "rect" , this . canvas . width , 0 , - this . canvas . width , this . canvas . height ] ;
3124+ var boundsCW = [ "rect" , bounds . left , bounds . top , bounds . width , bounds . height ] ;
3125+
3126+ this . ctx . save ( ) ;
3127+ var shape = [ canvasBorderCCW , boundsCW ] ;
3128+ this . shape ( shape ) . clip ( ) ;
30143129 callback . call ( context ) ;
30153130 this . ctx . restore ( ) ;
30163131} ;
@@ -3027,6 +3142,17 @@ CanvasRenderer.prototype.shape = function(shape) {
30273142 this . ctx . closePath ( ) ;
30283143 return this . ctx ;
30293144} ;
3145+ CanvasRenderer . prototype . shape2 = function ( shape ) {
3146+ this . ctx . beginPath ( ) ;
3147+ shape . forEach ( function ( point , index ) {
3148+ if ( point [ 0 ] === "rect" ) {
3149+ this . ctx . rect . apply ( this . ctx , point . slice ( 1 ) ) ;
3150+ } else {
3151+ this . ctx [ ( index === 0 ) ? "moveTo" : point [ 0 ] + "To" ] . apply ( this . ctx , point . slice ( 1 ) ) ;
3152+ }
3153+ } , this ) ;
3154+ return this . ctx ;
3155+ } ;
30303156
30313157CanvasRenderer . prototype . font = function ( color , style , variant , weight , size , family ) {
30323158 this . setFillStyle ( color ) . font = [ style , variant , weight , size , family ] . join ( " " ) . split ( "," ) [ 0 ] ;
0 commit comments