main font.c
  1/* wld: font.c
  2 *
  3 * Copyright (c) 2013, 2014 Michael Forney
  4 *
  5 * Permission is hereby granted, free of charge, to any person obtaining a copy
  6 * of this software and associated documentation files (the "Software"), to deal
  7 * in the Software without restriction, including without limitation the rights
  8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 * copies of the Software, and to permit persons to whom the Software is
 10 * furnished to do so, subject to the following conditions:
 11 *
 12 * The above copyright notice and this permission notice shall be included in
 13 * all copies or substantial portions of the Software.
 14 *
 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 21 * SOFTWARE.
 22 */
 23
 24#include "wld-private.h"
 25
 26#include <fontconfig/fcfreetype.h>
 27
 28EXPORT
 29struct wld_font_context *
 30wld_font_create_context()
 31{
 32	struct wld_font_context *context;
 33
 34	context = malloc(sizeof *context);
 35
 36	if (!context)
 37		goto error0;
 38
 39	if (FT_Init_FreeType(&context->library) != 0) {
 40		DEBUG("Failed to initialize FreeType library\n");
 41
 42		goto error1;
 43	}
 44
 45	return context;
 46
 47error1:
 48	free(context);
 49error0:
 50	return NULL;
 51}
 52
 53EXPORT
 54void
 55wld_font_destroy_context(struct wld_font_context *context)
 56{
 57	FT_Done_FreeType(context->library);
 58	free(context);
 59}
 60
 61EXPORT
 62struct wld_font *
 63wld_font_open_name(struct wld_font_context *context, const char *name)
 64{
 65	FcPattern *pattern, *match;
 66	FcResult result;
 67
 68	DEBUG("Opening font with name: %s\n", name);
 69
 70	pattern = FcNameParse((const FcChar8 *)name);
 71	FcConfigSubstitute(NULL, pattern, FcMatchPattern);
 72	FcDefaultSubstitute(pattern);
 73
 74	match = FcFontMatch(NULL, pattern, &result);
 75
 76	if (!match)
 77		return NULL;
 78
 79	return wld_font_open_pattern(context, match);
 80}
 81
 82EXPORT
 83struct wld_font *
 84wld_font_open_pattern(struct wld_font_context *context, FcPattern *match)
 85{
 86	char *filename;
 87	struct font *font;
 88	FcResult result;
 89	double pixel_size, aspect;
 90
 91	font = malloc(sizeof *font);
 92
 93	if (!font)
 94		goto error0;
 95
 96	font->context = context;
 97
 98	result = FcPatternGetString(match, FC_FILE, 0, (FcChar8 **)&filename);
 99
100	if (result == FcResultMatch) {
101		FT_Error error;
102
103		DEBUG("Loading font file: %s\n", filename);
104
105		error = FT_New_Face(context->library, filename, 0, &font->face);
106
107		if (error == 0)
108			goto load_face;
109	}
110
111	result = FcPatternGetFTFace(match, FC_FT_FACE, 0, &font->face);
112
113	if (result != FcResultMatch) {
114		DEBUG("Couldn't determine font filename or FreeType face\n");
115		goto error1;
116	}
117
118load_face:
119	result = FcPatternGetDouble(match, FC_PIXEL_SIZE, 0, &pixel_size);
120
121	result = FcPatternGetDouble(match, FC_ASPECT, 0, &aspect);
122
123	if (result == FcResultNoMatch)
124		aspect = 1.0;
125
126	if (font->face->face_flags & FT_FACE_FLAG_SCALABLE) {
127		FT_F26Dot6 width, height;
128
129		width = ((unsigned int)pixel_size) << 6;
130		height = ((unsigned int)(pixel_size * aspect)) << 6;
131
132		FT_Set_Char_Size(font->face, width, height, 0, 0);
133	} else {
134		FT_Set_Pixel_Sizes(font->face, (unsigned int)pixel_size,
135		                   (unsigned int)(pixel_size * aspect));
136	}
137
138	font->base.ascent = font->face->size->metrics.ascender >> 6;
139	font->base.descent = -font->face->size->metrics.descender >> 6;
140	font->base.height = font->base.ascent + font->base.descent;
141	font->base.max_advance = font->face->size->metrics.max_advance >> 6;
142
143	font->glyphs = calloc(font->face->num_glyphs, sizeof(struct glyph *));
144
145	return &font->base;
146
147error1:
148	free(font);
149error0:
150	return NULL;
151}
152
153EXPORT
154void
155wld_font_close(struct wld_font *font_base)
156{
157	struct font *font = (void *)font_base;
158
159	FT_Done_Face(font->face);
160	free(font);
161}
162
163bool
164font_ensure_glyph(struct font *font, FT_UInt glyph_index)
165{
166	if (glyph_index) {
167		if (!font->glyphs[glyph_index]) {
168			struct glyph *glyph;
169
170			glyph = malloc(sizeof *glyph);
171
172			if (!glyph)
173				return false;
174
175			FT_Load_Glyph(font->face, glyph_index,
176			              FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL);
177
178			FT_Bitmap_New(&glyph->bitmap);
179
180			FT_Bitmap_Copy(font->context->library,
181			               &font->face->glyph->bitmap, &glyph->bitmap);
182
183			glyph->advance = font->face->glyph->metrics.horiAdvance >> 6;
184			glyph->x = font->face->glyph->bitmap_left;
185			glyph->y = -font->face->glyph->bitmap_top;
186
187			font->glyphs[glyph_index] = glyph;
188		}
189
190		return true;
191	}
192
193	return false;
194}
195
196EXPORT
197bool
198wld_font_ensure_char(struct wld_font *font_base, uint32_t character)
199{
200	struct font *font = (void *)font_base;
201	FT_UInt glyph_index;
202
203	glyph_index = FT_Get_Char_Index(font->face, character);
204
205	return font_ensure_glyph(font, glyph_index);
206}
207
208EXPORT
209void
210wld_font_text_extents_n(struct wld_font *font_base,
211                        const char *text, int32_t length,
212                        struct wld_extents *extents)
213{
214	struct font *font = (void *)font_base;
215	int ret;
216	uint32_t c;
217	FT_UInt glyph_index;
218
219	extents->advance = 0;
220
221	while ((ret = FcUtf8ToUcs4((FcChar8 *)text, &c, length)) > 0 && c != '\0') {
222		length -= ret;
223		text += ret;
224		glyph_index = FT_Get_Char_Index(font->face, c);
225
226		if (!font_ensure_glyph(font, glyph_index))
227			continue;
228
229		extents->advance += font->glyphs[glyph_index]->advance;
230	}
231}