/* Copyright (C) 1992-2022 Free Software Foundation, Inc.
Written by James Clark (jjc@jclark.com)
This file is part of groff.
groff is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or
(at your option) any later version.
groff is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
/*
* xtotroff
*
* convert X font metrics into troff font metrics
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#define __GETOPT_PREFIX groff_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "XFontName.h"
#include "DviChar.h"
#define charWidth(fi,c) \
((fi)->per_char[(c) - (fi)->min_char_or_byte2].width)
#define charHeight(fi,c) \
((fi)->per_char[(c) - (fi)->min_char_or_byte2].ascent)
#define charDepth(fi,c) \
((fi)->per_char[(c) - (fi)->min_char_or_byte2].descent)
#define charLBearing(fi,c) \
((fi)->per_char[(c) - (fi)->min_char_or_byte2].lbearing)
#define charRBearing(fi,c) \
((fi)->per_char[(c) - (fi)->min_char_or_byte2].rbearing)
extern const char *Version_string;
static char *program_name;
Display *dpy;
unsigned resolution = 75;
unsigned point_size = 10;
char *destdir = NULL;
static bool charExists(XFontStruct * fi, int c)
{
XCharStruct *p;
/* 'c' is always >= 0 */
if ((unsigned int) c < fi->min_char_or_byte2
|| (unsigned int) c > fi->max_char_or_byte2)
return false;
p = fi->per_char + (c - fi->min_char_or_byte2);
return p->lbearing != 0 || p->rbearing != 0 || p->width != 0
|| p->ascent != 0 || p->descent != 0 || p->attributes != 0;
}
/* Canonicalize the font name by replacing scalable parts by *s. */
static bool CanonicalizeFontName(char *font_name, char *canon_font_name)
{
unsigned int attributes;
XFontName parsed;
if (!XParseFontName(font_name, &parsed, &attributes)) {
fprintf(stderr, "%s: not a standard font name: \"%s\"\n",
program_name, font_name);
return false;
}
attributes &= ~(FontNamePixelSize | FontNameAverageWidth
| FontNamePointSize
| FontNameResolutionX | FontNameResolutionY);
XFormatFontName(&parsed, attributes, canon_font_name);
return true;
}
static bool
FontNamesAmbiguous(const char *font_name, char **names, int count)
{
char name1[2048], name2[2048];
int i;
if (1 == count)
return false;
for (i = 0; i < count; i++) {
if (!CanonicalizeFontName(names[i], 0 == i ? name1 : name2)) {
fprintf(stderr, "%s: invalid font name: \"%s\"\n", program_name,
names[i]);
return true;
}
if (i > 0 && strcmp(name1, name2) != 0) {
fprintf(stderr, "%s: ambiguous font name: \"%s\"", program_name,
font_name);
fprintf(stderr, " matches \"%s\"", names[0]);
fprintf(stderr, " and \"%s\"", names[i]);
return true;
}
}
return false;
}
static void xtotroff_exit(int status)
{
free(destdir);
exit(status);
}
static bool MapFont(char *font_name, const char *troff_name)
{
XFontStruct *fi;
int count;
char **names;
FILE *out;
unsigned int c;
unsigned int attributes;
XFontName parsed;
int j, k;
DviCharNameMap *char_map;
/* 'encoding' needs to hold a CharSetRegistry (256), a CharSetEncoding
(256) [both from XFontName.h], a dash, and a null terminator. */
char encoding[256 * 2 + 1 + 1];
char *s;
int wid;
char name_string[2048];
if (!XParseFontName(font_name, &parsed, &attributes)) {
fprintf(stderr, "%s: not a standard font name: \"%s\"\n",
program_name, font_name);
return false;
}
attributes &= ~(FontNamePixelSize | FontNameAverageWidth);
attributes |= FontNameResolutionX;
attributes |= FontNameResolutionY;
attributes |= FontNamePointSize;
parsed.ResolutionX = resolution;
parsed.ResolutionY = resolution;
parsed.PointSize = point_size * 10;
XFormatFontName(&parsed, attributes, name_string);
names = XListFonts(dpy, name_string, 100000, &count);
if (count < 1) {
fprintf(stderr, "%s: invalid font name: \"%s\"\n", program_name,
font_name);
return false;
}
if (FontNamesAmbiguous(font_name, names, count))
return false;
XParseFontName(names[0], &parsed, &attributes);
size_t sz = sizeof encoding;
snprintf(encoding, sz, "%s-%s", parsed.CharSetRegistry,
parsed.CharSetEncoding);
for (s = encoding; *s; s++)
if (isupper(*s))
*s = tolower(*s);
char_map = DviFindMap(encoding);
if (!char_map) {
fprintf(stderr, "%s: not a standard encoding: \"%s\"\n",
program_name, encoding);
return false;
}
fi = XLoadQueryFont(dpy, names[0]);
if (!fi) {
fprintf(stderr, "%s: font does not exist: \"%s\"\n", program_name,
names[0]);
return false;
}
printf("%s -> %s\n", names[0], troff_name);
char *file_name = (char *)troff_name;
size_t dirlen = strlen(destdir);
if (dirlen > 0) {
size_t baselen = strlen(troff_name);
file_name = malloc(dirlen + baselen + 2 /* '/' and '\0' */);
if (NULL == file_name) {
fprintf(stderr, "%s: fatal error: unable to allocate memory\n",
program_name);
xtotroff_exit(EXIT_FAILURE);
}
(void) strcpy(file_name, destdir);
file_name[dirlen] = '/';
(void) strcpy((file_name + dirlen + 1), troff_name);
}
{ /* Avoid race while opening file */
int fd;
(void) unlink(file_name);
fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL, 0600);
out = fdopen(fd, "w");
}
if (NULL == out) {
fprintf(stderr, "%s: unable to create '%s': %s\n", program_name,
file_name, strerror(errno));
free(file_name);
return false;
}
fprintf(out, "name %s\n", troff_name);
if (!strcmp(char_map->encoding, "adobe-fontspecific"))
fprintf(out, "special\n");
if (charExists(fi, ' ')) {
int w = charWidth(fi, ' ');
if (w > 0)
fprintf(out, "spacewidth %d\n", w);
}
fprintf(out, "charset\n");
for (c = fi->min_char_or_byte2; c <= fi->max_char_or_byte2; c++) {
const char *name = DviCharName(char_map, c, 0);
if (charExists(fi, c)) {
int param[5];
wid = charWidth(fi, c);
fprintf(out, "%s\t%d", name ? name : "---", wid);
param[0] = charHeight(fi, c);
param[1] = charDepth(fi, c);
param[2] = 0; /* charRBearing (fi, c) - wid */
param[3] = 0; /* charLBearing (fi, c) */
param[4] = 0; /* XXX */
for (j = 0; j < 5; j++)
if (param[j] < 0)
param[j] = 0;
for (j = 4; j >= 0; j--)
if (param[j] != 0)
break;
for (k = 0; k <= j; k++)
fprintf(out, ",%d", param[k]);
fprintf(out, "\t0\t0%o\n", c);
if (name) {
for (k = 1; DviCharName(char_map, c, k); k++) {
fprintf(out, "%s\t\"\n", DviCharName(char_map, c, k));
}
}
}
}
XUnloadFont(dpy, fi->fid);
fclose(out);
free(file_name);
return true;
}
static void usage(FILE *stream)
{
fprintf(stream,
"usage: %s [-d destination-directory] [-r resolution]"
" [-s type-size] font-map\n"
"usage: %s {-v | --version}\n"
"usage: %s --help\n",
program_name, program_name, program_name);
}
int main(int argc, char **argv)
{
char troff_name[1024];
char font_name[1024];
char line[1024];
char *a, *b, c;
FILE *map;
int opt;
static const struct option long_options[] = {
{ "help", no_argument, 0, CHAR_MAX + 1 },
{ "version", no_argument, 0, 'v' },
{ NULL, 0, 0, 0 }
};
program_name = argv[0];
while ((opt = getopt_long(argc, argv, "d:gr:s:v", long_options,
NULL)) != EOF) {
switch (opt) {
case 'd':
destdir = strdup(optarg);
break;
case 'g':
/* unused; just for compatibility */
break;
case 'r':
sscanf(optarg, "%u", &resolution);
break;
case 's':
sscanf(optarg, "%u", &point_size);
break;
case 'v':
printf("GNU xtotroff (groff) version %s\n", Version_string);
xtotroff_exit(EXIT_SUCCESS);
break;
case CHAR_MAX + 1: /* --help */
usage(stdout);
xtotroff_exit(EXIT_SUCCESS);
break;
case '?':
usage(stderr);
xtotroff_exit(EXIT_FAILURE);
break;
}
}
if (argc - optind != 1) {
usage(stderr);
xtotroff_exit(EXIT_FAILURE);
}
dpy = XOpenDisplay(0);
if (!dpy) {
fprintf(stderr, "%s: fatal error: can't connect to the X server;"
" make sure the DISPLAY environment variable is set"
" correctly\n", program_name);
xtotroff_exit(EXIT_FAILURE);
}
map = fopen(argv[optind], "r");
if (NULL == map) {
fprintf(stderr, "%s: fatal error: unable to open map file '%s':"
" %s\n", program_name, argv[optind], strerror(errno));
xtotroff_exit(EXIT_FAILURE);
}
while (fgets(line, sizeof(line), map)) {
for (a = line, b = troff_name; *a; a++, b++) {
c = (*b = *a);
if (' ' == c || '\t' == c)
break;
}
*b = '\0';
while (*a && (' ' == *a || '\t' == *a))
++a;
for (b = font_name; *a; a++, b++)
if ((*b = *a) == '\n')
break;
*b = '\0';
if (!MapFont(font_name, troff_name))
xtotroff_exit(EXIT_FAILURE);
}
xtotroff_exit(EXIT_SUCCESS);
}
// Local Variables:
// fill-column: 72
// mode: C
// End:
// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: