Last active
January 5, 2025 19:21
-
-
Save puppybits/4958394 to your computer and use it in GitHub Desktop.
PSD to CSS web font parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
# Create customized Web Fonts CSS file from a folder of PSDs | |
1. Edit the root location for the font. | |
2. Add any mapping you might need/want from Desktop fonts to the webfonts. | |
3. In Photoshop go to File > Scripts > Browse... and select the fonts.jsx file | |
4. Select the Folder where you PSDs are. | |
5. Photoshop scripting is very slow, so wait for it to complete and save a fonts.css file to the location. | |
## Sample file created: | |
--- | |
.proximanova12bold { | |
font-family:"Proxima Nova Bold"; | |
font-size:12px; | |
} | |
.proximanova14medium { | |
font-family:"Proxima Nova Medium",Helvetica,Serif; | |
font-size:14px; | |
} | |
.proximanova16bold { | |
font-family:"Proxima Nova Bold"; | |
font-size:16px; | |
} | |
@font-face | |
{ | |
font-family:"Proxima Nova Bold"; | |
src:url("/fonts/ProximaNova-Bold-webfont.eot?iefix"); | |
src:local("ProximaNova-Bold-webfont"), | |
local("proxima_nova_rgbold"), | |
url("/fonts/ProximaNova-Bold-webfont.woff") format("woff"), | |
url("/fonts/ProximaNova-Bold-webfont.ttf") format("truetype"), | |
url("/fonts/ProximaNova-Bold-webfont.svg#proxima_nova_rgbold") format("svg"), | |
url("/fonts/ProximaNova-Bold-webfont.otf") format("opentype"); | |
font-weight:bold; | |
font-style:normal; | |
} | |
@font-face | |
{ | |
font-family:"Proxima Nova Medium"; | |
src:url("/fonts/ProximaNova-Reg-webfont.eot?iefix"); | |
src:local("ProximaNova-Reg-webfont"), | |
local("proxima_nova_rgregular"), | |
url("/fonts/ProximaNova-Reg-webfont.woff") format("woff"), | |
url("/fonts/ProximaNova-Reg-webfont.ttf") format("truetype"), | |
url("/fonts/ProximaNova-Reg-webfont.svg#proxima_nova_rgregular") format("svg"), | |
url("/fonts/ProximaNova-Reg-webfont.otf") format("opentype"); | |
font-weight:normal; | |
font-style:normal; | |
} | |
*/ | |
var root = '/fonts/', | |
mapper = {'Gotham' :{ | |
'font':'Proxima Nova', | |
'name':'proxima_nova_rgregular', | |
'file':'ProximaNova-Reg-webfont', | |
'familyorder':',Helvetica,Serif' | |
}, | |
'Gotham Black' :{ | |
'font':'Proxima Nova', | |
'name':'proxima_nova_blblack', | |
'file':'ProximaNova-Black-webfont', | |
'familyorder':'' | |
} | |
}; | |
function main(){ | |
Array.prototype.inArray = function( elem, arr ) { | |
for (var i in arr) | |
{ | |
if (arr[i] === elem) return true; | |
} | |
return false; | |
} | |
Stdlib = function Stdlib() {}; | |
Stdlib.findFiles = function(folder, mask) {//from Xbytor's Xtools | |
var files = Stdlib.getFiles(folder, mask); | |
var folders = Stdlib.getFolders(folder); | |
for (var i = 0; i < folders.length; i++) { | |
var f = folders[i]; | |
var ffs = Stdlib.findFiles(f, mask); | |
while (ffs.length > 0) { | |
files.push(ffs.shift()); | |
} | |
} | |
return files; | |
}; | |
Folder.prototype.findFiles = function(mask) { | |
return Stdlib.findFiles(this, mask); | |
}; | |
Stdlib.getFiles = function(folder, mask) { | |
var files = []; | |
var getF; | |
if (Folder.prototype._getFiles) { | |
getF = function(f, m) { return f._getFiles(m); } | |
} else { | |
getF = function(f, m) { return f.getFiles(m); } | |
} | |
if (mask instanceof RegExp) { | |
var allFiles = getF(folder); | |
for (var i = 0; i < allFiles.length; i = i + 1) { | |
var f = allFiles[i]; | |
if (decodeURI(f.absoluteURI).match(mask)) { | |
files.push(f); | |
} | |
} | |
} else if (typeof mask == "function") { | |
var allFiles = getF(folder); | |
for (var i = 0; i < allFiles.length; i = i + 1) { | |
var f = allFiles[i]; | |
if (mask(f)) { | |
files.push(f); | |
} | |
} | |
} else { | |
files = getF(folder, mask); | |
} | |
return files; | |
}; | |
Stdlib.getFolders = function(folder) { | |
return Stdlib.getFiles(folder, function(file) { | |
return file instanceof Folder; }); | |
} | |
function getDocFonts(){ | |
function traverseLayers (doc, ftn, reverse) { //from Xbytor | |
function _traverse(doc, layers, ftn, reverse) { | |
var ok = true; | |
for (var i = 1; i <= layers.length && ok != false; i++) { | |
var index = (reverse == true) ? layers.length-i : i - 1; | |
var layer = layers[index]; | |
if (layer.typename == "LayerSet") { | |
ok = _traverse(doc, layer.layers, ftn, reverse); | |
} else { | |
ok = ftn(doc, layer); | |
} | |
} | |
return ok; | |
}; | |
return _traverse(doc, doc.layers, ftn, reverse); | |
}; | |
function getLayersList(doc, reverse) { | |
function _ftn(doc, layer) { | |
if(layer.kind == LayerKind.TEXT){ _ftn.list.push(layer);}; | |
}; | |
_ftn.list = []; | |
traverseLayers(doc, _ftn, reverse); | |
return _ftn.list; | |
}; | |
Array.prototype.add = function(obj) { //from Andrew Hall | |
if (this.toString().search(RegExp("(?:^|,)" + obj.toString() + "(?:$|,)")) == -1) this.push(obj); | |
}; | |
function getFontInfo(){ | |
var info = {}; | |
var ref = new ActionReference(); | |
ref.putEnumerated( stringIDToTypeID( "layer" ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" )); | |
var desc= executeActionGet( ref ) | |
var list = desc.getObjectValue(charIDToTypeID("Txt ")) ; | |
var tsr = list.getList(charIDToTypeID("Txtt")) ; | |
for(var i = 0;i<tsr.count;i++){ | |
var tsr0 = tsr.getObjectValue(i) ; | |
var textStyle = tsr0.getObjectValue(charIDToTypeID("TxtS")); | |
var font = textStyle.getString(charIDToTypeID("FntN" )); | |
var style = textStyle.getString(charIDToTypeID( "FntS" )); | |
var fontProperties = {}; | |
for (var i=0;i<textStyle.count;i++) | |
{ | |
/* | |
fontPostScriptName:973 | |
fontName:1181643854 | |
fontStyleName:1181643859 | |
fontScript:1399026288 | |
fontTechnology:1181643860 | |
size:1400512544 | |
autoLeading:978 | |
leading:1281650279 | |
tracking:1416782699 | |
fontCaps:981 | |
digitSet:2330 | |
markYDistFromBaseline:2340 | |
textLanguage:1004 | |
color:1131180576 | |
*/ | |
keyval = textStyle.getKey(i); | |
param = typeIDToStringID(keyval); | |
if(param === 'size') | |
{ | |
fontProperties[param] = textStyle.getInteger(keyval); | |
} | |
else if (param === 'color') | |
{ | |
// info.push( param+':'+textStyle.getString(keyval) ) | |
v = 1; | |
} | |
else if (param === "fontStyleName" || | |
param === "fontPostScriptName" || | |
param === "fontName" || | |
param === "fontTechnology" ) | |
{ | |
fontProperties[param] = textStyle.getString(keyval); | |
} | |
} | |
realName = fontProperties['fontName']+' '+fontProperties['fontStyleName']; | |
font = fontProperties['fontName'] + ' ' + fontProperties['fontStyleName']; | |
fontName = ((mapper[font] && mapper[font].font) || fontProperties['fontName']); | |
fontFull = ((mapper[font] && mapper[font].font) || fontProperties['fontName'])+' '+fontProperties['fontStyleName']; | |
fontFile = ((mapper[font] && mapper[font].file) || fontProperties['fontName'].toLowerCase().replace(' ','')); | |
fontHash = ((mapper[font] && mapper[font].name) || fontProperties['fontName'].toLowerCase().replace(' ','')); | |
fontStyle = fontProperties['fontStyleName']; | |
font = fontFile + ' ' + fontStyle; | |
fontSize = fontProperties['size']; | |
familyorder = ((mapper[font] && mapper[font].familyorder) || ''); | |
fontClass = fontName.toLowerCase().replace(' ','') + fontSize + fontStyle.toLowerCase().replace(' ',''); | |
str = '/* '+realName+' */\n'; | |
str += '@font-face\n{\n\tfont-family:"'+fontFull+'";\n'; | |
str += '\tsrc:url("'+root+fontFile+'.eot?iefix");\n'; | |
str += '\tsrc:local("'+fontFile+'"),\n'; | |
str += '\t\tlocal("'+fontHash+'"),\n'; | |
str += '\t\turl("'+root+fontFile+'.woff") format("woff"),\n'; | |
str += '\t\turl("'+root+fontFile+'.ttf") format("truetype"),\n'; | |
str += '\t\turl("'+root+fontFile+'.svg#'+fontHash+'") format("svg"),\n'; | |
str += '\t\turl("'+root+fontFile+'.otf") format("opentype");\n'; | |
str += '\tfont-weight:'+( fontStyle.toLowerCase() === "bold" ? "bold" : "normal")+';\n'; | |
str += '\tfont-style:normal;\n'; | |
str += '}\n'; | |
info[font] = str; | |
str = '.'+fontClass+' {\n\tfont-family:"'+fontFull+'"'+familyorder+';\n\tfont-size:'+fontSize+'px;\n}\n'; | |
info[fontClass] = str; | |
} | |
return info; | |
}; | |
var doc = app.activeDocument; | |
var allLayers = getLayersList(doc); | |
if (allLayers.length>0){ | |
var allFonts = new Array; | |
for(var i = 0;i<allLayers.length;i++){ | |
if(allLayers[i].kind == LayerKind.TEXT){ | |
app.activeDocument.activeLayer = allLayers[i]; | |
allFonts.add(getFontInfo()); | |
} | |
}; | |
} | |
return allFonts; | |
}; | |
var listOfFonts = new Array; | |
var selectedFolder = Folder.selectDialog( "Please select the input folder", Folder( "~/Desktop" ) ); | |
if(selectedFolder == null) return; | |
var psdFiles = selectedFolder.findFiles(/\.psd$|\.PSD$/); | |
for(var i = 0;i < psdFiles.length; i++){ | |
var doc = open(psdFiles[i]); | |
var test = getDocFonts(); | |
if(test != undefined){ | |
listOfFonts.push([psdFiles[i].name,test]) | |
}; | |
doc.close(SaveOptions.DONOTSAVECHANGES); | |
}; | |
var f = new File(selectedFolder+"/fonts.css") ; | |
if (!f.open("w")) { | |
throw "Unable to open file: " + f.error; | |
}; | |
var fullList = new Array, | |
uniques = new Array; | |
for(var i = 0;i<listOfFonts.length;i++) | |
{ | |
for(var idx in listOfFonts[i][1]) | |
{ | |
for(var j in listOfFonts[i][1][idx]) | |
{ | |
if (typeof listOfFonts[i][1][idx][j] === "string" && !uniques.inArray(j, uniques)) | |
{ | |
uniques.push(j) | |
fullList.add(listOfFonts[i][1][idx][j]); | |
} | |
} | |
}; | |
}; | |
fullList.sort(); | |
for(var i = 0;i<fullList.length;i++){ | |
f.writeln(fullList[i]); | |
}; | |
f.close(); | |
alert('Found:\n'+uniques.join('\n')+"\n\nSaved "+selectedFolder+"/fonts.css"); | |
}; | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is so awesome.