Tuesday, September 23, 2008

Using Fonts Without Installation

And here we go again, the same old story - corporate environment, users unable to install additional fonts. I find the font management in Windows pretty weak, all users are locked into the same setting, without any kind of possibility how to tailor it to their preferences. Until now that is!

What happens during font installation:

  1. Font file is copied to %WINDIR%\Fonts directory
  2. New font entry is created in registry HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts describing font's name and file.
  3. Function AddFontResource is called, which updates the global font table.

Since the AddFontResource function doesn't require the font to reside in Windows Fonts directory neither the relevant entry in registry to exist, we can just call it with font placed anywhere. The following code illustrates how:

#include <windows.h>
#include <ctype.h>

char* lower(char *s)
{
 char *result = (char *)malloc(strlen(s)+1);
 result[strlen(s)] = '\0';
 for (int i = strlen(s) - 1; i >= 0; i--)
 {
  result[i] = tolower(s[i]);
 }
 
 return result;
}

void processDirectory(char *initial, char *dir)
{
 char *wildcard = (char *)malloc(strlen(initial) + strlen(dir) + 2);

 memset(wildcard, 0, strlen(initial) + strlen(dir) + 2);
 strcat(wildcard, initial);

 strcat(wildcard, "\\");
 strcat(wildcard, dir);

 char *fulldir = (char *)malloc(strlen(wildcard));
 strcpy(fulldir, wildcard);
 strcat(wildcard, "\\*");

 WIN32_FIND_DATA data;

 HANDLE search = FindFirstFile(wildcard, &data);

 if (search == INVALID_HANDLE_VALUE)
  return;

 do
 {
  if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)  
     && (strcmp(data.cFileName, ".") != 0)
     && (strcmp(data.cFileName, "..") != 0))
  {
   processDirectory(fulldir, data.cFileName);
  }
  else if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  {
   char *s = lower(data.cFileName);
   if (strstr(s+strlen(s)-4, ".ttf") != 0)
   {
    int fontLength = strlen(fulldir) + strlen(data.cFileName) + 2;
    char *font = (char*)malloc(fontLength);
    memset(font, 0, fontLength);
    strcat(font, fulldir);
    strcat(font, "\\");
    strcat(font, data.cFileName);

    AddFontResource(font);
    free(font);
   }
   free(s);
  }
 } while(FindNextFile(search, &data));

 free(wildcard);
 free(fulldir);

 FindClose(search);
}

int main(int argc, char *argv[])
{
 if (argc > 1)
 {
  for (int i = 1; i < argc; i++)
   processDirectory(argv[i], "");
 }
 else
  processDirectory(".", "");

 SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);

 return 0;
}

Most of the code is dealing with string concatenation and not the real task at hand, but I didn't want to use C++ string to keep it nicely Win32 :)

What it actually does is that it takes a list of directories as arguments and goes recursively through all of them and every file ending with .ttf adds as a new font resource to the system. In case no directories are supplied as parameters, the current directory is used.

If you want to use it regularly, create a directory with your fonts and add to your Startup folder link to fontload.exe and give it as a parameter the name of your folder. Then after every computer start, your fonts get loaded automatically.

F:\Projects\fontload\fontload.exe F:\MyFonts

Please note:

  • It takes quite a while to broadcast the notification that new fonts are available to all windows. The time is quite independent on amount of fonts installed and takes about twenty seconds on my computer.
  • Some applications might be very unhappy about this, if you encounter crashes, it could be this program's fault. Although it works fine for me, doesn't mean it will work for you :).

  • There is nothing preventing you from deleting the font files, which are loaded, so expect hilarious results upon doing so.
  • This program doesn't care about font unloading. If you want to unload the fonts, just restart the computer or figure out, how to use the RemoveFontResource function

Source and binary version can be downloaded here - fontload.zip - 7kB.

Next time, we will use this knowledge to create a simple font viewer, which will pollute the global font table only on request :).

Obligatory thanks to Mark James for creating the amazing Silk icons.

