wdvi

network DVI viewer
Log | Files | Refs

ft.c (12450B)


      1 /*========================================================================*\
      2 
      3 Copyright (c) 2013  Paul Vojta
      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
      7 deal in the Software without restriction, including without limitation the
      8 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
      9 sell 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
     18 PAUL VOJTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
     19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     21 
     22 \*========================================================================*/
     23 
     24 /*
     25  *	FreeType (PostScript Type 1 and TrueType) font reading routines.
     26  *	Public routines are load_ft_font and read_ft_char.
     27  */
     28 
     29 #include "font.h"	/* struct font */
     30 #include "util.h"	/* alloc_bitmap(), ROUNDUP, struct avl */
     31 #include "xdvi.h"
     32 
     33 #include <ctype.h>	/* isspace(), isdigit() */
     34 #include <err.h>
     35 #include <math.h>
     36 
     37 static	FT_Library	library		= NULL;
     38 
     39 static	struct avl_enc	*enc_head	= NULL;
     40 
     41 /* From xc/lib/X11/PutImage.c */
     42 
     43 #if !WORDS_BIGENDIAN
     44 static const unsigned char reverse_byte[0x100] = {
     45 	0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
     46 	0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
     47 	0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
     48 	0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
     49 	0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
     50 	0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
     51 	0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
     52 	0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
     53 	0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
     54 	0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
     55 	0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
     56 	0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
     57 	0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
     58 	0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
     59 	0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
     60 	0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
     61 	0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
     62 	0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
     63 	0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
     64 	0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
     65 	0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
     66 	0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
     67 	0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
     68 	0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
     69 	0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
     70 	0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
     71 	0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
     72 	0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
     73 	0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
     74 	0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
     75 	0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
     76 	0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
     77 };
     78 #endif /* !WORDS_BIGENDIAN */
     79 
     80 
     81 /*
     82  *	FreeType I/O stream functions
     83  */
     84 
     85 static	unsigned long
     86 xdvi_stream_read(stream, offset, buffer, count)
     87 	FT_Stream	stream;
     88 	unsigned long	offset;
     89 	unsigned char	*buffer;
     90 	unsigned long	count;
     91 {
     92 	struct font	*fontp;
     93 
     94 	fontp = ((struct ftfont *) stream->descriptor.pointer)->first_size;
     95 	open_font_file(fontp);
     96 	fseek(fontp->file, offset, SEEK_SET);
     97 	return (unsigned long) fread(buffer, 1, count, fontp->file);
     98 }
     99 
    100 static	void
    101 xdvi_stream_close(stream)
    102 	FT_Stream	stream;
    103 {
    104 	/* do nothing */
    105 }
    106 
    107 
    108 /*
    109  *	Public routines
    110  */
    111 
    112 static	void
    113 read_ft_char(struct font *fontp, unsigned char ch)
    114 {
    115 	struct glyph	*g;
    116 	FT_Face		face;
    117 	FT_GlyphSlot	slot;
    118 	int		bytes_wide;
    119 	int		err;
    120 
    121 	if (debug & DBG_PK)
    122 	    printf("Loading FreeType char %d", ch);
    123 
    124 	/*
    125 	 * Load it unscaled first, so that we can get a more accurate
    126 	 * (non-hinted) value of the horizontal advance.
    127 	 */
    128 
    129 	g = &fontp->glyph[ch];
    130 	face = fontp->ft->face;
    131 	FT_Activate_Size(fontp->size);
    132 	err = FT_Load_Glyph(face, g->addr, FT_LOAD_NO_SCALE);
    133 	if (err != 0)
    134 	    errx(1, "FT_Load_Glyph: error = %d", err);
    135 
    136 	g->dvi_adv = face->glyph->metrics.horiAdvance * fontp->ft->expn
    137 	  * fontp->spsize / face->units_per_EM + 0.5;
    138 
    139 	/* Now do the real loading.  */
    140 
    141 	err = FT_Load_Glyph(face, g->addr, FT_LOAD_RENDER | FT_LOAD_MONOCHROME);
    142 	if (err != 0)
    143 	    errx(1, "FT_Load_Glyph: error = %d", err);
    144 
    145 	slot = face->glyph;
    146 	g->bitmap.w = slot->bitmap.width;
    147 	g->bitmap.h = slot->bitmap.rows;
    148 
    149 	if (debug & DBG_PK)
    150 	    printf(", size=%dx%d, dvi_adv=%ld\n", g->bitmap.w, g->bitmap.h,
    151 		g->dvi_adv);
    152 
    153 	alloc_bitmap(&g->bitmap);
    154 	bytes_wide = ROUNDUP((int) g->bitmap.w, BMBITS) * BMBYTES;
    155 
    156 	if (slot->bitmap.width > 0) {
    157 	    int			i;
    158 	    unsigned char	*p;
    159 	    BMUNIT		*q;
    160 
    161 	    p = slot->bitmap.buffer;
    162 	    q = (BMUNIT *) g->bitmap.bits;
    163 	    for (i = g->bitmap.h; i > 0; --i) {
    164 #if WORDS_BIGENDIAN
    165 		memcpy(q, p, (slot->bitmap.width + 7) / 8);
    166 #else
    167 		unsigned char	*p1	= p;
    168 		BMUNIT		*q1	= q;
    169 		BMUNIT		data	= 0;
    170 		int		shift	= 0;
    171 		int		j;
    172 
    173 		for (j = (slot->bitmap.width + 7) / 8; j > 0; --j) {
    174 		    if (shift >= BMBITS) {
    175 			*q1++ = data;
    176 			data = 0;
    177 			shift = 0;
    178 		    }
    179 		    data |= reverse_byte[*p1++] << shift;
    180 		    shift += 8;
    181 		}
    182 		*q1 = data;
    183 #endif
    184 		p += slot->bitmap.pitch;
    185 		*((char **) &q) += bytes_wide;
    186 	    }
    187 	}
    188 
    189 	/* The offset of (-1,-1) is from comparing bitmaps of 'R' and 'a'
    190 	   between cmr10.pfb and cmr10.300pk.  */
    191 	g->x = -1 - slot->bitmap_left;
    192 	g->y = slot->bitmap_top - 1;
    193 }
    194 
    195 /*
    196  *	Do the ScaleFont, etc. directives.
    197  */
    198 
    199 static Boolean
    200 set_transform(ftp, str)
    201 	struct ftfont	*ftp;
    202 	const char	*str;
    203 {
    204 	double	x	= 1.0;
    205 	double	y	= 0.0;
    206 
    207 	for (;;) {
    208 	    while (isspace(*str)) ++str;
    209 	    if (*str == '\0')
    210 		break;
    211 
    212 	    if (isdigit(*str) || *str == '.' || *str == '-') {
    213 		double	arg	= strtod(str, (char **) &str);
    214 
    215 		while (isspace(*str)) ++str;
    216 
    217 		if (memcmp(str, "ObliqueSlant", 12) == 0
    218 		  && (isspace(str[12]) || str[12] == '\0')) {
    219 		    arg = -tan(arg);
    220 		    str += 12;
    221 		    while (isspace(*str)) ++str;
    222 		}
    223 
    224 		if (memcmp(str, "SlantFont", 9) == 0
    225 		  && (isspace(str[9]) || str[9] == '\0')) {
    226 		    y += arg;
    227 		    str += 9;
    228 		}
    229 		else if (memcmp(str, "ExtendFont", 10) == 0
    230 		  && (isspace(str[10]) || str[10] == '\0')) {
    231 		    x *= arg;
    232 		    str += 10;
    233 		}
    234 		else return False;
    235 	    }
    236 	    else {	/* other characters; presume encoding name */
    237 		while (!isspace(*str) && *str != '\0') ++str;
    238 		while (isspace(*str)) ++str;
    239 		if (memcmp(str, "ReEncodeFont", 12) == 0
    240 		  && (isspace(str[12]) || str[12] == '\0'))
    241 		    str += 12;
    242 		else
    243 		    return False;
    244 	    }
    245 	}
    246 
    247 	if (x != 1.0 || y != 0.0) {
    248 	    FT_Matrix mat;
    249 
    250 	    mat.xx = (FT_Fixed) (x * 0x10000 + 0.5);
    251 	    mat.xy = (FT_Fixed) (y * 0x10000 + 0.5);
    252 	    mat.yx = 0;
    253 	    mat.yy = 0x10000;
    254 
    255 	    FT_Set_Transform(ftp->face, &mat, NULL);
    256 	    ftp->expn = x;
    257 	}
    258 
    259 	return True;
    260 }
    261 
    262 
    263 /*
    264  *	When a document uses many freetype fonts, it can take a while to read
    265  *	them all (due mostly to disk access issues).  So, we delay loading
    266  *	freetype fonts until they're needed.
    267  *
    268  *	Therefore, this routine is not called until it is time to render a
    269  *	character from the font (see set_ft_char() in dvi-draw.c).
    270  *
    271  *	It loads and sets up the font.
    272  */
    273 
    274 Boolean
    275 load_ft_font(fontp)
    276 	struct font	*fontp;
    277 {
    278 	struct ftfont	*ftp;
    279 	struct avl_t1	*t1p;
    280 	struct font	*fontp2;
    281 	FT_Face		face;
    282 	FT_Size		size;
    283 	int		err;
    284 	const char	*path;
    285 	FILE		*f;
    286 
    287 	ftp = fontp->ft;
    288 	t1p = ftp->t1;
    289 	fontp2 = ftp->first_size;
    290 
    291 	if (t1p->bad) {
    292 	    if (debug & DBG_OPEN)
    293 		printf("Font %s was flagged as bad; skipping (size %d)\n",
    294 		  fontp->fontname, (int) (fontp->fsize + 0.5));
    295 	    return False;
    296 	}
    297 
    298 	face = ftp->face;
    299 	if (face != NULL) {	/* if this face is already in use */
    300 	    err = FT_New_Size(face, &size);
    301 	    if (err != 0) {
    302 		warnx("Could not load FreeType font '%s' at %d",
    303 		  fontp->fontname, (int) (fontp->fsize + 0.5));
    304 		warnx("FreeType FT_New_Size error = %d.", err);
    305 		warnx("Will try pixel version instead.");
    306 		warnx("Please see the FreeType documentation for details about this.");
    307 		return False;
    308 	    }
    309 	}
    310 	else {
    311 	    FT_Open_Args args;
    312 
    313 	    f = open_t1_font(t1p, &path);
    314 	    if (f == NULL)
    315 		return False;
    316 
    317 	    if (library == NULL) {
    318 		err = FT_Init_FreeType(&library);
    319 		if (err != 0) {
    320 		    warnx("Could not initialize FreeType library");
    321 		    warnx("FreeType FT_Init_FreeType error = %d.", err);
    322 		    warnx("Turning off use of FreeType.");
    323 		    warnx("Please see the FreeType documentation for details about this.");
    324 		    free((char *) path);
    325 		    fclose(f);
    326 		    return False;
    327 		}
    328 	    }
    329 
    330 	    fontp2->filename = path;
    331 	    fontp2->file = f;
    332 
    333 	    fseek(f, 0L, SEEK_END);
    334 	    ftp->stream.size = ftell(f);
    335 	    fseek(f, 0L, SEEK_SET);	/* this might not be necessary */
    336 
    337 	    ftp->stream.descriptor.pointer = ftp;
    338 	    ftp->stream.read = xdvi_stream_read;
    339 	    ftp->stream.close = xdvi_stream_close;
    340 
    341 	    args.flags = FT_OPEN_STREAM;
    342 	    args.stream = &ftp->stream;
    343 	    err = FT_Open_Face(library, &args, 0, &face);
    344 	    if (err != 0) {
    345 		warnx("Could not load FreeType font '%s'", fontp->fontname);
    346 		warnx("FreeType FT_Open_Face error = %d.", err);
    347 		warnx("Will try pixel version instead.");
    348 		warnx("Please see the FreeType documentation for details about this.");
    349 		free((char *) path);
    350 		fclose(f);
    351 		fontp2->file = NULL;
    352 		fontp2->filename = NULL;
    353 		return False;
    354 	    }
    355 	    ftp->face = face;
    356 
    357 	    if (!FT_IS_SCALABLE(face)) {
    358 		warnx("Font '%s' is not scalable.", fontp->fontname);
    359 		FT_Done_Face(face);
    360 		free((char *) path);
    361 		fclose(f);
    362 		fontp2->file = NULL;
    363 		fontp2->filename = NULL;
    364 		return False;
    365 	    }
    366 
    367 	    ftp->expn = 1.0;
    368 	    if (t1p->addinfo != NULL)
    369 		if (!set_transform(ftp, t1p->addinfo)) {
    370 		    FT_Done_Face(face);
    371 		    free((char *) path);
    372 		    fclose(f);
    373 		    fontp2->file = NULL;
    374 		    fontp2->filename = NULL;
    375 		    return False;
    376 		}
    377 
    378 	    if (face->charmap == NULL) {
    379 		if (face->num_charmaps > 0) {
    380 		    if ((debug & DBG_PK) && face->num_charmaps > 1)
    381 			printf("Choosing the first of %d charmaps.\n",
    382 			  face->num_charmaps);
    383 		}
    384 		FT_Set_Charmap(face, face->charmaps[0]);
    385 	    }
    386 	    else if (face->charmap->encoding == ft_encoding_unicode
    387 	      && FT_Select_Charmap(face, ft_encoding_adobe_custom) != 0
    388 	      && FT_Select_Charmap(face, ft_encoding_adobe_standard) != 0
    389 	      && FT_Select_Charmap(face, ft_encoding_adobe_expert) != 0) {
    390 		if (debug & DBG_PK)
    391 		    puts("Using unicode charmap for font.");
    392 		warnx("Using unicode charmap");	/* ||| */
    393 	    }
    394 
    395 	    ftp->enc = NULL;
    396 	    if (t1p->encname != NULL) {	/* if there's an encoding */
    397 		struct avl_enc	*encp;
    398 
    399 		encp = (struct avl_enc *) avladd(t1p->encname,
    400 		  strlen(t1p->encname),
    401 		  (struct avl **) &enc_head, sizeof *encp);
    402 		if (encp->key == t1p->encname)	/* if new record */
    403 		    read_encoding(encp);
    404 		if (encp->valid)
    405 		    ftp->enc = encp;
    406 	    }
    407 
    408 	    size = face->size;
    409 	}
    410 
    411 	/* Get character indices (store them in addr) */
    412 
    413 	if (debug & DBG_PK)
    414 	    printf("Character indices for %s:\n", fontp->fontname);
    415 
    416 	FT_Activate_Size(size);
    417 
    418 	err = FT_Set_Char_Size(face,
    419 	  (int) (fontp->spsize * (72<<6) / (pixels_per_inch << 16) + 0.5), 0,
    420 	  pixels_per_inch, pixels_per_inch);
    421 	if (err != 0)
    422 	    errx(1, "FT_Set_Char_Size: error = %d", err);
    423 
    424 	/* Look for already-computed character indices */
    425 	for (fontp2 = ftp->first_size;; fontp2 = fontp2->next_size) {
    426 	    int i;
    427 
    428 	    if (fontp2 == NULL) {	/* if not found */
    429 		struct avl_enc *encp = ftp->enc;
    430 
    431 		for (i = 0; i < 256; ++i) {
    432 		    int ci;
    433 
    434 		    if (encp == NULL)
    435 			ci = FT_Get_Char_Index(face, i);
    436 		    else {
    437 			const char *glyphname = encp->vec[i];
    438 
    439 			if (glyphname == NULL)
    440 			    ci = 0;
    441 			else
    442 			    ci = FT_Get_Name_Index(face,
    443 			      (FT_String *) glyphname);
    444 		    }
    445 
    446 		    fontp->glyph[i].addr = ci;
    447 		    if (debug & DBG_PK)
    448 			printf("%3d->%3d%s", i, ci, (i + 1) % 8 ? "  " : "\n");
    449 		}
    450 		break;
    451 	    }
    452 	    if (fontp2->size != NULL) {		/* found the information */
    453 		for (i = 0; i < 256; ++i)
    454 		    fontp->glyph[i].addr = fontp2->glyph[i].addr;
    455 		break;
    456 	    }
    457 	}
    458 
    459 	fontp->size = size;	/* it needed to be NULL in the above loop */
    460 	fontp->read_char = read_ft_char;
    461 
    462 	return True;
    463 }