Skip to content

Instantly share code, notes, and snippets.

@puppybits
Last active January 5, 2025 19:21
Show Gist options
  • Save puppybits/4958394 to your computer and use it in GitHub Desktop.
Save puppybits/4958394 to your computer and use it in GitHub Desktop.
PSD to CSS web font parser
/*
# 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();
@saibayadon
Copy link

This is so awesome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment