Add automatic font generator script for Photoshop.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Generated font pages go here. (placeholder to make CVS create dir)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,351 @@
|
||||
/* This file was written by Glenn Maynard, and is in the public domain.
|
||||
*
|
||||
* This is a Photoshop javascript to generate Kanji fonts.
|
||||
*
|
||||
* You'll need the plugin:
|
||||
*
|
||||
* http://www.adobe.com/support/downloads/detail.jsp?ftpID=1536
|
||||
*
|
||||
* Load this script from File->Automation->Scripts, browse. Expect it to take
|
||||
* a minute or two.
|
||||
*
|
||||
* Minimally, you'll need to edit the directories below.
|
||||
*
|
||||
* Each font page in FontPages is generated for each font in Fonts.
|
||||
*
|
||||
* I can't figure out a way to force a font to fixed-width, so this currently
|
||||
* only works with fixed-width fonts. I'm using this primarily for Japanese
|
||||
* fonts, so that's OK. If we could output Unicode, we could get around this,
|
||||
* by outputting each glyph in a separate layer and aligning them individually,
|
||||
* though that'd take a very long time.
|
||||
*/
|
||||
|
||||
/* Okay, this is a stupid way to pass by reference, but I don't speak JavaScript ... */
|
||||
function Integer(i) { this.val = i; }
|
||||
|
||||
var TemplateDir = "c:/temp/stepmania/utils/Font generation/Templates/";
|
||||
var OutputDir = "c:/temp/stepmania/utils/Font generation/Output/";
|
||||
|
||||
var Fonts = new Array(
|
||||
/* name font frame sz font sz AA border bold */
|
||||
new FontDef("_japanese 16px", "MS-Gothic", 32, 20, AntiAlias.STRONG, 2, true),
|
||||
new FontDef("_japanese 24px", "MS-Gothic", 32, 28, AntiAlias.SMOOTH, 2, true)
|
||||
);
|
||||
|
||||
|
||||
/* We can't send Unicode into the font ourself for some reason. So, we have to
|
||||
* store prototypes for each font page. These should just be PSD files that contain
|
||||
* a single text layer with the characters we want. The orientation, font, etc.
|
||||
* don't matter; they'll be overridden. */
|
||||
var FontPages = Array("main", "kanji 1", "kanji 2","kanji 3");
|
||||
|
||||
var sel, doc;
|
||||
|
||||
var strtRulerUnits = preferences.rulerUnits;
|
||||
var strtTypeUnits = preferences.typeUnits;
|
||||
preferences.rulerUnits = Units.PIXELS;
|
||||
preferences.typeUnits = TypeUnits.PIXELS;
|
||||
displayDialogs = DialogModes.NO;
|
||||
|
||||
go();
|
||||
|
||||
preferences.rulerUnits = strtRulerUnits;
|
||||
preferences.typeUnits = strtTypeUnits;
|
||||
|
||||
function FontDef(Name, /* filename prefix */
|
||||
Font, /* actual font name */
|
||||
FrameSize, /* frame size in pixels */
|
||||
FontSize, /* font character size in pixels (horiz) */
|
||||
AAMethod, /* antialiasing */
|
||||
Border, /* pixels or 0 for off */
|
||||
Bold /* boolean */
|
||||
)
|
||||
{
|
||||
this.Name = Name;
|
||||
this.Font = Font;
|
||||
|
||||
this.FrameSize = FrameSize;
|
||||
/* This is the width of each character, as output by the font renderer.
|
||||
* Changing this changes the size of the character. */
|
||||
|
||||
this.HorizSize = FontSize;
|
||||
/* This is the actual vertical size of characters, as output by the font renderer.
|
||||
* For MS Gothic, it always seems to be 4/3 of the vertical size. */
|
||||
this.VertSize = FontSize* 4 / 3;
|
||||
this.AAMethod = AAMethod;
|
||||
this.Border = Border;
|
||||
this.Bold = Bold;
|
||||
}
|
||||
|
||||
function go()
|
||||
{
|
||||
for(var f = 0; f < Fonts.length; ++f)
|
||||
{
|
||||
for(var i = 0; i < FontPages.length; ++i)
|
||||
{
|
||||
MakeFontPage(TemplateDir + FontPages[i] + " template.psd",
|
||||
OutputDir + Fonts[f].Name + " [" + FontPages[i] + "]",
|
||||
Fonts[f]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function MakeFontPage(TemplatePath, OutputPath, Font)
|
||||
{
|
||||
/* Open the template. */
|
||||
var file = new File(TemplatePath);
|
||||
if(!file.open("r"))
|
||||
{
|
||||
alert("Couldn't open " + TemplatePath);
|
||||
return;
|
||||
}
|
||||
open(file);
|
||||
file = null;
|
||||
|
||||
/* Change the template to a font page. */
|
||||
var frameX = new Integer;
|
||||
var frameY = new Integer;
|
||||
ConvertCurrentDocument(frameX, frameY, Font);
|
||||
|
||||
/* Save the template to the destination file and close the template. */
|
||||
var Save = new PNGSaveOptions;
|
||||
Save.interlaced = false;
|
||||
OutputPath += " " + frameX.val + "x" + frameY.val;
|
||||
OutputPath += ".png";
|
||||
activeDocument.saveAs(new File(OutputPath), Save, true, Extension.LOWERCASE);
|
||||
activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||
}
|
||||
|
||||
function GetTextLayer(Doc)
|
||||
{
|
||||
/* Find a text layer. */
|
||||
var TextLayer = null;
|
||||
for(var i = 0; i < activeDocument.artLayers.length; ++i)
|
||||
{
|
||||
if(activeDocument.artLayers[i].kind != LayerKind.TEXT)
|
||||
continue; /* irrelevant */
|
||||
|
||||
if(TextLayer != null)
|
||||
{
|
||||
alert("More than one text layer found. Confused.");
|
||||
return null;
|
||||
}
|
||||
|
||||
TextLayer = activeDocument.artLayers[i];
|
||||
}
|
||||
|
||||
if(TextLayer == null)
|
||||
{
|
||||
alert("No text layer found. Confused.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return TextLayer;
|
||||
}
|
||||
|
||||
/* We have an open document with a single text layer. Convert it to a font
|
||||
* page. The result will be a single RGB layer. */
|
||||
function ConvertCurrentDocument( frameX, frameY, Font )
|
||||
{
|
||||
// Create a new art layer and convert it to a text layer.
|
||||
// Set its contents, size and color.
|
||||
if (documents.length == 0)
|
||||
{
|
||||
alert ("You must have at least one open document to run this script.");
|
||||
return;
|
||||
}
|
||||
|
||||
doc = activeDocument;
|
||||
sel = doc.selection;
|
||||
|
||||
/* Find a text layer. */
|
||||
var TextLayer = GetTextLayer(doc);
|
||||
if(TextLayer == null) return;
|
||||
|
||||
/* Figure out the number of frames in each dimension, based on the line count
|
||||
* and max characters/line. */
|
||||
GetFrameCount(TextLayer.textItem.contents, frameX, frameY);
|
||||
|
||||
/* Based on the frame count and frame size, figure out the dimensions of
|
||||
* the whole page. */
|
||||
var PageWidth = frameX.val * Font.FrameSize;
|
||||
var PageHeight = frameY.val * Font.FrameSize;
|
||||
|
||||
/* Resize the image down to the size required to hold the images. This prevents
|
||||
* errors later when we move columns around if the image is too big. */
|
||||
doc.resizeCanvas(PageWidth, PageHeight, AnchorPosition.TOPLEFT);
|
||||
|
||||
{
|
||||
/* Using HorizSize as the vertical origin seems to align the text so that
|
||||
* it's centered in the top VertSize pixels. Adjust from there to being centered
|
||||
* in the frame. */
|
||||
var Dist = Font.FrameSize/2-Font.VertSize/2;
|
||||
TextLayer.textItem.position = Array(0, Font.HorizSize+Dist);
|
||||
}
|
||||
|
||||
TextLayer.textItem.font = Font.Font;
|
||||
TextLayer.textItem.antiAliasMethod = Font.AAMethod;
|
||||
var textColor = new SolidColor;
|
||||
textColor.rgb.red = 255;
|
||||
textColor.rgb.green = 255;
|
||||
textColor.rgb.blue = 255;
|
||||
|
||||
TextLayer.textItem.color = textColor;
|
||||
|
||||
TextLayer.textItem.useAutoLeading = 0;
|
||||
TextLayer.textItem.spaceBefore = Font.FrameSize;
|
||||
TextLayer.textItem.spaceAfter = 0;
|
||||
|
||||
TextLayer.textItem.size = Font.HorizSize;
|
||||
|
||||
TextLayer.textItem.fauxBold = Font.Bold;
|
||||
TextLayer.textItem.fauxItalic = false;
|
||||
|
||||
TextLayer.rasterize(RasterizeType.ENTIRELAYER);
|
||||
|
||||
var CurrentSpacing = Font.HorizSize;
|
||||
/* Faux bold adds half a pixel to the widht of each character. */
|
||||
if(Font.Bold)
|
||||
CurrentSpacing += 0.5;
|
||||
|
||||
/* We currently have a horizontal spacing of CurrentSpacing; adjust it to
|
||||
* have a vertical spacing of FrameSize. */
|
||||
Adjust(Font.FrameSize, CurrentSpacing, frameX.val);
|
||||
|
||||
if(Font.Border != 0)
|
||||
TextLayer = MakeBorder(TextLayer, doc.artLayers, Font.Border);
|
||||
|
||||
{
|
||||
/* Resize the texture to have the largest possible frame count without going
|
||||
* over a power of two. This way, when the font page grows, the frame count
|
||||
* (8x8) stays constant, so we don't have to keep renaming files in CVS. */
|
||||
var MaxX = power_of_two(PageWidth);
|
||||
var MaxY = power_of_two(PageHeight);
|
||||
while(PageWidth + Font.FrameSize <= MaxX)
|
||||
PageWidth += Font.FrameSize;
|
||||
while(PageHeight + Font.FrameSize <= MaxY)
|
||||
PageHeight += Font.FrameSize;
|
||||
|
||||
/* Actually, let's force the texture to be square, too, which reduces filename
|
||||
* changes even more. This eats a little texture memory. It also means that
|
||||
* if the texture is reduced at runtime, it'll be reduced equally in both
|
||||
* dimensions, which will probably look less weird. */
|
||||
if(PageHeight < PageWidth) PageHeight = PageWidth;
|
||||
else PageWidth = PageHeight;
|
||||
|
||||
frameX.val = PageWidth / Font.FrameSize;
|
||||
frameY.val = PageHeight / Font.FrameSize;
|
||||
}
|
||||
doc.resizeCanvas(PageWidth, PageHeight, AnchorPosition.TOPLEFT);
|
||||
}
|
||||
|
||||
function GetFrameCount(str, x, y)
|
||||
{
|
||||
/* Strip blank lines off the end. */
|
||||
for(var c = str.length-1; c >= 0; --c)
|
||||
{
|
||||
if(c != '\r' && c != ' ')
|
||||
break;
|
||||
str = str.substr(0, str.length-1);
|
||||
}
|
||||
|
||||
var NumLines = 0, MaxLength = 0, CurLength = 0;
|
||||
|
||||
for(var c = 0; c < str.length; ++c)
|
||||
{
|
||||
if(str[c] == '\r')
|
||||
{
|
||||
CurLength = 0;
|
||||
NumLines++;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurLength++;
|
||||
if(CurLength > MaxLength)
|
||||
MaxLength = CurLength;
|
||||
}
|
||||
}
|
||||
if(CurLength)
|
||||
NumLines++;
|
||||
|
||||
x.val = MaxLength;
|
||||
y.val = NumLines;
|
||||
}
|
||||
|
||||
function power_of_two(n)
|
||||
{
|
||||
var value = 1;
|
||||
|
||||
while (value < n)
|
||||
value *= 2;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* The current document is in columns of width NewWidth. Space it out so the columns
|
||||
* are of width OldWidth. */
|
||||
function Adjust(NewWidth, OldWidth, Columns)
|
||||
{
|
||||
// col = 7;
|
||||
for(var col = Columns-1; col >= 0; --col)
|
||||
{
|
||||
var selBounds = Array(
|
||||
Array(col*OldWidth,0),
|
||||
Array((col+1)*OldWidth, 0),
|
||||
Array((col+1)*OldWidth, activeDocument.height),
|
||||
Array(col*OldWidth, activeDocument.height));
|
||||
sel.select(selBounds);
|
||||
|
||||
var OldXCenter = (col + .5) * OldWidth;
|
||||
var NewXCenter = (col + .5) * NewWidth;
|
||||
var Distance = NewXCenter-OldXCenter;
|
||||
sel.translate(Distance);
|
||||
}
|
||||
sel.deselect();
|
||||
}
|
||||
|
||||
function SelectTransparencyFromCurrentLayer()
|
||||
{
|
||||
// Voodoo from the action recorder, since I couldn't find a way to
|
||||
// do this normally.
|
||||
var id15 = charIDToTypeID( "setd" );
|
||||
var desc3 = new ActionDescriptor();
|
||||
var id16 = charIDToTypeID( "null" );
|
||||
var ref1 = new ActionReference();
|
||||
var id17 = charIDToTypeID( "Chnl" );
|
||||
var id18 = charIDToTypeID( "fsel" );
|
||||
ref1.putProperty( id17, id18 );
|
||||
desc3.putReference( id16, ref1 );
|
||||
var id19 = charIDToTypeID( "T " );
|
||||
var ref2 = new ActionReference();
|
||||
var id20 = charIDToTypeID( "Chnl" );
|
||||
var id21 = charIDToTypeID( "Chnl" );
|
||||
var id22 = charIDToTypeID( "Trsp" );
|
||||
ref2.putEnumerated( id20, id21, id22 );
|
||||
desc3.putReference( id19, ref2 );
|
||||
executeAction( id15, desc3, DialogModes.NO );
|
||||
}
|
||||
|
||||
/* Add a border to the current layer. */
|
||||
function MakeBorder(Layer, Layers, Width)
|
||||
{
|
||||
SelectTransparencyFromCurrentLayer();
|
||||
sel.expand(Width);
|
||||
|
||||
var BorderLayer = Layers.add();
|
||||
BorderLayer.moveAfter(Layer);
|
||||
|
||||
var borderColor = new SolidColor;
|
||||
borderColor.rgb.red = 0;
|
||||
borderColor.rgb.green = 0;
|
||||
borderColor.rgb.blue = 0;
|
||||
|
||||
sel.fill(borderColor);
|
||||
sel.deselect();
|
||||
|
||||
Layer.merge();
|
||||
|
||||
return BorderLayer;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user