mirror of
https://github.com/michaelrsweet/pdfio.git
synced 2024-12-27 21:58:22 +01:00
2093 lines
57 KiB
C
2093 lines
57 KiB
C
//
|
|
// Implementation file for TTF library
|
|
//
|
|
// https://github.com/michaelrsweet/ttf
|
|
//
|
|
// Copyright © 2018-2024 by Michael R Sweet.
|
|
//
|
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
|
// information.
|
|
//
|
|
|
|
#ifdef _WIN32
|
|
# define _CRT_SECURE_NO_WARNINGS
|
|
#endif // _WIN32
|
|
|
|
#include "ttf.h"
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifdef _WIN32
|
|
# include <io.h>
|
|
# include <direct.h>
|
|
|
|
//
|
|
// Microsoft renames the POSIX functions to _name, and introduces
|
|
// a broken compatibility layer using the original names. As a result,
|
|
// random crashes can occur when, for example, strdup() allocates memory
|
|
// from a different heap than used by malloc() and free().
|
|
//
|
|
// To avoid moronic problems like this, we #define the POSIX function
|
|
// names to the corresponding non-standard Microsoft names.
|
|
//
|
|
|
|
# define access _access
|
|
# define close _close
|
|
# define fileno _fileno
|
|
# define lseek _lseek
|
|
# define mkdir(d,p) _mkdir(d)
|
|
# define open _open
|
|
# define read _read
|
|
# define rmdir _rmdir
|
|
# define snprintf _snprintf
|
|
# define strdup _strdup
|
|
# define unlink _unlink
|
|
# define vsnprintf _vsnprintf
|
|
# define write _write
|
|
|
|
//
|
|
// Map various parameters for POSIX...
|
|
//
|
|
|
|
# define F_OK 00
|
|
# define W_OK 02
|
|
# define R_OK 04
|
|
# define O_BINARY _O_BINARY
|
|
# define O_RDONLY _O_RDONLY
|
|
# define O_WRONLY _O_WRONLY
|
|
# define O_CREAT _O_CREAT
|
|
# define O_TRUNC _O_TRUNC
|
|
|
|
typedef __int64 ssize_t; // POSIX type not present on Windows... @private@
|
|
|
|
#else
|
|
# include <unistd.h>
|
|
# define O_BINARY 0
|
|
#endif // _WIN32
|
|
|
|
|
|
//
|
|
// DEBUG is typically defined for debug builds. TTF_DEBUG maps to fprintf when
|
|
// DEBUG is defined and is a no-op otherwise...
|
|
//
|
|
|
|
#ifdef DEBUG
|
|
# define TTF_DEBUG(...) fprintf(stderr, __VA_ARGS__)
|
|
#else
|
|
# define TTF_DEBUG(...)
|
|
#endif // DEBUG
|
|
|
|
|
|
//
|
|
// TTF_FORMAT_ARGS tells the compiler to validate printf-style format
|
|
// arguments, producing warnings when there are issues...
|
|
//
|
|
|
|
#if defined(__has_extension) || defined(__GNUC__)
|
|
# define TTF_FORMAT_ARGS(a,b) __attribute__ ((__format__(__printf__, a, b)))
|
|
#else
|
|
# define TTF_FORMAT_ARGS(a,b)
|
|
#endif // __has_extension || __GNUC__
|
|
|
|
|
|
//
|
|
// Constants...
|
|
//
|
|
|
|
#define TTF_FONT_MAX_CHAR 262144 // Maximum number of character values
|
|
#define TTF_FONT_MAX_GROUPS 65536 // Maximum number of sub-groups
|
|
#define TTF_FONT_MAX_NAMES 16777216// Maximum size of names table we support
|
|
|
|
|
|
//
|
|
// TTF/OFF tag constants...
|
|
//
|
|
|
|
#define TTF_OFF_cmap 0x636d6170 // Character to glyph mapping
|
|
#define TTF_OFF_head 0x68656164 // Font header
|
|
#define TTF_OFF_hhea 0x68686561 // Horizontal header
|
|
#define TTF_OFF_hmtx 0x686d7478 // Horizontal metrics
|
|
#define TTF_OFF_maxp 0x6d617870 // Maximum profile
|
|
#define TTF_OFF_name 0x6e616d65 // Naming table
|
|
#define TTF_OFF_OS_2 0x4f532f32 // OS/2 and Windows specific metrics
|
|
#define TTF_OFF_post 0x706f7374 // PostScript information
|
|
|
|
#define TTF_OFF_Unicode 0 // Unicode platform ID
|
|
|
|
#define TTF_OFF_Mac 1 // Macintosh platform ID
|
|
#define TTF_OFF_Mac_Roman 0 // Macintosh Roman encoding ID
|
|
#define TTF_OFF_Mac_USEnglish 0 // Macintosh US English language ID
|
|
|
|
#define TTF_OFF_Windows 3 // Windows platform ID
|
|
#define TTF_OFF_Windows_English 9 // Windows English language ID base
|
|
#define TTF_OFF_Windows_UCS2 1 // Windows UCS-2 encoding
|
|
#define TTF_OFF_Windows_UCS4 10 // Windows UCS-4 encoding
|
|
|
|
#define TTF_OFF_Copyright 0 // Copyright naming string
|
|
#define TTF_OFF_FontFamily 1 // Font family naming string ("Arial")
|
|
#define TTF_OFF_FontSubfamily 2 // Font sub-family naming string ("Bold")
|
|
#define TTF_OFF_FontFullName 4 // Font full name ("Arial Bold")
|
|
#define TTF_OFF_FontVersion 5 // Font version number
|
|
#define TTF_OFF_PostScriptName 6 // Font PostScript name
|
|
|
|
|
|
//
|
|
// Local types...
|
|
//
|
|
|
|
typedef struct _ttf_metric_s //*** Font metric information ****/
|
|
{
|
|
short width, // Advance width
|
|
left_bearing; // Left side bearing
|
|
} _ttf_metric_t;
|
|
|
|
typedef struct _ttf_off_dir_s // OFF/TTF directory entry
|
|
{
|
|
unsigned tag; // Table identifier
|
|
unsigned checksum; // Checksum of table
|
|
unsigned offset; // Offset from the beginning of the file
|
|
unsigned length; // Length
|
|
} _ttf_off_dir_t;
|
|
|
|
typedef struct _ttf_off_table_s // OFF/TTF offset table
|
|
{
|
|
int num_entries; // Number of table entries
|
|
_ttf_off_dir_t *entries; // Table entries
|
|
} _ttf_off_table_t;
|
|
|
|
typedef struct _ttf_off_name_s // OFF/TTF name string
|
|
{
|
|
unsigned short platform_id, // Platform identifier
|
|
encoding_id, // Encoding identifier
|
|
language_id, // Language identifier
|
|
name_id, // Name identifier
|
|
length, // Length of string
|
|
offset; // Offset from start of storage area
|
|
} _ttf_off_name_t;
|
|
|
|
typedef struct _ttf_off_names_s // OFF/TTF naming table
|
|
{
|
|
int num_names; // Number of names
|
|
_ttf_off_name_t *names; // Names
|
|
unsigned char *storage; // Storage area
|
|
unsigned storage_size; // Size of storage area
|
|
} _ttf_off_names_t;
|
|
|
|
struct _ttf_s
|
|
{
|
|
int fd; // File descriptor
|
|
size_t idx; // Font number in file
|
|
ttf_err_cb_t err_cb; // Error callback, if any
|
|
void *err_data; // Error callback data
|
|
_ttf_off_table_t table; // Offset table
|
|
_ttf_off_names_t names; // Names
|
|
size_t num_fonts; // Number of fonts in this file
|
|
char *copyright; // Copyright string
|
|
char *family; // Font family string
|
|
char *postscript_name; // PostScript name string
|
|
char *version; // Font version string
|
|
bool is_fixed; // Is this a fixed-width font?
|
|
int max_char, // Last character in font
|
|
min_char; // First character in font
|
|
size_t num_cmap; // Number of entries in glyph map
|
|
int *cmap; // Unicode character to glyph map
|
|
_ttf_metric_t *widths[TTF_FONT_MAX_CHAR / 256];
|
|
// Character metrics (sparse array)
|
|
float units; // Width units
|
|
short ascent, // Maximum ascent above baseline
|
|
descent, // Maximum descent below baseline
|
|
cap_height, // "A" height
|
|
x_height, // "x" height
|
|
x_max, // Bounding box
|
|
x_min,
|
|
y_max,
|
|
y_min,
|
|
weight; // Font weight
|
|
float italic_angle; // Angle of italic text
|
|
ttf_stretch_t stretch; // Font stretch value
|
|
ttf_style_t style; // Font style
|
|
};
|
|
|
|
typedef struct _ttf_off_cmap4_s // Format 4 cmap table
|
|
{
|
|
unsigned short startCode, // First character
|
|
endCode, // Last character
|
|
idRangeOffset; // Offset for range (modulo 65536)
|
|
short idDelta; // Delta for range (modulo 65536)
|
|
} _ttf_off_cmap4_t;
|
|
|
|
typedef struct _ttf_off_cmap12_s // Format 12 cmap table
|
|
{
|
|
unsigned startCharCode, // First character
|
|
endCharCode, // Last character
|
|
startGlyphID; // Glyph index for the first character
|
|
} _ttf_off_cmap12_t;
|
|
|
|
typedef struct _ttf_off_cmap13_s // Format 13 cmap table
|
|
{
|
|
unsigned startCharCode, // First character
|
|
endCharCode, // Last character
|
|
glyphID; // Glyph index for all characters
|
|
} _ttf_off_cmap13_t;
|
|
|
|
typedef struct _ttf_off_head_s // Font header
|
|
{
|
|
unsigned short unitsPerEm; // Units for widths/coordinates
|
|
short xMin, // Bounding box of all glyphs
|
|
yMin,
|
|
xMax,
|
|
yMax;
|
|
unsigned short macStyle; // Mac style bits
|
|
} _ttf_off_head_t;
|
|
|
|
#define TTF_OFF_macStyle_Bold 0x01
|
|
#define TTF_OFF_macStyle_Italic 0x02
|
|
#define TTF_OFF_macStyle_Underline 0x04
|
|
#define TTF_OFF_macStyle_Outline 0x08
|
|
#define TTF_OFF_macStyle_Shadow 0x10
|
|
#define TTF_OFF_macStyle_Condensed 0x20
|
|
#define TTF_OFF_macStyle_Extended 0x40
|
|
|
|
typedef struct _ttf_off_hhea_s // Horizontal header
|
|
{
|
|
short ascender, // Ascender
|
|
descender; // Descender
|
|
unsigned short numberOfHMetrics; // Number of horizontal metrics
|
|
} _ttf_off_hhea_t;
|
|
|
|
typedef struct _ttf_off_os_2_s // OS/2 information
|
|
{
|
|
unsigned short usWeightClass, // Font weight
|
|
usWidthClass, // Font weight
|
|
fsType; // Type bits
|
|
short sTypoAscender, // Ascender
|
|
sTypoDescender, // Descender
|
|
sxHeight, // xHeight
|
|
sCapHeight; // CapHeight
|
|
} _ttf_off_os_2_t;
|
|
|
|
typedef struct _ttf_off_post_s // PostScript information
|
|
{
|
|
float italicAngle; // Italic angle
|
|
unsigned isFixedPitch; // Fixed-width font?
|
|
} _ttf_off_post_t;
|
|
|
|
|
|
//
|
|
// Local functions...
|
|
//
|
|
|
|
static char *copy_name(ttf_t *font, unsigned name_id);
|
|
static void errorf(ttf_t *font, const char *message, ...) TTF_FORMAT_ARGS(2,3);
|
|
static bool read_cmap(ttf_t *font);
|
|
static bool read_head(ttf_t *font, _ttf_off_head_t *head);
|
|
static bool read_hhea(ttf_t *font, _ttf_off_hhea_t *hhea);
|
|
static _ttf_metric_t *read_hmtx(ttf_t *font, _ttf_off_hhea_t *hhea);
|
|
static int read_maxp(ttf_t *font);
|
|
static bool read_names(ttf_t *font);
|
|
static bool read_os_2(ttf_t *font, _ttf_off_os_2_t *os_2);
|
|
static bool read_post(ttf_t *font, _ttf_off_post_t *post);
|
|
static int read_short(ttf_t *font);
|
|
static bool read_table(ttf_t *font);
|
|
static unsigned read_ulong(ttf_t *font);
|
|
static int read_ushort(ttf_t *font);
|
|
static unsigned seek_table(ttf_t *font, unsigned tag, unsigned offset, bool required);
|
|
|
|
|
|
//
|
|
// 'ttfCreate()' - Create a new font object for the named font file.
|
|
//
|
|
// This function creates a new font object for the named TrueType or OpenType
|
|
// font file or collection. The "filename" argument specifies the name of the
|
|
// file to read.
|
|
//
|
|
// The "idx" argument specifies the font to load from a collection - the first
|
|
// font is number `0`. Once created, you can call the @link ttfGetNumFonts@
|
|
// function to determine whether the loaded font file is a collection with more
|
|
// than one font.
|
|
//
|
|
// The "err_cb" and "err_data" arguments specify a callback function and data
|
|
// pointer for receiving error messages. If `NULL`, errors are sent to the
|
|
// `stderr` file. The callback function receives the data pointer and a text
|
|
// message string, for example:
|
|
//
|
|
// ```
|
|
// void my_err_cb(void *err_data, const char *message)
|
|
// {
|
|
// fprintf(stderr, "ERROR: %s\n", message);
|
|
// }
|
|
// ```
|
|
//
|
|
|
|
ttf_t * // O - New font object
|
|
ttfCreate(const char *filename, // I - Filename
|
|
size_t idx, // I - Font number to create in collection (0-based)
|
|
ttf_err_cb_t err_cb, // I - Error callback or `NULL` to log to stderr
|
|
void *err_data) // I - Error callback data
|
|
{
|
|
ttf_t *font = NULL; // New font object
|
|
size_t i; // Looping var
|
|
_ttf_metric_t *widths = NULL; // Glyph metrics
|
|
_ttf_off_head_t head; // head table
|
|
_ttf_off_hhea_t hhea; // hhea table
|
|
_ttf_off_os_2_t os_2; // OS/2 table
|
|
_ttf_off_post_t post; // PostScript table
|
|
|
|
|
|
TTF_DEBUG("ttfCreate(filename=\"%s\", idx=%u, err_cb=%p, err_data=%p)\n", filename, (unsigned)idx, err_cb, err_data);
|
|
|
|
// Range check input..
|
|
if (!filename)
|
|
{
|
|
errno = EINVAL;
|
|
return (NULL);
|
|
}
|
|
|
|
// Allocate memory...
|
|
if ((font = (ttf_t *)calloc(1, sizeof(ttf_t))) == NULL)
|
|
return (NULL);
|
|
|
|
font->idx = idx;
|
|
font->err_cb = err_cb;
|
|
font->err_data = err_data;
|
|
|
|
// Open the font file...
|
|
if ((font->fd = open(filename, O_RDONLY | O_BINARY)) < 0)
|
|
{
|
|
errorf(font, "Unable to open '%s': %s", filename, strerror(errno));
|
|
goto error;
|
|
}
|
|
|
|
TTF_DEBUG("ttfCreate: fd=%d\n", font->fd);
|
|
|
|
// Read the table of contents and the identifying names...
|
|
if (!read_table(font))
|
|
goto error;
|
|
|
|
TTF_DEBUG("ttfCreate: num_entries=%d\n", font->table.num_entries);
|
|
|
|
if (!read_names(font))
|
|
goto error;
|
|
|
|
TTF_DEBUG("ttfCreate: num_names=%d\n", font->names.num_names);
|
|
|
|
// Copy key font meta data strings...
|
|
font->copyright = copy_name(font, TTF_OFF_Copyright);
|
|
font->family = copy_name(font, TTF_OFF_FontFamily);
|
|
font->postscript_name = copy_name(font, TTF_OFF_PostScriptName);
|
|
font->version = copy_name(font, TTF_OFF_FontVersion);
|
|
|
|
if (read_post(font, &post))
|
|
{
|
|
font->italic_angle = post.italicAngle;
|
|
font->is_fixed = post.isFixedPitch != 0;
|
|
}
|
|
|
|
TTF_DEBUG("ttfCreate: copyright=\"%s\"\n", font->copyright);
|
|
TTF_DEBUG("ttfCreate: family=\"%s\"\n", font->family);
|
|
TTF_DEBUG("ttfCreate: postscript_name=\"%s\"\n", font->postscript_name);
|
|
TTF_DEBUG("ttfCreate: version=\"%s\"\n", font->version);
|
|
TTF_DEBUG("ttfCreate: italic_angle=%g\n", font->italic_angle);
|
|
TTF_DEBUG("ttfCreate: is_fixed=%s\n", font->is_fixed ? "true" : "false");
|
|
|
|
if (!read_cmap(font))
|
|
goto error;
|
|
|
|
if (!read_head(font, &head))
|
|
goto error;
|
|
|
|
font->units = (float)head.unitsPerEm;
|
|
font->x_max = head.xMax;
|
|
font->x_min = head.xMin;
|
|
font->y_max = head.yMax;
|
|
font->y_min = head.yMin;
|
|
|
|
if (head.macStyle & TTF_OFF_macStyle_Italic)
|
|
{
|
|
if (font->postscript_name && strstr(font->postscript_name, "Oblique"))
|
|
font->style = TTF_STYLE_OBLIQUE;
|
|
else
|
|
font->style = TTF_STYLE_ITALIC;
|
|
}
|
|
else
|
|
font->style = TTF_STYLE_NORMAL;
|
|
|
|
if (!read_hhea(font, &hhea))
|
|
goto error;
|
|
|
|
font->ascent = hhea.ascender;
|
|
font->descent = hhea.descender;
|
|
|
|
if (read_maxp(font) < 0)
|
|
goto error;
|
|
|
|
if (hhea.numberOfHMetrics > 0)
|
|
{
|
|
if ((widths = read_hmtx(font, &hhea)) == NULL)
|
|
goto error;
|
|
}
|
|
else
|
|
{
|
|
errorf(font, "Number of horizontal metrics is 0.");
|
|
goto error;
|
|
}
|
|
|
|
if (read_os_2(font, &os_2))
|
|
{
|
|
// Copy key values from OS/2 table...
|
|
static const ttf_stretch_t stretches[] =
|
|
{
|
|
TTF_STRETCH_ULTRA_CONDENSED, // ultra-condensed
|
|
TTF_STRETCH_EXTRA_CONDENSED, // extra-condensed
|
|
TTF_STRETCH_CONDENSED, // condensed
|
|
TTF_STRETCH_SEMI_CONDENSED, // semi-condensed
|
|
TTF_STRETCH_NORMAL, // normal
|
|
TTF_STRETCH_SEMI_EXPANDED, // semi-expanded
|
|
TTF_STRETCH_EXPANDED, // expanded
|
|
TTF_STRETCH_EXTRA_EXPANDED, // extra-expanded
|
|
TTF_STRETCH_ULTRA_EXPANDED // ultra-expanded
|
|
};
|
|
|
|
if (os_2.usWidthClass >= 1 && os_2.usWidthClass <= (int)(sizeof(stretches) / sizeof(stretches[0])))
|
|
font->stretch = stretches[os_2.usWidthClass - 1];
|
|
|
|
font->weight = (short)os_2.usWeightClass;
|
|
font->cap_height = os_2.sCapHeight;
|
|
font->x_height = os_2.sxHeight;
|
|
}
|
|
else
|
|
{
|
|
// Default key values since there isn't an OS/2 table...
|
|
TTF_DEBUG("ttfCreate: Unable to read OS/2 table.\n");
|
|
|
|
font->weight = 400;
|
|
}
|
|
|
|
if (font->cap_height == 0)
|
|
font->cap_height = font->ascent;
|
|
|
|
if (font->x_height == 0)
|
|
font->x_height = 3 * font->ascent / 5;
|
|
|
|
// Build a sparse glyph widths table...
|
|
font->min_char = -1;
|
|
|
|
for (i = 0; i < font->num_cmap; i ++)
|
|
{
|
|
if (font->cmap[i] >= 0)
|
|
{
|
|
int bin = (int)i / 256, // Sub-array bin
|
|
glyph = font->cmap[i]; // Glyph index
|
|
|
|
// Update min/max...
|
|
if (font->min_char < 0)
|
|
font->min_char = (int)i;
|
|
|
|
font->max_char = (int)i;
|
|
|
|
// Allocate a sub-array as needed...
|
|
if (!font->widths[bin])
|
|
font->widths[bin] = (_ttf_metric_t *)calloc(256, sizeof(_ttf_metric_t));
|
|
|
|
// Copy the width of the specified glyph or the last one if we are past
|
|
// the end of the table...
|
|
if (glyph >= hhea.numberOfHMetrics)
|
|
font->widths[bin][i & 255] = widths[hhea.numberOfHMetrics - 1];
|
|
else
|
|
font->widths[bin][i & 255] = widths[glyph];
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (i >= ' ' && i < 127 && font->widths[0])
|
|
TTF_DEBUG("ttfCreate: width['%c']=%d(%d)\n", (char)i, font->widths[0][i].width, font->widths[0][i].left_bearing);
|
|
#endif // DEBUG
|
|
}
|
|
|
|
// Cleanup and return the font...
|
|
free(widths);
|
|
|
|
return (font);
|
|
|
|
// If we get here something bad happened...
|
|
error:
|
|
|
|
free(widths);
|
|
ttfDelete(font);
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfDelete()' - Free all memory used for a font family object.
|
|
//
|
|
|
|
void
|
|
ttfDelete(ttf_t *font) // I - Font
|
|
{
|
|
int i; // Looping var
|
|
|
|
|
|
// Range check input...
|
|
if (!font)
|
|
return;
|
|
|
|
// Close the font file...
|
|
if (font->fd >= 0)
|
|
close(font->fd);
|
|
|
|
// Free all memory used...
|
|
free(font->copyright);
|
|
free(font->family);
|
|
free(font->postscript_name);
|
|
free(font->version);
|
|
|
|
free(font->table.entries);
|
|
free(font->names.names);
|
|
free(font->names.storage);
|
|
|
|
free(font->cmap);
|
|
|
|
for (i = 0; i < 256; i ++)
|
|
free(font->widths[i]);
|
|
|
|
free(font);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetAscent()' - Get the maximum height of non-accented characters.
|
|
//
|
|
|
|
int // O - Ascent in 1000ths
|
|
ttfGetAscent(ttf_t *font) // I - Font
|
|
{
|
|
return (font ? (int)(1000 * font->ascent / font->units) : 0);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetBounds()' - Get the bounds of all characters in a font.
|
|
//
|
|
// This function gets the bounds of all characters in a font. The "bounds"
|
|
// argument is a pointer to a `ttf_rect_t` structure that will be filled with
|
|
// the limits for characters in the font scaled to a 1000x1000 unit square.
|
|
//
|
|
|
|
ttf_rect_t * // O - Bounds or `NULL` on error
|
|
ttfGetBounds(ttf_t *font, // I - Font
|
|
ttf_rect_t *bounds) // I - Bounds buffer
|
|
{
|
|
// Range check input...
|
|
if (!font || !bounds)
|
|
{
|
|
if (bounds)
|
|
memset(bounds, 0, sizeof(ttf_rect_t));
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
bounds->left = 1000.0f * font->x_min / font->units;
|
|
bounds->right = 1000.0f * font->x_max / font->units;
|
|
bounds->bottom = 1000.0f * font->y_min / font->units;
|
|
bounds->top = 1000.0f * font->y_max / font->units;
|
|
|
|
return (bounds);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetCapHeight()' - Get the height of capital letters.
|
|
//
|
|
|
|
int // O - Capital letter height in 1000ths
|
|
ttfGetCapHeight(ttf_t *font) // I - Font
|
|
{
|
|
return (font ? (int)(1000 * font->cap_height / font->units) : 0);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetCMap()' - Get the Unicode to glyph mapping table.
|
|
//
|
|
|
|
const int * // O - CMap table
|
|
ttfGetCMap(ttf_t *font, // I - Font
|
|
size_t *num_cmap) // O - Number of entries in table
|
|
{
|
|
// Range check input...
|
|
if (!font || !num_cmap)
|
|
{
|
|
if (num_cmap)
|
|
*num_cmap = 0;
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
*num_cmap = font->num_cmap;
|
|
return (font->cmap);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetCopyright()' - Get the copyright text for a font.
|
|
//
|
|
|
|
const char * // O - Copyright text
|
|
ttfGetCopyright(ttf_t *font) // I - Font
|
|
{
|
|
return (font ? font->copyright : NULL);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetDescent()' - Get the maximum depth of non-accented characters.
|
|
//
|
|
|
|
int // O - Descent in 1000ths
|
|
ttfGetDescent(ttf_t *font) // I - Font
|
|
{
|
|
return (font ? (int)(1000 * font->descent / font->units) : 0);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetExtents()' - Get the extents of a UTF-8 string.
|
|
//
|
|
// This function computes the extents of the UTF-8 string "s" when rendered
|
|
// using the specified font "font" and size "size". The "extents" argument is
|
|
// a pointer to a `ttf_rect_t` structure that is filled with the extents of a
|
|
// simple rendering of the string with no kerning or rewriting applied. The
|
|
// values are scaled using the specified font size.
|
|
//
|
|
|
|
ttf_rect_t * // O - Pointer to extents or `NULL` on error
|
|
ttfGetExtents(
|
|
ttf_t *font, // I - Font
|
|
float size, // I - Font size
|
|
const char *s, // I - String
|
|
ttf_rect_t *extents) // O - Extents of the string
|
|
{
|
|
bool first = true; // First character?
|
|
int ch, // Current character
|
|
width = 0; // Width
|
|
_ttf_metric_t *widths; // Widths
|
|
|
|
|
|
TTF_DEBUG("ttfGetExtents(font=%p, size=%.2f, s=\"%s\", extents=%p)\n", (void *)font, size, s, (void *)extents);
|
|
|
|
// Make sure extents is zeroed out...
|
|
if (extents)
|
|
memset(extents, 0, sizeof(ttf_rect_t));
|
|
|
|
// Range check input...
|
|
if (!font || size <= 0.0f || !s || !extents)
|
|
return (NULL);
|
|
|
|
// Loop through the string...
|
|
while (*s)
|
|
{
|
|
// Get the next Unicode character...
|
|
if ((*s & 0xe0) == 0xc0 && (s[1] & 0xc0) == 0x80)
|
|
{
|
|
// Two byte UTF-8
|
|
ch = ((*s & 0x1f) << 6) | (s[1] & 0x3f);
|
|
s += 2;
|
|
}
|
|
else if ((*s & 0xf0) == 0xe0 && (s[1] & 0xc0) == 0x80 && (s[2] & 0xc0) == 0x80)
|
|
{
|
|
// Three byte UTF-8
|
|
ch = ((*s & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f);
|
|
s += 3;
|
|
}
|
|
else if ((*s & 0xf8) == 0xf0 && (s[1] & 0xc0) == 0x80 && (s[2] & 0xc0) == 0x80 && (s[3] & 0xc0) == 0x80)
|
|
{
|
|
// Four byte UTF-8
|
|
ch = ((*s & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f);
|
|
s += 4;
|
|
}
|
|
else if (*s & 0x80)
|
|
{
|
|
// Invalid UTF-8
|
|
errorf(font, "Invalid UTF-8 sequence starting with 0x%02X.", *s & 255);
|
|
return (NULL);
|
|
}
|
|
else
|
|
{
|
|
// ASCII...
|
|
ch = *s++;
|
|
}
|
|
|
|
// Find its width...
|
|
if ((widths = font->widths[ch / 256]) != NULL)
|
|
{
|
|
if (first)
|
|
{
|
|
extents->left = -widths[ch & 255].left_bearing / font->units;
|
|
first = false;
|
|
}
|
|
|
|
width += widths[ch & 255].width;
|
|
}
|
|
else if ((widths = font->widths[0]) != NULL)
|
|
{
|
|
// Use the ".notdef" (0) glyph width...
|
|
if (first)
|
|
{
|
|
extents->left = -widths[0].left_bearing / font->units;
|
|
first = false;
|
|
}
|
|
|
|
width += widths[0].width;
|
|
}
|
|
}
|
|
|
|
// Calculate the bounding box for the text and return...
|
|
TTF_DEBUG("ttfGetExtents: width=%d\n", width);
|
|
|
|
extents->bottom = size * font->y_min / font->units;
|
|
extents->right = size * width / font->units + extents->left;
|
|
extents->top = size * font->y_max / font->units;
|
|
|
|
return (extents);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetFamily()' - Get the family name of a font.
|
|
//
|
|
|
|
const char * // O - Family name
|
|
ttfGetFamily(ttf_t *font) // I - Font
|
|
{
|
|
return (font ? font->family : NULL);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetItalicAngle()' - Get the italic angle.
|
|
//
|
|
|
|
float // O - Angle in degrees
|
|
ttfGetItalicAngle(ttf_t *font) // I - Font
|
|
{
|
|
return (font ? font->italic_angle : 0.0f);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetMaxChar()' - Get the last character in the font.
|
|
//
|
|
|
|
int // O - Last character in font
|
|
ttfGetMaxChar(ttf_t *font) // I - Font
|
|
{
|
|
return (font ? font->max_char : 0);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetMinChar()' - Get the first character in the font.
|
|
//
|
|
|
|
int // O - First character in font
|
|
ttfGetMinChar(ttf_t *font) // I - Font
|
|
{
|
|
return (font ? font->min_char : 0);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetNumFonts()' - Get the number of fonts in this collection.
|
|
//
|
|
|
|
size_t // O - Number of fonts
|
|
ttfGetNumFonts(ttf_t *font) // I - Font
|
|
{
|
|
return (font ? font->num_fonts : 0);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetPostScriptName()' - Get the PostScript name of a font.
|
|
//
|
|
|
|
const char * // O - PostScript name
|
|
ttfGetPostScriptName(
|
|
ttf_t *font) // I - Font
|
|
{
|
|
return (font ? font->postscript_name : NULL);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetStretch()' - Get the font "stretch" value...
|
|
//
|
|
|
|
ttf_stretch_t // O - Stretch value
|
|
ttfGetStretch(ttf_t *font) // I - Font
|
|
{
|
|
return (font ? font->stretch : TTF_STRETCH_NORMAL);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetStyle()' - Get the font style.
|
|
//
|
|
|
|
ttf_style_t // O - Style
|
|
ttfGetStyle(ttf_t *font) // I - Font
|
|
{
|
|
return (font ? font->style : TTF_STYLE_NORMAL);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetVersion()' - Get the version number of a font.
|
|
//
|
|
|
|
const char * // O - Version number
|
|
ttfGetVersion(ttf_t *font) // I - Font
|
|
{
|
|
return (font ? font->version : NULL);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetWeight()' - Get the weight of a font.
|
|
//
|
|
|
|
ttf_weight_t // O - Weight
|
|
ttfGetWeight(ttf_t *font) // I - Font
|
|
{
|
|
return (font ? (ttf_weight_t)font->weight : TTF_WEIGHT_400);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetWidth()' - Get the width of a single character.
|
|
//
|
|
|
|
int // O - Width in 1000ths
|
|
ttfGetWidth(ttf_t *font, // I - Font
|
|
int ch) // I - Unicode character
|
|
{
|
|
int bin = ch >> 8; // Bin in widths array
|
|
|
|
|
|
// Range check input...
|
|
if (!font || ch < ' ' || ch == 0x7f)
|
|
return (0);
|
|
|
|
if (font->widths[bin])
|
|
return ((int)(1000.0f * font->widths[bin][ch & 255].width / font->units));
|
|
else if (font->widths[0]) // .notdef
|
|
return ((int)(1000.0f * font->widths[0][0].width / font->units));
|
|
else
|
|
return (0);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfGetXHeight()' - Get the height of lowercase letters.
|
|
//
|
|
|
|
int // O - Lowercase letter height in 1000ths
|
|
ttfGetXHeight(ttf_t *font) // I - Font
|
|
{
|
|
return (font ? (int)(1000 * font->x_height / font->units) : 0);
|
|
}
|
|
|
|
|
|
//
|
|
// 'ttfIsFixedPitch()' - Determine whether a font is fixedpitch.
|
|
//
|
|
|
|
bool // O - `true` if fixed pitch, `false` otherwise
|
|
ttfIsFixedPitch(ttf_t *font) // I - Font
|
|
{
|
|
return (font ? font->is_fixed : false);
|
|
}
|
|
|
|
|
|
//
|
|
// 'copy_name()' - Copy a name string from a font.
|
|
//
|
|
|
|
static char * // O - Name string or `NULL`
|
|
copy_name(ttf_t *font, // I - Font
|
|
unsigned name_id) // I - Name identifier
|
|
{
|
|
int i; // Looping var
|
|
_ttf_off_name_t *name; // Current name
|
|
|
|
|
|
for (i = font->names.num_names, name = font->names.names; i > 0; i --, name ++)
|
|
{
|
|
if (name->name_id == name_id &&
|
|
((name->platform_id == TTF_OFF_Mac && name->language_id == TTF_OFF_Mac_USEnglish) ||
|
|
(name->platform_id == TTF_OFF_Windows && (name->language_id & 0xff) == TTF_OFF_Windows_English)))
|
|
{
|
|
char temp[1024], // Temporary string buffer
|
|
*tempptr, // Pointer into temporary string
|
|
*storptr; // Pointer into storage
|
|
int chars, // Length of string to copy in characters
|
|
bpc; // Bytes per character
|
|
|
|
if ((unsigned)(name->offset + name->length) > font->names.storage_size)
|
|
{
|
|
TTF_DEBUG("copy_name: offset(%d)+length(%d) > storage_size(%d)\n", name->offset, name->length, font->names.storage_size);
|
|
continue;
|
|
}
|
|
|
|
if (name->platform_id == TTF_OFF_Windows && name->encoding_id == TTF_OFF_Windows_UCS2)
|
|
{
|
|
storptr = (char *)font->names.storage + name->offset;
|
|
chars = name->length / 2;
|
|
bpc = 2;
|
|
}
|
|
else if (name->platform_id == TTF_OFF_Windows && name->encoding_id == TTF_OFF_Windows_UCS4)
|
|
{
|
|
storptr = (char *)font->names.storage + name->offset;
|
|
chars = name->length / 4;
|
|
bpc = 4;
|
|
}
|
|
else
|
|
{
|
|
storptr = (char *)font->names.storage + name->offset;
|
|
chars = name->length;
|
|
bpc = 1;
|
|
}
|
|
|
|
for (tempptr = temp; chars > 0; storptr += bpc, chars --)
|
|
{
|
|
int ch; // Current character
|
|
|
|
// Convert to Unicode...
|
|
if (bpc == 1)
|
|
ch = *storptr;
|
|
else if (bpc == 2)
|
|
ch = ((storptr[0] & 255) << 8) | (storptr[1] & 255);
|
|
else
|
|
ch = ((storptr[0] & 255) << 24) | ((storptr[1] & 255) << 16) | ((storptr[2] & 255) << 8) | (storptr[3] & 255);
|
|
|
|
// Convert to UTF-8...
|
|
if (ch < 0x80)
|
|
{
|
|
// ASCII...
|
|
if (tempptr < (temp + sizeof(temp) - 1))
|
|
*tempptr++ = (char)ch;
|
|
else
|
|
break;
|
|
}
|
|
else if (ch < 0x400)
|
|
{
|
|
// Two byte UTF-8
|
|
if (tempptr < (temp + sizeof(temp) - 2))
|
|
{
|
|
*tempptr++ = (char)(0xc0 | (ch >> 6));
|
|
*tempptr++ = (char)(0x80 | (ch & 0x3f));
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
else if (ch < 0x10000)
|
|
{
|
|
// Three byte UTF-8
|
|
if (tempptr < (temp + sizeof(temp) - 3))
|
|
{
|
|
*tempptr++ = (char)(0xe0 | (ch >> 12));
|
|
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
|
|
*tempptr++ = (char)(0x80 | (ch & 0x3f));
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Four byte UTF-8
|
|
if (tempptr < (temp + sizeof(temp) - 4))
|
|
{
|
|
*tempptr++ = (char)(0xf0 | (ch >> 18));
|
|
*tempptr++ = (char)(0x80 | ((ch >> 12) & 0x3f));
|
|
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
|
|
*tempptr++ = (char)(0x80 | (ch & 0x3f));
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
*tempptr = '\0';
|
|
|
|
TTF_DEBUG("copy_name: name_id(%d) = \"%s\"\n", name_id, temp);
|
|
|
|
return (strdup(temp));
|
|
}
|
|
}
|
|
|
|
TTF_DEBUG("copy_name: No English name string for %d.\n", name_id);
|
|
#ifdef DEBUG
|
|
for (i = font->names.num_names, name = font->names.names; i > 0; i --, name ++)
|
|
{
|
|
if (name->name_id == name_id)
|
|
TTF_DEBUG("copy_name: Found name_id=%d, platform_id=%d, language_id=%d(0x%04x)\n", name_id, name->platform_id, name->language_id, name->language_id);
|
|
}
|
|
#endif // DEBUG
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
//
|
|
// 'errorf()' - Show an error message.
|
|
//
|
|
|
|
static void
|
|
errorf(ttf_t *font, // I - Font
|
|
const char *message, // I - `printf`-style message string
|
|
...) // I - Addition arguments as needed
|
|
{
|
|
va_list ap; // Argument pointer
|
|
char buffer[2048]; // Message buffer
|
|
|
|
|
|
// Format the message...
|
|
va_start(ap, message);
|
|
vsnprintf(buffer, sizeof(buffer), message, ap);
|
|
va_end(ap);
|
|
|
|
// If an error callback is set, sent the message there. Otherwise write it
|
|
// to stderr...
|
|
if (font && font->err_cb)
|
|
(font->err_cb)(font->err_data, buffer);
|
|
else
|
|
fprintf(stderr, "%s\n", message);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'read_cmap()' - Read the cmap table, getting the Unicode mapping table.
|
|
*/
|
|
|
|
static bool // O - `true` on success, `false` on error
|
|
read_cmap(ttf_t *font) // I - Font
|
|
{
|
|
unsigned length; // Length of cmap table
|
|
int i, // Looping var
|
|
temp, // Temporary value
|
|
num_tables, // Number of cmap tables
|
|
platform_id, // Platform identifier (Windows or Mac)
|
|
encoding_id, // Encoding identifier (varies)
|
|
cformat; // Formap of cmap data
|
|
unsigned clength, // Length of cmap data
|
|
coffset = 0, // Offset to cmap data
|
|
roman_offset = 0; // MacRoman offset
|
|
int *cmapptr; // Pointer into cmap
|
|
#if 0
|
|
const int *unimap = NULL; // Unicode character map, if any
|
|
static const int romanmap[256] = // MacRoman to Unicode map
|
|
{
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
|
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
|
|
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
|
0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
|
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
|
|
0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
|
|
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
|
|
0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
|
|
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
|
|
0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
|
|
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
|
|
0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
|
|
0xC4, 0xC5, 0xC7, 0xC9, 0xD1, 0xD6, 0xDC, 0xE1,
|
|
0xE0, 0xE2, 0xE4, 0xE3, 0xE5, 0xE7, 0xE9, 0xE8,
|
|
0xEA, 0xEB, 0xED, 0xEC, 0xEE, 0xEF, 0xF1, 0xF3,
|
|
0xF2, 0xF4, 0xF6, 0xF5, 0xFA, 0xF9, 0xFB, 0xFC,
|
|
0x2020, 0xB0, 0xA2, 0xA3, 0xA7, 0x2022, 0xB6, 0xDF,
|
|
0xAE, 0xA9, 0x2122, 0xB4, 0xA8, 0x2260, 0xC6, 0xD8,
|
|
0x221E, 0xB1, 0x2264, 0x2265, 0xA5, 0xB5, 0x2202, 0x2211,
|
|
0x220F, 0x3C0, 0x222B, 0xAA, 0xBA, 0x3A9, 0xE6, 0xF8,
|
|
0xBF, 0xA1, 0xAC, 0x221A, 0x192, 0x2248, 0x2206, 0xAB,
|
|
0xBB, 0x2026, 0xA0, 0xC0, 0xC3, 0xD5, 0x152, 0x153,
|
|
0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0xF7, 0x25CA,
|
|
0xFF, 0x178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02,
|
|
0x2021, 0xB7, 0x201A, 0x201E, 0x2030, 0xC2, 0xCA, 0xC1,
|
|
0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0xD3, 0xD4,
|
|
0xF8FF, 0xD2, 0xDA, 0xDB, 0xD9, 0x131, 0x2C6, 0x2DC,
|
|
0xAF, 0x2D8, 0x2D9, 0x2DA, 0xB8, 0x2DD, 0x2DB, 0x2C7
|
|
};
|
|
#endif /* 0 */
|
|
|
|
|
|
// Find the cmap table...
|
|
if (seek_table(font, TTF_OFF_cmap, 0, true) == 0)
|
|
return (false);
|
|
|
|
if ((temp = read_ushort(font)) != 0)
|
|
{
|
|
errorf(font, "Unknown cmap version %d.", temp);
|
|
return (false);
|
|
}
|
|
|
|
if ((num_tables = read_ushort(font)) < 1)
|
|
{
|
|
errorf(font, "No cmap tables to read.");
|
|
return (false);
|
|
}
|
|
|
|
TTF_DEBUG("read_cmap: num_tables=%d\n", num_tables);
|
|
|
|
// Find a Unicode table we can use...
|
|
for (i = 0; i < num_tables; i ++)
|
|
{
|
|
platform_id = read_ushort(font);
|
|
encoding_id = read_ushort(font);
|
|
coffset = read_ulong(font);
|
|
|
|
TTF_DEBUG("read_cmap: table[%d].platform_id=%d, encoding_id=%d, coffset=%u\n", i, platform_id, encoding_id, coffset);
|
|
|
|
if (platform_id == TTF_OFF_Unicode || (platform_id == TTF_OFF_Windows && encoding_id == TTF_OFF_Windows_UCS2))
|
|
break;
|
|
|
|
if (platform_id == TTF_OFF_Mac && encoding_id == TTF_OFF_Mac_Roman)
|
|
roman_offset = coffset;
|
|
}
|
|
|
|
if (i >= num_tables)
|
|
{
|
|
if (roman_offset)
|
|
{
|
|
TTF_DEBUG("read_cmap: Using MacRoman cmap table.\n");
|
|
coffset = roman_offset;
|
|
}
|
|
else
|
|
{
|
|
errorf(font, "No usable cmap table.");
|
|
return (false);
|
|
}
|
|
}
|
|
|
|
if ((length = seek_table(font, TTF_OFF_cmap, coffset, true)) == 0)
|
|
return (false);
|
|
|
|
if ((cformat = read_ushort(font)) < 0)
|
|
{
|
|
errorf(font, "Unable to read cmap table format at offset %u.", coffset);
|
|
return (false);
|
|
}
|
|
|
|
TTF_DEBUG("read_cmap: cformat=%d\n", cformat);
|
|
|
|
switch (cformat)
|
|
{
|
|
case 0 :
|
|
{
|
|
// Format 0: Byte encoding table.
|
|
//
|
|
// This is a simple 8-bit mapping.
|
|
size_t j; // Looping var
|
|
unsigned char bmap[256]; // Byte map buffer
|
|
|
|
if ((unsigned)read_ushort(font) == (unsigned)-1)
|
|
{
|
|
errorf(font, "Unable to read cmap table length at offset %u.", coffset);
|
|
return (false);
|
|
}
|
|
|
|
/* language = */ read_ushort(font);
|
|
|
|
if (length > (256 + 6))
|
|
{
|
|
errorf(font, "Bad cmap table length at offset %u.", coffset);
|
|
return (false);
|
|
}
|
|
|
|
font->num_cmap = length - 6;
|
|
|
|
if ((font->cmap = (int *)malloc(font->num_cmap * sizeof(int))) == NULL)
|
|
{
|
|
errorf(font, "Unable to allocate cmap table.");
|
|
return (false);
|
|
}
|
|
|
|
if (read(font->fd, bmap, font->num_cmap) != (ssize_t)font->num_cmap)
|
|
{
|
|
errorf(font, "Unable to read cmap table length at offset %u.", coffset);
|
|
return (false);
|
|
}
|
|
|
|
// Copy into the actual cmap table...
|
|
for (j = 0; j < font->num_cmap; j ++)
|
|
font->cmap[j] = bmap[j];
|
|
}
|
|
break;
|
|
|
|
case 4 :
|
|
{
|
|
// Format 4: Segment mapping to delta values.
|
|
//
|
|
// This is an overly complicated linear way of encoding a sparse
|
|
// mapping table. And it uses 1-based indexing with modulo
|
|
// arithmetic...
|
|
int ch, // Current character
|
|
seg, // Current segment
|
|
glyph, // Current glyph
|
|
segCount, // Number of segments
|
|
numGlyphIdArray,// Number of glyph IDs
|
|
*glyphIdArray; // Glyph IDs
|
|
_ttf_off_cmap4_t *segments, // Segment data
|
|
*segment; // This segment
|
|
|
|
|
|
// Read the table...
|
|
if ((clength = (unsigned)read_ushort(font)) == (unsigned)-1)
|
|
{
|
|
errorf(font, "Unable to read cmap table length at offset %u.", coffset);
|
|
return (false);
|
|
}
|
|
|
|
TTF_DEBUG("read_cmap: clength=%u\n", clength);
|
|
|
|
/* language = */ read_ushort(font);
|
|
segCount = read_ushort(font) / 2;
|
|
/* searchRange = */ read_ushort(font);
|
|
/* entrySelectoed = */ read_ushort(font);
|
|
/* rangeShift = */ read_ushort(font);
|
|
|
|
TTF_DEBUG("read_cmap: segCount=%d\n", segCount);
|
|
|
|
if (segCount < 2)
|
|
{
|
|
errorf(font, "Bad cmap table.");
|
|
return (false);
|
|
}
|
|
|
|
numGlyphIdArray = ((int)clength - 8 * segCount - 16) / 2;
|
|
segments = (_ttf_off_cmap4_t *)calloc((size_t)segCount, sizeof(_ttf_off_cmap4_t));
|
|
glyphIdArray = (int *)calloc((size_t)numGlyphIdArray, sizeof(int));
|
|
|
|
if (!segments || !glyphIdArray)
|
|
{
|
|
errorf(font, "Unable to allocate memory for cmap.");
|
|
free(segments);
|
|
free(glyphIdArray);
|
|
return (false);
|
|
}
|
|
|
|
TTF_DEBUG("read_cmap: numGlyphIdArray=%d\n", numGlyphIdArray);
|
|
|
|
for (i = 0; i < segCount; i ++)
|
|
segments[i].endCode = (unsigned short)read_ushort(font);
|
|
|
|
/* reservedPad = */ read_ushort(font);
|
|
|
|
for (i = 0; i < segCount; i ++)
|
|
segments[i].startCode = (unsigned short)read_ushort(font);
|
|
|
|
for (i = 0; i < segCount; i ++)
|
|
segments[i].idDelta = (short)read_short(font);
|
|
|
|
for (i = 0; i < segCount; i ++)
|
|
segments[i].idRangeOffset = (unsigned short)read_ushort(font);
|
|
|
|
for (i = 0; i < numGlyphIdArray; i ++)
|
|
glyphIdArray[i] = read_ushort(font);
|
|
|
|
for (i = 0, segment = segments; i < segCount; i ++, segment ++)
|
|
{
|
|
TTF_DEBUG("read_cmap: segment[%d].startCode=%d, endCode=%d, idDelta=%d, idRangeOffset=%d\n", i, segment->startCode, segment->endCode, segment->idDelta, segment->idRangeOffset);
|
|
|
|
if (segment->startCode > segment->endCode)
|
|
{
|
|
errorf(font, "Bad cmap table segment %u to %u.", segments->startCode, segment->endCode);
|
|
return (false);
|
|
}
|
|
|
|
// Based on the end code of the segment table, allocate space for the
|
|
// uncompressed cmap table...
|
|
if (segment->endCode >= font->num_cmap)
|
|
font->num_cmap = segment->endCode + 1;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
for (i = 0; i < numGlyphIdArray; i ++)
|
|
TTF_DEBUG("read_cmap: glyphIdArray[%d]=%d\n", i, glyphIdArray[i]);
|
|
#endif /* DEBUG */
|
|
|
|
if (font->num_cmap == 0 || font->num_cmap > TTF_FONT_MAX_CHAR)
|
|
{
|
|
errorf(font, "Invalid cmap table with %u characters.", (unsigned)font->num_cmap);
|
|
return (false);
|
|
}
|
|
|
|
font->cmap = cmapptr = (int *)malloc(font->num_cmap * sizeof(int));
|
|
|
|
if (!font->cmap)
|
|
{
|
|
errorf(font, "Unable to allocate memory for cmap.");
|
|
free(segments);
|
|
free(glyphIdArray);
|
|
return (false);
|
|
}
|
|
|
|
memset(cmapptr, -1, font->num_cmap * sizeof(int));
|
|
|
|
// Now loop through the segments and assign glyph indices from the
|
|
// array...
|
|
for (seg = segCount, segment = segments; seg > 0; seg --, segment ++)
|
|
{
|
|
for (ch = segment->startCode; ch <= segment->endCode; ch ++)
|
|
{
|
|
if (segment->idRangeOffset)
|
|
{
|
|
// Use an "obscure indexing trick" (words from the spec, not
|
|
// mine) to look up the glyph index...
|
|
temp = (int)(segment->idRangeOffset / 2 - segCount + (ch - segment->startCode) + (segment - segments));
|
|
|
|
TTF_DEBUG("read_cmap: ch=%d, temp=%d\n", ch, temp);
|
|
if (temp < 0 || temp >= numGlyphIdArray)
|
|
glyph = -1;
|
|
else
|
|
glyph = (glyphIdArray[temp] + segment->idDelta) & 65535;
|
|
}
|
|
else
|
|
{
|
|
// Just use idDelta to compute a glyph index...
|
|
glyph = (ch + segment->idDelta) & 65535;
|
|
}
|
|
|
|
cmapptr[ch] = glyph;
|
|
}
|
|
}
|
|
|
|
// Free the segment data...
|
|
free(segments);
|
|
free(glyphIdArray);
|
|
}
|
|
break;
|
|
|
|
case 12 :
|
|
{
|
|
// Format 12: Segmented coverage
|
|
//
|
|
// A simple sparse linear segment mapping format.
|
|
unsigned ch, // Current character
|
|
gidx, // Current group
|
|
nGroups; // Number of groups
|
|
_ttf_off_cmap12_t *groups, // Groups
|
|
*group; // This group
|
|
|
|
// Read the table...
|
|
/* reserved */ read_ushort(font);
|
|
|
|
if (read_ulong(font) == 0)
|
|
{
|
|
errorf(font, "Unable to read cmap table length at offset %u.", coffset);
|
|
return (false);
|
|
}
|
|
|
|
/* language = */ read_ulong(font);
|
|
nGroups = read_ulong(font);
|
|
|
|
TTF_DEBUG("read_cmap: nGroups=%u\n", nGroups);
|
|
|
|
if (nGroups > TTF_FONT_MAX_GROUPS)
|
|
{
|
|
errorf(font, "Invalid cmap table with %u groups.", nGroups);
|
|
return (false);
|
|
}
|
|
|
|
if ((groups = (_ttf_off_cmap12_t *)calloc(nGroups, sizeof(_ttf_off_cmap12_t))) == NULL)
|
|
{
|
|
errorf(font, "Unable to allocate memory for cmap.");
|
|
return (false);
|
|
}
|
|
|
|
for (gidx = 0, group = groups, font->num_cmap = 0; gidx < nGroups; gidx ++, group ++)
|
|
{
|
|
group->startCharCode = read_ulong(font);
|
|
group->endCharCode = read_ulong(font);
|
|
group->startGlyphID = read_ulong(font);
|
|
TTF_DEBUG("read_cmap: [%u] startCharCode=%u, endCharCode=%u, startGlyphID=%u\n", gidx, group->startCharCode, group->endCharCode, group->startGlyphID);
|
|
|
|
if (group->startCharCode > group->endCharCode)
|
|
{
|
|
errorf(font, "Bad cmap table segment %u to %u.", group->startCharCode, group->endCharCode);
|
|
return (false);
|
|
}
|
|
|
|
if (group->endCharCode >= font->num_cmap)
|
|
font->num_cmap = group->endCharCode + 1;
|
|
}
|
|
|
|
// Based on the end code of the segent table, allocate space for the
|
|
// uncompressed cmap table...
|
|
TTF_DEBUG("read_cmap: num_cmap=%u\n", (unsigned)font->num_cmap);
|
|
|
|
if (font->num_cmap == 0 || font->num_cmap > TTF_FONT_MAX_CHAR)
|
|
{
|
|
errorf(font, "Invalid cmap table with %u characters.", (unsigned)font->num_cmap);
|
|
return (false);
|
|
}
|
|
|
|
font->cmap = cmapptr = (int *)malloc(font->num_cmap * sizeof(int));
|
|
|
|
if (!font->cmap)
|
|
{
|
|
errorf(font, "Unable to allocate memory for cmap.");
|
|
free(groups);
|
|
return (false);
|
|
}
|
|
|
|
memset(cmapptr, -1, font->num_cmap * sizeof(int));
|
|
|
|
// Now loop through the groups and assign glyph indices from the
|
|
// array...
|
|
for (gidx = 0, group = groups; gidx < nGroups; gidx ++, group ++)
|
|
{
|
|
for (ch = group->startCharCode; ch <= group->endCharCode && ch < TTF_FONT_MAX_CHAR; ch ++)
|
|
cmapptr[ch] = (int)(group->startGlyphID + ch - group->startCharCode);
|
|
}
|
|
|
|
// Free the group data...
|
|
free(groups);
|
|
}
|
|
break;
|
|
|
|
case 13 :
|
|
{
|
|
// Format 13: Many-to-one range mappings
|
|
//
|
|
// Typically used for fonts of last resort where multiple characters
|
|
// map to the same glyph.
|
|
unsigned ch, // Current character
|
|
gidx, // Current group
|
|
nGroups; // Number of groups
|
|
_ttf_off_cmap13_t *groups, // Groups
|
|
*group; // This group
|
|
|
|
// Read the table...
|
|
/* reserved */ read_ushort(font);
|
|
|
|
if (read_ulong(font) == 0)
|
|
{
|
|
errorf(font, "Unable to read cmap table length at offset %u.", coffset);
|
|
return (false);
|
|
}
|
|
|
|
/* language = */ read_ulong(font);
|
|
nGroups = read_ulong(font);
|
|
|
|
TTF_DEBUG("read_cmap: nGroups=%u\n", nGroups);
|
|
|
|
if (nGroups > TTF_FONT_MAX_GROUPS)
|
|
{
|
|
errorf(font, "Invalid cmap table with %u groups.", nGroups);
|
|
return (false);
|
|
}
|
|
|
|
if ((groups = (_ttf_off_cmap13_t *)calloc(nGroups, sizeof(_ttf_off_cmap13_t))) == NULL)
|
|
{
|
|
errorf(font, "Unable to allocate memory for cmap.");
|
|
return (false);
|
|
}
|
|
|
|
for (gidx = 0, group = groups, font->num_cmap = 0; gidx < nGroups; gidx ++, group ++)
|
|
{
|
|
group->startCharCode = read_ulong(font);
|
|
group->endCharCode = read_ulong(font);
|
|
group->glyphID = read_ulong(font);
|
|
TTF_DEBUG("read_cmap: [%u] startCharCode=%u, endCharCode=%u, glyphID=%u\n", gidx, group->startCharCode, group->endCharCode, group->glyphID);
|
|
|
|
if (group->startCharCode > group->endCharCode)
|
|
{
|
|
errorf(font, "Bad cmap table segment %u to %u.", group->startCharCode, group->endCharCode);
|
|
return (false);
|
|
}
|
|
|
|
if (group->endCharCode >= font->num_cmap)
|
|
font->num_cmap = group->endCharCode + 1;
|
|
}
|
|
|
|
// Based on the end code of the segent table, allocate space for the
|
|
// uncompressed cmap table...
|
|
TTF_DEBUG("read_cmap: num_cmap=%u\n", (unsigned)font->num_cmap);
|
|
|
|
if (font->num_cmap == 0 || font->num_cmap > TTF_FONT_MAX_CHAR)
|
|
{
|
|
errorf(font, "Invalid cmap table with %u characters.", (unsigned)font->num_cmap);
|
|
return (false);
|
|
}
|
|
|
|
font->cmap = cmapptr = (int *)malloc(font->num_cmap * sizeof(int));
|
|
|
|
if (!font->cmap)
|
|
{
|
|
errorf(font, "Unable to allocate cmap.");
|
|
free(groups);
|
|
return (false);
|
|
}
|
|
|
|
memset(cmapptr, -1, font->num_cmap * sizeof(int));
|
|
|
|
// Now loop through the groups and assign glyph indices from the
|
|
// array...
|
|
for (gidx = 0, group = groups; gidx < nGroups; gidx ++, group ++)
|
|
{
|
|
for (ch = group->startCharCode; ch <= group->endCharCode && ch < TTF_FONT_MAX_CHAR; ch ++)
|
|
cmapptr[ch] = (int)group->glyphID;
|
|
}
|
|
|
|
// Free the group data...
|
|
free(groups);
|
|
}
|
|
break;
|
|
|
|
default :
|
|
errorf(font, "Format %d cmap tables are not yet supported.", cformat);
|
|
return (false);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
cmapptr = font->cmap;
|
|
for (i = 0; i < (int)font->num_cmap && i < 127; i ++)
|
|
{
|
|
if (cmapptr[i] >= 0)
|
|
TTF_DEBUG("read_cmap; cmap[%d]=%d\n", i, cmapptr[i]);
|
|
}
|
|
#endif // DEBUG
|
|
|
|
return (true);
|
|
}
|
|
|
|
|
|
//
|
|
// 'read_head()' - Read the head table.
|
|
//
|
|
|
|
static bool // O - `true` on success, `false` on error
|
|
read_head(ttf_t *font, // I - Font
|
|
_ttf_off_head_t *head) // O - head table data
|
|
{
|
|
memset(head, 0, sizeof(_ttf_off_head_t));
|
|
|
|
if (seek_table(font, TTF_OFF_head, 0, true) == 0)
|
|
return (false);
|
|
|
|
/* majorVersion */ read_ushort(font);
|
|
/* minorVersion */ read_ushort(font);
|
|
/* fontRevision */ read_ulong(font);
|
|
/* checkSumAdjustment */ read_ulong(font);
|
|
/* magicNumber */ read_ulong(font);
|
|
/* flags */ read_ushort(font);
|
|
head->unitsPerEm = (unsigned short)read_ushort(font);
|
|
/* created */ read_ulong(font); read_ulong(font);
|
|
/* modified */ read_ulong(font); read_ulong(font);
|
|
head->xMin = (short)read_short(font);
|
|
head->yMin = (short)read_short(font);
|
|
head->xMax = (short)read_short(font);
|
|
head->yMax = (short)read_short(font);
|
|
head->macStyle = (unsigned short)read_ushort(font);
|
|
|
|
return (true);
|
|
}
|
|
|
|
|
|
//
|
|
// 'read_hhea()' - Read the hhea table.
|
|
//
|
|
|
|
static bool // O - `true` on success, `false` on error
|
|
read_hhea(ttf_t *font, // I - Font
|
|
_ttf_off_hhea_t *hhea) // O - hhea table data
|
|
{
|
|
memset(hhea, 0, sizeof(_ttf_off_hhea_t));
|
|
|
|
if (seek_table(font, TTF_OFF_hhea, 0, true) == 0)
|
|
return (false);
|
|
|
|
/* majorVersion */ read_ushort(font);
|
|
/* minorVersion */ read_ushort(font);
|
|
hhea->ascender = (short)read_short(font);
|
|
hhea->descender = (short)read_short(font);
|
|
/* lineGap */ read_short(font);
|
|
/* advanceWidthMax */ read_ushort(font);
|
|
/* minLeftSideBearing */ read_short(font);
|
|
/* minRightSideBearing */ read_short(font);
|
|
/* mMaxExtent */ read_short(font);
|
|
/* caretSlopeRise */ read_short(font);
|
|
/* caretSlopeRun */ read_short(font);
|
|
/* caretOffset */ read_short(font);
|
|
/* (reserved) */ read_short(font);
|
|
/* (reserved) */ read_short(font);
|
|
/* (reserved) */ read_short(font);
|
|
/* (reserved) */ read_short(font);
|
|
/* metricDataFormat */ read_short(font);
|
|
hhea->numberOfHMetrics = (unsigned short)read_ushort(font);
|
|
|
|
return (true);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'read_hmtx()' - Read the horizontal metrics from the font.
|
|
*/
|
|
|
|
static _ttf_metric_t * // O - Array of glyph metrics
|
|
read_hmtx(ttf_t *font, // I - Font
|
|
_ttf_off_hhea_t *hhea) // O - hhea table data
|
|
{
|
|
unsigned length; // Length of hmtx table
|
|
unsigned i; // Looping var
|
|
_ttf_metric_t *widths; // Glyph metrics array
|
|
|
|
|
|
if ((length = seek_table(font, TTF_OFF_hmtx, 0, true)) == 0)
|
|
return (NULL);
|
|
|
|
if (length < (unsigned)(4 * hhea->numberOfHMetrics))
|
|
{
|
|
errorf(font, "Length of hhea table is only %u, expected at least %d.", length, 4 * hhea->numberOfHMetrics);
|
|
return (NULL);
|
|
}
|
|
|
|
if ((widths = (_ttf_metric_t *)calloc((size_t)hhea->numberOfHMetrics, sizeof(_ttf_metric_t))) == NULL)
|
|
return (NULL);
|
|
|
|
for (i = 0; i < hhea->numberOfHMetrics; i ++)
|
|
{
|
|
widths[i].width = (short)read_ushort(font);
|
|
widths[i].left_bearing = (short)read_short(font);
|
|
|
|
TTF_DEBUG("read_hmtx: widths[%d].width=%d, .left_bearing=%d\n", i, widths[i].width, widths[i].left_bearing);
|
|
}
|
|
|
|
return (widths);
|
|
}
|
|
|
|
|
|
//
|
|
// 'read_maxp()' - Read the number of glyphs in the font.
|
|
//
|
|
|
|
static int // O - Number of glyphs or -1 on error
|
|
read_maxp(ttf_t *font) // I - Font
|
|
{
|
|
// All we care about is the number of glyphs, so just grab that...
|
|
if (seek_table(font, TTF_OFF_maxp, 4, true) == 0)
|
|
return (-1);
|
|
else
|
|
return (read_ushort(font));
|
|
}
|
|
|
|
|
|
//
|
|
// 'read_names()' - Read the name strings from a font.
|
|
//
|
|
|
|
static bool // O - `true` on success, `false` on error
|
|
read_names(ttf_t *font) // I - Font
|
|
{
|
|
unsigned length; // Length of names table
|
|
int i, // Looping var
|
|
format, // Name table format
|
|
offset; // Offset to storage
|
|
_ttf_off_name_t *name; // Current name
|
|
|
|
|
|
// Find the name table...
|
|
if ((length = seek_table(font, TTF_OFF_name, 0, true)) == 0)
|
|
return (false);
|
|
|
|
if ((format = read_ushort(font)) < 0 || format > 1)
|
|
{
|
|
errorf(font, "Unsupported name table format %d.", format);
|
|
return (false);
|
|
}
|
|
|
|
TTF_DEBUG("read_names: format=%d\n", format);
|
|
|
|
if ((font->names.num_names = read_ushort(font)) < 1)
|
|
return (false);
|
|
|
|
if ((font->names.names = (_ttf_off_name_t *)calloc((size_t)font->names.num_names, sizeof(_ttf_off_name_t))) == NULL)
|
|
return (false);
|
|
|
|
if ((offset = read_ushort(font)) < 0 || (unsigned)offset >= length)
|
|
return (false);
|
|
|
|
font->names.storage_size = length - (unsigned)offset;
|
|
if (font->names.storage_size > TTF_FONT_MAX_NAMES)
|
|
{
|
|
errorf(font, "Name table too large - %u bytes.", (unsigned)font->names.storage_size);
|
|
return (false);
|
|
}
|
|
|
|
if ((font->names.storage = malloc(font->names.storage_size)) == NULL)
|
|
return (false);
|
|
|
|
memset(font->names.storage, 'A', font->names.storage_size);
|
|
|
|
for (i = font->names.num_names, name = font->names.names; i > 0; i --, name ++)
|
|
{
|
|
name->platform_id = (unsigned short)read_ushort(font);
|
|
name->encoding_id = (unsigned short)read_ushort(font);
|
|
name->language_id = (unsigned short)read_ushort(font);
|
|
name->name_id = (unsigned short)read_ushort(font);
|
|
name->length = (unsigned short)read_ushort(font);
|
|
name->offset = (unsigned short)read_ushort(font);
|
|
|
|
TTF_DEBUG("name->platform_id=%d, encoding_id=%d, language_id=%d(0x%04x), name_id=%d, length=%d, offset=%d\n", name->platform_id, name->encoding_id, name->language_id, name->language_id, name->name_id, name->length, name->offset);
|
|
}
|
|
|
|
if (format == 1)
|
|
{
|
|
// Skip language_id table...
|
|
int count = read_ushort(font); // Number of language IDs
|
|
|
|
TTF_DEBUG("read_names: Skipping language_id table...\n");
|
|
|
|
while (count > 0)
|
|
{
|
|
/* length = */read_ushort(font);
|
|
/* offset = */read_ushort(font);
|
|
count --;
|
|
}
|
|
}
|
|
|
|
length -= (unsigned)offset;
|
|
|
|
if (read(font->fd, font->names.storage, length) < 0)
|
|
{
|
|
errorf(font, "Unable to read name table: %s", strerror(errno));
|
|
return (false);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
|
|
//
|
|
// 'read_os_2()' - Read the OS/2 table.
|
|
//
|
|
|
|
static bool // O - `true` on success, `false` on error
|
|
read_os_2(ttf_t *font, // I - Font
|
|
_ttf_off_os_2_t *os_2) // O - OS/2 table
|
|
{
|
|
int version; // OS/2 table version
|
|
unsigned char panose[10]; // panose value
|
|
|
|
|
|
memset(os_2, 0, sizeof(_ttf_off_os_2_t));
|
|
|
|
// Find the OS/2 table...
|
|
if (seek_table(font, TTF_OFF_OS_2, 0, false) == 0)
|
|
return (false);
|
|
|
|
if ((version = read_ushort(font)) < 0)
|
|
return (false);
|
|
|
|
TTF_DEBUG("read_names: version=%d\n", version);
|
|
|
|
/* xAvgCharWidth */ read_short(font);
|
|
os_2->usWeightClass = (unsigned short)read_ushort(font);
|
|
os_2->usWidthClass = (unsigned short)read_ushort(font);
|
|
os_2->fsType = (unsigned short)read_ushort(font);
|
|
/* ySubscriptXSize */ read_short(font);
|
|
/* ySubscriptYSize */ read_short(font);
|
|
/* ySubscriptXOffset */ read_short(font);
|
|
/* ySubscriptYOffset */ read_short(font);
|
|
/* ySuperscriptXSize */ read_short(font);
|
|
/* ySuperscriptYSize */ read_short(font);
|
|
/* ySuperscriptXOffset */ read_short(font);
|
|
/* ySuperscriptYOffset */ read_short(font);
|
|
/* yStrikeoutSize */ read_short(font);
|
|
/* yStrikeoutOffset */ read_short(font);
|
|
/* sFamilyClass */ read_short(font);
|
|
/* panose[10] */
|
|
if (read(font->fd, panose, sizeof(panose)) != (ssize_t)sizeof(panose))
|
|
return (false);
|
|
/* ulUnicodeRange1 */ read_ulong(font);
|
|
/* ulUnicodeRange2 */ read_ulong(font);
|
|
/* ulUnicodeRange3 */ read_ulong(font);
|
|
/* ulUnicodeRange4 */ read_ulong(font);
|
|
/* achVendID */ read_ulong(font); read_ulong(font);
|
|
read_ulong(font); read_ulong(font);
|
|
/* fsSelection */ read_ushort(font);
|
|
/* usFirstCharIndex */ read_ushort(font);
|
|
/* usLastCharIndex */ read_ushort(font);
|
|
os_2->sTypoAscender = (short)read_short(font);
|
|
os_2->sTypoDescender = (short)read_short(font);
|
|
/* sTypoLineGap */ read_short(font);
|
|
/* usWinAscent */ read_ushort(font);
|
|
/* usWinDescent */ read_ushort(font);
|
|
|
|
if (version >= 4)
|
|
{
|
|
/* ulCodePageRange1 */ read_ulong(font);
|
|
/* ulCodePageRange2 */ read_ulong(font);
|
|
os_2->sxHeight = (short)read_short(font);
|
|
os_2->sCapHeight = (short)read_short(font);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
|
|
//
|
|
// 'read_post()' - Read the PostScript table.
|
|
//
|
|
|
|
static bool // O - `true` on success, `false` otherwise
|
|
read_post(ttf_t *font, // I - Font
|
|
_ttf_off_post_t *post) // I - PostScript table
|
|
{
|
|
memset(post, 0, sizeof(_ttf_off_post_t));
|
|
|
|
if (seek_table(font, TTF_OFF_post, 0, false) == 0)
|
|
return (false);
|
|
|
|
/* version = */read_ulong(font);
|
|
post->italicAngle = (int)read_ulong(font) / 65536.0f;
|
|
/* underlinePosition = */read_ushort(font);
|
|
/* underlineThickness = */read_ushort(font);
|
|
post->isFixedPitch = read_ulong(font);
|
|
|
|
return (true);
|
|
}
|
|
|
|
|
|
//
|
|
// 'read_short()' - Read a 16-bit signed integer.
|
|
//
|
|
|
|
static int // O - 16-bit signed integer value or EOF
|
|
read_short(ttf_t *font) // I - Font
|
|
{
|
|
unsigned char buffer[2]; // Read buffer
|
|
|
|
|
|
if (read(font->fd, buffer, sizeof(buffer)) != sizeof(buffer))
|
|
return (EOF);
|
|
else if (buffer[0] & 0x80)
|
|
return (((buffer[0] << 8) | buffer[1]) - 65536);
|
|
else
|
|
return ((buffer[0] << 8) | buffer[1]);
|
|
}
|
|
|
|
|
|
//
|
|
// 'read_table()' - Read an OFF/TTF offset table.
|
|
//
|
|
|
|
static bool // O - `true` on success, `false` on error
|
|
read_table(ttf_t *font) // I - Font
|
|
{
|
|
int i; // Looping var
|
|
unsigned temp; // Temporary value
|
|
_ttf_off_dir_t *current; // Current table entry
|
|
|
|
|
|
// Read the table header:
|
|
//
|
|
// Fixed sfnt version (should be 0x10000 for version 1.0)
|
|
// USHORT numTables
|
|
// USHORT searchRange
|
|
// USHORT entrySelector
|
|
// USHORT rangeShift
|
|
/* sfnt version */
|
|
if ((temp = read_ulong(font)) != 0x10000 && temp != 0x4f54544f && temp != 0x74746366)
|
|
{
|
|
errorf(font, "Invalid font file.");
|
|
return (false);
|
|
}
|
|
|
|
if (temp == 0x74746366)
|
|
{
|
|
// Font collection, get the number of fonts and then seek to the start of
|
|
// the offset table for the desired font...
|
|
size_t idx; // Current index
|
|
|
|
TTF_DEBUG("read_table: Font collection\n");
|
|
|
|
/* Version */
|
|
if ((temp = read_ulong(font)) != 0x10000 && temp != 0x20000)
|
|
{
|
|
errorf(font, "Unsupported font collection version %f.", temp / 65536.0);
|
|
return (false);
|
|
}
|
|
|
|
TTF_DEBUG("read_table: Collection version=%f\n", temp / 65536.0);
|
|
|
|
/* numFonts */
|
|
if ((temp = read_ulong(font)) == 0)
|
|
{
|
|
errorf(font, "No fonts in collection.");
|
|
return (false);
|
|
}
|
|
|
|
font->num_fonts = (size_t)temp;
|
|
|
|
TTF_DEBUG("read_table: numFonts=%u\n", temp);
|
|
|
|
if (font->idx >= font->num_fonts)
|
|
return (false);
|
|
|
|
/* OffsetTable */
|
|
temp = read_ulong(font);
|
|
for (idx = font->idx; idx > 0; idx --)
|
|
temp = read_ulong(font);
|
|
|
|
TTF_DEBUG("read_table: Offset for font %u is %u.\n", (unsigned)font->idx, temp);
|
|
|
|
if (lseek(font->fd, temp + 4, SEEK_SET) < 0)
|
|
{
|
|
errorf(font, "Unable to seek to font %u: %s", (unsigned)font->idx, strerror(errno));
|
|
return (false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Not a collection so just one font...
|
|
font->num_fonts = 1;
|
|
}
|
|
|
|
// numTables
|
|
if ((font->table.num_entries = read_ushort(font)) <= 0)
|
|
{
|
|
errorf(font, "Unable to read font tables.");
|
|
return (false);
|
|
}
|
|
|
|
TTF_DEBUG("read_table: num_entries=%u\n", (unsigned)font->table.num_entries);
|
|
|
|
// searchRange
|
|
if (read_ushort(font) < 0)
|
|
{
|
|
errorf(font, "Unable to read font tables.");
|
|
return (false);
|
|
}
|
|
|
|
// entrySelector
|
|
if (read_ushort(font) < 0)
|
|
{
|
|
errorf(font, "Unable to read font tables.");
|
|
return (false);
|
|
}
|
|
|
|
// rangeShift
|
|
if (read_ushort(font) < 0)
|
|
{
|
|
errorf(font, "Unable to read font tables.");
|
|
return (false);
|
|
}
|
|
|
|
/*
|
|
* Read the table entries...
|
|
*/
|
|
|
|
if ((font->table.entries = calloc((size_t)font->table.num_entries, sizeof(_ttf_off_dir_t))) == NULL)
|
|
{
|
|
errorf(font, "Unable to allocate memory for font tables.");
|
|
return (false);
|
|
}
|
|
|
|
for (i = font->table.num_entries, current = font->table.entries; i > 0; i --, current ++)
|
|
{
|
|
current->tag = read_ulong(font);
|
|
current->checksum = read_ulong(font);
|
|
current->offset = read_ulong(font);
|
|
current->length = read_ulong(font);
|
|
|
|
TTF_DEBUG("read_table: [%d] tag='%c%c%c%c' checksum=%u offset=%u length=%u\n", i, (current->tag >> 24) & 255, (current->tag >> 16) & 255, (current->tag >> 8) & 255, current->tag & 255, current->checksum, current->offset, current->length);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
|
|
//
|
|
// 'read_ulong()' - Read a 32-bit unsigned integer.
|
|
//
|
|
|
|
static unsigned // O - 32-bit unsigned integer value or EOF
|
|
read_ulong(ttf_t *font) // I - Font
|
|
{
|
|
unsigned char buffer[4]; // Read buffer
|
|
|
|
|
|
if (read(font->fd, buffer, sizeof(buffer)) != sizeof(buffer))
|
|
return ((unsigned)EOF);
|
|
else
|
|
return ((unsigned)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]));
|
|
}
|
|
|
|
|
|
//
|
|
// 'read_ushort()' - Read a 16-bit unsigned integer.
|
|
//
|
|
|
|
static int // O - 16-bit unsigned integer value or EOF
|
|
read_ushort(ttf_t *font) // I - Font
|
|
{
|
|
unsigned char buffer[2]; // Read buffer
|
|
|
|
|
|
if (read(font->fd, buffer, sizeof(buffer)) != sizeof(buffer))
|
|
return (EOF);
|
|
else
|
|
return ((buffer[0] << 8) | buffer[1]);
|
|
}
|
|
|
|
|
|
//
|
|
// 'seek_table()' - Seek to a specific table in a font.
|
|
//
|
|
|
|
static unsigned // O - Length of table or 0 if not found
|
|
seek_table(ttf_t *font, // I - Font
|
|
unsigned tag, // I - Tag to find
|
|
unsigned offset, // I - Additional offset
|
|
bool required) // I - Required table?
|
|
{
|
|
int i; // Looping var
|
|
_ttf_off_dir_t *current; // Current entry
|
|
|
|
|
|
// Look up the tag in the table...
|
|
for (i = font->table.num_entries, current = font->table.entries; i > 0; i --, current ++)
|
|
{
|
|
if (current->tag == tag)
|
|
{
|
|
// Found it, seek and return...
|
|
if (lseek(font->fd, current->offset + offset, SEEK_SET) == (current->offset + offset))
|
|
{
|
|
// Successful seek...
|
|
return (current->length - offset);
|
|
}
|
|
else
|
|
{
|
|
// Seek failed...
|
|
errorf(font, "Unable to seek to %c%c%c%c table: %s", (tag >> 24) & 255, (tag >> 16) & 255, (tag >> 8) & 255, tag & 255, strerror(errno));
|
|
return (0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Not found, return 0...
|
|
if (required)
|
|
errorf(font, "%c%c%c%c table not found.", (tag >> 24) & 255, (tag >> 16) & 255, (tag >> 8) & 255, tag & 255);
|
|
|
|
return (0);
|
|
}
|