summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/xetexdir/XeTeXFontMgr.cpp
diff options
context:
space:
mode:
authorJonathan Kew <jfkthame@googlemail.com>2006-07-26 16:19:45 +0000
committerJonathan Kew <jfkthame@googlemail.com>2006-07-26 16:19:45 +0000
commit622bf4a7c90729be28a038c49e4c237ce9f31297 (patch)
tree77466d5c0a89c433bd1c08f4cbeaf410fda7b25c /Build/source/texk/web2c/xetexdir/XeTeXFontMgr.cpp
parente88aafe4ca4779a73487925dea63e8b55dc5e251 (diff)
added main xetex source directory
git-svn-id: svn://tug.org/texlive/trunk@1920 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/xetexdir/XeTeXFontMgr.cpp')
-rw-r--r--Build/source/texk/web2c/xetexdir/XeTeXFontMgr.cpp621
1 files changed, 621 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/xetexdir/XeTeXFontMgr.cpp b/Build/source/texk/web2c/xetexdir/XeTeXFontMgr.cpp
new file mode 100644
index 00000000000..e6a3efdbdbb
--- /dev/null
+++ b/Build/source/texk/web2c/xetexdir/XeTeXFontMgr.cpp
@@ -0,0 +1,621 @@
+/****************************************************************************\
+ Part of the XeTeX typesetting system
+ copyright (c) 1994-2006 by SIL International
+ written by Jonathan Kew
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL SIL INTERNATIONAL BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of SIL International
+shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in this Software without prior written
+authorization from SIL International.
+\****************************************************************************/
+
+#ifdef XETEX_MAC
+#include "XeTeXFontMgr_Mac.h"
+#else
+#include "XeTeXFontMgr_Linux.h"
+#endif
+
+#include "XeTeXLayoutInterface.h"
+#include "XeTeXswap.h"
+
+#include "Features.h"
+#include "GlyphPositioningTables.h"
+
+#include "sfnt.h"
+
+#include <math.h>
+
+XeTeXFontMgr* XeTeXFontMgr::sFontManager = NULL;
+char XeTeXFontMgr::sReqEngine = 0;
+
+/* use our own fmax function because it seems to be missing on certain platforms
+ (solaris2.9, at least) */
+static inline double
+my_fmax(double x, double y)
+{
+ return (x > y) ? x : y;
+}
+
+XeTeXFontMgr*
+XeTeXFontMgr::GetFontManager()
+{
+ if (sFontManager == NULL) {
+#ifdef XETEX_MAC
+ sFontManager = new XeTeXFontMgr_Mac;
+#else
+ sFontManager = new XeTeXFontMgr_Linux;
+#endif
+ sFontManager->initialize();
+ }
+
+ return sFontManager;
+}
+
+void
+XeTeXFontMgr::Terminate()
+{
+ if (sFontManager != NULL) {
+ sFontManager->terminate();
+ // we don't actually deallocate the manager, just ask it to clean up
+ // any auxiliary data such as the cocoa pool or freetype/fontconfig stuff
+ // as we still need to access font names after this is called
+ }
+}
+
+PlatformFontRef
+XeTeXFontMgr::findFont(const char* name, char* variant, double ptSize)
+ // ptSize is in TeX points
+ // "variant" string will be shortened (in-place) by removal of /B and /I if present
+{
+ std::string nameStr(name);
+ Font* font = NULL;
+
+ for (int pass = 0; pass < 2; ++pass) {
+ // try full name as given
+ std::map<std::string,Font*>::iterator i = nameToFont.find(nameStr);
+ if (i != nameToFont.end()) {
+ font = i->second;
+ break;
+ }
+
+ // if there's a hyphen, split there and try Family-Style
+ int hyph = nameStr.find('-');
+ if (hyph > 0 && hyph < nameStr.length() - 1) {
+ std::string family(nameStr.begin(), nameStr.begin() + hyph);
+ std::map<std::string,Family*>::iterator f = nameToFamily.find(family);
+ if (f != nameToFamily.end()) {
+ std::string style(nameStr.begin() + hyph + 1, nameStr.end());
+ i = f->second->styles->find(style);
+ if (i != f->second->styles->end()) {
+ font = i->second;
+ break;
+ }
+ }
+ }
+
+ // try as PostScript name
+ i = psNameToFont.find(nameStr);
+ if (i != psNameToFont.end()) {
+ font = i->second;
+ break;
+ }
+
+ // try for the name as a family name
+ std::map<std::string,Family*>::iterator f = nameToFamily.find(nameStr);
+
+ if (f != nameToFamily.end()) {
+ // look for a family member with the "regular" bit set in OS/2
+ for (i = f->second->styles->begin(); i != f->second->styles->end(); ++i)
+ if (i->second->isReg) {
+ font = i->second;
+ break;
+ }
+
+ if (font == NULL) {
+ // try for style "Regular", "Plain", or "Normal"
+ i = f->second->styles->find("Regular");
+ if (i != f->second->styles->end())
+ font = i->second;
+ else {
+ i = f->second->styles->find("Plain");
+ if (i != f->second->styles->end())
+ font = i->second;
+ else {
+ i = f->second->styles->find("Normal");
+ if (i != f->second->styles->end())
+ font = i->second;
+ }
+ }
+ }
+
+ if (font == NULL) {
+ // look through the family for the (weight, width, slant) nearest to (80, 100, 0)
+ font = bestMatchFromFamily(f->second, 80, 100, 0);
+ }
+
+ if (font != NULL)
+ break;
+ }
+
+ if (pass == 0) {
+ // didn't find it in our caches, so do a platform search (may be relatively expensive);
+ // this will update the caches with any fonts that seem to match the name given,
+ // so that the second pass might find it
+ searchForHostPlatformFonts(nameStr);
+ }
+ }
+
+ if (font == NULL)
+ return 0;
+
+ Family* parent = font->parent;
+
+ // if there are variant requests, try to apply them
+ // and delete B or I codes from the string
+ sReqEngine = 0;
+ bool reqBold = false;
+ bool reqItal = false;
+ if (font != NULL && variant != NULL) {
+ std::string varString;
+ char* cp = variant;
+ while (*cp) {
+ if (strncmp(cp, "AAT", 3) == 0) {
+ sReqEngine = 'A';
+ cp += 3;
+ if (varString.length() > 0 && *(varString.end() - 1) != '/')
+ varString.append("/");
+ varString.append("AAT");
+ goto skip_to_slash;
+ }
+ if (strncmp(cp, "ICU", 3) == 0) {
+ sReqEngine = 'I';
+ cp += 3;
+ if (varString.length() > 0 && *(varString.end() - 1) != '/')
+ varString.append("/");
+ varString.append("ICU");
+ goto skip_to_slash;
+ }
+/*
+ if (strncmp(cp, "USP", 3) == 0) {
+ sReqEngine = 'U';
+ cp += 3;
+ if (varString.length() > 0 && *(varString.end() - 1) != '/')
+ varString.append("/");
+ varString.append("USP");
+ goto skip_to_slash;
+ }
+ if (strncmp(cp, "PAN", 5) == 0) {
+ sReqEngine = 'P';
+ cp += 3;
+ if (varString.length() > 0 && *(varString.end() - 1) != '/')
+ varString.append("/");
+ varString.append("PAN");
+ goto skip_to_slash;
+ }
+ if (strncmp(cp, "GRA", 8) == 0) {
+ sReqEngine = 'G';
+ cp += 3;
+ if (varString.length() > 0 && *(varString.end() - 1) != '/')
+ varString.append("/");
+ varString.append("GRA");
+ goto skip_to_slash;
+ }
+*/
+ if (*cp == 'S') {
+ char* start = cp;
+ ++cp;
+ if (*cp == '=')
+ ++cp;
+ ptSize = 0.0;
+ while (*cp >= '0' && *cp <= '9') {
+ ptSize = ptSize * 10 + *cp - '0';
+ ++cp;
+ }
+ if (*cp == '.') {
+ double dec = 1.0;
+ ++cp;
+ while (*cp >= '0' && *cp <= '9') {
+ dec = dec * 10.0;
+ ptSize = ptSize + (*cp - '0') / dec;
+ ++cp;
+ }
+ }
+ if (varString.length() > 0 && *(varString.end() - 1) != '/')
+ varString.append("/");
+ varString.append(start, cp);
+ goto skip_to_slash;
+ }
+
+ /* if the code is "B" or "I", we skip putting it in varString */
+ while (1) {
+ if (*cp == 'B') {
+ reqBold = true;
+ ++cp;
+ continue;
+ }
+ if (*cp == 'I') {
+ reqItal = true;
+ ++cp;
+ continue;
+ }
+ break;
+ }
+
+ skip_to_slash:
+ while (*cp && *cp != '/')
+ ++cp;
+ if (*cp == '/')
+ ++cp;
+ }
+ strcpy(variant, varString.c_str());
+
+ std::map<std::string,Font*>::iterator i;
+ if (reqItal) {
+ Font* bestMatch = font;
+ if (font->slant < parent->maxSlant)
+ // try for a face with more slant
+ bestMatch = bestMatchFromFamily(parent, font->weight, font->width, parent->maxSlant);
+
+ if (bestMatch == font && font->slant > parent->minSlant)
+ // maybe the slant is negated, or maybe this was something like "Times-Italic/I"
+ bestMatch = bestMatchFromFamily(parent, font->weight, font->width, parent->minSlant);
+
+ if (parent->minWeight == parent->maxWeight && bestMatch->isBold != font->isBold) {
+ // try again using the bold flag, as we can't trust weight values
+ Font* newBest = NULL;
+ for (i = parent->styles->begin(); i != parent->styles->end(); ++i) {
+ if (i->second->isBold == font->isBold) {
+ if (newBest == NULL && i->second->isItalic != font->isItalic) {
+ newBest = i->second;
+ break;
+ }
+ }
+ }
+ if (newBest != NULL)
+ bestMatch = newBest;
+ }
+
+ if (bestMatch == font) {
+ // maybe slant values weren't present; try the style bits as a fallback
+ bestMatch = NULL;
+ for (i = parent->styles->begin(); i != parent->styles->end(); ++i)
+ if (i->second->isItalic == !font->isItalic)
+ if (parent->minWeight != parent->maxWeight) {
+ // weight info was available, so try to match that
+ if (bestMatch == NULL || weightAndWidthDiff(i->second, font) < weightAndWidthDiff(bestMatch, font))
+ bestMatch = i->second;
+ }
+ else {
+ // no weight info, so try matching style bits
+ if (bestMatch == NULL && i->second->isBold == font->isBold) {
+ bestMatch = i->second;
+ break; // found a match, no need to look further as we can't distinguish!
+ }
+ }
+ }
+ if (bestMatch != NULL)
+ font = bestMatch;
+ }
+
+ if (reqBold) {
+ // try for more boldness, with the same width and slant
+ Font* bestMatch = font;
+ if (font->weight < parent->maxWeight) {
+ bestMatch = bestMatchFromFamily(parent, parent->maxWeight, font->width, font->slant);
+ if (parent->minSlant == parent->maxSlant) {
+ // double-check the italic flag, as we can't trust slant values
+ Font* newBest = NULL;
+ for (i = parent->styles->begin(); i != parent->styles->end(); ++i) {
+ if (i->second->isItalic == font->isItalic) {
+ if (newBest == NULL || weightAndWidthDiff(i->second, bestMatch) < weightAndWidthDiff(newBest, bestMatch))
+ newBest = i->second;
+ }
+ }
+ if (newBest != NULL)
+ bestMatch = newBest;
+ }
+ }
+ if (bestMatch == font && !font->isBold) {
+ for (i = parent->styles->begin(); i != parent->styles->end(); ++i) {
+ if (i->second->isItalic == font->isItalic && i->second->isBold) {
+ bestMatch = i->second;
+ break;
+ }
+ }
+ }
+ font = bestMatch;
+ }
+ }
+
+ // if there's optical size info, try to apply it
+ if (font != NULL && font->opSizeInfo.subFamilyID != 0 && ptSize != 0.0) {
+ ptSize = ptSize * 720 / 72.27; // convert TeX points to PS decipoints for comparison with the opSize values
+ double bestMismatch = my_fmax(font->opSizeInfo.minSize - ptSize, ptSize - font->opSizeInfo.maxSize);
+ if (bestMismatch > 0.0) {
+ Font* bestMatch = font;
+ for (std::map<std::string,Font*>::iterator i = parent->styles->begin(); i != parent->styles->end(); ++i) {
+ if (i->second->opSizeInfo.subFamilyID != font->opSizeInfo.subFamilyID)
+ continue;
+ double mismatch = my_fmax(i->second->opSizeInfo.minSize - ptSize, ptSize - i->second->opSizeInfo.maxSize);
+ if (mismatch < bestMismatch) {
+ bestMatch = i->second;
+ bestMismatch = mismatch;
+ }
+ if (bestMismatch <= 0.0)
+ break;
+ }
+ font = bestMatch;
+ }
+ }
+
+ return font->fontRef;
+}
+
+const char*
+XeTeXFontMgr::getPSName(PlatformFontRef font) const
+{
+ std::map<PlatformFontRef,Font*>::const_iterator i = platformRefToFont.find(font);
+ if (i == platformRefToFont.end())
+ die("internal error %d in XeTeXFontMgr", 1);
+ return i->second->psName->c_str();
+}
+
+const char*
+XeTeXFontMgr::getFullName(PlatformFontRef font) const
+{
+ std::map<PlatformFontRef,Font*>::const_iterator i = platformRefToFont.find(font);
+ if (i == platformRefToFont.end())
+ die("internal error %d in XeTeXFontMgr", 2);
+ if (i->second->fullName != NULL)
+ return i->second->fullName->c_str();
+ else
+ return i->second->psName->c_str();
+}
+
+void
+XeTeXFontMgr::getNames(PlatformFontRef font, const char** psName,
+ const char** famName, const char** styName) const
+{
+ std::map<PlatformFontRef,Font*>::const_iterator i = platformRefToFont.find(font);
+ if (i == platformRefToFont.end())
+ die("internal error %d in XeTeXFontMgr", 3);
+ *psName = i->second->psName->c_str();
+ *famName = i->second->familyName->c_str();
+ *styName = i->second->styleName->c_str();
+}
+
+char
+XeTeXFontMgr::getReqEngine() const
+{
+ return sReqEngine;
+}
+
+int
+XeTeXFontMgr::weightAndWidthDiff(const Font* a, const Font* b) const
+{
+ if (a->weight == 0 && a->width == 0) {
+ // assume there was no OS/2 info
+ if (a->isBold == b->isBold)
+ return 0;
+ else
+ return 10000;
+ }
+
+ int widDiff = labs(a->width - b->width);
+ if (widDiff < 10)
+ widDiff *= 50;
+
+ return labs(a->weight - b->weight) + widDiff;
+}
+
+int
+XeTeXFontMgr::styleDiff(const Font* a, int wt, int wd, int slant) const
+{
+ int widDiff = labs(a->width - wd);
+ if (widDiff < 10)
+ widDiff *= 200;
+
+ return labs(labs(a->slant) - labs(slant)) * 2 + labs(a->weight - wt) + widDiff;
+}
+
+XeTeXFontMgr::Font*
+XeTeXFontMgr::bestMatchFromFamily(const Family* fam, int wt, int wd, int slant) const
+{
+ Font* bestMatch = NULL;
+ for (std::map<std::string,Font*>::iterator s = fam->styles->begin(); s != fam->styles->end(); ++s)
+ if (bestMatch == NULL || styleDiff(s->second, wt, wd, slant) < styleDiff(bestMatch, wt, wd, slant))
+ bestMatch = s->second;
+ return bestMatch;
+}
+
+void
+XeTeXFontMgr::getOpSizeRecAndStyleFlags(Font* theFont)
+{
+ XeTeXFont font = createFont(theFont->fontRef, 655360);
+ if (font != 0) {
+ const GlyphPositioningTableHeader* gposTable = (const GlyphPositioningTableHeader*)getFontTablePtr(font, LE_GPOS_TABLE_TAG);
+ if (gposTable != NULL) {
+ FeatureListTable* featureListTable = (FeatureListTable*)((char*)gposTable + SWAP(gposTable->featureListOffset));
+ for (int i = 0; i < SWAP(featureListTable->featureCount); ++i) {
+ UInt32 tag = SWAPT(featureListTable->featureRecordArray[i].featureTag);
+ if (tag == LE_SIZE_FEATURE_TAG) {
+ FeatureTable* feature = (FeatureTable*)((char*)featureListTable + SWAP(featureListTable->featureRecordArray[i].featureTableOffset));
+ OpSizeRec* pSizeRec = (OpSizeRec*)((char*)featureListTable + SWAP(feature->featureParamsOffset));
+ theFont->opSizeInfo.designSize = SWAP(pSizeRec->designSize);
+ theFont->opSizeInfo.subFamilyID = SWAP(pSizeRec->subFamilyID);
+ theFont->opSizeInfo.nameCode = SWAP(pSizeRec->nameCode);
+ theFont->opSizeInfo.minSize = SWAP(pSizeRec->minSize);
+ theFont->opSizeInfo.maxSize = SWAP(pSizeRec->maxSize);
+ break;
+ }
+ }
+ }
+
+ const OS2TableHeader* os2Table = (const OS2TableHeader*)getFontTablePtr(font, LE_OS_2_TABLE_TAG);
+ if (os2Table != NULL) {
+ theFont->weight = SWAP(os2Table->usWeightClass);
+ theFont->width = SWAP(os2Table->usWidthClass);
+ UInt16 sel = SWAP(os2Table->fsSelection);
+ theFont->isReg = (sel & (1 << 6)) != 0;
+ theFont->isBold = (sel & (1 << 5)) != 0;
+ theFont->isItalic = (sel & (1 << 0)) != 0;
+ }
+
+ const HEADTable* headTable = (const HEADTable*)getFontTablePtr(font, LE_HEAD_TABLE_TAG);
+ if (headTable != NULL) {
+ UInt16 ms = SWAP(headTable->macStyle);
+ if ((ms & (1 << 0)) != 0)
+ theFont->isBold = true;
+ if ((ms & (1 << 1)) != 0)
+ theFont->isItalic = true;
+ }
+
+ const POSTTable* postTable = (const POSTTable*)getFontTablePtr(font, LE_POST_TABLE_TAG);
+ if (postTable != NULL) {
+ theFont->slant = (int)(1000 * (tan(Fix2X(-SWAP(postTable->italicAngle)) * M_PI / 180.0)));
+ }
+ deleteFont(font);
+ }
+}
+
+// append a name but only if it's not already in the list
+void
+XeTeXFontMgr::appendToList(std::list<std::string>* list, const char* str)
+{
+ for (std::list<std::string>::const_iterator i = list->begin(); i != list->end(); ++i)
+ if (*i == str)
+ return;
+ list->push_back(str);
+}
+
+// prepend a name, removing it from later in the list if present
+void
+XeTeXFontMgr::prependToList(std::list<std::string>* list, const char* str)
+{
+ for (std::list<std::string>::iterator i = list->begin(); i != list->end(); ++i)
+ if (*i == str) {
+ list->erase(i);
+ break;
+ }
+ list->push_front(str);
+}
+
+void
+XeTeXFontMgr::addToMaps(PlatformFontRef platformFont, const NameCollection* names)
+{
+ if (names->psName.length() == 0)
+ return; // can't use a font that lacks a PostScript name
+
+ if (psNameToFont.find(names->psName) != psNameToFont.end())
+ return; // duplicates an earlier PS name, so skip
+
+ Font* thisFont = new Font(platformFont);
+ thisFont->psName = new std::string(names->psName);
+ getOpSizeRecAndStyleFlags(thisFont);
+
+ psNameToFont[names->psName] = thisFont;
+ platformRefToFont[platformFont] = thisFont;
+
+ if (names->fullNames.size() > 0)
+ thisFont->fullName = new std::string(*(names->fullNames.begin()));
+
+ if (names->familyNames.size() > 0)
+ thisFont->familyName = new std::string(*(names->familyNames.begin()));
+ else
+ thisFont->familyName = new std::string(names->psName);
+
+ if (names->styleNames.size() > 0)
+ thisFont->styleName = new std::string(*(names->styleNames.begin()));
+ else
+ thisFont->styleName = new std::string;
+
+ std::list<std::string>::const_iterator i;
+ for (i = names->familyNames.begin(); i != names->familyNames.end(); ++i) {
+ std::map<std::string,Family*>::iterator iFam = nameToFamily.find(*i);
+ Family* family;
+ if (iFam == nameToFamily.end()) {
+ family = new Family;
+ nameToFamily[*i] = family;
+ family->minWeight = thisFont->weight;
+ family->maxWeight = thisFont->weight;
+ family->minWidth = thisFont->width;
+ family->maxWidth = thisFont->width;
+ family->minSlant = thisFont->slant;
+ family->maxSlant = thisFont->slant;
+ }
+ else {
+ family = iFam->second;
+ if (thisFont->weight < family->minWeight)
+ family->minWeight = thisFont->weight;
+ if (thisFont->weight > family->maxWeight)
+ family->maxWeight = thisFont->weight;
+ if (thisFont->width < family->minWidth)
+ family->minWidth = thisFont->width;
+ if (thisFont->width > family->maxWidth)
+ family->maxWidth = thisFont->width;
+ if (thisFont->slant < family->minSlant)
+ family->minSlant = thisFont->slant;
+ if (thisFont->slant > family->maxSlant)
+ family->maxSlant = thisFont->slant;
+ }
+
+ if (thisFont->parent == NULL)
+ thisFont->parent = family;
+
+ // ensure all style names in the family point to thisFont
+ for (std::list<std::string>::const_iterator j = names->styleNames.begin(); j != names->styleNames.end(); ++j) {
+ std::map<std::string,Font*>::iterator iFont = family->styles->find(*j);
+ if (iFont == family->styles->end())
+ (*family->styles)[*j] = thisFont;
+/*
+ else if (iFont->second != thisFont)
+ fprintf(stderr, "# Font name warning: ambiguous Style \"%s\" in Family \"%s\" (PSNames \"%s\" and \"%s\")\n",
+ j->c_str(), i->c_str(), iFont->second->psName->c_str(), thisFont->psName->c_str());
+*/
+ }
+ }
+
+ for (i = names->fullNames.begin(); i != names->fullNames.end(); ++i) {
+ std::map<std::string,Font*>::iterator iFont = nameToFont.find(*i);
+ if (iFont == nameToFont.end())
+ nameToFont[*i] = thisFont;
+/*
+ else if (iFont->second != thisFont)
+ fprintf(stderr, "# Font name warning: ambiguous FullName \"%s\" (PSNames \"%s\" and \"%s\")\n",
+ i->c_str(), iFont->second->psName->c_str(), thisFont->psName->c_str());
+*/
+ }
+}
+
+void
+XeTeXFontMgr::die(const char*s, int i) const
+{
+ fprintf(stderr, s, i);
+ fprintf(stderr, " - exiting\n");
+ exit(3);
+}
+
+void
+XeTeXFontMgr::terminate()
+{
+}
+