WordPress has the most well-designed blog skins. There is an interesting feature, if you see its web page throughly, that all of the subjects of the postings are actually images. I guess this is same technology as MediaWiki-math package has: you render the font inside the server, make it as an image file, and upload it instead of the text. Two advantages arise: 1) you can use the fonts that the clients do not have, and 2) you don't have to rely on the horrible font rendering of the web browsers. 1) is actually very very important especially for Korean web sites, because there are a few Korean basic fonts.
My goal is to build a TextCube plugin. This small project is composed of four steps: 1) searching a good Korean type which is open to everybody, 2) finding a good font renderer, 3) making a simple program that generates the image with the given font, text, and several variables, and 4) building the TextCube plugin using this program.
Contents |
잠 못 이루는 Seattle의 밤...
: Chosunilbo Myeongjo
: Baekmuk Batang
: Yoon Design Batang
사실 웹페이지의 글씨가 후진 것은 브라우저의 후진 렌더링 때문인 것이기에, 포토샵에서 불러서 비교하여 보았다. 22pt 사이즈 정도로 읽어들였으며, 글씨체가 큰 편이기에 자간을 줄여서 보았다. 우선 폰트 공작소에서 무료 배포판 서체들을 구할 수 있는 곳을 잘 정리해 놓았으며, 여기서 많은 도움을 받았음을 밝힌다. 아, 또 쓸만한 폰트 뷰어는 Nexus Font Viewer라는 것이 있는데, 윈도우즈 상에 서체를 인스톨 하지 않아도 대충 뭐가 어떻게 보이는지 알기 편하다.
There is a nice font rendering open source program called FreeType - I think that site has one of the most fanciest image among source project web sites.
Here is a sample program that reads fonts and builds an image. To run this program, you should include CImg Library. Also, you should handle unicode case, and I got the base code from romantica[2].
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include "CImg.h"
using namespace cimg_library;
#define WIDTH 800
#define HEIGHT 800
unsigned char image[HEIGHT][WIDTH];
void
draw_bitmap( FT_Bitmap* bitmap,
FT_Int x,
FT_Int y)
{
FT_Int i, j, p, q;
FT_Int x_max = x + bitmap->width;
FT_Int y_max = y + bitmap->rows;
for ( i = x, p = 0; i < x_max; i++, p++ )
{
for ( j = y, q = 0; j < y_max; j++, q++ )
{
if ( i >= WIDTH || j >= HEIGHT )
continue;
image[i][j] |= bitmap->buffer[q * bitmap->width + p];
}
}
}
void
show_image( void )
{
int i, j;
CImg<unsigned char> img(HEIGHT, WIDTH, 1, 3, 0);
for ( i = 0; i < HEIGHT; i++ ) {
for ( j = 0; j < WIDTH; j++ ) {
const unsigned char color[] = {255-image[i][j], 255-image[i][j], 255-image[i][j]};
img.draw_point(i, j, color);
}
}
img.save("temp.bmp");
}
const char utf8_skip[256] = {
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
};
static inline unsigned int utf8_get_char (char *p)
{
unsigned int result;
unsigned char c = *p;
int i, len;
if (!(c & 0x80))
return (unsigned int) c;
if (c > 0xfd)
return (unsigned int) -1;
len = utf8_skip[c];
result = p[0] & (0x7c >> len);
for (i = 1; i < len; i++) {
if ((p[i] & 0xc0) != 0x80)
return (unsigned int) -1;
result <<= 6;
result |= p[i] & 0x3f;
}
return result;
}
int main( int argc, char** argv )
{
FT_Library library;
FT_Face face;
FT_GlyphSlot slot;
FT_UInt glyph_index;
FT_Vector pen;
FT_Error error;
char* filename;
char* text;
int target_height;
int num_chars;
int *steps;
unsigned int *chars;
if ( argc != 3 )
{
fprintf ( stderr, "usage: %s font sample-text\n", argv[0] );
exit( 1 );
}
filename = argv[1];
text = argv[2];
num_chars = strlen( text );
target_height = HEIGHT - 200;
error = FT_Init_FreeType( &library );
error = FT_New_Face( library, argv[1], 0, &face );
error = FT_Select_Charmap( face, FT_ENCODING_UNICODE );
error = FT_Set_Char_Size( face, 50 * 64, 0, 100, 0 );
slot = face->glyph;
pen.x = 0;
pen.y = 100 * 64;
chars = (unsigned int *)calloc(sizeof(unsigned int), num_chars);
if (!chars)
return 1;
steps = (int*)calloc(sizeof(int), num_chars);
if (!steps) {
free(chars);
return 1;
}
int offset;
FT_Bool use_kerning;
FT_UInt previous;
use_kerning = FT_HAS_KERNING( face );
previous = 0;
for (offset=0; offset<num_chars; offset+=steps[offset]) {
steps[offset] = utf8_skip[(unsigned char)text[offset]];
chars[offset] = utf8_get_char(&text[offset]);
glyph_index = FT_Get_Char_Index(face, chars[offset]);
if (use_kerning && previous && glyph_index) {
FT_Vector delta;
error = FT_Get_Kerning( face, previous, glyph_index, FT_KERNING_DEFAULT, &delta );
pen.x += delta.x >> 6;
}
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
draw_bitmap( &slot->bitmap,
pen.x + slot->bitmap_left,
target_height - slot->bitmap_top );
pen.x += slot->advance.x >> 6;
pen.y += slot->advance.y >> 6;
previous = glyph_index;
}
show_image();
FT_Done_Face ( face );
FT_Done_FreeType( library );
return 0;
}