@@ -148,6 +148,7 @@ var lastFileName = "";
148148var lastFileData ;
149149var warnings = [ ] ;
150150var svgStyles = { } ;
151+ var gradients = { } ;
151152var globalSvg ;
152153
153154var clipPathEnabled = false ;
@@ -235,6 +236,10 @@ function recursiveTreeWalk(parent, groupLevel, clipPath) {
235236}
236237
237238function preprocessReferences ( svg ) {
239+ svg . find ( "defs" ) . children ( ) . each ( function ( ) {
240+ var current = $ ( this ) ;
241+ substituteUseRef ( svg , current ) ;
242+ } ) ;
238243 svg . find ( "use" ) . each ( function ( ) {
239244 var current = $ ( this ) ;
240245 substituteUseRef ( svg , current ) ;
@@ -272,14 +277,20 @@ function substituteUseRef(parent, current) {
272277 //Find definition in svg
273278 var defs = $ ( parent ) . find ( "[id='" + href . substr ( 1 ) + "']" ) ;
274279 if ( defs . length ) {
275- defs = defs . clone ( ) ;
276-
277- //Copy overriding attributes into children
278- $ . each ( current . prop ( "attributes" ) , function ( ) {
279- defs . attr ( this . name , this . value ) ;
280+ //Copy non-overridden attributes
281+ $ . each ( defs . prop ( "attributes" ) , function ( ) {
282+ var attr = current . attr ( this . name )
283+ if ( typeof attr !== typeof undefined && attr !== false ) {
284+ return ;
285+ }
286+ current . attr ( this . name , this . value ) ;
280287 } ) ;
281288
282- current . replaceWith ( defs ) ;
289+ //Copy children
290+ defs . children ( ) . each ( function ( ) {
291+ var child = $ ( this ) ;
292+ current . append ( child . clone ( ) ) ;
293+ } ) ;
283294 } else {
284295 console . warn ( "Found <use> tag but did not found appropriate block in <defs> for id " + href ) ;
285296 }
@@ -289,6 +300,10 @@ function substituteUseRef(parent, current) {
289300
290301function parseGroup ( groupTag ) {
291302 var transform = groupTag . attr ( "transform" ) ;
303+ if ( typeof transform === "undefined" ) {
304+ transform = groupTag . attr ( "gradientTransform" ) ;
305+ }
306+
292307 var id = groupTag . attr ( "id" ) ;
293308 var groupTransform = { transformX : 0 , transformY : 0 , scaleX : 1 , scaleY : 1 ,
294309 rotate :0 , rotatePivotX :- 1 , rotatePivotY :- 1 , id :"" , isSet :false , clipPathId :null } ;
@@ -520,11 +535,11 @@ function printPath(pathData, stylesArray, groupLevel, clipPath) {
520535 }
521536
522537 //If strokeWidth is needed but omitted, default to 1
523- var needsStrokeWidth = ( typeof styles [ "stroke" ] !== "undefined" ) ||
524- ( typeof styles [ "stroke-opacity" ] !== "undefined" ) ||
525- ( typeof styles [ "stroke-alpha" ] !== "undefined" ) ||
526- ( typeof styles [ "stroke-linejoin" ] !== "undefined" ) ||
527- ( typeof styles [ "stroke-miterlimit" ] !== "undefined" ) ||
538+ var needsStrokeWidth = ( typeof styles [ "stroke" ] !== "undefined" ) ||
539+ ( typeof styles [ "stroke-opacity" ] !== "undefined" ) ||
540+ ( typeof styles [ "stroke-alpha" ] !== "undefined" ) ||
541+ ( typeof styles [ "stroke-linejoin" ] !== "undefined" ) ||
542+ ( typeof styles [ "stroke-miterlimit" ] !== "undefined" ) ||
528543 ( typeof styles [ "stroke-linecap" ] !== "undefined" ) ;
529544 if ( needsStrokeWidth && ( typeof styles [ "stroke-width" ] === "undefined" ) ) {
530545 styles [ "stroke-width" ] = "1" ;
@@ -534,16 +549,37 @@ function printPath(pathData, stylesArray, groupLevel, clipPath) {
534549 if ( ! clipPath ) {
535550 generatedOutput += INDENT . repeat ( groupLevel + 1 ) + '<path\n' ;
536551 if ( toBool ( localStorage . useIdAsName ) ) generatedOutput += generateAttr ( 'name' , styles [ "id" ] , groupLevel , "" ) ;
537- generatedOutput += generateAttr ( 'fillColor' , parseColorToHex ( styles [ "fill" ] ) , groupLevel , "none" ) ;
552+ var gradientID = null ;
553+ var gradientAttr = null ;
554+ if ( ( typeof styles [ "fill" ] !== "undefined" ) && styles [ "fill" ] . startsWith ( "url(" ) ) {
555+ gradientID = styles [ "fill" ] . slice ( 5 , - 1 ) ;
556+ gradientAttr = "fillColor" ;
557+ } else {
558+ generatedOutput += generateAttr ( 'fillColor' , parseColorToHex ( styles [ "fill" ] ) , groupLevel , "none" ) ;
559+ }
538560 generatedOutput += generateAttr ( 'fillAlpha' , styles [ "fill-opacity" ] , groupLevel , "1" ) ;
539561 generatedOutput += generateAttr ( 'fillType' , styles [ "fill-rule" ] , groupLevel , "nonZero" ) ;
540- generatedOutput += generateAttr ( 'strokeColor' , parseColorToHex ( styles [ "stroke" ] ) , groupLevel , "none" ) ;
562+ if ( ( typeof styles [ "stroke" ] !== "undefined" ) && styles [ "stroke" ] . startsWith ( "url(" ) ) {
563+ gradientID = styles [ "stroke" ] . slice ( 5 , - 1 ) ;
564+ gradientAttr = "strokeColor" ;
565+ } else {
566+ generatedOutput += generateAttr ( 'strokeColor' , parseColorToHex ( styles [ "stroke" ] ) , groupLevel , "none" ) ;
567+ }
541568 generatedOutput += generateAttr ( 'strokeAlpha' , styles [ "stroke-opacity" ] , groupLevel , "1" ) ;
542569 generatedOutput += generateAttr ( 'strokeWidth' , removeNonNumeric ( styles [ "stroke-width" ] ) , groupLevel , "0" ) ;
543570 generatedOutput += generateAttr ( 'strokeLineJoin' , styles [ "stroke-linejoin" ] , groupLevel , "miter" ) ;
544571 generatedOutput += generateAttr ( 'strokeMiterLimit' , styles [ "stroke-miterlimit" ] , groupLevel , "4" ) ;
545572 generatedOutput += generateAttr ( 'strokeLineCap' , styles [ "stroke-linecap" ] , groupLevel , "butt" ) ;
546- generatedOutput += generateAttr ( 'pathData' , pathData , groupLevel , null , true ) ;
573+ if ( gradientID !== null ) {
574+ generatedOutput += generateAttr ( 'pathData' , pathData , groupLevel , null ) ;
575+ generatedOutput += '>\n' ;
576+ generatedOutput += INDENT . repeat ( groupLevel + 2 ) + '<aapt:attr name="android:' + gradientAttr + '">\n' ;
577+ generatedOutput += gradients [ gradientID ] + '\n' ;
578+ generatedOutput += INDENT . repeat ( groupLevel + 2 ) + '</aapt:attr>\n' ;
579+ generatedOutput += INDENT . repeat ( groupLevel + 1 ) + '</path>\n' ;
580+ } else {
581+ generatedOutput += generateAttr ( 'pathData' , pathData , groupLevel , null , true ) ;
582+ }
547583 pathsParsedCount ++ ;
548584 } else {
549585 clipPathMerged . push ( pathData ) ;
@@ -555,6 +591,104 @@ function printPath(pathData, stylesArray, groupLevel, clipPath) {
555591 }
556592}
557593
594+ function parseGradients ( svg ) {
595+ svg . find ( "defs" ) . children ( ) . each ( function ( ) {
596+ var current = $ ( this ) ;
597+ if ( current . prop ( "tagName" ) . endsWith ( "Gradient" ) ) {
598+ var transform = parseGroup ( current ) ;
599+ if ( typeof transform . transformX === "string" ) {
600+ transform . transformX = parseFloat ( transform . transformX ) ;
601+ }
602+ if ( typeof transform . transformY === "string" ) {
603+ transform . transformX = parseFloat ( transform . transformX ) ;
604+ }
605+ if ( typeof transform . scaleX === "string" ) {
606+ transform . scaleX = parseFloat ( transform . scaleX ) ;
607+ }
608+ if ( typeof transform . scaleY === "string" ) {
609+ transform . scaleY = parseFloat ( transform . scaleY ) ;
610+ }
611+
612+ var androidXML = '<gradient\n' ;
613+ androidXML += generateAttr ( 'angle' , transform . rotate , 1 , 0 ) ;
614+ if ( current . prop ( "tagName" ) . startsWith ( "linear" ) ) {
615+ var x1 = 0.0 ;
616+ if ( typeof current . attr ( "x1" ) !== "undefined" ) {
617+ x1 = parseFloat ( current . attr ( "x1" ) ) + transform . transformX ;
618+ }
619+ var y1 = 0.0 ;
620+ if ( typeof current . attr ( "y1" ) !== "undefined" ) {
621+ y1 = parseFloat ( current . attr ( "y1" ) ) + transform . transformY ;
622+ }
623+ var x2 = 0.0 ;
624+ if ( typeof current . attr ( "x2" ) !== "undefined" ) {
625+ x2 = parseFloat ( current . attr ( "x2" ) ) + transform . transformX ;
626+ }
627+ var y2 = 0.0 ;
628+ if ( typeof current . attr ( "y2" ) !== "undefined" ) {
629+ y2 = parseFloat ( current . attr ( "y2" ) ) + transform . transformY ;
630+ }
631+
632+ var width = Math . abs ( x2 - x1 ) ;
633+ var height = Math . abs ( y2 - y1 ) ;
634+ var newWidth = width * transform . scaleX ;
635+ var newHeight = height * transform . scaleY ;
636+ var dX = ( width - newWidth ) / 2 ;
637+ var dY = ( height - newHeight ) / 2 ;
638+ if ( x1 < x2 ) {
639+ x1 += dX ;
640+ x2 -= dX ;
641+ } else {
642+ x1 -= dX ;
643+ x2 += dX ;
644+ }
645+ if ( y1 < y2 ) {
646+ y1 += dY ;
647+ y2 -= dY ;
648+ } else {
649+ y1 -= dY ;
650+ y2 += dY ;
651+ }
652+
653+ androidXML += generateAttr ( 'startX' , x1 , 1 , 0.0 ) ;
654+ androidXML += generateAttr ( 'startY' , y1 , 1 , 0.0 ) ;
655+ androidXML += generateAttr ( 'endX' , x2 , 1 , 0.0 ) ;
656+ androidXML += generateAttr ( 'endY' , y2 , 1 , 0.0 ) ;
657+ androidXML += INDENT . repeat ( 1 ) + 'android:type="linear">' ;
658+ } else if ( current . prop ( "tagName" ) . startsWith ( "radial" ) ) {
659+ androidXML += generateAttr ( 'centerX' , parseFloat ( current . attr ( "cx" ) ) + transform . transformX , 1 , 0.0 ) ;
660+ androidXML += generateAttr ( 'centerY' , parseFloat ( current . attr ( "cy" ) ) + transform . transformY , 1 , 0.0 ) ;
661+ androidXML += generateAttr ( 'gradientRadius' , current . attr ( "r" ) , 1 , "0" ) ;
662+ androidXML += INDENT . repeat ( 1 ) + 'android:type="radial">' ;
663+ }
664+ current . children ( ) . each ( function ( ) {
665+ var stopTag = $ ( this ) ;
666+ var stopStyles = parseStyles ( stopTag ) ;
667+ if ( typeof stopTag . attr ( "stop-color" ) !== "undefined" ) {
668+ stopStyles [ "stop-color" ] = stopTag . attr ( "stop-color" ) ;
669+ }
670+ if ( typeof stopTag . attr ( "stop-opacity" ) !== "undefined" ) {
671+ stopStyles [ "stop-opacity" ] = stopTag . attr ( "stop-opacity" ) ;
672+ }
673+ androidXML += INDENT . repeat ( 2 ) + '<item\n' ;
674+ androidXML += generateAttr ( 'offset' , stopTag . attr ( "offset" ) , 3 , null ) ;
675+ if ( typeof stopStyles [ "stop-opacity" ] !== "undefined" ) {
676+ var alpha = ( "0" + Math . round ( parseFloat ( stopStyles [ "stop-opacity" ] ) * 255 ) . toString ( 16 ) ) . slice ( - 2 )
677+ var color = parseColorToHex ( stopStyles [ "stop-color" ] ) . slice ( - 6 )
678+
679+ androidXML += generateAttr ( 'color' , "#" + alpha + color , 3 , null , true ) ;
680+ } else if ( typeof stopStyles [ "stop-color" ] !== "undefined" ) {
681+ androidXML += generateAttr ( 'color' , parseColorToHex ( stopStyles [ "stop-color" ] ) , 3 , null , true ) ;
682+ } else {
683+ androidXML += " />" ;
684+ }
685+ } ) ;
686+ androidXML += '</gradient>' ;
687+ gradients [ current . attr ( "id" ) ] = androidXML ;
688+ }
689+ } ) ;
690+ }
691+
558692function generateCode ( inputXml ) {
559693 var resultData = { error :null , warnings :null , code :null } ;
560694
@@ -574,6 +708,7 @@ function generateCode(inputXml) {
574708 var svg = xml . find ( "svg" ) ;
575709 globalSvg = svg ;
576710 preprocessReferences ( svg ) ;
711+ parseGradients ( svg ) ;
577712
578713 if ( toBool ( localStorage . bakeTransforms ) ) {
579714 try {
@@ -598,6 +733,7 @@ function generateCode(inputXml) {
598733 //XML Vector start
599734 generatedOutput = '<?xml version="1.0" encoding="utf-8"?>\n' ;
600735 generatedOutput += '<vector xmlns:android="http://schemas.android.com/apk/res/android"' ;
736+ generatedOutput += '\n' + INDENT + 'xmlns:aapt="http://schemas.android.com/aapt"'
601737
602738 generatedOutput += '\n' + INDENT + 'android:width="{0}dp"\n' . f ( width ) ;
603739 generatedOutput += INDENT + 'android:height="{0}dp"\n' . f ( height ) ;
@@ -832,6 +968,11 @@ function parseColorToHex(color) {
832968
833969 //Is hex already
834970 if ( color . substr ( 0 , 1 ) === "#" ) {
971+ // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
972+ var shorthandRegex = / ^ # ? ( [ a - f \d ] ) ( [ a - f \d ] ) ( [ a - f \d ] ) $ / i;
973+ color = color . replace ( shorthandRegex , function ( m , r , g , b ) {
974+ return r + r + g + g + b + b ;
975+ } ) ;
835976 return color ;
836977 } else {
837978 if ( color . startsWith ( "rgb(" ) ) {
@@ -890,4 +1031,4 @@ function showLastUpdate(repo) {
8901031 }
8911032 } ) ;
8921033 } ) ;
893- }
1034+ }
0 commit comments