1 module des.fonts.ftrender;
2 
3 import des.math.linear;
4 import des.il;
5 import des.util.logsys;
6 import des.util.arch.emm;
7 import des.util.stdext;
8 
9 import derelict.freetype.ft;
10 import derelict.freetype.types;
11 
12 import std.traits;
13 
14 import des.fonts.base;
15 
16 ///
17 class FTException : FontException
18 {
19     ///
20     FTError code;
21     ///
22     this( string msg, FTError code, string file=__FILE__, size_t line=__LINE__ ) @safe pure nothrow
23     {
24         this.code = code;
25         super( msg, file, line );
26     }
27 }
28 
29 void checkFTCall(alias fnc, string info="", string file=__FILE__, size_t line=__LINE__,Args...)( Args args )
30 {
31     auto err = cast(FTError)fnc(args);
32     auto fmtInfo = info ? ( format( " (%s)", info ) ) : "";
33     if( err != FTError.NONE )
34         throw new FTException( format( "'%s'%s fails: %s", fnc.stringof, fmtInfo, err ), err );
35 }
36 
37 class FTFontRender : FontRender, ExternalMemoryManager
38 {
39     mixin EMM;
40 
41 protected:
42 
43     void selfDestroy()
44     { 
45         if( FT_Done_Face !is null )
46             FT_Done_Face( face ); 
47     }
48 
49 private:
50 
51     static lib_inited = false;
52 
53     static FT_Library ft;
54 
55     static FTFontRender[string] openRender;
56 
57     static this()
58     {
59         if( !lib_inited )
60         {
61             DerelictFT.load();
62             checkFTCall!FT_Init_FreeType( &ft );
63             lib_inited = true;
64         }
65     }
66 
67     static ~this() 
68     { 
69         if( lib_inited && FT_Done_FreeType !is null ) 
70             checkFTCall!FT_Done_FreeType( ft ); 
71     }
72 
73 protected:
74 
75     this( string fontname )
76     {
77         import std.file;
78 
79         if( !fontname.exists )
80             throw new FontException( "Couldn't open font '" ~ fontname ~ "': file not exist" );
81 
82         checkFTCall!FT_New_Face( ft, fontname.toStringz, 0, &face );
83 
84         checkFTCall!(FT_Select_Charmap,"unicode")( face, FT_ENCODING_UNICODE );
85     }
86 
87     FT_Face face;
88 
89     FontRenderParam param;
90 
91     static immutable ElemInfo imtype = ElemInfo( 1, DataType.UBYTE );
92 
93 public:
94 
95     static FontRender get( string fontname )
96     {
97         if( fontname !in openRender )
98             openRender[fontname] = new FTFontRender( fontname );
99         return openRender[fontname];
100     }
101 
102     void setParams( in FontRenderParam p ) { param = p; }
103 
104     GlyphImage renderChar( wchar ch )
105     {
106         checkFTCall!FT_Load_Char( face, cast(ulong)ch, FT_LOAD_RENDER );
107 
108         auto g = face.glyph;
109 
110         ivec2 sz;
111         ubyte[] img_data;
112 
113         if( ch == ' ' )
114         {
115             auto width = g.metrics.horiAdvance >> 6;
116             sz = ivec2( width, g.bitmap.rows );
117             img_data.length = sz.x * sz.y;
118         }
119         else
120         {
121             sz = ivec2( g.bitmap.width, g.bitmap.rows );
122             img_data = g.bitmap.buffer[0 .. sz.x*sz.y].dup;
123         }
124 
125         return GlyphImage(
126                    Glyph( ""w ~ ch,
127                        ivec2( g.bitmap_left, -g.bitmap_top ),
128                        ivec2( g.advance.x >> 6, g.advance.y >> 6 ),
129                        sz
130                    ),
131                    Image.external( sz, imtype, img_data )
132                );
133     }
134 
135     BitmapFont generateBitmapFont( wstring chars )
136     {
137         checkFTCall!FT_Set_Pixel_Sizes( face, 0, param.height );
138 
139         logger.Debug( param.height );
140 
141         BitmapFont res;
142         res.height = param.height;
143 
144         GlyphImage[wchar] glyphs;
145 
146         foreach( c; chars ) glyphs[c] = renderChar(c);
147 
148         uint maxh = 0;
149         uint width = 0;
150 
151         foreach( gi; glyphs )
152         {
153             if( gi.image.size[1] > maxh )
154                 maxh = cast(uint)(gi.image.size[1]);
155             width += gi.image.size[0];
156         }
157 
158         res.image = Image( ivec2(width,maxh), imtype );
159 
160         auto offset = ivec2(0);
161 
162         foreach( key, gi; glyphs )
163         {
164             res.info[key] = BitmapGlyph( gi.glyph, offset );
165             imCopy( res.image, offset, gi.image );
166             offset += ivec2( gi.image.size[0], 0 );
167         }
168 
169         return res;
170     }
171 }
172 
173 ///
174 enum FTError
175 {
176     NONE                          = 0x00, /// no error
177 
178     CANNOT_OPEN_RESOURCE          = 0x01, /// cannot open resource
179     UNKNOWN_FILE_FORMAT           = 0x02, /// unknown file format
180     INVALID_FILE_FORMAT           = 0x03, /// broken file
181     INVALID_VERSION               = 0x04, /// invalid FreeType version
182     LOWER_MODULE_VERSION          = 0x05, /// module version is too low
183     INVALID_ARGUMENT              = 0x06, /// invalid argument
184     UNIMPLEMENTED_FEATURE         = 0x07, /// unimplemented feature
185     INVALID_TABLE                 = 0x08, /// broken table
186     INVALID_OFFSET                = 0x09, /// broken offset within table
187     ARRAY_TOO_LARGE               = 0x0A, /// array allocation size too large
188 
189     /* GLYPH/CHARACTER ERRORS */
190 
191     INVALID_GLYPH_INDEX           = 0x10, /// invalid glyph index
192     INVALID_CHARACTER_CODE        = 0x11, /// invalid character code
193     INVALID_GLYPH_FORMAT          = 0x12, /// unsupported glyph image format
194     CANNOT_RENDER_GLYPH           = 0x13, /// cannot render this glyph format
195     INVALID_OUTLINE               = 0x14, /// invalid outline
196     INVALID_COMPOSITE             = 0x15, /// invalid composite glyph
197     TOO_MANY_HINTS                = 0x16, /// too many hints
198     INVALID_PIXEL_SIZE            = 0x17, /// invalid pixel size
199 
200     /* HANDLE ERRORS */
201 
202     INVALID_HANDLE                = 0x20, /// invalid object handle
203     INVALID_LIBRARY_HANDLE        = 0x21, /// invalid library handle
204     INVALID_DRIVER_HANDLE         = 0x22, /// invalid module handle
205     INVALID_FACE_HANDLE           = 0x23, /// invalid face handle
206     INVALID_SIZE_HANDLE           = 0x24, /// invalid size handle
207     INVALID_SLOT_HANDLE           = 0x25, /// invalid glyph slot handle
208     INVALID_CHARMAP_HANDLE        = 0x26, /// invalid charmap handle
209     INVALID_CACHE_HANDLE          = 0x27, /// invalid cache manager handle
210     INVALID_STREAM_HANDLE         = 0x28, /// invalid stream handle
211 
212     /* DRIVER ERRORS */
213 
214     TOO_MANY_DRIVERS              = 0x30, /// too many modules
215     TOO_MANY_EXTENSIONS           = 0x31, /// too many extensions
216 
217     /* MEMORY ERRORS */
218 
219     OUT_OF_MEMORY                 = 0x40, /// out of memory
220     UNLISTED_OBJECT               = 0x41, /// unlisted object
221 
222     /* STREAM ERRORS */
223 
224     CANNOT_OPEN_STREAM            = 0x51, /// cannot open stream
225     INVALID_STREAM_SEEK           = 0x52, /// invalid stream seek
226     INVALID_STREAM_SKIP           = 0x53, /// invalid stream skip
227     INVALID_STREAM_READ           = 0x54, /// invalid stream read
228     INVALID_STREAM_OPERATION      = 0x55, /// invalid stream operation
229     INVALID_FRAME_OPERATION       = 0x56, /// invalid frame operation
230     NESTED_FRAME_ACCESS           = 0x57, /// nested frame access
231     INVALID_FRAME_READ            = 0x58, /// invalid frame read
232 
233     /* RASTER ERRORS */
234 
235     RASTER_UNINITIALIZED          = 0x60, /// raster uninitialized
236     RASTER_CORRUPTED              = 0x61, /// raster corrupted
237     RASTER_OVERFLOW               = 0x62, /// raster overflow
238     RASTER_NEGATIVE_HEIGHT        = 0x63, /// negative height while rastering
239 
240     /* CACHE ERRORS */
241 
242     TOO_MANY_CACHES               = 0x70, /// too many registered caches
243 
244     /* TRUETYPE AND SFNT ERRORS */
245 
246     INVALID_OPCODE                = 0x80, /// invalid opcode
247     TOO_FEW_ARGUMENTS             = 0x81, /// too few arguments
248     STACK_OVERFLOW                = 0x82, /// stack overflow
249     CODE_OVERFLOW                 = 0x83, /// code overflow
250     BAD_ARGUMENT                  = 0x84, /// bad argument
251     DIVIDE_BY_ZERO                = 0x85, /// division by zero
252     INVALID_REFERENCE             = 0x86, /// invalid reference
253     DEBUG_OPCODE                  = 0x87, /// found debug opcode
254     ENDF_IN_EXEC_STREAM           = 0x88, /// found ENDF opcode in execution stream
255     NESTED_DEFS                   = 0x89, /// nested DEFS
256     INVALID_CODERANGE             = 0x8A, /// invalid code range
257     EXECUTION_TOO_LONG            = 0x8B, /// execution context too long
258     TOO_MANY_FUNCTION_DEFS        = 0x8C, /// too many function definitions
259     TOO_MANY_INSTRUCTION_DEFS     = 0x8D, /// too many instruction definitions
260     TABLE_MISSING                 = 0x8E, /// SFNT font table missing
261     HORIZ_HEADER_MISSING          = 0x8F, /// horizontal header (hhea) table missing
262     LOCATIONS_MISSING             = 0x90, /// locations (loca) table missing
263     NAME_TABLE_MISSING            = 0x91, /// name table missing
264     CMAP_TABLE_MISSING            = 0x92, /// character map (cmap) table missing
265     HMTX_TABLE_MISSING            = 0x93, /// horizontal metrics (hmtx) table missing
266     POST_TABLE_MISSING            = 0x94, /// PostScript (post) table missing
267     INVALID_HORIZ_METRICS         = 0x95, /// invalid horizontal metrics
268     INVALID_CHARMAP_FORMAT        = 0x96, /// invalid character map (cmap) format
269     INVALID_PPEM                  = 0x97, /// invalid ppem value
270     INVALID_VERT_METRICS          = 0x98, /// invalid vertical metrics
271     COULD_NOT_FIND_CONTEXT        = 0x99, /// could not find context
272     INVALID_POST_TABLE_FORMAT     = 0x9A, /// invalid PostScript (post) table format
273     INVALID_POST_TABLE            = 0x9B, /// invalid PostScript (post) table
274 
275     /* CFF, CID, AND TYPE 1 ERRORS */
276 
277     SYNTAX_ERROR                  = 0xA0, /// opcode syntax error
278     STACK_UNDERFLOW               = 0xA1, /// argument stack underflow
279     IGNORE                        = 0xA2, /// ignore
280     NO_UNICODE_GLYPH_NAME         = 0xA3, /// no Unicode glyph name found
281 
282 
283     /* BDF ERRORS */
284 
285     MISSING_STARTFONT_FIELD       = 0xB0, /// `STARTFONT' field missing
286     MISSING_FONT_FIELD            = 0xB1, /// `FONT' field missing
287     MISSING_SIZE_FIELD            = 0xB2, /// `SIZE' field missing
288     MISSING_FONTBOUNDINGBOX_FIELD = 0xB3, /// `FONTBOUNDINGBOX' field missing
289     MISSING_CHARS_FIELD           = 0xB4, /// `CHARS' field missing
290     MISSING_STARTCHAR_FIELD       = 0xB5, /// `STARTCHAR' field missing
291     MISSING_ENCODING_FIELD        = 0xB6, /// `ENCODING' field missing
292     MISSING_BBX_FIELD             = 0xB7, /// `BBX' field missing
293     BBX_TOO_BIG                   = 0xB8, /// `BBX' too big
294     CORRUPTED_FONT_HEADER         = 0xB9, /// Font header corrupted or missing fields
295     CORRUPTED_FONT_GLYPHS         = 0xBA, /// Font glyphs corrupted or missing fields
296 }