Friday, September 12, 2008

Listing Full Command Line on HP-UX

There is one very annoying limitation on HP-UX - there is no /proc folder and ps command can display maximum 63 characters from processes command line. There is no simple way how to display full command line of a specified process.

If you're working with Java programs, you know, that the first 63 characters barely reach the classpath definition. Usually it's just the path to the java binary.

Yesterday I needed to find out, if one java program is not running multiple times. And guess what, it's almost impossible to find out on a server with tens of different java programs running. After lot of searching I came up with this program - it lists all the running process ids and their respective command lines. There is no original code happening, it's basically, this forum post merged with this manual page.

#include <stdio.h>
#include <sys/pstat.h>
#include <sys/pstat/pstat_ops.h>
#define BURST 20
#define MAX_LENGTH 1024

struct pst_status pst[BURST];

int main(void)
{
char command [MAX_LENGTH];
        union pstun pu;

        int i, count;
        int idx = 0;/* index within the context */
        /* loop until count == 0, will occur all have been returned */

        while ((count=pstat_getproc(pst, sizeof(pst[0]),BURST,idx))>0)
        {
                for (i = 0; i < count; i++)
                {
                        pu.pst_command = command;
                        if (pstat(PSTAT_GETCOMMANDLINE, pu, MAX_LENGTH, 1, pst[i].pst_pid) == -1)
                        {
                                printf("ERROR: failure using pid(%d)\n", pst[i].pst_pid);
                                exit(-1);
                        }
                        printf("%d: %s\n",  pst[i].pst_pid, pu.pst_command);
                }
                idx = pst[count-1].pst_idx + 1;
        }

        if (count == -1)
                perror("pstat_getproc()");
}

Compile the code with the following command

cc pstat.c -o pstat +DD64

You have to include the +DD64 if you're on a 64bit system. Otherwise, you'll get following error:

pstat_getproc(): Value too large to be stored in data type

Of course, about five seconds after finishing this article I found a way how to do it with the original HP-UX ps command, so please disregard my blabbering :)

ps -exx

Monday, September 1, 2008

Integrating SDL with Python

Last time we embedded Lua interpreter into a simple OpenGL application and today I'm going to try the same with

I will use the same SDL skeleton, so we can concentrate only on the Python part.

Python skeleton

Embedding Python interpreter is a little more complicated than embedding Lua, so we will actually make it in three distinct steps.

1. Minimal Python
#include <Python.h>

int main(void)
{
        Py_Initialize();
        FILE *f = fopen("skeleton.py", "r");
        PyRun_SimpleFile(f, "skeleton.py");
        fclose(f);
        Py_Finalize();
}
g++ minimal.cpp -o python_minimal -lpython25

For compiling on Linux just replace -lpython25 with -lpython2.5. The library is named differently

Compile this program with Mingw on Windows and you will experience a crash. Compiling with Microsoft compiler or compiling on Linux works correctly. What's going on? After some searching, I found the answer in Blender source, which pointed me to the main page of Python embedding documentation, where it explicitly says, that the file descriptors passed to PyRun_*File function, have to be created by the same runtime libraries. Since I'm using Mingw, I had to avoid all the PyRun_*File functions and pass the Python script to the interpreter as a string.

2. Minimal Python as a String
#include <Python.h>

int main(int argc, char*argv[])
{
 Py_Initialize();

 FILE *f = fopen("skeleton.py", "r");
 fseek(f, 0, SEEK_END);
 size_t length = ftell(f);
 fseek(f, 0, SEEK_SET);
 char *data = (char *)malloc(length + 1);
 fread(data, 1, length, f);
 data[length] = '\0';
 fclose(f);

 PyRun_SimpleString(data);
 Py_Finalize();

 return 0;
}

All this code does is opening a file, reading the contents and passing it to Python interpreter, nothing really spectacular. You can place any Python code into skeleton.py such as:

print 'Hello from Python'
3. Calling C Functions from Python

To be able to call a C function from Python you have to do a little more than in case of Lua. You have to create a new module for a set of functions with Py_InitModule. In subsequent Python code you have to import from this module.

