Click here to Skip to main content
15,881,812 members
Articles / Multimedia / GDI

EBDT (Mono Embedded Bitmap Data in TrueType Font) to BM (BitMap)

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
3 Mar 2020CPOL8 min read 6K   100   1  
Randomly read any embedded_mono_matrix in TTF file, export it to bitmap files (in package)
In this article, you will learn how to randomly read embedded_mono_kanji_matrix from a TTF file, how to export some matrices to some BitMap files, how to export all of them into 1 package file, how to make a TTF file from 1 package file and finally how to make a package file by 7z.

Introduction

This source code shows:

  1. How to random read embedded_mono_kanji_matrix from a TTF (TrueType Font) file
  2. How to export some matrices to some BitMap files (If you just see them in GUI, and nobody can check them in another platform, then it is teasing me.)
  3. How to export them all into 1 package file (Thousands of tiny files = large footprint)
  4. After (0.1)~(0.3), they are checked ok, how to show them
  5. How to make a TTF file (just mono EBDT, no vector data) from 1 package file
  6. How to make a package file by 7z

In medium and low-end embedded software, we maybe use several kanji fonts. But there are thousands of small matrices(16*16? 20*20?) in a font, too many file fragments. If we pack them into 1 package file, then our art designer will waste a lot of time to find a kanji matrix (which he wants) in the package file.

Our solution is pack them into 1 TTF file. So in the embedded software: no file fragment, and we random read embedded_mono_matrix as usual. In the PC: our art designer uses the TTF to textout any matrix (which he wants) freely.

If anybody wants to modify (rare, but NOT never) some matrices in his TTF file, he can export all matrices, modify some of them, pack them into 1 package and make a new TTF file from it. If these situations occur regularly, don't delete the matrices BitMap, it can be used next time.

Background

TrueType Font https://developer.apple.com/fonts/TrueType-Reference-Manual/
"Embedded Bitmap Data Table "

or "Bitmap Data Table"

https://docs.microsoft.com/en-us/typography/opentype/spec/ebdt

https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bdat.html

Portable BitMap http://netpbm.sourceforge.net/doc/pbm.html
7z http://cpansearch.perl.org/src/BJOERN/Compress-Deflate7-0.9/7zip/DOC/7zFormat.txt

Using the Code

1.1. ebdt2bm.c\get_matrix_fail(TTF_STRUCT* p_ttf, unsigned int scale_index, unsigned short glyph_index,..., unsigned char* dst_bmp, ...)

The ttf file stores matrices in regular sequence of scale_index and glyph_index. For example:

  glyph_1 glyph_2 glyph_3 glyph_4 glyph_5 glyph_6 glyph_7 glyph_8 glyph_9 ...
scale_0
(8*8)
matrix_01 matrix_02 matrix_03 matrix_04     matrix_07 matrix_08 matrix_09 ...
scale_1
(16*16)
    matrix_13 matrix_14 matrix_15 matrix_16 matrix_17 matrix_18   ...
scale_2
(20*20)
  matrix_22 matrix_23   matrix_25 matrix_26   matrix_28   ...
... ...

So, "get_matrix_fail(&ttf,0,2,..,&bmp...)" will get matrix_02 in bmp;

"get_matrix_fail(&ttf,1,5,..,&bmp...)" will get matrix_15 in bmp;

"get_matrix_fail(&ttf,2,7,..,&bmp...)" will fail.

The remain parameters of function "get_matrix_fail()", include "unsigned short *p_out_w" and "unsigned short *p_out_h". That may be helpful for export BitMaps.

1.2. How to Export Some Matrices to Some BitMap Files

After getting the matrix, we can:

