diff options
Diffstat (limited to 'Build/source/texk/web2c/xetexdir/XeTeXFontMgr.cpp')
-rw-r--r-- | Build/source/texk/web2c/xetexdir/XeTeXFontMgr.cpp | 661 |
1 files changed, 661 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..11094b10b47 --- /dev/null +++ b/Build/source/texk/web2c/xetexdir/XeTeXFontMgr.cpp @@ -0,0 +1,661 @@ +/****************************************************************************\ + Part of the XeTeX typesetting system + Copyright (c) 1994-2008 by SIL International + Copyright (c) 2009-2014 by Jonathan Kew + + SIL Author(s): 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 THE COPYRIGHT HOLDERS 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 the copyright holders +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in this Software without prior written +authorization from the copyright holders. +\****************************************************************************/ + +#include <w2c/config.h> + +#include "XeTeX_web.h" + +#ifdef XETEX_MAC +#include "XeTeXFontMgr_Mac.h" +#else +#include "XeTeXFontMgr_FC.h" +#endif +#include "XeTeXFontInst.h" + +#include <hb-ot.h> + +// see cpascal.h +#define printcstring(STR) \ + do { \ + const char* ch_ptr = (STR); \ + while (*ch_ptr) \ + zprintchar(*(ch_ptr++)); \ + } while (0) + +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_FC; +#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, or negative for 'scaled' factor + // "variant" string will be shortened (in-place) by removal of /B and /I if present +{ + std::string nameStr(name); + Font* font = NULL; + double dsize = 10.0; + loadedfontdesignsize = 655360L; + + for (int pass = 0; pass < 2; ++pass) { + // try full name as given + std::map<std::string,Font*>::iterator i = m_nameToFont.find(nameStr); + if (i != m_nameToFont.end()) { + font = i->second; + if (font->opSizeInfo.designSize != 0.0) + dsize = font->opSizeInfo.designSize; + 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 = m_nameToFamily.find(family); + if (f != m_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; + if (font->opSizeInfo.designSize != 0.0) + dsize = font->opSizeInfo.designSize; + break; + } + } + } + + // try as PostScript name + i = m_psNameToFont.find(nameStr); + if (i != m_psNameToFont.end()) { + font = i->second; + if (font->opSizeInfo.designSize != 0.0) + dsize = font->opSizeInfo.designSize; + break; + } + + // try for the name as a family name + std::map<std::string,Family*>::iterator f = m_nameToFamily.find(nameStr); + + if (f != m_nameToFamily.end()) { + // look for a family member with the "regular" bit set in OS/2 + int regFonts = 0; + for (i = f->second->styles->begin(); i != f->second->styles->end(); ++i) + if (i->second->isReg) { + if (regFonts == 0) + font = i->second; + ++regFonts; + } + + // families with Ornament or similar fonts may flag those as Regular, + // which confuses the search above... so try some known names + if (font == NULL || regFonts > 1) { + // try for style "Regular", "Plain", "Normal", "Roman" + 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; + else { + i = f->second->styles->find("Roman"); + 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, I, and S=... codes from the string, just retain /engine option + sReqEngine = 0; + bool reqBold = false; + bool reqItal = false; + if (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) { // for backword compatability + sReqEngine = 'O'; + cp += 3; + if (varString.length() > 0 && *(varString.end() - 1) != '/') + varString.append("/"); + varString.append("OT"); + goto skip_to_slash; + } + if (strncmp(cp, "OT", 2) == 0) { + sReqEngine = 'O'; + cp += 2; + if (varString.length() > 0 && *(varString.end() - 1) != '/') + varString.append("/"); + varString.append("OT"); + goto skip_to_slash; + } + if (strncmp(cp, "GR", 2) == 0) { + sReqEngine = 'G'; + cp += 2; + if (varString.length() > 0 && *(varString.end() - 1) != '/') + varString.append("/"); + varString.append("GR"); + goto skip_to_slash; + } + if (*cp == 'S') { + ++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; + } + } + 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) { + // try to increase weight by 1/2 x (max - min), rounding up + bestMatch = bestMatchFromFamily(parent, + font->weight + (parent->maxWeight - parent->minWeight) / 2 + 1, + 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 (ptSize < 0.0) + ptSize = dsize; + if (font != NULL && font->opSizeInfo.subFamilyID != 0 && ptSize > 0.0) { + 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; + } + } + + if (font != NULL && font->opSizeInfo.designSize != 0.0) + loadedfontdesignsize = unsigned(font->opSizeInfo.designSize * 65536.0 + 0.5); + + if (gettracingfontsstate() > 0) { + begindiagnostic(); + zprintnl(' '); + printcstring("-> "); + printcstring(getPlatformFontDesc(font->fontRef).c_str()); + zenddiagnostic(0); + } + + return font->fontRef; +} + +const char* +XeTeXFontMgr::getFullName(PlatformFontRef font) const +{ + std::map<PlatformFontRef,Font*>::const_iterator i = m_platformRefToFont.find(font); + if (i == m_platformRefToFont.end()) + die("internal error %d in XeTeXFontMgr", 2); + if (i->second->m_fullName != NULL) + return i->second->m_fullName->c_str(); + else + return i->second->m_psName->c_str(); +} + +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; +} + +const XeTeXFontMgr::OpSizeRec* +XeTeXFontMgr::getOpSize(XeTeXFont font) +{ + hb_font_t* hbFont = ((XeTeXFontInst*)font)->getHbFont(); + if (hbFont != NULL) { + hb_face_t* face = hb_font_get_face(hbFont); + OpSizeRec* pSizeRec = (OpSizeRec*) xmalloc(sizeof(OpSizeRec)); + + unsigned int designSize, minSize, maxSize; + bool ok = hb_ot_layout_get_size_params(face, + &designSize, + &pSizeRec->subFamilyID, + &pSizeRec->nameCode, + &minSize, + &maxSize); + + if (ok) { + // Convert sizes from PostScript deci-points to TeX points + pSizeRec->designSize = designSize * 72.27 / 72.0 / 10.0; + pSizeRec->minSize = minSize * 72.27 / 72.0 / 10.0; + pSizeRec->maxSize = maxSize * 72.27 / 72.0 / 10.0; + return pSizeRec; + } + + free(pSizeRec); + return NULL; + } + + return NULL; +} + +double +XeTeXFontMgr::getDesignSize(XeTeXFont font) +{ + const OpSizeRec* pSizeRec = getOpSize(font); + if (pSizeRec != NULL) + return pSizeRec->designSize; + else + return 10.0; +} + +void +XeTeXFontMgr::getOpSizeRecAndStyleFlags(Font* theFont) +{ + XeTeXFont font = createFont(theFont->fontRef, 655360); + XeTeXFontInst* fontInst = (XeTeXFontInst*) font; + if (font != 0) { + const OpSizeRec* pSizeRec = getOpSize(font); + if (pSizeRec != NULL) { + theFont->opSizeInfo.designSize = pSizeRec->designSize; + if (pSizeRec->subFamilyID == 0 + && pSizeRec->nameCode == 0 + && pSizeRec->minSize == 0.0 + && pSizeRec->maxSize == 0.0) + goto done_size; // feature is valid, but no 'size' range + theFont->opSizeInfo.subFamilyID = pSizeRec->subFamilyID; + theFont->opSizeInfo.nameCode = pSizeRec->nameCode; + theFont->opSizeInfo.minSize = pSizeRec->minSize; + theFont->opSizeInfo.maxSize = pSizeRec->maxSize; + } + done_size: + + const TT_OS2* os2Table = (TT_OS2*) fontInst->getFontTable(ft_sfnt_os2); + if (os2Table != NULL) { + theFont->weight = os2Table->usWeightClass; + theFont->width = os2Table->usWidthClass; + uint16_t sel = os2Table->fsSelection; + theFont->isReg = (sel & (1 << 6)) != 0; + theFont->isBold = (sel & (1 << 5)) != 0; + theFont->isItalic = (sel & (1 << 0)) != 0; + } + + const TT_Header* headTable = (TT_Header*) fontInst->getFontTable(ft_sfnt_head); + if (headTable != NULL) { + uint16_t ms = headTable->Mac_Style; + if ((ms & (1 << 0)) != 0) + theFont->isBold = true; + if ((ms & (1 << 1)) != 0) + theFont->isItalic = true; + } + + const TT_Postscript* postTable = (const TT_Postscript*) fontInst->getFontTable(ft_sfnt_post); + if (postTable != NULL) { + theFont->slant = (int)(1000 * (tan(Fix2D(-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 (m_platformRefToFont.find(platformFont) != m_platformRefToFont.end()) + return; // this font has already been cached + + if (names->m_psName.length() == 0) + return; // can't use a font that lacks a PostScript name + + if (m_psNameToFont.find(names->m_psName) != m_psNameToFont.end()) + return; // duplicates an earlier PS name, so skip + + Font* thisFont = new Font(platformFont); + thisFont->m_psName = new std::string(names->m_psName); + getOpSizeRecAndStyleFlags(thisFont); + + m_psNameToFont[names->m_psName] = thisFont; + m_platformRefToFont[platformFont] = thisFont; + + if (names->m_fullNames.size() > 0) + thisFont->m_fullName = new std::string(*(names->m_fullNames.begin())); + + if (names->m_familyNames.size() > 0) + thisFont->m_familyName = new std::string(*(names->m_familyNames.begin())); + else + thisFont->m_familyName = new std::string(names->m_psName); + + if (names->m_styleNames.size() > 0) + thisFont->m_styleName = new std::string(*(names->m_styleNames.begin())); + else + thisFont->m_styleName = new std::string; + + std::list<std::string>::const_iterator i; + for (i = names->m_familyNames.begin(); i != names->m_familyNames.end(); ++i) { + std::map<std::string,Family*>::iterator iFam = m_nameToFamily.find(*i); + Family* family; + if (iFam == m_nameToFamily.end()) { + family = new Family; + m_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->m_styleNames.begin(); j != names->m_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->m_psName->c_str(), thisFont->m_psName->c_str()); +*/ + } + } + + for (i = names->m_fullNames.begin(); i != names->m_fullNames.end(); ++i) { + std::map<std::string,Font*>::iterator iFont = m_nameToFont.find(*i); + if (iFont == m_nameToFont.end()) + m_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->m_psName->c_str(), thisFont->m_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() +{ +} + |