#include <Python.h>

// our function, which we call from Python
PyObject * redraw(PyObject *self, PyObject *args)
{
 double theta;
 PyArg_ParseTuple(args, "d", &theta);
 printf("%g\n", theta);

 return Py_BuildValue("");
}

// list of methods, which will put into module Foo
static PyMethodDef methods[] =
{
 { "redraw", redraw, METH_VARARGS, "updates the screen" },
 {NULL}
};

int main(int argc, char*argv[])
{
 Py_Initialize();

 FILE *f = fopen("mixed.py", "r");
 fseek(f, 0, SEEK_END);
 size_t length = ftell(f);
 fseek(f, 0, SEEK_SET);
 char *data = (char *)malloc(length+1);
 fread(data, 1, length, f);
 data[length] = '\0';
 fclose(f);

 // init our module
 PyObject *module = Py_InitModule("Foo", methods);
 PyRun_SimpleString(data);
 Py_Finalize();

 return 0;
}

When run in conjunction with this little Python script, it will print out all the rotations of the triangle, which will we display in SDL version later.

from Foo import *
i = 1
while (i < 2000):
 redraw(i)
 i += 0.5

Skeletons mixed

And now we need just to merge in the SDL code together and we're done

#include <Python.h>
#include <SDL/SDL.h>
#include <windows.h>
#include <gl/gl.h>

PyObject * redraw(PyObject *self, PyObject *args)
{
 double theta;
 PyArg_ParseTuple(args, "d", &theta);

 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 glLoadIdentity();
 glTranslatef(0.0f,0.0f,0.0f);
 glRotatef(theta, 0.0f, 0.0f, 1.0f);
 glBegin(GL_TRIANGLES);
 glColor3f(1.0f, 0.0f, 0.0f);
 glVertex2f(0.0f, 1.0f);
 glColor3f(0.0f, 1.0f, 0.0f);
 glVertex2f(0.87f, -0.5f);
 glColor3f(0.0f, 0.0f, 1.0f);
 glVertex2f(-0.87f, -0.5f);
 glEnd();

 SDL_GL_SwapBuffers();
 return Py_BuildValue("");
}

static PyMethodDef methods[] =
{
 { "redraw", redraw, METH_VARARGS, "updates the screen" },
 {NULL}
};

int main(int argc, char*argv[])
{
 atexit(SDL_Quit);
 if( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
  fprintf(stderr,"Couldn't initialize SDL: %s\n", SDL_GetError());
  exit(1);
 }

 SDL_Surface *screen = SDL_SetVideoMode(400, 400, 32, SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_OPENGL);

 glViewport(0, 0, 400, 400);
 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
 glClearDepth(1.0);
 glDepthFunc(GL_LESS);
 glEnable(GL_DEPTH_TEST);
 glShadeModel(GL_SMOOTH);
 glMatrixMode(GL_PROJECTION);
 glMatrixMode(GL_MODELVIEW);

 Py_Initialize();

 FILE *f = fopen("mixed.py", "r");
 fseek(f, 0, SEEK_END);
 size_t length = ftell(f);
 fseek(f, 0, SEEK_SET);
 char *data = (char *)malloc(length+1);
 fread(data, 1, length, f);
 data[length] = '\0';
 fclose(f);

 PyObject *module = Py_InitModule("Foo", methods);
 PyRun_SimpleString(data);
 Py_Finalize();

 return 0;
}

The result, we're getting, is of course the same as last time

Colorful triangle drawn by OpenGL called from Python

In this case as well I couldn't find any statistically significant speed difference between native C and interpreted Python.

All sources and binaries can be downloaded here 4 MB

Very interesting side note: when implementing this example I found out an extremely awesome thing. New versions of Mingw allow linking directly to DLLs. You don't need no .a, .la or .lib files. You can just direct the compiler/linker to the directory with the DLL you're referencing and it will automagically resolve the symbols from within the DLL - simply AWESOME!!!