ae6a55dbac
To generate new fonts, the freetype2 library is required. This can now be specified by configure in the usual way. If it's missing, new fonts cannot be generated.
414 lines
11 KiB
C
414 lines
11 KiB
C
/**
|
|
* xrdp: A Remote Desktop Protocol server.
|
|
*
|
|
* Copyright (C) Jay Sorg 2004-2022
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* fonts
|
|
*/
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include <config_ac.h>
|
|
#endif
|
|
|
|
#include <limits.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
|
|
#include "list.h"
|
|
#include "os_calls.h"
|
|
#include "parse.h"
|
|
#include "string_calls.h"
|
|
#include "fv1.h"
|
|
|
|
/**
|
|
* What the program is doing
|
|
*/
|
|
enum program_mode
|
|
{
|
|
PM_UNSPECIFIED = 0,
|
|
PM_INFO,
|
|
PM_GLYPH_INFO_TABLE,
|
|
PM_SHOW_CHAR
|
|
};
|
|
|
|
/**
|
|
* Parsed program arguments
|
|
*/
|
|
struct program_args
|
|
{
|
|
const char *font_file;
|
|
enum program_mode mode;
|
|
int ucode; /* Unicode character to display in 'c' mode */
|
|
};
|
|
|
|
/**************************************************************************//**
|
|
* Parses the program args
|
|
*
|
|
* @param argc Passed to main
|
|
* @param @argv Passed to main
|
|
* @param pa program_pargs structure for resulting values
|
|
* @return !=0 for success
|
|
*/
|
|
static int
|
|
parse_program_args(int argc, char *argv[], struct program_args *pa)
|
|
{
|
|
int params_ok = 1;
|
|
int opt;
|
|
|
|
pa->font_file = NULL;
|
|
pa->mode = PM_UNSPECIFIED;
|
|
pa->ucode = 0;
|
|
|
|
while ((opt = getopt(argc, argv, "c:it")) != -1)
|
|
{
|
|
switch (opt)
|
|
{
|
|
case 'i':
|
|
if (pa->mode == PM_UNSPECIFIED)
|
|
{
|
|
pa->mode = PM_INFO;
|
|
}
|
|
else
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Can't have two modes set");
|
|
params_ok = 0;
|
|
}
|
|
break;
|
|
|
|
case 't':
|
|
if (pa->mode == PM_UNSPECIFIED)
|
|
{
|
|
pa->mode = PM_GLYPH_INFO_TABLE;
|
|
}
|
|
else
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Can't have two modes set");
|
|
params_ok = 0;
|
|
}
|
|
break;
|
|
|
|
case 'c':
|
|
if (pa->mode == PM_UNSPECIFIED)
|
|
{
|
|
pa->mode = PM_SHOW_CHAR;
|
|
if (toupper(optarg[0]) == 'U' && optarg[1] == '+')
|
|
{
|
|
char *hex = g_strdup(optarg);
|
|
hex[0] = '0';
|
|
hex[1] = 'x';
|
|
pa->ucode = g_atoix(hex);
|
|
g_free(hex);
|
|
}
|
|
else if (optarg[0] == '@')
|
|
{
|
|
pa->ucode = optarg[1];
|
|
}
|
|
else
|
|
{
|
|
pa->ucode = g_atoix(optarg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Can't have two modes set");
|
|
params_ok = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
LOG(LOG_LEVEL_ERROR, "Unrecognised switch '%c'", (char)opt);
|
|
params_ok = 0;
|
|
}
|
|
}
|
|
|
|
if (argc <= optind)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "No font file specified");
|
|
params_ok = 0;
|
|
}
|
|
else if ((argc - optind) > 1)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Unexpected arguments after font file");
|
|
params_ok = 0;
|
|
}
|
|
else
|
|
{
|
|
pa->font_file = argv[optind];
|
|
}
|
|
|
|
return params_ok;
|
|
}
|
|
|
|
/**************************************************************************//**
|
|
* Displays information about a font file
|
|
*
|
|
* @param fv1 loaded font file
|
|
*/
|
|
static void
|
|
display_font_file_info(const struct fv1_file *fv1)
|
|
{
|
|
g_printf("Font name : %s\n", fv1->font_name);
|
|
g_printf("Point size (%d DPI) : %d\n", FV1_DEVICE_DPI, fv1->point_size);
|
|
g_printf("Style : %d\n", fv1->style);
|
|
if (fv1->body_height == 0 && fv1->glyphs->count > 0)
|
|
{
|
|
const struct fv1_glyph *g =
|
|
(const struct fv1_glyph *)fv1->glyphs->items[0];
|
|
g_printf("Body height : %d (from 1st glyph)\n", -g->baseline + 1);
|
|
}
|
|
else
|
|
{
|
|
g_printf("Body height : %d\n", fv1->body_height);
|
|
}
|
|
g_printf("Descender : %d\n", fv1->min_descender);
|
|
|
|
if (fv1->glyphs->count == 0)
|
|
{
|
|
g_printf("\nFile contains no glyphs\n");
|
|
}
|
|
else
|
|
{
|
|
g_printf("Min glyph index : U+%04X\n", FV1_MIN_CHAR);
|
|
g_printf("Max glyph index : U+%04X\n", FV1_GLYPH_END(fv1) - 1);
|
|
|
|
/* Work out the statistics */
|
|
unsigned short max_width = 0;
|
|
int max_width_ucode = 0;
|
|
unsigned short max_height = 0;
|
|
int max_height_ucode = 0;
|
|
short min_baseline = 0;
|
|
int min_baseline_ucode = 0;
|
|
short min_offset = 0;
|
|
int min_offset_ucode = 0;
|
|
short max_offset = 0;
|
|
int max_offset_ucode = 0;
|
|
unsigned short max_inc_by = 0;
|
|
int max_inc_by_ucode = 0;
|
|
|
|
/* Derived quantities */
|
|
short min_y_descender = SHRT_MAX;
|
|
int min_y_descender_ucode = 0;
|
|
int max_datasize = -1;
|
|
int max_datasize_ucode = 0;
|
|
|
|
/* Loop and output macros */
|
|
#define SET_MIN(ucode,field,val) \
|
|
if ((field) < (val)) \
|
|
{ \
|
|
val = (field); \
|
|
val##_ucode = (ucode); \
|
|
}
|
|
|
|
#define SET_MAX(ucode,field,val) \
|
|
if ((field) > (val)) \
|
|
{ \
|
|
val = (field); \
|
|
val##_ucode = (ucode); \
|
|
}
|
|
|
|
#define OUTPUT_INFO(string, val) \
|
|
if (val##_ucode > 0) \
|
|
{ \
|
|
g_printf(string, val, val##_ucode); \
|
|
}
|
|
int u;
|
|
for (u = FV1_MIN_CHAR ; u < FV1_GLYPH_END(fv1); ++u)
|
|
{
|
|
const struct fv1_glyph *g = FV1_GET_GLYPH(fv1, u);
|
|
if (g != NULL)
|
|
{
|
|
short y_descender = - (g->baseline + g->height);
|
|
int datasize = FONT_DATASIZE(g);
|
|
|
|
SET_MAX(u, g->width, max_width);
|
|
SET_MAX(u, g->height, max_height);
|
|
SET_MIN(u, g->baseline, min_baseline);
|
|
SET_MIN(u, g->offset, min_offset);
|
|
SET_MAX(u, g->offset, max_offset);
|
|
SET_MAX(u, g->inc_by, max_inc_by);
|
|
SET_MIN(u, y_descender, min_y_descender);
|
|
SET_MAX(u, datasize, max_datasize);
|
|
}
|
|
}
|
|
|
|
OUTPUT_INFO("Max glyph width : %d (U+%04X)\n", max_width);
|
|
OUTPUT_INFO("Max glyph height : %d (U+%04X)\n", max_height);
|
|
OUTPUT_INFO("Min glyph y-baseline : %d (U+%04X)\n", min_baseline);
|
|
OUTPUT_INFO("Min glyph y-descender : %d (U+%04X)\n", min_y_descender);
|
|
OUTPUT_INFO("Min glyph x-offset : %d (U+%04X)\n", min_offset);
|
|
OUTPUT_INFO("Max glyph x-offset : %d (U+%04X)\n", max_offset);
|
|
OUTPUT_INFO("Max glyph x-inc_by : %d (U+%04X)\n", max_inc_by);
|
|
OUTPUT_INFO("Max glyph datasize : %d (U+%04X)\n", max_datasize);
|
|
|
|
#undef SET_MIN
|
|
#undef SET_MAX
|
|
#undef OUTPUT_INFO
|
|
}
|
|
}
|
|
|
|
/**************************************************************************//**
|
|
* Display info in a table about all the glyphs
|
|
* @param fv1 font file
|
|
*/
|
|
static void
|
|
display_glyph_info_table(const struct fv1_file *fv1)
|
|
{
|
|
int u;
|
|
g_printf("character,width,height,baseline,offset,inc_by,datasize\n");
|
|
|
|
for (u = FV1_MIN_CHAR; u < FV1_GLYPH_END(fv1); ++u)
|
|
{
|
|
const struct fv1_glyph *g = FV1_GET_GLYPH(fv1, u);
|
|
if (g != NULL)
|
|
{
|
|
int datasize = FONT_DATASIZE(g);
|
|
g_printf("%d,%hu,%hu,%hd,%hd,%hu,%d\n",
|
|
u, g->width, g->height, g->baseline,
|
|
g->offset, g->inc_by, datasize);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************************************************************************//**
|
|
* Converts a font data row to a printable string
|
|
*
|
|
* @param rowdata Pointer to the first byte of the row data
|
|
* @param width Number of pixels in the row
|
|
* @param out Output buffer. Must be sized by the caller to be at
|
|
* least width+1 bytes
|
|
*/
|
|
static void
|
|
row_to_str(const unsigned char *rowdata, int width, char *out)
|
|
{
|
|
int x;
|
|
int mask = 1 << 7;
|
|
for (x = 0 ; x < width ; ++x)
|
|
{
|
|
out[x] = ((*rowdata & mask) != 0) ? 'X' : '.';
|
|
mask >>= 1;
|
|
if (mask == 0)
|
|
{
|
|
mask = 1 << 7;
|
|
++rowdata;
|
|
}
|
|
}
|
|
out[width] = '\0';
|
|
}
|
|
|
|
/**************************************************************************//**
|
|
* Display info about a specific glyph
|
|
* @param ucode Unicode character value
|
|
* @param g Glyph
|
|
*/
|
|
static void
|
|
display_glyph_info(int ucode, const struct fv1_glyph *g)
|
|
{
|
|
|
|
char *row_buffer = (char *)g_malloc(g->width + 1, 0);
|
|
if (row_buffer == NULL)
|
|
{
|
|
g_printf("<No memory>\n");
|
|
}
|
|
else
|
|
{
|
|
g_printf("Glyph : U+%04X\n", ucode);
|
|
g_printf(" Width : %d\n", g->width);
|
|
g_printf(" Height : %d\n", g->height);
|
|
g_printf(" Baseline : %d\n", g->baseline);
|
|
g_printf(" Offset : %d\n", g->offset);
|
|
g_printf(" Inc By : %d\n", g->inc_by);
|
|
|
|
g_printf(" Data :\n");
|
|
int y;
|
|
const unsigned char *dataptr = g->data;
|
|
|
|
for (y = 0 ; y < g->height; ++y)
|
|
{
|
|
row_to_str(dataptr, g->width, row_buffer);
|
|
g_printf(" %+3d: %s\n", y + g->baseline, row_buffer);
|
|
dataptr += (g->width + 7) / 8;
|
|
}
|
|
g_free(row_buffer);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************//**
|
|
* Main
|
|
*
|
|
* @param argc Argument count
|
|
* @param argv Arguments
|
|
*/
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
struct fv1_file *fv1 = NULL;
|
|
struct log_config *logging;
|
|
struct program_args pa;
|
|
int rv = 1;
|
|
|
|
logging = log_config_init_for_console(LOG_LEVEL_WARNING,
|
|
g_getenv("DUMPFV1_LOG_LEVEL"));
|
|
log_start_from_param(logging);
|
|
log_config_free(logging);
|
|
|
|
if (parse_program_args(argc, argv, &pa) &&
|
|
(fv1 = fv1_file_load(pa.font_file)) != NULL)
|
|
{
|
|
switch (pa.mode)
|
|
{
|
|
case PM_INFO:
|
|
display_font_file_info(fv1);
|
|
rv = 0;
|
|
break;
|
|
|
|
case PM_GLYPH_INFO_TABLE:
|
|
display_glyph_info_table(fv1);
|
|
rv = 0;
|
|
break;
|
|
|
|
case PM_SHOW_CHAR:
|
|
if (pa.ucode < FV1_MIN_CHAR)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Value for -c must be at least U+%04X",
|
|
FV1_MIN_CHAR);
|
|
}
|
|
else if (pa.ucode >= FV1_GLYPH_END(fv1))
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Value for -c must be less than U+%04X",
|
|
FV1_GLYPH_END(fv1));
|
|
}
|
|
else
|
|
{
|
|
const struct fv1_glyph *g =
|
|
(const struct fv1_glyph *)
|
|
list_get_item(fv1->glyphs, pa.ucode - FV1_MIN_CHAR);
|
|
display_glyph_info(pa.ucode, g);
|
|
rv = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
LOG(LOG_LEVEL_ERROR, "Specify one of '-i' or '-c'");
|
|
break;
|
|
}
|
|
}
|
|
|
|
fv1_file_free(fv1);
|
|
log_end();
|
|
|
|
return rv;
|
|
}
|