1.2.1. Add BitMap (#define "USE_BMP" or "USE_PBM" in ebdt2bm.c) header to it.

1.2.2. Give it a file name.

1.2.3. And save the BitMap file.

ebdt2bm.c\int ebdt2bm_fail(TTF_STRUCT* p_ttf, unsigned int scale_index, unsigned short glyph_index,...) is an example:

C++
unsigned short w,h;
unsigned int	file_size;

     if((ret=get_matrix_fail(p_ttf,scale_index,glyph_index,..,
         &(bmp_buf[0x3e])..,&w,&h)!=0)return(ret);
//	if we save in bmp format, we reserve (sizeof(BITMAPFILEHEADER) + 
//  sizeof(BITMAPINFOHEADER) + 2*sizeof(RGBQUAD))==0x3e bytes.

else if(make_bm_header_fail(-1,&(bmp_buf[0x3e]),w,h, &bmp_head,&file_size,NULL))return(11);
//	(1.2.1)add BitMap header to it.
//	if we save in  bmp format, we fill its header, get the file_size of bmp;
//	if we save in pbm  format, we decrease header_size to 9 byte, 
//  reget the header, get the file_size of pbm.
...

else if(get_fileName_from_glyIdx_fail(p_ttf,wtr_bmp_name, glyph_index,w,h))return(12);
//	(1.2.2)give it a file name.
//	I use "wsprintfW(wtr_bmp_name,L"%04x_%02dx%02d(%c).pbm",unicode,w,h,unicode)" here, 
//  just to avoid duplication of file_names.
//	Of course you can do anything here.

else if((ret=save_bmp_fail(wtr_bmp_name, bmp_head,file_size...))!=0)return(20+ret);
//	(1.2.3)save the BitMap file, the details code are:
//	hFile=fcreat_z(wtr_bmp_name,"wb"...);
//	ret  =fwrite_z(bmp_head,1,file_size,hFile);
//	fclose_z(hFile);

else return(0);

gui calls this function in 1.5.3.

1.3. How to Export All Matrices Into 1 Package File

After #define "USE_ZIP" or "USE_7Z" in writeZip.c, we can use function zip_fcreat_fail() and zip_fclose(). Between them, we trans all gly_index's EBDT matrices to BitMaps.

ebdt2bm.c\export_zip(WCHAR* wtr_zip_name, TTF_STRUCT* p_ttf, unsigned int scale_index ...) is a example:

C++
int res=0;
if(zip_fcreat_fail(wtr_zip_name,"wb",...))return(1);//constructor
 else	{for(show_seq=0; show_seq<max_show; show_seq++)
		{glyph_index=g_ttf.get_glyIdx_from_seq[show_seq];
		if(0xffff==glyph_index);//for above example: scale_2, glyph_7
		else if(ebdt2bm_fail(p_ttf, scale_index,
                glyph_index,...,&g_zip))//write sub_file in zip
			{res=2; break;}
		}
	}
 zip_fclose(&g_zip);//destructor
 return(res);

gui calls this function in 1.5.3.

1.4. ebdt2bm.c

From 1.1.~1.3. are function descriptions of ebdt2bm.c. It is a single c file (ebdt2bm.c). At the front of the file, there are notes for how to compile it from c to console EXE. The size of EXE is 18K bytes.

User can open vc6 project (ebdt2bm.dsw) to compile it too.

In cmd console, input "ebdt2bm.exe /?" we would get str_default[] ={"ebdt2bm.exe <file.ttf> [font height]\r\n\t(default: font height=24)"}

Example 1: We input "ebdt2bm.exe demo.ttf 25", and we would get demo.7z [which includes 0020_12x25.pbm, 0021_12x25.pbm, ..., ff0c_12x25(,).pbm].

Example 2: We input "ebdt2bm.exe demo.ttf", and we would get demo.7z [which includes 0020_12x24.pbm, 0021_12x24.pbm, ..., ff0c_12x24(,).pbm].

Example 3: We input "ebdt2bm.exe demo.ttf 20", and we get report "24x24,25x25, no scale match your font height".

And it exports 2 functions: fopen_ttf_fail() and fclose_ttf() as constructor and destructor.

C++
int  main(int argc,char** argv)
{
 unsigned int	font_height;
 WCHAR		wtr_zip_name[256];
 WCHAR		wtr_ttf_name[256];
 TTF_STRUCT	g_ttf;
 unsigned int	scale_index;

 mem_set(&g_ttf,0,sizeof(TTF_STRUCT));
 if((res=get_fontName_and_fontHeight(argc,argv,wtr_ttf_name,&font_height))!=0)
	put_s(...);

 else if(fopen_ttf_fail(wtr_ttf_name, font_height, &g_ttf,&scale_index ...))//constructor
	put_s(...);
 ...
 else if(get_zipName_from_ttfName_fail(wtr_zip_name, wtr_ttf_name));

 else	{res=export_zip(wtr_zip_name,&g_ttf,scale_index, ...);//see in (1.3)
	if(0==res)
		put_s(str_ok);
	else 
		put_s(...);
	}
 fclose_ttf(&g_ttf);//destructor
}

gui calls these constructor and destructor in 1.5.3.

1.5. charMap2.c

ebdt2bm.exe is just a console EXE.

What a pity!

We can give it a GUI shell,
whose outlook is like
Microsoft's charmap.exe,
so we call it charMap2.exe.

Image 1

charMap2.c is a single c file too. At the front of the file, there are notes for how to compile it from c to gui EXE. The size of EXE is 26K bytes.

User can open vc6 project (charMap2.dsw) to compile it too.

1.5.1. Its Outlook

Image 2

It shows all embedded_mono_matrices in the TTF file, which are stored in regular sequence of scale_index and glyph_index. All font scales are shown at the top-right combobox. All glyph_index are shown in status bar as "g=%04xh". Every matrix has its matrix_offset (in TTF file), matrix_length_byte_cnt, matrix_width(pixels), and matrix_height(pixels), so they are shown in status bar as "ofs=%xh,len=%xh", and "w=%d,h=%d".

But the glyph_index is used inside the TTF file. In the human world, we use unicode, so we show printf("U+%04xh(%c)",ucs16,ucs16) in status bar. In the capture image above, we can see the focus = unicode 'A', and its hex_unicode_value=41h, and its glyph_index=21h.

If we want to change the focus by unicode, by hex_unicode_value, or by glyph_index, we can set one of them in the bottom edit_box of dialog, press enter_key or click the arrow_button above the edit_box, to update the old value in the status bar.

If we just travel the matrices, we can use arrow_key, pgUp, pgDn, or click the scroll bar. Nice for travel!

1.5.2. How Do We Do That?

init_instance() call prepare_bmp_content(), and the later's code are: str_bmp=malloc(...), and:

C++
p_wh=p_ttf->str_whls;//Width,Height,Length_byte_cnt,offSet
for(show_seq=0; show_seq<max_show; show_seq++,p_wh++)
	{...
	x=...; y=...;
	glyph_index=g_ttf.get_glyIdx_from_seq[show_seq];
	if(0xffff==glyph_index);
	else	{get_matrix_fail(p_ttf, d1.cur_combox_index, (unsigned short)glyph_index,0,
			str_bmp,bmp_byte_per_line,
			x+1,y+1, x+(mtx_w+1), y+(mtx_h+1),//the area which designated by us
			&ofs,&byteCnt,&fmt,&w,&h);
		p_wh->w=w; p_wh->h=h; p_wh->fmt=fmt; p_wh->leng=byteCnt; p_wh->ofs=ofs;
		}
	}

This code gets every matrix in the str_bmp area which is designated by us, and marks down any information of the matrix (such as Width, Height, Length_byte_cnt, offSet) in our array. So we can show any matrix in window, and show its information in status bar.

1.5.3. Its Menu

Image 3

In charMap2.c\WinMain():

C++
if(WM_COMMAND==g_msg.message){

if(ID_MENU_OPEN_FNT==g_msg.wParam)
	get_open_name();   //GetOpenFileNameW()
                       //+ fopen_ttf_fail(new_name) + 
                       //  fclose_ttf(old_ttf);
                       //see (1.4)
else if(ID_MENU_SAVE_1_BM==g_msg.wParam)
	get_save_name();   //GetSaveFileNameW() + ebdt2bm_fail() //see (1.2)
else if(ID_MENU_SAVE_ALL_BM==g_msg.wParam)
	get_export_name(); //GetSaveFileNameW() + export_zip()
	                   //see (1.3)
else{;}
}

Even if we don't use menu, we can use accelerate key(ctrl+O, ctrl+S) to call get_open_name() or get_save_name().

1.6. How to Make a TTF File from 1 Package File?

Use bm2ebdt.exe, whose source C file is a single C file (bm2ebdt.c). At the front of the C file, there are notes for how to compile it from c to console EXE. The size of EXE is 16K bytes.

User can open vc6 project (bm2ebdt.dsw) to compile it too.

We can #define "USE_ZIP" or "USE_7Z" in readZip.c (a sub file of bm2ebdt.c), default = "USE_7Z".

In cmd console, input "bm2ebdt.exe /?" we would get str_default[] ={"bm2ebdt.exe <file.7z>"}

Example 4: We input "bm2ebdt.exe demo.7z", and we would get demo.ttf.

1.6.1. Mark Down All Width, Height, Matrices of BitMaps

In bm2ebdt.c\int find_every_file_from_folder_fail(char* folder_name,...):

C++
if(find_1st_file_fail(folder_name, &hSeek,...);
else
do {...
   hFile_tmp=f_open(psp.cFileName,...,"rb",...);
   check_bm_head_fail(hFile_tmp,...,&w,&h...);
   read_mtx_fail(hFile_tmp...);
   ...
   f_close(hFile_tmp);
   }while(find_nx_file_ok(&hSeek,&psp...)

1.6.2. Add TTF Header for Those Matrices

In bm2ebdt.c\int write_ttf_fail(char* ttf_name...):

C++
hFile=_lcreat(ttf_name,0);
_lwrite(hFile,(char*)str_ttf_file_head,SIZEOFFILEHEAD);/* write 12 bytes of head */
_lwrite(hFile,(char*)str_dir,MAX_DIR<<4));/* write 160 bytes of table_directory */

for(i=0;i<MAX_DIR;i++)
   {switch(i)
   {
   case HCMAP: j=write_cmap(hFile); break;
   case HHEAD: j=write_head(hFile); break;
   case HNAME: j=write_name(hFile); break;
   case HPOST: j=write_post(hFile); break;
   case HOS2:  j=write_os2 (hFile); break;
   case HMAXP: j=write_maxp(hFile); break;
   case HHHEA: j=write_hhea(hFile); break;
   case HHMTX: j=write_hmtx(hFile); break;
   case HLOCA: j=write_loca(hFile); break;
   case HGLYF: j=write_glyf(hFile); break;
   case HEBLC: j=write_eblc(hFile); break;
   case HEBDT: j=write_ebdt(hFile); break;
   }
    if(j)//err
   {...}
    else{;}
    }
_lclose(hFile);

The last line in the above switch is bm2ebdt.c\int write_ebdt(HFILE hFile), it writes the body of matrices:

C++
unsigned char strbuf[864];
for(j=0;j<(unsigned short)c_map.gly_cnt;j++)
   {...
   size=get_mtx(...,strbuf);
   _lwrite(hFile,(char*)strbuf,size)
   }

1.7. How to Make a Package File by 7z

If we use 7z format
(#define "USE_7Z" in writeZip.c and readZip.c), we can choose 
compression_level="Store", and parameters="hc=off tm=off"(disables Header_Compressing, disables TiMestamps for files), to make a package for (1.6).

Image 4

If we use zip format 
(#define "USE_ZIP" in
writeZip.c and readZip.c),
we can choose compression_level="Store" too, and parameters="cu"
(uses UTF-8 for file_names)
to make a package for (1.6).

Image 5

1.8. Sundries

1.8.1. Smooth Edges of Screen Fonts

Of course, before use this font (just mono EBDT, no vector data), we must disable the "Smooth Edges of Screen Fonts" of window os, compel the os to use EBDT of this font.

1.8.2. Recalculate the Font Size

For example, we make a font by 24_dot matrix, so windows assume the font can be used as LOGPIXELS==72_dpi and font_height=24_dot in Windows 3.0.

If our LCD's LOGPIXELS=96_dpi, then we can see the EBDT by font_size=18(in ChooseFont()),it means font_size="18/72" inch on our LCD, so tagLOGFONT.lfHeight=18/72 inch * 96_DotPerInch =24_dot.

If our LCD's LOGPIXELS=120_dpi, then we can see the EBDT by font_size=15 (in ChooseFont()), it means font_size="15/72" inch on our LCD, so tagLOGFONT.lfHeight=15/72 inch * 120_DotPerInch=25_dot.

These 24_dot and 25_dot are "unsigned int h_96dpi,h_120dpi" in bm2ebdt.c, and they are scale_0 and scale_1 in demo.ttf.

Image 6

1.8.3. Use in DOS

ebdt2bm.com and charMap2.com and bm2ebdt.com are compiled by tc2.0 (At the front of the corresponding c_file, there are notes for how to compile them from c to xxx.com), they can be used in [dosbox] or [vdos] or [dos with xms driver].

In dosbox, we use "memsize=63" to get 63 mega bytes xms, and use "cycles=max" and "core=full" to get full speed.

In vdos, we use "XMEM = 63 XMS" to get 63 mega bytes xms, use "SCALE = 1" to forbid vdos zoom out, and use "MOUSE = ON" to show mouse in charMap2.com.

But they can NOT be used in window's cmd console, because they direct access extended memory.

Points of Interest

There is a black humor in P.R.China:

If [our embedded production's matrices] are NOT point to point match [the matrices of National Standard(abbreviation of it is "GB")], our production MAYBE (not sure, just maybe) be claimed to be "rejected product" by NITS (China National Information Technology Standardization Technical Committee).

But if the matrices point to point match them, then we MAYBE will be involved in patent infringement about them.

That is a catch 22. The NITS do {ignore small companies deliberately;} while (the companies!=rich);

After the companies come to rich, they must buy the copyright of fonts in every 4_years. According to oemfont.com, in January 2005, the font's price are GB2312_font=$15'000/4_years, GB13000_font=$40'000/4_years, GB18030_font=$40'000/4_years.

History

  • 3rd March, 2020: Submitted, can be compiled with bc 5.2 and tc 2.0
  • August 2016: Can be compiled with vc6.0 and armCC

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
China China
BLOGS(gb2312):
-----------------
https://leisurebamboo1.blogspot.com
https://leisurebamboo.wordpress.com

Comments and Discussions

 
-- There are no messages in this forum --