-
Notifications
You must be signed in to change notification settings - Fork 0
/
css_image_injector.py
executable file
·148 lines (116 loc) · 5.04 KB
/
css_image_injector.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/env python
"""
Injects base64 encoded image data into css file
author Paul Hayes
"""
import sys
import os
import re
import io
import base64
import cssutils
from cssutils import parseFile
from cssutils.css import CSSRule
from PIL import Image
from PIL import ImageOps
from string import Template
#Add CSS Profiles for browser prefixes
macros = {
'timingfunction':'linear|ease|ease-in|ease-out|ease-in-out|cubic-bezier\(\s*{number}\s*,\s*{number}\s*,\s*{number}\s*,\s*{number}\s*\)',
'func':'(translate|scale)\(({number}|{length}|{percentage})(\s*,\s*{number}|{length}|{percentage})?\s*\)'
}
common = {
'transform' : '({func}\s*){1,}',
'transition' : '{ident}\s+{time}\s+{timingfunction}?',
'transform-origin' : '({length}|{percentage}) ({length}|{percentage})',
}
cssutils.profile.addProfile('CSS3 Advanced', common, macros)
cssutils.profile.addProfile('Webkit Prefixes',{ '-webkit-%s' % k : common[k] for k in common }, macros)
cssutils.profile.addProfile('Moz Prefixes',{ '-moz-%s' % k : common[k] for k in common }, macros)
cssutils.profile.addProfile('O Prefixes',{ '-o-%s' % k : common[k] for k in common }, macros)
cssutils.profile.addProfile('IE Prefixes',{ '-ms-%s' % k : common[k] for k in common }, macros)
def css_inject_images(stylesheet,sourceDir):
injectedImages = {}
injectedImageNames = []
index=0
def get_val(val):
return int( re.match(r'[\-0-9]+','%s' % val).group() )
for rule in stylesheet.cssRules:
if not rule.type == CSSRule.STYLE_RULE :
continue
style = rule.style
backgroundImage = style.getPropertyValue('background-image', True)
backgroundPosition = style.getPropertyValue('background-position', True)
if not backgroundImage :
background = style.getPropertyValue('background', True)
backgroundProperties = background.split(' ')
if len( backgroundProperties ) >= 3 and re.match( r"url\([^)]+\)", backgroundProperties[0] ) :
backgroundImage = backgroundProperties[0]
if "px" in backgroundProperties[1] and "px" in backgroundProperties[2] :
backgroundPosition = tuple( -get_val(n) for n in backgroundProperties[1:3] )
else:
sys.exit('Unable to process background position properties, %s' % background )
#print ', '.join( backgroundProperties )
if backgroundImage :
backgroundImageURL = re.match( r"url\(([^)]+)\)", backgroundImage ).group(1)
isPNG = ".png" in backgroundImageURL.lower()
imagePath = os.path.realpath( os.path.join( sourceDir,backgroundImageURL ) )
name = backgroundImageURL
if not os.path.isfile( imagePath ) :
sys.exit('Referenced image file not found %s ' % backgroundImageURL)
image = Image.open(imagePath)
#print '1. writing %s' % os.path.relpath( imagePath )
#print '2. %d,%d' % image.size
if backgroundPosition:
width = get_val( style.getPropertyValue("width") )
height = get_val( style.getPropertyValue("height") )
dimensions = image.size
bounds = backgroundPosition + (width,height)
#print("3. bounds %d,%d %d,%d,%d,%d " % ( bounds + (width,height) ) )
borders = ( bounds[0],bounds[1],max( 0,dimensions[0]-bounds[2]-bounds[0] ), max( 0,dimensions[1]-bounds[3]-bounds[1]) );
image = ImageOps.crop( image, borders )
name = '%s,%d,%d,%d,%d' % ( (backgroundImageURL,)+borders )
#print( "4. cropping %d, %d, %d, %d" % borders )
output = io.BytesIO()
base64Output = io.BytesIO()
format='png' if isPNG else 'jpeg'
image.save( output, format=format.upper() )
output.seek(0)
base64.encode( output, base64Output )
base64Output.seek(0)
imageTemplatePlaceholder = 'url($image%d)' % index
base64EncodedImage = '"data:image/%s;base64,%s"' % (format,base64Output.getvalue())
injectedImages['image%d' % index] = base64EncodedImage
index+=1
base64Output.close()
output.close()
style.setProperty('background-image', imageTemplatePlaceholder )
style.removeProperty('background-position',True)
style.removeProperty('background',normalize=True)
if name in injectedImageNames :
sys.stderr.write('WARNING: Image already injected : %s' % name )
else :
injectedImageNames.append( name )
return Template(stylesheet.cssText).safe_substitute( injectedImages )
def main():
if len(sys.argv) < 3:
sys.exit('Usage: %s sourceFile outputFile' % sys.argv[0])
css_file_inject_images(sys.argv[1],sys.argv[2])
def css_file_inject_images(infile,outfile):
sourceDir = os.path.dirname( infile )
sourceFile = os.path.basename( infile )
outputFile = os.path.basename(outfile)
outputDir = os.path.dirname(os.path.abspath( outfile ) )
if not os.path.isdir(sourceDir):
sys.exit('ERROR: Source directory not found %s' % sourceDir )
if not os.path.isfile( os.path.join( sourceDir, sourceFile ) ):
sys.exit('ERROR: Source file not found %s' % sourceFile )
if not os.path.isdir(outputDir):
sys.exit('ERROR: Output directory not found %s' % outputDir )
stylesheet = parseFile( os.path.join( sourceDir, sourceFile ) )
outputCSS = css_inject_images(stylesheet,sourceDir)
out = open( os.path.join( outputDir, outputFile ), "w" )
out.write( outputCSS )
out.close()
if __name__ == '__main__':
main()