reactos/rosapps/smartpdf/fitz/mupdf/pdf_fontfilems.c
Daniel Reimer a7fddf9c07 Delete all Trailing spaces in code.
svn path=/trunk/; revision=29689
2007-10-19 23:05:02 +00:00

807 lines
16 KiB
C

#include <fitz.h>
#include <mupdf.h>
#include <windows.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#define SAFE_FZ_READ(file, buf, size)\
byteread = fz_read((file), (char*)(buf), (size));\
if(byteread<0) err = fz_ioerror(file);\
if(byteread != (size)) err = fz_throw("ioerror");\
if(err) goto cleanup;
#define ARRAY_SIZE(a) sizeof(a) / sizeof(a[0])
#define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
#define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
#define PLATFORM_UNICODE 0
#define PLATFORM_MACINTOSH 1
#define PLATFORM_ISO 2
#define PLATFORM_MICROSOFT 3
#define UNI_ENC_UNI_1 0
#define UNI_ENC_UNI_1_1 1
#define UNI_ENC_ISO 2
#define UNI_ENC_UNI_2_BMP 3
#define UNI_ENC_UNI_2_FULL_REPERTOIRE 4
#define MAC_ROMAN 0
#define MAC_JAPANESE 1
#define MAC_CHINESE_TRADITIONAL 2
#define MAC_KOREAN 3
#define MAC_CHINESE_SIMPLIFIED 25
#define MS_ENC_SYMBOL 0
#define MS_ENC_UNI_BMP 1
#define MS_ENC_SHIFTJIS 2
#define MS_ENC_PRC 3
#define MS_ENC_BIG5 4
#define MS_ENC_WANSUNG 5
#define MS_ENC_JOHAB 6
#define MS_ENC_UNI_FULL_REPETOIRE 10
#define TTC_VERSION1 0x00010000
#define TTC_VERSION2 0x00020000
typedef struct pdf_fontmapMS_s pdf_fontmapMS;
typedef struct pdf_fontlistMS_s pdf_fontlistMS;
struct pdf_fontmapMS_s
{
char fontface[128];
char fontpath[MAX_PATH+1];
int index;
};
struct pdf_fontlistMS_s
{
pdf_fontmapMS *fontmap;
int len;
int cap;
};
typedef struct _tagTT_OFFSET_TABLE
{
USHORT uMajorVersion;
USHORT uMinorVersion;
USHORT uNumOfTables;
USHORT uSearchRange;
USHORT uEntrySelector;
USHORT uRangeShift;
} TT_OFFSET_TABLE;
typedef struct _tagTT_TABLE_DIRECTORY
{
char szTag[4]; //table name
ULONG uCheckSum; //Check sum
ULONG uOffset; //Offset from beginning of file
ULONG uLength; //length of the table in bytes
} TT_TABLE_DIRECTORY;
typedef struct _tagTT_NAME_TABLE_HEADER
{
USHORT uFSelector; //format selector. Always 0
USHORT uNRCount; //Name Records count
USHORT uStorageOffset; //Offset for strings storage, from start of the table
} TT_NAME_TABLE_HEADER;
typedef struct _tagTT_NAME_RECORD
{
USHORT uPlatformID;
USHORT uEncodingID;
USHORT uLanguageID;
USHORT uNameID;
USHORT uStringLength;
USHORT uStringOffset; //from start of storage area
} TT_NAME_RECORD;
typedef struct _tagFONT_COLLECTION
{
char Tag[4];
ULONG Version;
ULONG NumFonts;
}FONT_COLLECTION;
static char *basenames[13] =
{
"Courier",
"Courier-Bold",
"Courier-Oblique",
"Courier-BoldOblique",
"Helvetica",
"Helvetica-Bold",
"Helvetica-Oblique",
"Helvetica-BoldOblique",
"Times-Roman",
"Times-Bold",
"Times-Italic",
"Times-BoldItalic",
"Symbol",
};
static char *basepatterns[13] =
{
"CourierNewPSMT",
"CourierNewPS-BoldMT",
"CourierNewPS-ItalicMT",
"CourierNewPS-BoldItalicMT",
"ArialMT",
"Arial-BoldMT",
"Arial-ItalicMT",
"Arial-BoldItalicMT",
"TimesNewRomanPSMT",
"TimesNewRomanPS-BoldMT",
"TimesNewRomanPS-ItalicMT",
"TimesNewRomanPS-BoldItalicMT",
"SymbolMT"
};
static pdf_fontlistMS fontlistMS =
{
NULL,
0,
0,
};
static int
compare(const void *elem1, const void *elem2)
{
pdf_fontmapMS *val1 = (pdf_fontmapMS *)elem1;
pdf_fontmapMS *val2 = (pdf_fontmapMS *)elem2;
if(val1->fontface[0] == 0)
return 1;
if(val2->fontface[0] == 0)
return -1;
return stricmp(val1->fontface, val2->fontface);
}
static void *
localbsearch (const void *key, const void *base, size_t num,
size_t width, int (*compare)(const void *, const void *))
{
char *lo = (char *)base;
char *hi = (char *)base + (num - 1) * width;
char *mid;
unsigned int half;
int result;
while (lo <= hi)
if (half = num / 2)
{
mid = lo + (num & 1 ? half : (half - 1)) * width;
if (!(result = (*compare)(key,mid)))
return(mid);
else if (result < 0)
{
hi = mid - width;
num = num & 1 ? half : half-1;
}
else {
lo = mid + width;
num = half;
}
}
else if (num)
return((*compare)(key,lo) ? 0 : lo);
else
break;
return(0);
}
static void
removeredundancy(pdf_fontlistMS *fl)
{
int i;
int roffset = 0;
int redundancy_count = 0;
qsort(fl->fontmap,fl->len,sizeof(pdf_fontmapMS),compare);
for(i = 0; i < fl->len - 1; ++i)
{
if(strcmp(fl->fontmap[i].fontface,fl->fontmap[i+1].fontface) == 0)
{
fl->fontmap[i].fontface[0] = 0;
++redundancy_count;
}
}
qsort(fl->fontmap,fl->len,sizeof(pdf_fontmapMS),compare);
fl->len -= redundancy_count;
#ifndef NDEBUG
for(i = 0; i < fl->len; ++i)
fprintf(stdout,"%s , %s , %d\n",fl->fontmap[i].fontface,
fl->fontmap[i].fontpath,fl->fontmap[i].index);
#endif
}
static fz_error *
swapword(char* pbyte, int nLen)
{
int i;
char tmp;
int nMax;
if(nLen%2)
return fz_throw("fonterror");
nMax = nLen / 2;
for(i = 0; i < nLen; ++i) {
tmp = pbyte[i*2];
pbyte[i*2] = pbyte[i*2+1];
pbyte[i*2+1] = tmp;
}
return 0;
}
/* pSouce and PDest can be same */
static fz_error *
decodeunicodeBMP(char* source, int sourcelen,char* dest, int destlen)
{
wchar_t tmp[1024*2];
int converted;
memset(tmp,0,sizeof(tmp));
memcpy(tmp,source,sourcelen);
swapword((char*)tmp,sourcelen);
converted = WideCharToMultiByte(CP_ACP, 0, tmp,
-1, dest, destlen, NULL, NULL);
if(converted == 0)
return fz_throw("fonterror");
return 0;
}
static fz_error *
decodeunicodeplatform(char* source, int sourcelen,char* dest, int destlen, int enctype)
{
fz_error *err = nil;
switch(enctype)
{
case UNI_ENC_UNI_1:
case UNI_ENC_UNI_2_BMP:
err = decodeunicodeBMP(source,sourcelen,dest,destlen);
break;
case UNI_ENC_UNI_2_FULL_REPERTOIRE:
case UNI_ENC_UNI_1_1:
case UNI_ENC_ISO:
default:
err = fz_throw("fonterror : unsupported encoding");
break;
}
return err;
}
static fz_error *
decodemacintoshplatform(char* source, int sourcelen,char* dest, int destlen, int enctype)
{
fz_error *err = nil;
switch(enctype)
{
case MAC_ROMAN:
if(sourcelen + 1 > destlen)
err = fz_throw("fonterror : short buf lenth");
else
{
memcpy(source,dest,sourcelen);
dest[sourcelen] = 0;
}
break;
default:
err = fz_throw("fonterror : unsupported encoding");
break;
}
return err;
}
static fz_error *
decodemicrosoftplatform(char* source, int sourcelen,char* dest, int destlen, int enctype)
{
fz_error *err = nil;
switch(enctype)
{
case MS_ENC_SYMBOL:
case MS_ENC_UNI_BMP:
err = decodeunicodeBMP(source,sourcelen,dest,destlen);
break;
default:
err = fz_throw("fonterror : unsupported encoding");
break;
}
return err;
}
static fz_error *
growfontlist(pdf_fontlistMS *fl)
{
int newcap;
pdf_fontmapMS *newitems;
if(fl->cap == 0)
newcap = 32;
else
newcap = fl->cap * 2;
newitems = fz_realloc(fl->fontmap, sizeof(pdf_fontmapMS) * newcap);
if (!newitems)
return fz_outofmem;
memset(newitems + fl->cap, 0,
sizeof(struct fz_keyval_s) * (newcap - fl->cap));
fl->fontmap = newitems;
fl->cap = newcap;
return nil;
}
static fz_error *
insertmapping(pdf_fontlistMS *fl, char *facename, char *path, int index)
{
fz_error *err;
if(fl->len == fl->cap) {
err = growfontlist(fl);
if(err) return err;
}
if(fl->len >= fl->cap)
return fz_throw("fonterror : fontlist overflow");
strlcpy(fl->fontmap[fl->len].fontface, facename,
sizeof(fl->fontmap[0].fontface));
strlcpy(fl->fontmap[fl->len].fontpath, path,
sizeof(fl->fontmap[0].fontpath));
fl->fontmap[fl->len].index = index;
++fl->len;
return nil;
}
static fz_error *
parseTTF(fz_stream *file, int offset, int index, char *path)
{
fz_error *err = nil;
int byteread;
TT_OFFSET_TABLE ttOffsetTable;
TT_TABLE_DIRECTORY tblDir;
TT_NAME_TABLE_HEADER ttNTHeader;
TT_NAME_RECORD ttRecord;
char szTemp[4096];
int found;
int i;
fz_seek(file,offset,0);
SAFE_FZ_READ(file, &ttOffsetTable, sizeof(TT_OFFSET_TABLE));
ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
//check is this is a true type font and the version is 1.0
if(ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
return fz_throw("fonterror : invalid font version");
found = 0;
for(i = 0; i< ttOffsetTable.uNumOfTables; i++)
{
SAFE_FZ_READ(file,&tblDir,sizeof(TT_TABLE_DIRECTORY));
memcpy(szTemp, tblDir.szTag, 4);
szTemp[4] = 0;
if (stricmp(szTemp, "name") == 0)
{
found = 1;
tblDir.uLength = SWAPLONG(tblDir.uLength);
tblDir.uOffset = SWAPLONG(tblDir.uOffset);
break;
}
else if (szTemp[0] == 0)
{
break;
}
}
if (found)
{
fz_seek(file,tblDir.uOffset,0);
SAFE_FZ_READ(file,&ttNTHeader,sizeof(TT_NAME_TABLE_HEADER));
ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
offset = tblDir.uOffset + sizeof(TT_NAME_TABLE_HEADER);
for(i = 0; i < ttNTHeader.uNRCount && err == nil; ++i)
{
fz_seek(file, offset + sizeof(TT_NAME_RECORD)*i, 0);
SAFE_FZ_READ(file,&ttRecord,sizeof(TT_NAME_RECORD));
ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
ttRecord.uLanguageID = SWAPWORD(ttRecord.uLanguageID);
// Full Name
if(ttRecord.uNameID == 6)
{
ttRecord.uPlatformID = SWAPWORD(ttRecord.uPlatformID);
ttRecord.uEncodingID = SWAPWORD(ttRecord.uEncodingID);
ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
fz_seek(file, tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset, 0);
SAFE_FZ_READ(file, szTemp, ttRecord.uStringLength);
switch(ttRecord.uPlatformID)
{
case PLATFORM_UNICODE:
err = decodeunicodeplatform(szTemp, ttRecord.uStringLength,
szTemp, sizeof(szTemp), ttRecord.uEncodingID);
break;
case PLATFORM_MACINTOSH:
err = decodemacintoshplatform(szTemp, ttRecord.uStringLength,
szTemp, sizeof(szTemp), ttRecord.uEncodingID);
break;
case PLATFORM_ISO:
err = fz_throw("fonterror : unsupported platform");
break;
case PLATFORM_MICROSOFT:
err = decodemicrosoftplatform(szTemp, ttRecord.uStringLength,
szTemp, sizeof(szTemp), ttRecord.uEncodingID);
break;
}
if(err == nil)
err = insertmapping(&fontlistMS, szTemp, path, index);
}
}
}
cleanup:
return err;
}
static fz_error *
parseTTFs(char *path)
{
fz_error *err = nil;
fz_stream *file = nil;
err = fz_openrfile(&file, path);
if(err)
goto cleanup;
err = parseTTF(file,0,0,path);
if(err)
goto cleanup;
cleanup:
if(file)
fz_dropstream(file);
return err;
}
static fz_error *
parseTTCs(char *path)
{
fz_error *err = nil;
int byteread;
fz_stream *file = nil;
FONT_COLLECTION fontcollectioin;
ULONG i;
err = fz_openrfile(&file, path);
if(err)
goto cleanup;
SAFE_FZ_READ(file, &fontcollectioin, sizeof(FONT_COLLECTION));
if(memcmp(fontcollectioin.Tag,"ttcf",sizeof(fontcollectioin.Tag)) == 0)
{
fontcollectioin.Version = SWAPLONG(fontcollectioin.Version);
fontcollectioin.NumFonts = SWAPLONG(fontcollectioin.NumFonts);
if( fontcollectioin.Version == TTC_VERSION1 ||
fontcollectioin.Version == TTC_VERSION2 )
{
ULONG *offsettable = fz_malloc(sizeof(ULONG)*fontcollectioin.NumFonts);
if(offsettable == nil)
{
err = fz_outofmem;
goto cleanup;
}
SAFE_FZ_READ(file, offsettable, sizeof(ULONG)*fontcollectioin.NumFonts);
for(i = 0; i < fontcollectioin.NumFonts; ++i)
{
offsettable[i] = SWAPLONG(offsettable[i]);
parseTTF(file,offsettable[i],i,path);
}
fz_free(offsettable);
}
else
{
err = fz_throw("fonterror : invalid version");
goto cleanup;
}
}
else
{
err = fz_throw("fonterror: wrong format");
goto cleanup;
}
cleanup:
if(file)
fz_dropstream(file);
return err;
}
static fz_error*
pdf_createfontlistMS()
{
char szFontDir[MAX_PATH*2];
char szSearch[MAX_PATH*2];
char szFile[MAX_PATH*2];
BOOL fFinished;
HANDLE hList;
WIN32_FIND_DATA FileData;
fz_error *err;
if (fontlistMS.len != 0)
return nil;
GetWindowsDirectory(szFontDir, sizeof(szFontDir));
// Get the proper directory path
strcat(szFontDir,"\\Fonts\\");
sprintf(szSearch,"%s*.tt?",szFontDir);
// Get the first file
hList = FindFirstFile(szSearch, &FileData);
if (hList == INVALID_HANDLE_VALUE)
{
/* Don't complain about missing directories */
if (errno == ENOENT)
return fz_throw("fonterror : can't find system fonts dir");
return fz_throw("ioerror");
}
// Traverse through the directory structure
fFinished = FALSE;
while (!fFinished)
{
if (!(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
// Get the full path for sub directory
sprintf(szFile,"%s%s", szFontDir, FileData.cFileName);
if (szFile[strlen(szFile)-1] == 'c' || szFile[strlen(szFile)-1] == 'C')
{
err = parseTTCs(szFile);
// ignore error parsing a given font file
}
else if (szFile[strlen(szFile)-1] == 'f'|| szFile[strlen(szFile)-1] == 'F')
{
err = parseTTFs(szFile);
// ignore error parsing a given font file
}
}
if (!FindNextFile(hList, &FileData))
{
if (GetLastError() == ERROR_NO_MORE_FILES)
{
fFinished = TRUE;
}
}
}
removeredundancy(&fontlistMS);
return nil;
}
void
pdf_destoryfontlistMS()
{
if (fontlistMS.fontmap != nil)
fz_free(fontlistMS.fontmap);
fontlistMS.len = 0;
fontlistMS.cap = 0;
}
#if 0 /* This is for testing if localbsearch() fails unexpectedly */
pdf_fontmapMS *
findlinear(pdf_fontlistMS *list, pdf_fontmapMS *item)
{
int i, len;
pdf_fontmapMS *curritem = list->fontmap;
len = list->len;
for (i = 0; i < len; i++)
{
if (0 == stricmp(curritem->fontface, item->fontface))
return curritem;
++curritem;
}
return 0;
}
#endif
fz_error *
pdf_lookupfontMS(char *fontname, char **fontpath, int *index)
{
pdf_fontmapMS fontmap;
pdf_fontmapMS *found = nil;
char *pattern;
int i;
if (fontlistMS.len == 0)
return fz_throw("fonterror : no fonts in the system");
pattern = fontname;
for (i = 0; i < ARRAY_SIZE(basenames); i++)
{
if (0 == strcmp(fontname, basenames[i]))
{
pattern = basepatterns[i];
break;
}
}
strlcpy(fontmap.fontface,pattern,sizeof(fontmap.fontface));
found = localbsearch(&fontmap, fontlistMS.fontmap, fontlistMS.len, sizeof(pdf_fontmapMS),compare);
#if 0
if (!found)
found = findlinear(&fontlistMS, &fontmap);
#endif
if (found)
{
*fontpath = found->fontpath;
*index = found->index;
}
else
{
*fontpath = fontlistMS.fontmap[0].fontpath;
*index = fontlistMS.fontmap[0].index;
}
return nil;
}
static FT_Library ftlib = nil;
static fz_error *initfontlibs(void)
{
int fterr;
int maj, min, pat;
fz_error *err;
if (ftlib)
return nil;
fterr = FT_Init_FreeType(&ftlib);
if (fterr)
return fz_throw("freetype failed initialisation: 0x%x", fterr);
FT_Library_Version(ftlib, &maj, &min, &pat);
if (maj == 2 && min == 1 && pat < 7)
return fz_throw("freetype version too old: %d.%d.%d", maj, min, pat);
err = pdf_createfontlistMS();
if(err)
return err;
return nil;
}
fz_error *initfontlibs_ms(void)
{
return initfontlibs();
}
void deinitfontlibs_ms(void)
{
pdf_destoryfontlistMS();
FT_Done_FreeType(ftlib);
ftlib = nil;
}
fz_error *
pdf_loadbuiltinfont(pdf_font *font, char *basefont)
{
fz_error *error;
int fterr;
FT_Face face;
char *file;
int index;
error = initfontlibs();
if (error)
return error;
error = pdf_lookupfontMS(basefont,&file,&index);
if(error)
return error;
fterr = FT_New_Face(ftlib, file, index, &face);
if (fterr)
return fz_throw("freetype could not load font file '%s': 0x%x", file, fterr);
font->ftface = face;
return nil;
}
fz_error *
pdf_loadsystemfont(pdf_font *font, char *basefont, char *collection)
{
fz_error *error;
int fterr;
FT_Face face;
char *file;
int index;
error = initfontlibs();
if (error)
return error;
error = pdf_lookupfontMS(basefont,&file,&index);
if(error)
goto cleanup;
fterr = FT_New_Face(ftlib, file, index, &face);
if (fterr) {
return fz_throw("freetype could not load font file '%s': 0x%x", file, fterr);
}
font->ftface = face;
return nil;
cleanup:
return error;
}
fz_error *
pdf_loadembeddedfont(pdf_font *font, pdf_xref *xref, fz_obj *stmref)
{
fz_error *error;
int fterr;
FT_Face face;
fz_buffer *buf;
error = initfontlibs();
if (error)
return error;
error = pdf_loadstream(&buf, xref, fz_tonum(stmref), fz_togen(stmref));
if (error)
return error;
fterr = FT_New_Memory_Face(ftlib, buf->rp, buf->wp - buf->rp, 0, &face);
if (fterr) {
fz_free(buf);
return fz_throw("freetype could not load embedded font: 0x%x", fterr);
}
font->ftface = face;
font->fontdata = buf;
return nil;
}