418 lines
12 KiB
C
Raw Normal View History

/*---------------------------------------------------------------------------*
| PDFlib - A library for generating PDF on the fly |
+---------------------------------------------------------------------------+
| Copyright (c) 1997-2005 Thomas Merz and PDFlib GmbH. All rights reserved. |
+---------------------------------------------------------------------------+
| |
| This software is subject to the PDFlib license. It is NOT in the |
| public domain. Extended versions and commercial licenses are |
| available, please check http://www.pdflib.com. |
| |
*---------------------------------------------------------------------------*/
/* $Id: p_pfm.c,v 1.1 2006-05-04 16:36:51 brugno Exp $
*
* PDFlib routines for fast reading of PFM font metrics files
*
*/
#include "p_intern.h"
#include "p_font.h"
/* read data types from the PFM */
#define PFM_BYTE(offset) pfm[offset]
#define PFM_WORD(offset) PDC_GET_WORD(&pfm[offset])
#define PFM_SHORT(offset) PDC_GET_SHORT(&pfm[offset])
#define PFM_DWORD(offset) PDC_GET_DWORD3(&pfm[offset])
/* Offsets in the buffer containing the various PFM structures */
#define header_base 0
#define header_dfVersion (PFM_WORD(header_base + 0))
#define header_dfSize (PFM_DWORD(header_base + 2))
#define header_dfAscent (PFM_WORD(header_base + 74))
#define header_dfItalic (PFM_BYTE(header_base + 80))
#define header_dfWeight (PFM_WORD(header_base + 83))
#define header_dfCharSet (PFM_BYTE(header_base + 85))
#define header_dfPitchAndFamily (PFM_BYTE(header_base + 90))
#define header_dfMaxWidth (PFM_WORD(header_base + 93))
#define header_dfFirstChar (PFM_BYTE(header_base + 95))
#define header_dfLastChar (PFM_BYTE(header_base + 96))
#define header_dfDefaultChar (PFM_BYTE(header_base + 97))
#define ext_base 117
#define ext_dfExtentTable (PFM_DWORD(ext_base + 6))
#define ext_dfKernPairs (PFM_DWORD(ext_base + 14))
#define ext_dfKernTrack (PFM_DWORD(ext_base + 18))
#define ext_dfDriverInfo (PFM_DWORD(ext_base + 22))
#define etm_base 147
#define etmCapHeight (PFM_SHORT(etm_base + 14))
#define etmXHeight (PFM_SHORT(etm_base + 16))
#define etmLowerCaseAscent (PFM_SHORT(etm_base + 18))
#define etmLowerCaseDescent (PFM_SHORT(etm_base + 20))
#define etmSlant (PFM_SHORT(etm_base + 22))
#define etmUnderlineOffset (PFM_SHORT(etm_base + 32))
#define etmUnderlineWidth (PFM_SHORT(etm_base + 34))
#define dfDevice 199
/* Windows font descriptor flags */
#define PDF_FIXED_PITCH 0x01 /* Fixed width font; rarely used flag */
#define PDF_DONTCARE 0x00 /* Don't care or don't know. */
#define PDF_ROMAN 0x10 /* Variable stroke width, serifed */
#define PDF_SWISS 0x20 /* Variable stroke width, sans-serifed */
#define PDF_MODERN 0x30 /* fixed pitch */
#define PDF_SCRIPT 0x40 /* Cursive, etc. */
#define PDF_DECORATIVE 0x50 /* Old English, etc. */
/* Windows character set flags */
#define PFM_ANSI_CHARSET 0
#define PFM_SYMBOL_CHARSET 2
#define PFM_GREEK_CHARSET 161
#define PFM_TURKISH_CHARSET 162
#define PFM_VIETNAMESE_CHARSET 163
#define PFM_HEBREW_CHARSET 177
#define PFM_ARABIC_CHARSET 178
#define PFM_BALTIC_CHARSET 186
#define PFM_RUSSIAN_CHARSET 204
#define PFM_THAI_CHARSET 222
#define PFM_EASTEUROPE_CHARSET 238
static const pdc_keyconn pdf_charset_keylist[] =
{
{"winansi", PFM_ANSI_CHARSET },
{"", PFM_SYMBOL_CHARSET },
{"cp1253", PFM_GREEK_CHARSET },
{"cp1254", PFM_TURKISH_CHARSET },
{"cp1258", PFM_VIETNAMESE_CHARSET},
{"cp1255", PFM_HEBREW_CHARSET },
{"cp1256", PFM_ARABIC_CHARSET },
{"cp1257", PFM_BALTIC_CHARSET },
{"cp1251", PFM_RUSSIAN_CHARSET },
{"cp874", PFM_THAI_CHARSET },
{"cp1250", PFM_EASTEUROPE_CHARSET},
{NULL, 0},
};
#define PDF_STRING_PostScript \
((const char*) "\120\157\163\164\123\143\162\151\160\164")
/*
* Kerning pairs
*/
typedef struct kern_
{
pdc_byte first; /* First character */
pdc_byte second; /* Second character */
pdc_byte kern[2]; /* Kern distance */
}
KERN;
pdc_bool
pdf_check_pfm_encoding(PDF *p, pdc_font *font, const char *fontname,
pdc_encoding enc)
{
const char *encname =
pdc_errprintf(p->pdc, "%.*s", PDC_ET_MAXSTRLEN,
pdf_get_encoding_name(p, enc, font));
const char *newencname = NULL;
pdc_encoding newenc = pdc_invalidenc;
pdc_bool isstdlatin = pdc_undef;
/* Font encoding */
newencname = pdc_get_keyword(font->encoding, pdf_charset_keylist);
if (newencname == NULL)
{
pdc_set_errmsg(p->pdc, PDF_E_T1_BADCHARSET,
pdc_errprintf(p->pdc, "%d", font->encoding), fontname, 0, 0);
if (font->verbose == pdc_true)
pdc_error(p->pdc, -1, 0, 0, 0, 0);
return pdc_false;
}
if (strlen(newencname))
{
int codepage = 0;
pdc_trace_protocol(p->pdc, 2, trc_font,
"\tFont internal encoding \"%s\" found\n", newencname);
newenc = pdf_find_encoding(p, newencname);
if (newenc == pdc_invalidenc)
newenc = pdf_insert_encoding(p, newencname, &codepage, pdc_true);
font->isstdlatin = pdc_true;
}
else
{
pdc_trace_protocol(p->pdc, 2, trc_font, "\tSymbol font\n");
font->isstdlatin = pdc_false;
}
/* builtin */
if (enc == pdc_builtin)
isstdlatin = pdc_false;
/* unicode */
if (enc == pdc_unicode)
{
font->unibyte = pdc_true;
isstdlatin = pdc_true;
enc = newenc;
}
/* encoding is subset of 8-bit encoding */
if (enc >= pdc_winansi && newenc >= pdc_winansi)
{
if (pdf_is_encoding_subset(p, pdf_get_encoding_vector(p, enc),
pdf_get_encoding_vector(p, newenc)))
{
if (enc != pdc_winansi && newenc == pdc_winansi &&
strcmp(encname, "iso8859-1"))
font->towinansi = pdc_invalidenc;
isstdlatin = pdc_true;
enc = newenc;
}
}
/* illegal encoding */
if (isstdlatin == pdc_undef || font->isstdlatin == pdc_undef)
{
pdc_set_errmsg(p->pdc, PDF_E_FONT_BADENC, fontname, encname, 0, 0);
if (font->verbose == pdc_true)
pdc_error(p->pdc, -1, 0, 0, 0, 0);
return pdc_false;
}
font->encoding = enc;
if (!isstdlatin && font->isstdlatin)
{
if (font->verbose == pdc_true && strcmp(font->encapiname, "auto"))
pdc_warning(p->pdc, PDF_E_FONT_FORCEENC,
pdf_get_encoding_name(p, newenc, NULL),
encname, fontname, 0);
font->encoding = newenc;
}
if (isstdlatin && !font->isstdlatin)
{
if (font->verbose == pdc_true && strcmp(font->encapiname, "auto"))
pdc_warning(p->pdc, PDF_E_FONT_FORCEENC,
pdf_get_encoding_name(p, pdc_builtin, NULL),
encname, fontname, 0);
font->encoding = pdc_builtin;
font->towinansi = pdc_invalidenc;
}
if (font->towinansi != pdc_invalidenc)
pdf_transform_fontwidths(p, font,
pdf_get_encoding_vector(p, font->encoding),
pdf_get_encoding_vector(p, font->towinansi));
return pdc_true;
}
/*
* Currently we do not populate the following fields correctly:
* - serif flag
*/
static pdc_bool
pdf_parse_pfm(PDF *p, pdc_file *fp, pdc_font *font)
{
static const char fn[] = "pdf_parse_pfm";
size_t length;
pdc_byte *pfm;
pdc_bool ismem;
int i, dfFirstChar, dfLastChar, default_width;
double w;
unsigned long dfExtentTable;
/* read whole file and close it */
pfm = (pdc_byte *) pdc_freadall(fp, &length, &ismem);
pdc_fclose(fp);
/* check whether this is really a valid PostScript PFM file */
if (pfm == NULL ||
(header_dfVersion != 0x100 && header_dfVersion != 0x200) ||
dfDevice > length ||
strncmp((const char *) pfm + dfDevice, PDF_STRING_PostScript, 10) ||
ext_dfDriverInfo > length)
{
if (!ismem)
pdc_free(p->pdc, pfm);
return pdc_false;
}
/* fetch relevant data from the PFM */
font->type = pdc_Type1;
w = header_dfWeight / 65.0;
font->StdVW = (int) (PDF_STEMV_MIN + w * w);
font->name = pdc_strdup(p->pdc, (const char *)pfm + ext_dfDriverInfo);
pdc_trace_protocol(p->pdc, 1, trc_font,
"\tPostScript fontname: \"%s\"\n", font->name);
switch (header_dfPitchAndFamily & 0xF0)
{
case PDF_ROMAN:
font->flags |= SERIF;
break;
case PDF_MODERN:
/* Has to be ignored, contrary to MS's specs */
break;
case PDF_SCRIPT:
font->flags |= SCRIPT;
break;
case PDF_DECORATIVE:
/* the dfCharSet flag lies in this case... */
header_dfCharSet = PFM_SYMBOL_CHARSET;
break;
case PDF_SWISS:
case PDF_DONTCARE:
default:
break;
}
/* temporarily */
font->encoding = (pdc_encoding) header_dfCharSet;
dfFirstChar = header_dfFirstChar;
dfLastChar = header_dfLastChar;
dfExtentTable = ext_dfExtentTable;
/*
* Some rare PFMs do not contain any ExtentTable if the fixed pitch flag
* is set. Use the dfMaxWidth entry for all glyphs in this case.
* If the user forced the font to be monospaced we use this value instead.
*/
if ((!(header_dfPitchAndFamily & PDF_FIXED_PITCH) && dfExtentTable == 0) ||
font->monospace)
{
font->isFixedPitch = pdc_true;
default_width = font->monospace ? font->monospace :
(int) header_dfMaxWidth;
}
else
{
/* default values -- don't take the width of the default character */
default_width = PDF_DEFAULT_WIDTH;
}
font->widths = (int *) pdc_calloc(p->pdc,
font->numOfCodes * sizeof(int), fn);
for (i = 0; i < font->numOfCodes; i++)
font->widths[i] = default_width;
if (!font->isFixedPitch)
{
if (ext_dfExtentTable == 0 ||
ext_dfExtentTable + 2 * (header_dfLastChar-header_dfFirstChar) + 1 >
length)
{
if (!ismem)
pdc_free(p->pdc, pfm);
return pdc_false;
}
for (i = dfFirstChar; i <= dfLastChar; i++)
font->widths[i] =
(int) PFM_WORD(dfExtentTable + 2 * (i - dfFirstChar));
/*
* Check whether the font is actually monospaced
* (the fixed pitch flag is not necessarily set)
*/
default_width = font->widths[dfFirstChar];
for (i = dfFirstChar+1; i <= dfLastChar; i++)
if (default_width != font->widths[i])
break;
if (i == dfLastChar + 1)
font->isFixedPitch = pdc_true;
}
font->defWidth = default_width;
font->italicAngle = (header_dfItalic ? etmSlant/(10.0) : 0.0);
font->capHeight = etmCapHeight;
font->xHeight = etmXHeight;
font->descender = -etmLowerCaseDescent;
font->ascender = (int) header_dfAscent;
font->underlinePosition = -etmUnderlineOffset;
font->underlineThickness = etmUnderlineWidth;
pdf_font_set_missvalues(p, font);
font->llx = -200;
font->lly = font->descender;
font->urx = header_dfMaxWidth;
font->ury = font->ascender;
/* try to fix broken entries */
if (font->lly > font->ury)
font->ury = font->lly + 200;
if (font->llx > font->urx)
font->urx = 1000;
if (!ismem)
pdc_free(p->pdc, pfm);
return pdc_true;
}
pdc_bool
pdf_get_metrics_pfm(
PDF *p,
pdc_font *font,
const char *fontname,
pdc_encoding enc,
const char *filename)
{
char fullname[PDC_FILENAMELEN];
pdc_file *pfmfile;
/* open PFM file */
pfmfile = pdf_fopen_name(p, filename, fullname, "PFM ", PDC_FILE_BINARY);
if (pfmfile == NULL)
{
if (font->verbose_open)
pdc_error(p->pdc, -1, 0, 0, 0, 0);
return pdc_undef;
}
pdc_trace_protocol(p->pdc, 1, trc_font,
"\tLoading PFM metric fontfile \"%s\":\n", filename);
/* Read PFM metrics */
if (!pdf_parse_pfm(p, pfmfile, font))
{
pdc_set_errmsg(p->pdc, PDF_E_FONT_CORRUPT, "PFM", filename, 0, 0);
if (font->verbose == pdc_true)
pdc_error(p->pdc, -1, 0, 0, 0, 0);
return pdc_false;
}
/* Check encoding */
if (!pdf_check_pfm_encoding(p, font, fontname, enc))
return pdc_false;
if (!pdf_make_fontflag(p, font))
return pdc_false;
return pdc_true;
}