diff --git a/dlls/wineps/Makefile.in b/dlls/wineps/Makefile.in index fd7e8a0578cdde6896a9283ce1864750285e6578..fa45bd82ebc4f3adae0ccd52ed15304ce2531319 100644 --- a/dlls/wineps/Makefile.in +++ b/dlls/wineps/Makefile.in @@ -69,6 +69,7 @@ C_SRCS = \ ps.c \ text.c \ truetype.c \ + type1afm.c \ $(DATA_C_SRCS) RC_SRCS= \ diff --git a/dlls/wineps/afm.c b/dlls/wineps/afm.c index 6ca61f2d01be1013031a890b78907e3b7250d675..c153984d3564d6aff1a42f4cdf9dab0756de1d90 100644 --- a/dlls/wineps/afm.c +++ b/dlls/wineps/afm.c @@ -1,454 +1,25 @@ /* - * Adobe Font Metric (AFM) file parsing - * See http://partners.adobe.com/asn/developer/PDFS/TN/5004.AFM_Spec.pdf + * Font metric functions common to Type 1 (AFM) and TrueType font files. + * Functions specific to Type 1 and TrueType fonts are in type1afm.c and + * truetype.c respectively. * * Copyright 1998 Huw D M Davies + * Copyright 2001 Ian Pilcher * */ #include "config.h" #include <string.h> -#include <stdlib.h> /* qsort() & bsearch() */ -#include <stdio.h> -#include <dirent.h> -#include <limits.h> /* INT_MIN */ -#ifdef HAVE_FLOAT_H -# include <float.h> /* FLT_MAX */ -#endif -#include "winnt.h" /* HEAP_ZERO_MEMORY */ -#include "winreg.h" + #include "psdrv.h" #include "debugtools.h" DEFAULT_DEBUG_CHANNEL(psdrv); -#include <ctype.h> /* ptr to fonts for which we have afm files */ FONTFAMILY *PSDRV_AFMFontList = NULL; -/* qsort/bsearch callback functions */ -typedef int (*compar_callback_fn) (const void *, const void *); - -static VOID SortFontMetrics(AFM *afm, AFMMETRICS *metrics); -static VOID CalcWindowsMetrics(AFM *afm); -static void PSDRV_ReencodeCharWidths(AFM *afm); - -/******************************************************************************* - * IsWinANSI - * - * Checks whether Unicode value is part of Microsoft code page 1252 - * - */ -static const INT ansiChars[21] = -{ - 0x0152, 0x0153, 0x0160, 0x0161, 0x0178, 0x017d, 0x017e, 0x0192, 0x02c6, - 0x02c9, 0x02dc, 0x03bc, 0x2013, 0x2014, 0x2026, 0x2030, 0x2039, 0x203a, - 0x20ac, 0x2122, 0x2219 -}; - -static int cmpUV(const INT *a, const INT *b) -{ - return *a - *b; -} - -inline static BOOL IsWinANSI(INT uv) -{ - if ((0x0020 <= uv && uv <= 0x007e) || (0x00a0 <= uv && uv <= 0x00ff) || - (0x2018 <= uv && uv <= 0x201a) || (0x201c <= uv && uv <= 0x201e) || - (0x2020 <= uv && uv <= 2022)) - return TRUE; - - if (bsearch(&uv, ansiChars, 21, sizeof(INT), - (compar_callback_fn)cmpUV) != NULL) - return TRUE; - - return FALSE; -} - -/******************************************************************************* - * CheckMetrics - * - * Check an AFMMETRICS structure to make sure all elements have been properly - * filled in. (Don't check UV or L.) - * - */ -static const AFMMETRICS badMetrics = -{ - INT_MIN, /* C */ - INT_MIN, /* UV */ - FLT_MAX, /* WX */ - NULL, /* N */ - { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }, /* B */ - NULL /* L */ -}; - -inline static BOOL CheckMetrics(const AFMMETRICS *metrics) -{ - if ( metrics->C == badMetrics.C || - metrics->WX == badMetrics.WX || - metrics->N == badMetrics.N || - metrics->B.llx == badMetrics.B.llx || - metrics->B.lly == badMetrics.B.lly || - metrics->B.urx == badMetrics.B.urx || - metrics->B.ury == badMetrics.B.ury ) - return FALSE; - - return TRUE; -} - - -/*********************************************************** - * - * PSDRV_AFMGetCharMetrics - * - * Parses CharMetric section of AFM file. - * - * Actually only collects the widths of numbered chars and puts then in - * afm->CharWidths. - */ -static BOOL PSDRV_AFMGetCharMetrics(AFM *afm, FILE *fp) -{ - unsigned char line[256], valbuf[256]; - unsigned char *cp, *item, *value, *curpos, *endpos; - int i; - AFMMETRICS *metric, *retval; - - metric = HeapAlloc( PSDRV_Heap, 0, afm->NumofMetrics * sizeof(AFMMETRICS)); - if (metric == NULL) - return FALSE; - - retval = metric; - - for(i = 0; i < afm->NumofMetrics; i++, metric++) { - - *metric = badMetrics; - - do { - if(!fgets(line, sizeof(line), fp)) { - ERR("Unexpected EOF\n"); - HeapFree(PSDRV_Heap, 0, retval); - return FALSE; - } - cp = line + strlen(line); - do { - *cp = '\0'; - cp--; - } while(cp >= line && isspace(*cp)); - } while (!(*line)); - - curpos = line; - while(*curpos) { - item = curpos; - while(isspace(*item)) - item++; - value = strpbrk(item, " \t"); - if (!value) { - ERR("No whitespace found.\n"); - HeapFree(PSDRV_Heap, 0, retval); - return FALSE; - } - while(isspace(*value)) - value++; - cp = endpos = strchr(value, ';'); - if (!cp) { - ERR("missing ;, failed. [%s]\n", line); - HeapFree(PSDRV_Heap, 0, retval); - return FALSE; - } - while(isspace(*--cp)) - ; - memcpy(valbuf, value, cp - value + 1); - valbuf[cp - value + 1] = '\0'; - value = valbuf; - - if(!strncmp(item, "C ", 2)) { - value = strchr(item, ' '); - sscanf(value, " %d", &metric->C); - - } else if(!strncmp(item, "CH ", 3)) { - value = strrchr(item, ' '); - sscanf(value, " %x", &metric->C); - } - - else if(!strncmp("WX ", item, 3) || !strncmp("W0X ", item, 4)) { - sscanf(value, "%f", &metric->WX); - if(metric->C >= 0 && metric->C <= 0xff) - afm->CharWidths[metric->C] = metric->WX; - } - - else if(!strncmp("N ", item, 2)) { - metric->N = PSDRV_GlyphName(value); - } - - else if(!strncmp("B ", item, 2)) { - sscanf(value, "%f%f%f%f", &metric->B.llx, &metric->B.lly, - &metric->B.urx, &metric->B.ury); - - /* Store height of Aring to use as lfHeight */ - if(metric->N && !strncmp(metric->N->sz, "Aring", 5)) - afm->FullAscender = metric->B.ury; - } - - /* Ligatures go here... */ - - curpos = endpos + 1; - } - - if (CheckMetrics(metric) == FALSE) { - ERR("Error parsing character metrics\n"); - HeapFree(PSDRV_Heap, 0, retval); - return FALSE; - } - - TRACE("Metrics for '%s' WX = %f B = %f,%f - %f,%f\n", - metric->N->sz, metric->WX, metric->B.llx, metric->B.lly, - metric->B.urx, metric->B.ury); - } - - SortFontMetrics(afm, retval); - - afm->Metrics = retval; - - return TRUE; -} - - -/*********************************************************** - * - * PSDRV_AFMParse - * - * Fills out an AFM structure and associated substructures (see psdrv.h) - * for a given AFM file. All memory is allocated from the driver heap. - * Returns a ptr to the AFM structure or NULL on error. - * - * This is not complete (we don't handle kerning yet) and not efficient - */ - -static const AFM *PSDRV_AFMParse(char const *file) -{ - FILE *fp; - unsigned char buf[256]; - unsigned char *value; - AFM *afm; - unsigned char *cp; - int afmfile = 0; - int c; - LPSTR font_name = NULL, full_name = NULL, family_name = NULL, - encoding_scheme = NULL; - - TRACE("parsing '%s'\n", file); - - if((fp = fopen(file, "r")) == NULL) { - MESSAGE("Can't open AFM file '%s'. Please check wine.conf .\n", file); - return NULL; - } - - afm = HeapAlloc(PSDRV_Heap, 0, sizeof(AFM)); - if(!afm) { - fclose(fp); - return NULL; - } - - cp = buf; - while ( ( c = fgetc ( fp ) ) != EOF ) { - *cp = c; - if ( *cp == '\r' || *cp == '\n' || cp - buf == sizeof(buf)-2 ) { - if ( cp == buf ) - continue; - *(cp+1)='\0'; - } - else { - cp ++; - continue; - } - - cp = buf + strlen(buf); - do { - *cp = '\0'; - cp--; - } while(cp > buf && isspace(*cp)); - - cp = buf; - - if ( afmfile == 0 && strncmp ( buf, "StartFontMetrics", 16 ) ) - break; - afmfile = 1; - - value = strchr(buf, ' '); - if(value) - while(isspace(*value)) - value++; - - if(!strncmp("FontName", buf, 8)) { - if (!(afm->FontName = font_name = HeapAlloc(PSDRV_Heap, 0, strlen(value)+1 ))) - goto cleanup_fp; - strcpy( (char *)afm->FontName, value ); - continue; - } - - if(!strncmp("FullName", buf, 8)) { - if (!(afm->FullName = full_name = HeapAlloc(PSDRV_Heap, 0, strlen(value)+1 ))) - goto cleanup_fp; - strcpy( (char *)afm->FullName, value ); - continue; - } - - if(!strncmp("FamilyName", buf, 10)) { - if (!(afm->FamilyName = family_name = HeapAlloc(PSDRV_Heap, 0, strlen(value)+1 ))) - goto cleanup_fp; - strcpy( (char *)afm->FamilyName, value ); - continue; - } - - if(!strncmp("Weight", buf, 6)) { - if(!strncmp("Roman", value, 5) || !strncmp("Medium", value, 6) - || !strncmp("Book", value, 4) || !strncmp("Regular", value, 7) - || !strncmp("Normal", value, 6)) - afm->Weight = FW_NORMAL; - else if(!strncmp("Demi", value, 4)) - afm->Weight = FW_DEMIBOLD; - else if(!strncmp("Bold", value, 4)) - afm->Weight = FW_BOLD; - else if(!strncmp("Light", value, 5)) - afm->Weight = FW_LIGHT; - else if(!strncmp("Black", value, 5)) - afm->Weight = FW_BLACK; - else { - WARN("%s specifies unknown Weight '%s'; treating as Roman\n", - file, value); - afm->Weight = FW_NORMAL; - } - continue; - } - - if(!strncmp("ItalicAngle", buf, 11)) { - sscanf(value, "%f", &(afm->ItalicAngle)); - continue; - } - - if(!strncmp("IsFixedPitch", buf, 12)) { - if(!strncasecmp("false", value, 5)) - afm->IsFixedPitch = FALSE; - else - afm->IsFixedPitch = TRUE; - continue; - } - - if(!strncmp("FontBBox", buf, 8)) { - sscanf(value, "%f %f %f %f", &(afm->FontBBox.llx), - &(afm->FontBBox.lly), &(afm->FontBBox.urx), - &(afm->FontBBox.ury) ); - continue; - } - - if(!strncmp("UnderlinePosition", buf, 17)) { - sscanf(value, "%f", &(afm->UnderlinePosition) ); - continue; - } - - if(!strncmp("UnderlineThickness", buf, 18)) { - sscanf(value, "%f", &(afm->UnderlineThickness) ); - continue; - } - - if(!strncmp("CapHeight", buf, 9)) { - sscanf(value, "%f", &(afm->CapHeight) ); - continue; - } - - if(!strncmp("XHeight", buf, 7)) { - sscanf(value, "%f", &(afm->XHeight) ); - continue; - } - - if(!strncmp("Ascender", buf, 8)) { - sscanf(value, "%f", &(afm->Ascender) ); - continue; - } - - if(!strncmp("Descender", buf, 9)) { - sscanf(value, "%f", &(afm->Descender) ); - continue; - } - - if(!strncmp("StartCharMetrics", buf, 16)) { - sscanf(value, "%d", &(afm->NumofMetrics) ); - if (PSDRV_AFMGetCharMetrics(afm, fp) == FALSE) - goto cleanup_fp; - continue; - } - - if(!strncmp("EncodingScheme", buf, 14)) { - if (!(afm->EncodingScheme = encoding_scheme = HeapAlloc(PSDRV_Heap, 0, strlen(value)+1))) - goto cleanup_fp; - strcpy( (char *)afm->EncodingScheme, value ); - continue; - } - - } - fclose(fp); - - if (afmfile == 0) { - HeapFree ( PSDRV_Heap, 0, afm ); - return NULL; - } - - if(afm->FontName == NULL) { - WARN("%s contains no FontName.\n", file); - if (!(afm->FontName = font_name = HeapAlloc(PSDRV_Heap, 0, 7))) - goto cleanup; - strcpy( (char *)afm->FontName, "nofont" ); - } - if(afm->FullName == NULL) - { - if (!(afm->FullName = full_name = HeapAlloc(PSDRV_Heap, 0, strlen(afm->FontName)+1 ))) - goto cleanup; - strcpy( (char *)afm->FullName, afm->FontName ); - } - if(afm->FamilyName == NULL) - { - if (!(afm->FamilyName = family_name = HeapAlloc(PSDRV_Heap, 0, strlen(afm->FontName)+1 ))) - goto cleanup; - strcpy( (char *)afm->FamilyName, afm->FontName ); - } - - if(afm->Ascender == 0.0) - afm->Ascender = afm->FontBBox.ury; - if(afm->Descender == 0.0) - afm->Descender = afm->FontBBox.lly; - if(afm->FullAscender == 0.0) - afm->FullAscender = afm->Ascender; - if(afm->Weight == 0) - afm->Weight = FW_NORMAL; - - CalcWindowsMetrics(afm); - - if (afm->EncodingScheme != NULL && - strcmp(afm->EncodingScheme, "AdobeStandardEncoding") == 0) - PSDRV_ReencodeCharWidths(afm); - - return afm; - - cleanup_fp: - - fclose(fp); - - cleanup: - - if (font_name == NULL) - HeapFree(PSDRV_Heap, 0, font_name); - if (full_name == NULL) - HeapFree(PSDRV_Heap, 0, full_name); - if (family_name == NULL) - HeapFree(PSDRV_Heap, 0, family_name); - if (encoding_scheme == NULL) - HeapFree(PSDRV_Heap, 0, encoding_scheme); - - HeapFree(PSDRV_Heap, 0, afm); - - return NULL; -} /*********************************************************** * @@ -479,7 +50,7 @@ void PSDRV_FreeAFMList( FONTFAMILY *head ) * Returns ptr to an AFM if name (which is a PS font name) exists in list * headed by head. */ -const AFM *PSDRV_FindAFMinList(FONTFAMILY *head, char *name) +const AFM *PSDRV_FindAFMinList(FONTFAMILY *head, LPCSTR name) { FONTFAMILY *family; AFMLISTENTRY *afmle; @@ -564,40 +135,6 @@ BOOL PSDRV_AddAFMtoList(FONTFAMILY **head, const AFM *afm, BOOL *p_added) return TRUE; } -/********************************************************** - * - * PSDRV_ReencodeCharWidths - * - * Re map the CharWidths field of the afm to correspond to an ANSI encoding - * - */ -static void PSDRV_ReencodeCharWidths(AFM *afm) -{ - int i, j; - const AFMMETRICS *metric; - - for(i = 0; i < 256; i++) { - if(isalnum(i)) - continue; - if(PSDRV_ANSIVector[i] == NULL) { - afm->CharWidths[i] = 0.0; - continue; - } - for (j = 0, metric = afm->Metrics; j < afm->NumofMetrics; j++, metric++) { - if(metric->N && !strcmp(metric->N->sz, PSDRV_ANSIVector[i])) { - afm->CharWidths[i] = metric->WX; - break; - } - } - if(j == afm->NumofMetrics) { - WARN("Couldn't find glyph '%s' in font '%s'\n", - PSDRV_ANSIVector[i], afm->FontName); - afm->CharWidths[i] = 0.0; - } - } - return; -} - /*********************************************************** * @@ -629,77 +166,6 @@ static void PSDRV_DumpFontList(void) return; } -/******************************************************************************* - * SortFontMetrics - * - * Initializes the UV member of each glyph's AFMMETRICS and sorts each font's - * Metrics by Unicode Value. If the font has a standard encoding (i.e. it is - * using the Adobe Glyph List encoding vector), look up each glyph's Unicode - * Value based on it's glyph name. If the font has a font-specific encoding, - * map the default PostScript encodings into the Unicode private use area. - * - */ -static int UnicodeGlyphByNameIndex(const UNICODEGLYPH *a, const UNICODEGLYPH *b) -{ - return a->name->index - b->name->index; -} - -static int AFMMetricsByUV(const AFMMETRICS *a, const AFMMETRICS *b) -{ - return a->UV - b->UV; -} - -static VOID SortFontMetrics(AFM *afm, AFMMETRICS *metrics) -{ - INT i; - - TRACE("%s\n", afm->FontName); - - if (strcmp(afm->EncodingScheme, "FontSpecific") != 0) - { - PSDRV_IndexGlyphList(); /* enable searching by name index */ - - for (i = 0; i < afm->NumofMetrics; ++i) - { - UNICODEGLYPH ug, *pug; - - ug.name = metrics[i].N; - - pug = bsearch(&ug, PSDRV_AGLbyName, PSDRV_AGLbyNameSize, - sizeof(UNICODEGLYPH), - (compar_callback_fn)UnicodeGlyphByNameIndex); - if (pug == NULL) - { - WARN("Glyph '%s' in font '%s' does not have a UV\n", - ug.name->sz, afm->FullName); - metrics[i].UV = -1; - } - else - { - metrics[i].UV = pug->UV; - } - } - } - else /* FontSpecific encoding */ - { - for (i = 0; i < afm->NumofMetrics; ++i) - metrics[i].UV = metrics[i].C; - } - - qsort(metrics, afm->NumofMetrics, sizeof(AFMMETRICS), - (compar_callback_fn)AFMMetricsByUV); - - for (i = 0; i < afm->NumofMetrics; ++i) /* count unencoded glyphs */ - if (metrics[i].UV >= 0) - break; - - if (i != 0) - { - TRACE("Ignoring %i unencoded glyphs\n", i); - afm->NumofMetrics -= i; - memmove(metrics, metrics + i, afm->NumofMetrics * sizeof(*metrics)); - } -} /******************************************************************************* * PSDRV_CalcAvgCharWidth @@ -755,83 +221,6 @@ SHORT PSDRV_CalcAvgCharWidth(const AFM *afm) return (SHORT)(w + 0.5); } -/******************************************************************************* - * CalcWindowsMetrics - * - * Calculates several Windows-specific font metrics for each font. - * - */ -static VOID CalcWindowsMetrics(AFM *afm) -{ - WINMETRICS wm; - INT i; - - wm.usUnitsPerEm = 1000; /* for PostScript fonts */ - wm.sTypoAscender = (SHORT)(afm->Ascender + 0.5); - wm.sTypoDescender = (SHORT)(afm->Descender - 0.5); - - wm.sTypoLineGap = 1200 - (wm.sTypoAscender - wm.sTypoDescender); - if (wm.sTypoLineGap < 0) - wm.sTypoLineGap = 0; - - wm.usWinAscent = 0; - wm.usWinDescent = 0; - - for (i = 0; i < afm->NumofMetrics; ++i) - { - if (IsWinANSI(afm->Metrics[i].UV) == FALSE) - continue; - - if (afm->Metrics[i].B.ury > 0) - { - USHORT ascent = (USHORT)(afm->Metrics[i].B.ury + 0.5); - - if (ascent > wm.usWinAscent) - wm.usWinAscent = ascent; - } - - if (afm->Metrics[i].B.lly < 0) - { - USHORT descent = (USHORT)(-(afm->Metrics[i].B.lly) + 0.5); - - if (descent > wm.usWinDescent) - wm.usWinDescent = descent; - } - } - - if (wm.usWinAscent == 0 && afm->FontBBox.ury > 0) - wm.usWinAscent = (USHORT)(afm->FontBBox.ury + 0.5); - - if (wm.usWinDescent == 0 && afm->FontBBox.lly < 0) - wm.usWinDescent = (USHORT)(-(afm->FontBBox.lly) + 0.5); - - wm.sAscender = wm.usWinAscent; - wm.sDescender = -(wm.usWinDescent); - - wm.sLineGap = 1150 - (wm.sAscender - wm.sDescender); - if (wm.sLineGap < 0) - wm.sLineGap = 0; - - wm.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm); - - TRACE("Windows metrics for '%s':\n", afm->FullName); - TRACE("\tsAscender = %i\n", wm.sAscender); - TRACE("\tsDescender = %i\n", wm.sDescender); - TRACE("\tsLineGap = %i\n", wm.sLineGap); - TRACE("\tusUnitsPerEm = %u\n", wm.usUnitsPerEm); - TRACE("\tsTypoAscender = %i\n", wm.sTypoAscender); - TRACE("\tsTypoDescender = %i\n", wm.sTypoDescender); - TRACE("\tsTypoLineGap = %i\n", wm.sTypoLineGap); - TRACE("\tusWinAscent = %u\n", wm.usWinAscent); - TRACE("\tusWinDescent = %u\n", wm.usWinDescent); - TRACE("\tsAvgCharWidth = %i\n", wm.sAvgCharWidth); - - afm->WinMetrics = wm; - - /* See afm2c.c and mkagl.c for an explanation of this */ - /* PSDRV_AFM2C(afm); */ -} - /******************************************************************************* * AddBuiltinAFMs @@ -863,129 +252,33 @@ static BOOL AddBuiltinAFMs() * * PSDRV_GetFontMetrics * - * Parses all afm files listed in [afmfiles] and [afmdirs] of wine.conf + * Parses all afm files listed in [afmdirs] and [TrueType Font Directories] + * sections of Wine configuration file. Adds built-in data last, so it can + * be overridden by user-supplied AFM or TTF files. * * If this function fails, PSDRV_Init will destroy PSDRV_Heap, so don't worry * about freeing all the memory that's been allocated. */ - -static BOOL PSDRV_ReadAFMDir(const char* afmdir) { - DIR *dir; - const AFM *afm; - BOOL added; - - dir = opendir(afmdir); - if (dir) { - struct dirent *dent; - while ((dent=readdir(dir))) { - if (strstr(dent->d_name,".afm")) { - char *afmfn; - - afmfn=(char*)HeapAlloc(PSDRV_Heap,0, - strlen(afmdir)+strlen(dent->d_name)+2); - if (afmfn == NULL) { - closedir(dir); - return FALSE; - } - strcpy(afmfn,afmdir); - strcat(afmfn,"/"); - strcat(afmfn,dent->d_name); - TRACE("loading AFM %s\n",afmfn); - afm = PSDRV_AFMParse(afmfn); - if (afm) { - if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added) == FALSE) { - closedir(dir); - return FALSE; - } - } - else { - WARN("Error parsing %s\n", afmfn); - } - HeapFree(PSDRV_Heap,0,afmfn); - } - } - closedir(dir); - } - else { - WARN("Error opening %s\n", afmdir); - } - - return TRUE; -} - + BOOL PSDRV_GetFontMetrics(void) { - int idx; - char key[256]; - char value[256]; - HKEY hkey; - DWORD type, key_len, value_len; - BOOL added; - if (PSDRV_GlyphListInit() != 0) - return FALSE; - - if(RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\afmfiles", - 0, KEY_READ, &hkey)) - goto no_afmfiles; - - idx = 0; - key_len = sizeof(key); - value_len = sizeof(value); - while(!RegEnumValueA(hkey, idx++, key, &key_len, NULL, &type, value, &value_len)) - { - const AFM* afm = PSDRV_AFMParse(value); + return FALSE; - if (afm) { - if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added) == FALSE) { - RegCloseKey(hkey); - return FALSE; - } - } - else { - WARN("Error parsing %s\n", value); - } - - /* initialize lengths for new iteration */ - key_len = sizeof(key); - value_len = sizeof(value); - } - RegCloseKey(hkey); - -no_afmfiles: - - if(RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\afmdirs", - 0, KEY_READ, &hkey)) - goto no_afmdirs; - - idx = 0; - key_len = sizeof(key); - value_len = sizeof(value); - while(!RegEnumValueA(hkey, idx++, key, &key_len, NULL, &type, value, &value_len)) - { - if (PSDRV_ReadAFMDir (value) == FALSE) - { - RegCloseKey(hkey); - return FALSE; - } - - /* initialize lengths for new iteration */ - key_len = sizeof(key); - value_len = sizeof(value); - } - RegCloseKey(hkey); - -no_afmdirs: - - if (AddBuiltinAFMs() == FALSE) + if (PSDRV_GetType1Metrics() == FALSE) return FALSE; -#ifdef HAVE_FREETYPE +#ifdef HAVE_FREETYPE if (PSDRV_GetTrueTypeMetrics() == FALSE) return FALSE; - PSDRV_IndexGlyphList(); #endif + if (AddBuiltinAFMs() == FALSE) + return FALSE; + + PSDRV_IndexGlyphList(); /* Enable fast searching of glyph names */ + PSDRV_DumpFontList(); + return TRUE; } diff --git a/dlls/wineps/psdrv.h b/dlls/wineps/psdrv.h index dc1601d9988a29db3bf3b5322d29967bc07bd8c4..5e01f70f1de34cff015c79b05f7f9be96a01a1a6 100644 --- a/dlls/wineps/psdrv.h +++ b/dlls/wineps/psdrv.h @@ -303,7 +303,7 @@ extern void PSDRV_MergeDevmodes(PSDRV_DEVMODEA *dm1, PSDRV_DEVMODEA *dm2, extern BOOL PSDRV_GetFontMetrics(void); extern PPD *PSDRV_ParsePPD(char *fname); extern PRINTERINFO *PSDRV_FindPrinterInfo(LPCSTR name); -extern const AFM *PSDRV_FindAFMinList(FONTFAMILY *head, char *name); +extern const AFM *PSDRV_FindAFMinList(FONTFAMILY *head, LPCSTR name); extern BOOL PSDRV_AddAFMtoList(FONTFAMILY **head, const AFM *afm, BOOL *p_added); extern void PSDRV_FreeAFMList( FONTFAMILY *head ); @@ -431,10 +431,11 @@ extern DWORD PSDRV_DeviceCapabilities(LPSTR lpszDriver, LPCSTR lpszDevice, WORD fwCapability, LPSTR lpszOutput, LPDEVMODEA lpdm); VOID PSDRV_DrawLine( DC *dc ); -INT PSDRV_GlyphListInit(); +INT PSDRV_GlyphListInit(void); const GLYPHNAME *PSDRV_GlyphName(LPCSTR szName); -VOID PSDRV_IndexGlyphList(); -BOOL PSDRV_GetTrueTypeMetrics(); +VOID PSDRV_IndexGlyphList(void); +BOOL PSDRV_GetTrueTypeMetrics(void); +BOOL PSDRV_GetType1Metrics(void); const AFMMETRICS *PSDRV_UVMetrics(LONG UV, const AFM *afm); SHORT PSDRV_CalcAvgCharWidth(const AFM *afm); diff --git a/dlls/wineps/truetype.c b/dlls/wineps/truetype.c index 8da8b0cfffe1435729519dc6f6b1aa9d14251642..6bed04bd1a9bf3b7282b9eb5319f3b98eeeec47c 100644 --- a/dlls/wineps/truetype.c +++ b/dlls/wineps/truetype.c @@ -577,7 +577,7 @@ BOOL PSDRV_GetTrueTypeMetrics(void) HKEY hkey; DWORD type, name_len, value_len; - if(RegOpenKeyExA(HKEY_LOCAL_MACHINE, + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\TrueType Font Directories", 0, KEY_READ, &hkey) != ERROR_SUCCESS) return TRUE; @@ -593,7 +593,7 @@ BOOL PSDRV_GetTrueTypeMetrics(void) name_len = sizeof(name_buf); value_len = sizeof(value_buf); - while(RegEnumValueA(hkey, i++, name_buf, &name_len, NULL, &type, value_buf, + while (RegEnumValueA(hkey, i++, name_buf, &name_len, NULL, &type, value_buf, &value_len) == ERROR_SUCCESS) { value_buf[sizeof(value_buf) - 1] = '\0'; diff --git a/dlls/wineps/type1afm.c b/dlls/wineps/type1afm.c new file mode 100644 index 0000000000000000000000000000000000000000..2ce118713f7c98b290fe7b9e2ffff17d1225cc12 --- /dev/null +++ b/dlls/wineps/type1afm.c @@ -0,0 +1,1201 @@ +/******************************************************************************* + * Adobe Font Metric (AFM) file parsing finctions for Wine PostScript driver. + * See http://partners.adobe.com/asn/developer/pdfs/tn/5004.AFM_Spec.pdf. + * + * Copyright 2001 Ian Pilcher + * + * + * NOTE: Many of the functions in this file can return either fatal errors + * (memory allocation failure) or non-fatal errors (unusable AFM file). + * Fatal errors are indicated by returning FALSE; see individual function + * descriptions for how they indicate non-fatal errors. + * + */ + +#include "config.h" + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <dirent.h> +#include <errno.h> +#include <ctype.h> +#include <limits.h> /* INT_MIN */ + +#ifdef HAVE_FLOAT_H +#include <float.h> /* FLT_MAX */ +#endif + +#include "winnt.h" +#include "winerror.h" +#include "winreg.h" +#include "psdrv.h" +#include "debugtools.h" + +DEFAULT_DEBUG_CHANNEL(psdrv); + +/******************************************************************************* + * ReadLine + * + * Reads a line from a text file into the buffer and trims trailing whitespace. + * Can handle DOS and Unix text files, including weird DOS EOF. Returns FALSE + * for unexpected I/O errors; otherwise returns TRUE and sets *p_result to + * either the number of characters in the returned string or one of the + * following: + * + * 0: Blank (or all whitespace) line. This is just a special case + * of the normal behavior. + * + * EOF: End of file has been reached. + * + * INT_MIN: Buffer overflow. Returned string is truncated (duh!) and + * trailing whitespace is *not* trimmed. Remaining text in + * line is discarded. (I.e. the file pointer is positioned at + * the beginning of the next line.) + * + */ +static BOOL ReadLine(FILE *file, CHAR buffer[], INT bufsize, INT *p_result) +{ + CHAR *cp; + INT i; + + if (fgets(buffer, bufsize, file) == NULL) + { + if (feof(file) == 0) /* EOF or error? */ + { + ERR("%s\n", strerror(errno)); + return FALSE; + } + + *p_result = EOF; + return TRUE; + } + + cp = strchr(buffer, '\n'); + if (cp == NULL) + { + i = strlen(buffer); + + if (i == bufsize - 1) /* max possible; was line truncated? */ + { + do + i = fgetc(file); /* find the newline or EOF */ + while (i != '\n' && i != EOF); + + if (i == EOF) + { + if (feof(file) == 0) /* EOF or error? */ + { + ERR("%s\n", strerror(errno)); + return FALSE; + } + + WARN("No newline at EOF\n"); + } + + *p_result = INT_MIN; + return TRUE; + } + else /* no newline and not truncated */ + { + if (strcmp(buffer, "\x1a") == 0) /* test for DOS EOF */ + { + *p_result = EOF; + return TRUE; + } + + WARN("No newline at EOF\n"); + cp = buffer + i; /* points to \0 where \n should have been */ + } + } + + do + { + *cp = '\0'; /* trim trailing whitespace */ + if (cp == buffer) + break; /* don't underflow buffer */ + --cp; + } + while (isspace(*cp)); + + *p_result = strlen(buffer); + return TRUE; +} + +/******************************************************************************* + * FindLine + * + * Finds a line in the file that begins with the given string. Returns FALSE + * for unexpected I/O errors; returns an empty (zero character) string if the + * requested line is not found. + * + * NOTE: The file pointer *MUST* be positioned at the beginning of a line when + * this function is called. Otherwise, an infinite loop can result. + * + */ +static BOOL FindLine(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key) +{ + INT len = strlen(key); + LONG start = ftell(file); + + do + { + INT result; + BOOL ok; + + ok = ReadLine(file, buffer, bufsize, &result); + if (ok == FALSE) + return FALSE; + + if (result > 0 && strncmp(buffer, key, len) == 0) + return TRUE; + + if (result == EOF) + { + rewind(file); + } + else if (result == INT_MIN) + { + WARN("Line beginning '%32s...' is too long; ignoring\n", buffer); + } + } + while (ftell(file) != start); + + WARN("Couldn't find line '%s...' in AFM file\n", key); + buffer[0] = '\0'; + return TRUE; +} + +/******************************************************************************* + * DoubleToFloat + * + * Utility function to convert double to float while checking for overflow. + * Will also catch strtod overflows, since HUGE_VAL > FLT_MAX (at least on + * Linux x86/gcc). + * + */ +inline static BOOL DoubleToFloat(float *p_f, double d) +{ + if (d > (double)FLT_MAX || d < -(double)FLT_MAX) + return FALSE; + + *p_f = (float)d; + return TRUE; +} + +/******************************************************************************* + * Round + * + * Utility function to add or subtract 0.5 before converting to integer type. + * + */ +inline static float Round(float f) +{ + return (f >= 0.0) ? (f + 0.5) : (f - 0.5); +} + +/******************************************************************************* + * ReadFloat + * + * Finds and parses a line of the form '<key> <value>', where value is a + * number. Sets *p_found to FALSE if a corresponding line cannot be found, or + * it cannot be parsed; also sets *p_ret to 0.0, so calling functions can just + * skip the check of *p_found if the item is not required. + * + */ +static BOOL ReadFloat(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key, + FLOAT *p_ret, BOOL *p_found) +{ + CHAR *cp, *end_ptr; + double d; + + if (FindLine(file, buffer, bufsize, key) == FALSE) + return FALSE; + + if (buffer[0] == '\0') /* line not found */ + { + *p_found = FALSE; + *p_ret = 0.0; + return TRUE; + } + + cp = buffer + strlen(key); /* first char after key */ + errno = 0; + d = strtod(cp, &end_ptr); + + if (end_ptr == cp || errno != 0 || DoubleToFloat(p_ret, d) == FALSE) + { + WARN("Error parsing line '%s'\n", buffer); + *p_found = FALSE; + *p_ret = 0.0; + return TRUE; + } + + *p_found = TRUE; + return TRUE; +} + +/******************************************************************************* + * ReadInt + * + * See description of ReadFloat. + * + */ +static BOOL ReadInt(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key, + INT *p_ret, BOOL *p_found) +{ + BOOL retval; + FLOAT f; + + retval = ReadFloat(file, buffer, bufsize, key, &f, p_found); + if (retval == FALSE || *p_found == FALSE) + { + *p_ret = 0; + return retval; + } + + f = Round(f); + + if (f > (FLOAT)INT_MAX || f < (FLOAT)INT_MIN) + { + WARN("Error parsing line '%s'\n", buffer); + *p_ret = 0; + *p_found = FALSE; + return TRUE; + } + + *p_ret = (INT)f; + return TRUE; +} + +/******************************************************************************* + * ReadString + * + * Returns FALSE on I/O error or memory allocation failure; sets *p_str to NULL + * if line cannot be found or can't be parsed. + * + */ +static BOOL ReadString(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key, + LPSTR *p_str) +{ + CHAR *cp; + + if (FindLine(file, buffer, bufsize, key) == FALSE) + return FALSE; + + if (buffer[0] == '\0') + { + *p_str = NULL; + return TRUE; + } + + cp = buffer + strlen(key); /* first char after key */ + if (*cp == '\0') + { + *p_str = NULL; + return TRUE; + } + + while (isspace(*cp)) /* find first non-whitespace char */ + ++cp; + + *p_str = HeapAlloc(PSDRV_Heap, 0, strlen(cp) + 1); + if (*p_str == NULL) + return FALSE; + + strcpy(*p_str, cp); + return TRUE; +} + +/******************************************************************************* + * ReadBBox + * + * Similar to ReadFloat above. + * + */ +static BOOL ReadBBox(FILE *file, CHAR buffer[], INT bufsize, AFM *afm, + BOOL *p_found) +{ + CHAR *cp, *end_ptr; + double d; + + if (FindLine(file, buffer, bufsize, "FontBBox") == FALSE) + return FALSE; + + if (buffer[0] == '\0') + { + *p_found = FALSE; + return TRUE; + } + + errno = 0; + + cp = buffer + sizeof("FontBBox"); + d = strtod(cp, &end_ptr); + if (end_ptr == cp || errno != 0 || + DoubleToFloat(&(afm->FontBBox.llx), d) == FALSE) + goto parse_error; + + cp = end_ptr; + d = strtod(cp, &end_ptr); + if (end_ptr == cp || errno != 0 || + DoubleToFloat(&(afm->FontBBox.lly), d) == FALSE) + goto parse_error; + + cp = end_ptr; + d = strtod(cp, &end_ptr); + if (end_ptr == cp || errno != 0 + || DoubleToFloat(&(afm->FontBBox.urx), d) == FALSE) + goto parse_error; + + cp = end_ptr; + d = strtod(cp, &end_ptr); + if (end_ptr == cp || errno != 0 + || DoubleToFloat(&(afm->FontBBox.ury), d) == FALSE) + goto parse_error; + + *p_found = TRUE; + return TRUE; + + parse_error: + WARN("Error parsing line '%s'\n", buffer); + *p_found = FALSE; + return TRUE; +} + +/******************************************************************************* + * ReadWeight + * + * Finds and parses the 'Weight' line of an AFM file. Only tries to determine + * if a font is bold (FW_BOLD) or not (FW_NORMAL) -- ignoring all those cute + * little FW_* typedefs in the Win32 doc. AFAICT, this is what the Windows + * PostScript driver does. + * + */ +static const struct { LPCSTR keyword; INT weight; } afm_weights[] = +{ + { "REGULAR", FW_NORMAL }, + { "ROMAN", FW_NORMAL }, + { "BOLD", FW_BOLD }, + { "BOOK", FW_NORMAL }, + { "MEDIUM", FW_NORMAL }, + { "LIGHT", FW_NORMAL }, + { "BLACK", FW_BOLD }, + { "HEAVY", FW_BOLD }, + { "DEMI", FW_BOLD }, + { "ULTRA", FW_BOLD }, + { "SUPER" , FW_BOLD }, + { NULL, 0 } +}; + +static BOOL ReadWeight(FILE *file, CHAR buffer[], INT bufsize, AFM *afm, + BOOL *p_found) +{ + LPSTR sz; + CHAR *cp; + INT i; + + if (ReadString(file, buffer, bufsize, "Weight", &sz) == FALSE) + return FALSE; + + if (sz == NULL) + { + *p_found = FALSE; + return TRUE; + } + + for (cp = sz; *cp != '\0'; ++cp) + *cp = toupper(*cp); + + for (i = 0; afm_weights[i].keyword != NULL; ++i) + { + if (strstr(sz, afm_weights[i].keyword) != NULL) + { + afm->Weight = afm_weights[i].weight; + *p_found = TRUE; + HeapFree(PSDRV_Heap, 0, sz); + return TRUE; + } + } + + WARN("Unknown weight '%s'; treating as Roman\n", sz); + + afm->Weight = FW_NORMAL; + *p_found = TRUE; + HeapFree(PSDRV_Heap, 0, sz); + return TRUE; +} + +/******************************************************************************* + * ReadFixedPitch + * + */ +static BOOL ReadFixedPitch(FILE *file, CHAR buffer[], INT bufsize, AFM *afm, + BOOL *p_found) +{ + LPSTR sz; + + if (ReadString(file, buffer, bufsize, "IsFixedPitch", &sz) == FALSE) + return FALSE; + + if (sz == NULL) + { + *p_found = FALSE; + return TRUE; + } + + if (strcasecmp(sz, "false") == 0) + { + afm->IsFixedPitch = FALSE; + *p_found = TRUE; + HeapFree(PSDRV_Heap, 0, sz); + return TRUE; + } + + if (strcasecmp(sz, "true") == 0) + { + afm->IsFixedPitch = TRUE; + *p_found = TRUE; + HeapFree(PSDRV_Heap, 0, sz); + return TRUE; + } + + WARN("Can't parse line '%s'\n", buffer); + + *p_found = FALSE; + HeapFree(PSDRV_Heap, 0, sz); + return TRUE; +} + +/******************************************************************************* + * ReadFontMetrics + * + * Allocates space for the AFM on the driver heap and reads basic font metrics. + * Returns FALSE for memory allocation failure; sets *p_afm to NULL if AFM file + * is unusable. + * + */ +static BOOL ReadFontMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM **p_afm) +{ + AFM *afm; + BOOL retval, found; + + *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm)); + if (afm == NULL) + return FALSE; + + retval = ReadWeight(file, buffer, bufsize, afm, &found); + if (retval == FALSE || found == FALSE) + goto cleanup_afm; + + retval = ReadFloat(file, buffer, bufsize, "ItalicAngle", + &(afm->ItalicAngle), &found); + if (retval == FALSE || found == FALSE) + goto cleanup_afm; + + retval = ReadFixedPitch(file, buffer, bufsize, afm, &found); + if (retval == FALSE || found == FALSE) + goto cleanup_afm; + + retval = ReadBBox(file, buffer, bufsize, afm, &found); + if (retval == FALSE || found == FALSE) + goto cleanup_afm; + + retval = ReadFloat(file, buffer, bufsize, "UnderlinePosition", + &(afm->UnderlinePosition), &found); + if (retval == FALSE || found == FALSE) + goto cleanup_afm; + + retval = ReadFloat(file, buffer, bufsize, "UnderlineThickness", + &(afm->UnderlineThickness), &found); + if (retval == FALSE || found == FALSE) + goto cleanup_afm; + + retval = ReadFloat(file, buffer, bufsize, "CapHeight", /* optional */ + &(afm->CapHeight), &found); + if (retval == FALSE) + goto cleanup_afm; + + retval = ReadFloat(file, buffer, bufsize, "XHeight", /* optional */ + &(afm->XHeight), &found); + if (retval == FALSE) + goto cleanup_afm; + + retval = ReadFloat(file, buffer, bufsize, "Ascender", /* optional */ + &(afm->Ascender), &found); + if (retval == FALSE) + goto cleanup_afm; + + retval = ReadFloat(file, buffer, bufsize, "Descender", /* optional */ + &(afm->Descender), &found); + if (retval == FALSE) + goto cleanup_afm; + + afm->WinMetrics.usUnitsPerEm = 1000; + afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->Ascender); + afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->Descender); + + if (afm->WinMetrics.sTypoAscender == 0) + afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->FontBBox.ury); + + if (afm->WinMetrics.sTypoDescender == 0) + afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->FontBBox.lly); + + afm->WinMetrics.sTypoLineGap = 1200 - + (afm->WinMetrics.sTypoAscender - afm->WinMetrics.sTypoDescender); + if (afm->WinMetrics.sTypoLineGap < 0) + afm->WinMetrics.sTypoLineGap = 0; + + return TRUE; + + cleanup_afm: /* handle fatal or non-fatal errors */ + HeapFree(PSDRV_Heap, 0, afm); + *p_afm = NULL; + return retval; +} + +/******************************************************************************* + * ParseC + * + * Fatal error: return FALSE (none defined) + * + * Non-fatal error: leave metrics->C set to INT_MAX + * + */ +static BOOL ParseC(LPSTR sz, AFMMETRICS *metrics) +{ + int base = 10; + long l; + CHAR *cp, *end_ptr; + + cp = sz + 1; + + if (*cp == 'H') + { + base = 16; + ++cp; + } + + errno = 0; + l = strtol(cp, &end_ptr, base); + if (end_ptr == cp || errno != 0 || l > INT_MAX || l < INT_MIN) + { + WARN("Error parsing character code '%s'\n", sz); + return TRUE; + } + + metrics->C = (INT)l; + return TRUE; +} + +/******************************************************************************* + * ParseW + * + * Fatal error: return FALSE (none defined) + * + * Non-fatal error: leave metrics->WX set to FLT_MAX + * + */ +static BOOL ParseW(LPSTR sz, AFMMETRICS *metrics) +{ + CHAR *cp, *end_ptr; + BOOL vector = TRUE; + double d; + + cp = sz + 1; + + if (*cp == '0') + ++cp; + + if (*cp == 'X') + { + vector = FALSE; + ++cp; + } + + if (!isspace(*cp)) + goto parse_error; + + errno = 0; + d = strtod(cp, &end_ptr); + if (end_ptr == cp || errno != 0 || + DoubleToFloat(&(metrics->WX), d) == FALSE) + goto parse_error; + + if (vector == FALSE) + return TRUE; + + /* Make sure that Y component of vector is zero */ + + d = strtod(cp, &end_ptr); /* errno == 0 */ + if (end_ptr == cp || errno != 0 || d != 0.0) + { + metrics->WX = FLT_MAX; + goto parse_error; + } + + return TRUE; + + parse_error: + WARN("Error parsing character width '%s'\n", sz); + return TRUE; +} + +/******************************************************************************* + * + * ParseB + * + * Fatal error: return FALSE (none defined) + * + * Non-fatal error: leave metrics->B.ury set to FLT_MAX + * + */ +static BOOL ParseB(LPSTR sz, AFMMETRICS *metrics) +{ + CHAR *cp, *end_ptr; + double d; + + errno = 0; + + cp = sz + 1; + d = strtod(cp, &end_ptr); + if (end_ptr == cp || errno != 0 || + DoubleToFloat(&(metrics->B.llx), d) == FALSE) + goto parse_error; + + cp = end_ptr; + d = strtod(cp, &end_ptr); + if (end_ptr == cp || errno != 0 || + DoubleToFloat(&(metrics->B.lly), d) == FALSE) + goto parse_error; + + cp = end_ptr; + d = strtod(cp, &end_ptr); + if (end_ptr == cp || errno != 0 || + DoubleToFloat(&(metrics->B.urx), d) == FALSE) + goto parse_error; + + cp = end_ptr; + d = strtod(cp, &end_ptr); + if (end_ptr == cp || errno != 0 || + DoubleToFloat(&(metrics->B.ury), d) == FALSE) + goto parse_error; + + return TRUE; + + parse_error: + WARN("Error parsing glyph bounding box '%s'\n", sz); + return TRUE; +} + +/******************************************************************************* + * ParseN + * + * Fatal error: return FALSE (PSDRV_GlyphName failure) + * + * Non-fatal error: leave metrics-> set to NULL + * + */ +static BOOL ParseN(LPSTR sz, AFMMETRICS *metrics) +{ + CHAR save, *cp, *end_ptr; + + cp = sz + 1; + + while (isspace(*cp)) + ++cp; + + end_ptr = cp; + + while (*end_ptr != '\0' && !isspace(*end_ptr)) + ++end_ptr; + + if (end_ptr == cp) + { + WARN("Error parsing glyph name '%s'\n", sz); + return TRUE; + } + + save = *end_ptr; + *end_ptr = '\0'; + + metrics->N = PSDRV_GlyphName(cp); + if (metrics->N == NULL) + return FALSE; + + *end_ptr = save; + return TRUE; +} + +/******************************************************************************* + * ParseCharMetrics + * + * Parses the metrics line for a single glyph in an AFM file. Returns FALSE on + * fatal error; sets *metrics to 'badmetrics' on non-fatal error. + * + */ +static const AFMMETRICS badmetrics = +{ + INT_MAX, /* C */ + LONG_MAX, /* UV */ + FLT_MAX, /* WX */ + NULL, /* N */ + { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }, /* B */ + NULL /* L */ +}; + +static BOOL ParseCharMetrics(LPSTR buffer, INT len, AFMMETRICS *metrics) +{ + CHAR *cp = buffer; + + *metrics = badmetrics; + + while (*cp != '\0') + { + while (isspace(*cp)) + ++cp; + + switch(*cp) + { + case 'C': if (ParseC(cp, metrics) == FALSE) + return FALSE; + break; + + case 'W': if (ParseW(cp, metrics) == FALSE) + return FALSE; + break; + + case 'N': if (ParseN(cp, metrics) == FALSE) + return FALSE; + break; + + case 'B': if (ParseB(cp, metrics) == FALSE) + return FALSE; + break; + } + + cp = strchr(cp, ';'); + if (cp == NULL) + { + WARN("No terminating semicolon\n"); + break; + } + + ++cp; + } + + if (metrics->C == INT_MAX || metrics->WX == FLT_MAX || metrics->N == NULL || + metrics->B.ury == FLT_MAX) + { + *metrics = badmetrics; + return TRUE; + } + + return TRUE; +} + +/******************************************************************************* + * IsWinANSI + * + * Checks whether Unicode value is part of Microsoft code page 1252 + * + */ +static const LONG ansiChars[21] = +{ + 0x0152, 0x0153, 0x0160, 0x0161, 0x0178, 0x017d, 0x017e, 0x0192, 0x02c6, + 0x02c9, 0x02dc, 0x03bc, 0x2013, 0x2014, 0x2026, 0x2030, 0x2039, 0x203a, + 0x20ac, 0x2122, 0x2219 +}; + +static int cmpUV(const void *a, const void *b) +{ + return (int)(*((const LONG *)a) - *((const LONG *)b)); +} + +inline static BOOL IsWinANSI(LONG uv) +{ + if ((0x0020 <= uv && uv <= 0x007e) || (0x00a0 <= uv && uv <= 0x00ff) || + (0x2018 <= uv && uv <= 0x201a) || (0x201c <= uv && uv <= 0x201e) || + (0x2020 <= uv && uv <= 0x2022)) + return TRUE; + + if (bsearch(&uv, ansiChars, 21, sizeof(INT), cmpUV) != NULL) + return TRUE; + + return FALSE; +} + +/******************************************************************************* + * Unicodify + * + * Determines Unicode value (UV) for each glyph, based on font encoding. + * + * FontSpecific: Usable encodings (0x20 - 0xff) are mapped into the + * Unicode private use range U+F020 - U+F0FF. + * + * other: UV determined by glyph name, based on Adobe Glyph List. + * + * Also does some font metric calculations that require UVs to be known. + * + */ +static int UnicodeGlyphByNameIndex(const void *a, const void *b) +{ + return ((const UNICODEGLYPH *)a)->name->index - + ((const UNICODEGLYPH *)b)->name->index; +} + +static VOID Unicodify(AFM *afm, AFMMETRICS *metrics) +{ + INT i; + + if (strcmp(afm->EncodingScheme, "FontSpecific") == 0) + { + for (i = 0; i < afm->NumofMetrics; ++i) + { + if (metrics[i].C >= 0x20 && metrics[i].C <= 0xff) + { + metrics[i].UV = ((LONG)(metrics[i].C)) | 0xf000L; + } + else + { + TRACE("Unencoded glyph '%s'\n", metrics[i].N->sz); + metrics[i].UV = -1L; + } + } + + afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury); + afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly); + } + else /* non-FontSpecific encoding */ + { + UNICODEGLYPH ug, *p_ug; + + PSDRV_IndexGlyphList(); /* for fast searching of glyph names */ + + afm->WinMetrics.sAscender = afm->WinMetrics.sDescender = 0; + + for (i = 0; i < afm->NumofMetrics; ++i) + { + ug.name = metrics[i].N; + p_ug = bsearch(&ug, PSDRV_AGLbyName, PSDRV_AGLbyNameSize, + sizeof(ug), UnicodeGlyphByNameIndex); + if (p_ug == NULL) + { + TRACE("Glyph '%s' not in Adobe Glyph List\n", ug.name->sz); + metrics[i].UV = -1L; + } + else + { + metrics[i].UV = p_ug->UV; + + if (IsWinANSI(p_ug->UV)) + { + SHORT ury = (SHORT)Round(metrics[i].B.ury); + SHORT lly = (SHORT)Round(metrics[i].B.lly); + + if (ury > afm->WinMetrics.sAscender) + afm->WinMetrics.sAscender = ury; + if (lly < afm->WinMetrics.sDescender) + afm->WinMetrics.sDescender = lly; + } + } + } + + if (afm->WinMetrics.sAscender == 0) + afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury); + if (afm->WinMetrics.sDescender == 0) + afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly); + } + + afm->WinMetrics.sLineGap = + 1150 - (afm->WinMetrics.sAscender - afm->WinMetrics.sDescender); + if (afm->WinMetrics.sLineGap < 0) + afm->WinMetrics.sLineGap = 0; + + afm->WinMetrics.usWinAscent = (afm->WinMetrics.sAscender > 0) ? + afm->WinMetrics.sAscender : 0; + afm->WinMetrics.usWinDescent = (afm->WinMetrics.sDescender < 0) ? + -(afm->WinMetrics.sDescender) : 0; +} + +/******************************************************************************* + * ReadCharMetrics + * + * Reads metrics for all glyphs. *p_metrics will be NULL on non-fatal error. + * + */ +static int AFMMetricsByUV(const void *a, const void *b) +{ + return ((const AFMMETRICS *)a)->UV - ((const AFMMETRICS *)b)->UV; +} + +static BOOL ReadCharMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM *afm, + AFMMETRICS **p_metrics) +{ + BOOL retval, found; + AFMMETRICS *metrics; + INT i, len; + + retval = ReadInt(file, buffer, bufsize, "StartCharMetrics", + &(afm->NumofMetrics), &found); + if (retval == FALSE || found == FALSE) + { + *p_metrics = NULL; + return retval; + } + + afm->Metrics = *p_metrics = metrics = HeapAlloc(PSDRV_Heap, 0, + afm->NumofMetrics * sizeof(*metrics)); + if (metrics == NULL) + return FALSE; + + for (i = 0; i < afm->NumofMetrics; ++i) + { + retval = ReadLine(file, buffer, bufsize, &len); + if (retval == FALSE) + goto cleanup_metrics; + + if(len > 0) + { + retval = ParseCharMetrics(buffer, len, metrics + i); + if (retval == FALSE || metrics[i].C == INT_MAX) + goto cleanup_metrics; + + continue; + } + + switch (len) + { + case 0: --i; + continue; + + case INT_MIN: WARN("Ignoring long line '%32s...'\n", buffer); + goto cleanup_metrics; /* retval == TRUE */ + + case EOF: WARN("Unexpected EOF\n"); + goto cleanup_metrics; /* retval == TRUE */ + } + } + + Unicodify(afm, metrics); /* wait until all glyph names have been read */ + + qsort(metrics, afm->NumofMetrics, sizeof(*metrics), AFMMetricsByUV); + + for (i = 0; metrics[i].UV == -1; ++i); /* count unencoded glyphs */ + + if (i != 0) + { + AFMMETRICS *new_metrics; + + TRACE("Ignoring %i unencoded glyphs\n", i); + + afm->NumofMetrics -= i; + memmove(metrics, metrics + i, afm->NumofMetrics * sizeof(*metrics)); + + new_metrics = HeapReAlloc(PSDRV_Heap, 0, metrics, + afm->NumofMetrics * sizeof(*metrics)); + if (new_metrics == NULL) + { + retval = FALSE; + goto cleanup_metrics; + } + + afm->Metrics = *p_metrics = new_metrics; + } + + afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm); + + return TRUE; + + cleanup_metrics: /* handle fatal or non-fatal errors */ + HeapFree(PSDRV_Heap, 0, metrics); + *p_metrics = NULL; + return retval; +} + +/******************************************************************************* + * BuildAFM + * + * Builds the AFM for a PostScript font and adds it to the driver font list. + * Returns FALSE only on an unexpected error (memory allocation or I/O error). + * + */ +static BOOL BuildAFM(FILE *file) +{ + CHAR buffer[258]; /* allow for <cr>, <lf>, and <nul> */ + AFM *afm; + AFMMETRICS *metrics; + LPSTR font_name, full_name, family_name, encoding_scheme; + BOOL retval, added; + + retval = ReadFontMetrics(file, buffer, sizeof(buffer), &afm); + if (retval == FALSE || afm == NULL) + return retval; + + retval = ReadString(file, buffer, sizeof(buffer), "FontName", &font_name); + if (retval == FALSE || font_name == NULL) + goto cleanup_afm; + + retval = ReadString(file, buffer, sizeof(buffer), "FullName", &full_name); + if (retval == FALSE || full_name == NULL) + goto cleanup_font_name; + + retval = ReadString(file, buffer, sizeof(buffer), "FamilyName", + &family_name); + if (retval == FALSE || family_name == NULL) + goto cleanup_full_name; + + retval = ReadString(file, buffer, sizeof(buffer), "EncodingScheme", + &encoding_scheme); + if (retval == FALSE || encoding_scheme == NULL) + goto cleanup_family_name; + + afm->FontName = font_name; + afm->FullName = full_name; + afm->FamilyName = family_name; + afm->EncodingScheme = encoding_scheme; + + retval = ReadCharMetrics(file, buffer, sizeof(buffer), afm, &metrics); + if (retval == FALSE || metrics == FALSE) + goto cleanup_encoding_scheme; + + retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added); + if (retval == FALSE || added == FALSE) + goto cleanup_encoding_scheme; + + return TRUE; + + /* clean up after fatal or non-fatal errors */ + + cleanup_encoding_scheme: + HeapFree(PSDRV_Heap, 0, encoding_scheme); + cleanup_family_name: + HeapFree(PSDRV_Heap, 0, family_name); + cleanup_full_name: + HeapFree(PSDRV_Heap, 0, full_name); + cleanup_font_name: + HeapFree(PSDRV_Heap, 0, font_name); + cleanup_afm: + HeapFree(PSDRV_Heap, 0, afm); + + return retval; +} + +/******************************************************************************* + * ReadAFMFile + * + * Reads font metrics from Type 1 AFM file. Only returns FALSE for + * unexpected errors (memory allocation or I/O). + * + */ +static BOOL ReadAFMFile(LPCSTR filename) +{ + FILE *f; + BOOL retval; + + TRACE("%s\n", filename); + + f = fopen(filename, "r"); + if (f == NULL) + { + WARN("%s: %s\n", filename, strerror(errno)); + return TRUE; + } + + retval = BuildAFM(f); + + fclose(f); + return retval; +} + +/******************************************************************************* + * ReadAFMDir + * + * Reads all Type 1 AFM files in a directory. + * + */ +static BOOL ReadAFMDir(LPCSTR dirname) +{ + struct dirent *dent; + DIR *dir; + CHAR filename[256]; + + dir = opendir(dirname); + if (dir == NULL) + { + WARN("%s: %s\n", dirname, strerror(errno)); + return TRUE; + } + + while ((dent = readdir(dir)) != NULL) + { + CHAR *file_extension = strchr(dent->d_name, '.'); + int fn_len; + + if (file_extension == NULL || strcasecmp(file_extension, ".afm") != 0) + continue; + + fn_len = snprintf(filename, 256, "%s/%s", dirname, dent->d_name); + if (fn_len < 0 || fn_len > sizeof(filename) - 1) + { + WARN("Path '%s/%s' is too long\n", dirname, dent->d_name); + continue; + } + + if (ReadAFMFile(filename) == FALSE) + { + closedir(dir); + return FALSE; + } + } + + closedir(dir); + return TRUE; +} + +/******************************************************************************* + * PSDRV_GetType1Metrics + * + * Reads font metrics from Type 1 AFM font files in directories listed in the + * [afmdirs] section of the Wine configuration file. + * + * If this function fails (returns FALSE), the dirver will fail to initialize + * and the driver heap will be destroyed, so it's not necessary to HeapFree + * everything in that event. + * + */ +BOOL PSDRV_GetType1Metrics(void) +{ + CHAR name_buf[256], value_buf[256]; + INT i = 0; + HKEY hkey; + DWORD type, name_len, value_len; + + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, + "Software\\Wine\\Wine\\Config\\afmdirs", + 0, KEY_READ, &hkey) != ERROR_SUCCESS) + return TRUE; + + name_len = sizeof(name_buf); + value_len = sizeof(value_buf); + + while (RegEnumValueA(hkey, i++, name_buf, &name_len, NULL, &type, value_buf, + &value_len) == ERROR_SUCCESS) + { + value_buf[sizeof(value_buf) - 1] = '\0'; + + if (ReadAFMDir(value_buf) == FALSE) + { + RegCloseKey(hkey); + return FALSE; + } + + /* initialize lengths for new iteration */ + + name_len = sizeof(name_buf); + value_len = sizeof(value_buf); + } + + RegCloseKey(hkey); + return TRUE; +}