/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
/* * Sun Font Tools * * Author: Alexander Gelfenbain *
*/
/*- In horizontal writing mode right sidebearing is calculated using this formula
*- rsb = aw - (lsb + xMax - xMin) -*/ struct TTGlyphMetrics {
sal_Int16 xMin;
sal_Int16 yMin;
sal_Int16 xMax;
sal_Int16 yMax;
sal_uInt16 aw; /*- Advance Width (horizontal writing mode) */
sal_Int16 lsb; /*- Left sidebearing (horizontal writing mode) */
sal_uInt16 ah; /*- advance height (vertical writing mode) */
};
}
/*- Data access methods for data stored in big-endian format */ static sal_Int16 GetInt16(const sal_uInt8 *ptr, size_t offset)
{
sal_Int16 t;
assert(ptr != nullptr);
static F16Dot16 fixedMul(F16Dot16 a, F16Dot16 b)
{ return fix16_mul(a, b);
}
static F16Dot16 fixedDiv(F16Dot16 a, F16Dot16 b)
{ return fix16_div(a, b);
}
/*- returns a * b / c -*/ /* XXX provide a real implementation that preserves accuracy */ static F16Dot16 fixedMulDiv(F16Dot16 a, F16Dot16 b, F16Dot16 c)
{
F16Dot16 res = fixedMul(a, b); return fixedDiv(res, c);
}
/*- Translate units from TT to PS (standard 1/1000) -*/ staticint XUnits(int unitsPerEm, int n)
{ return (n * 1000) / unitsPerEm;
}
/* Outline Extraction functions */
/* fills the aw and lsb entries of the TTGlyphMetrics structure from hmtx table -*/ staticvoid GetMetrics(AbstractTrueTypeFont const *ttf, sal_uInt32 glyphID, TTGlyphMetrics *metrics)
{
sal_uInt32 nSize; const sal_uInt8* table = ttf->table(O_hmtx, nSize);
if (metrics) { /*- GetCompoundTTOutline() calls this function with NULL metrics -*/
metrics->xMin = GetInt16(ptr, GLYF_xMin_offset);
metrics->yMin = GetInt16(ptr, GLYF_yMin_offset);
metrics->xMax = GetInt16(ptr, GLYF_xMax_offset);
metrics->yMax = GetInt16(ptr, GLYF_yMax_offset);
GetMetrics(ttf, glyphID, metrics);
}
/* determine the last point and be extra safe about it. But probably this code is not needed */
sal_uInt16 lastPoint=0; for (i=0; i<numberOfContours; i++)
{ const sal_uInt16 t = GetUInt16(ptr, nContourOffset + i * 2); if (t > lastPoint)
lastPoint = t;
}
//at a minimum its one byte per entry if (palen > nBytesRemaining || lastPoint > nBytesRemaining-1)
{
SAL_WARN("vcl.fonts", "Font " << OUString::createFromAscii(ttf->fileName()) << "claimed a palen of "
<< palen << " but max bytes remaining is " << nBytesRemaining); return 0;
}
std::vector<ControlPoint> pa(palen);
i = 0; while (i <= lastPoint) { if (!nBytesRemaining)
{
SAL_WARN("vcl.fonts", "short read"); break;
}
sal_uInt8 flag = *p++;
--nBytesRemaining;
pa[i++].flags = static_cast<sal_uInt32>(flag); if (flag & 8) { /*- repeat flag */ if (!nBytesRemaining)
{
SAL_WARN("vcl.fonts", "short read"); break;
}
n = *p++;
--nBytesRemaining; // coverity[tainted_data : FALSE] - i > lastPoint extra checks the n loop bound for (j=0; j<n; j++) { if (i > lastPoint) { /*- if the font is really broken */ return 0;
}
pa[i++].flags = flag;
}
}
}
/*- Process the X coordinate */
z = 0; for (i = 0; i <= lastPoint; i++) { if (pa[i].flags & 0x02) { if (!nBytesRemaining)
{
SAL_WARN("vcl.fonts", "short read"); break;
} if (pa[i].flags & 0x10) {
z += static_cast<int>(*p++);
} else {
z -= static_cast<int>(*p++);
}
--nBytesRemaining;
} elseif ( !(pa[i].flags & 0x10)) { if (nBytesRemaining < 2)
{
SAL_WARN("vcl.fonts", "short read"); break;
}
z += GetInt16(p, 0);
p += 2;
nBytesRemaining -= 2;
}
pa[i].x = static_cast<sal_Int16>(z);
}
/*- Process the Y coordinate */
z = 0; for (i = 0; i <= lastPoint; i++) { if (pa[i].flags & 0x04) { if (!nBytesRemaining)
{
SAL_WARN("vcl.fonts", "short read"); break;
} if (pa[i].flags & 0x20) {
z += *p++;
} else {
z -= *p++;
}
--nBytesRemaining;
} elseif ( !(pa[i].flags & 0x20)) { if (nBytesRemaining < 2)
{
SAL_WARN("vcl.fonts", "short read"); break;
}
z += GetInt16(p, 0);
p += 2;
nBytesRemaining -= 2;
}
pa[i].y = static_cast<sal_Int16>(z);
}
for (i=0; i<numberOfContours; i++) {
sal_uInt16 offset = GetUInt16(ptr, 10 + i * 2);
SAL_WARN_IF(offset >= palen, "vcl.fonts", "Font " << OUString::createFromAscii(ttf->fileName()) << " contour " << i << " claimed an illegal offset of "
<< offset << " but max offset is " << palen-1); if (offset >= palen) continue;
pa[offset].flags |= 0x00008000; /*- set the end contour flag */
}
SAL_WARN_IF(np && (!m || !n), "vcl.fonts", "Parsing error in " << OUString::createFromAscii(ttf->fileName()) << ": divide by zero");
if (m != 0 && n != 0) { for (i=0; i<np; i++) {
F16Dot16 t;
ControlPoint cp;
cp.flags = nextComponent[i].flags; const sal_uInt16 x = nextComponent[i].x; const sal_uInt16 y = nextComponent[i].y;
t = o3tl::saturating_add(o3tl::saturating_add(fixedMulDiv(a, x << 16, m), fixedMulDiv(c, y << 16, m)), sal_Int32(sal_uInt16(e) << 16));
cp.x = static_cast<sal_Int16>(fixedMul(t, m) >> 16);
t = o3tl::saturating_add(o3tl::saturating_add(fixedMulDiv(b, x << 16, n), fixedMulDiv(d, y << 16, n)), sal_Int32(sal_uInt16(f) << 16));
cp.y = static_cast<sal_Int16>(fixedMul(t, n) >> 16);
myPoints.push_back( cp );
}
}
if (myPoints.size() > SAL_MAX_UINT16) {
SAL_WARN("vcl.fonts", "number of points has to be limited to max value GlyphData::npoints can contain, abandon effort");
myPoints.clear(); break;
}
} while (flags & MORE_COMPONENTS);
// #i123417# some fonts like IFAOGrec have no outline points in some compound glyphs // so this unlikely but possible scenario should be handled gracefully if( myPoints.empty() ) return 0;
np = myPoints.size();
pointArray = std::move(myPoints);
return np;
}
/* NOTE: GetTTGlyphOutline() returns -1 if the glyphID is incorrect, * but Get{Simple|Compound}GlyphOutline returns 0 in such a case. * * NOTE: glyphlist is the stack of glyphs traversed while constructing * a composite glyph. This is a safeguard against endless recursion * in corrupted fonts.
*/ staticint GetTTGlyphOutline(AbstractTrueTypeFont *ttf, sal_uInt32 glyphID, std::vector<ControlPoint>& pointArray, TTGlyphMetrics *metrics, std::vector< sal_uInt32 >* glyphlist)
{
sal_uInt32 glyflength; const sal_uInt8 *table = ttf->table(O_glyf, glyflength);
sal_Int16 numberOfContours; int res;
pointArray.clear();
if (metrics)
memset(metrics, 0, sizeof(TTGlyphMetrics));
sal_uInt32 nOffset = ttf->glyphOffset(glyphID); if (nOffset > nNextOffset) return -1;
int length = nNextOffset - nOffset; if (length == 0) { /*- empty glyphs still have hmtx and vmtx metrics values */ if (metrics) GetMetrics(ttf, glyphID, metrics); return 0;
}
/*- Extracts a string from the name table and allocates memory for it -*/
static OString nameExtract( const sal_uInt8* name, int nTableSize, int n, int dbFlag, OUString* ucs2result )
{
OStringBuffer res; const sal_uInt8* ptr = name + GetUInt16(name, 4) + GetUInt16(name + 6, 12 * n + 10); int len = GetUInt16(name+6, 12 * n + 8);
do { constint i = (l + r) >> 1;
t1 = GetUInt32(name + 6, i * 12 + 0);
t2 = GetUInt32(name + 6, i * 12 + 4);
if (! ((m1 < t1) || ((m1 == t1) && (m2 < t2)))) l = i + 1; if (! ((m1 > t1) || ((m1 == t1) && (m2 > t2)))) r = i - 1;
} while (l <= r);
if (l - r == 2) { return l - 1;
}
return -1;
}
/* XXX marlett.ttf uses (3, 0, 1033) instead of (3, 1, 1033) and does not have any Apple tables. * Fix: if (3, 1, 1033) is not found - need to check for (3, 0, 1033) * * /d/fonts/ttzh_tw/Big5/Hanyi/ma6b5p uses (1, 0, 19) for English strings, instead of (1, 0, 0) * and does not have (3, 1, 1033) * Fix: if (1, 0, 0) and (3, 1, 1033) are not found need to look for (1, 0, *) - that will * require a change in algorithm * * /d/fonts/fdltest/Korean/h2drrm has unsorted names and an unknown (to me) Mac LanguageID, * but (1, 0, 1042) strings usable * Fix: change algorithm, and use (1, 0, *) if both standard Mac and MS strings are not found
*/
/* #i60349# sanity check psname * psname practically has to be 7bit ASCII and should not contain spaces * there is a class of broken fonts which do not fulfill that at all, so let's try * if the family name is 7bit ASCII and take it instead if so
*/ /* check psname */ for( i = 0; i < t->psname.getLength() && bPSNameOK; i++ ) if( t->psname[ i ] < 33 || (t->psname[ i ] & 0x80) )
bPSNameOK = false; if( bPSNameOK ) return;
/* check if family is a suitable replacement */ if( t->ufamily.isEmpty() && t->family.isEmpty() ) return;
bool bReplace = true;
for( i = 0; i < t->ufamily.getLength() && bReplace; i++ ) if( t->ufamily[ i ] < 33 || t->ufamily[ i ] > 127 )
bReplace = false; if( bReplace )
{
t->psname = t->family;
}
}
/*- Public functions */
int CountTTCFonts(constchar* fname)
{
FILE* fd; #ifdef LINUX int nFD; int n; if (sscanf(fname, "/:FD:/%d%n", &nFD, &n) == 1 && fname[n] == '\0')
{
lseek(nFD, 0, SEEK_SET); int nDupFd = dup(nFD);
fd = nDupFd != -1 ? fdopen(nDupFd, "rb") : nullptr;
} else #endif
fd = fopen(fname, "rb");
//Feel free to calc the exact max possible number of fonts a file //could contain given its physical size. But this will clamp it to //a sane starting point //http://processingjs.nihongoresources.com/the_smallest_font/ //https://github.com/grzegorzrolek/null-ttf constint nMaxFontsPossible = fileSize / 528; if (nFonts > nMaxFontsPossible)
{
SAL_WARN("vcl.fonts", "font file " << fname <<" claims to have "
<< nFonts << " fonts, but only "
<< nMaxFontsPossible << " are possible");
nFonts = nMaxFontsPossible;
}
}
if( (*ttf)->fileName().empty() )
{
ret = SFErrCodes::Memory; goto cleanup;
}
int nFD; int n; if (sscanf(fname, "/:FD:/%d%n", &nFD, &n) == 1 && fname[n] == '\0')
{
lseek(nFD, 0, SEEK_SET);
fd = dup(nFD);
} else
fd = open(fname, O_RDONLY);
if (fd == -1) {
ret = SFErrCodes::BadFile; goto cleanup;
}
if (fstat(fd, &st) == -1) {
ret = SFErrCodes::FileIo; goto cleanup;
}
(*ttf)->fsize = st.st_size;
/* On Mac OS, most likely will happen if a Mac user renames a font file * to be .ttf when it's really a Mac resource-based font. * Size will be 0, but fonts smaller than 4 bytes would be broken anyway.
*/ if ((*ttf)->fsize == 0) {
ret = SFErrCodes::BadFile; goto cleanup;
}
if (((*ttf)->ptr = static_cast<sal_uInt8 *>(mmap(nullptr, (*ttf)->fsize, PROT_READ, MAP_SHARED, fd, 0))) == MAP_FAILED) {
ret = SFErrCodes::Memory; goto cleanup;
}
if (hasTable(O_glyf) && (table = this->table(O_loca, table_size))) /* TTF or TTF-OpenType */
{ int k = (table_size / (indexfmt ? 4 : 2)) - 1; if (k < static_cast<int>(m_nGlyphs)) /* Hack for broken Chinese fonts */
m_nGlyphs = k;
m_aGlyphOffsets.clear();
m_aGlyphOffsets.reserve(m_nGlyphs + 1); for (int i = 0; i <= static_cast<int>(m_nGlyphs); ++i)
m_aGlyphOffsets.push_back(indexfmt ? GetUInt32(table, i << 2) : static_cast<sal_uInt32>(GetUInt16(table, i << 1)) << 1);
} elseif (this->table(O_CFF, table_size)) /* PS-OpenType */
{ int k = (table_size / 2) - 1; /* set a limit here, presumably much lower than the table size, but establishes some sort of physical bound */ if (k < static_cast<int>(m_nGlyphs))
m_nGlyphs = k;
m_aGlyphOffsets.clear(); /* TODO: implement to get subsetting */
} else { // Bitmap font, accept for now. // TODO: We only need this for fonts with CBDT table since they usually // lack glyf or CFF table, the check should be more specific, or better // non-subsetting code should not be calling this.
m_aGlyphOffsets.clear();
}
/* Fixup offsets when only a TTC extract was provided */ if (facenum == sal_uInt32(~0))
{
sal_uInt8* pHead = const_cast<sal_uInt8*>(m_aTableList[O_head].pData); if (!pHead) return SFErrCodes::TtFormat;
/* limit Head candidate to TTC extract's limits */ if (pHead > ptr + (fsize - 54))
pHead = ptr + (fsize - 54);
/* TODO: find better method than searching head table's magic */
sal_uInt8* p = nullptr; for (p = pHead + 12; p > ptr; --p)
{ if( p[0]==0x5F && p[1]==0x0F && p[2]==0x3C && p[3]==0xF5 ) { int nDelta = (pHead + 12) - p; if( nDelta ) for( int j = 0; j < NUM_TAGS; ++j ) if (hasTable(j))
m_aTableList[j].pData -= nDelta; break;
}
} if (p <= ptr) return SFErrCodes::TtFormat;
}
/* Check the table offsets after TTC correction */ for (i=0; i<NUM_TAGS; i++) { /* sanity check: table must lay completely within the file * at this point one could check the checksum of all contained * tables, but this would be quite time intensive. * Try to fix tables, so we can cope with minor problems.
*/
sal_uInt32 nOffset = ttf->glyphOffset(glyphID); if (nOffset > nNextOffset) return 0;
if (std::find(glyphlist.begin(), glyphlist.end(), glyphID) != glyphlist.end())
{
SAL_INFO("vcl.fonts", "Already have this glyph, don't need to get it again"); return 0;
}
glyphlist.push_back( glyphID );
// Empty glyph. if (nOffset == nNextOffset) return n;
// mac fonts usually do not have an OS2-table // => get valid ascent/descent values from other tables if (!rInfo.m_nAscent)
rInfo.m_nAscent = +aTTInfo.typoAscender; if (!rInfo.m_nAscent)
rInfo.m_nAscent = +aTTInfo.ascender; if (!rInfo.m_nDescent)
rInfo.m_nDescent = +aTTInfo.typoDescender; if (!rInfo.m_nDescent)
rInfo.m_nDescent = -aTTInfo.descender;
// Multiple questions: // - Why is there a glyph limit? // MacOS used to handle 257 glyphs... // Also the much more complex PrintFontManager variant has this limit. // Also the very first implementation has the limit in // commit 8789ed701e98031f2a1657ea0dfd6f7a0b050992 // - Why doesn't the PrintFontManager care about the fake glyph? It // is used on all unx platforms to create the subset font. // - Should the SAL_WARN actually be asserts, like on MacOS? if (nOrigGlyphCount > 256)
{
SAL_WARN("vcl.fonts", "too many glyphs for subsetting"); returnfalse;
}
int nGlyphCount = nOrigGlyphCount;
sal_uInt16 aShortIDs[256];
sal_uInt8 aTempEncs[256];
// handle the undefined / first font glyph int nNotDef = -1, i; for (i = 0; i < nGlyphCount; ++i)
{
aTempEncs[i] = pEncoding[i];
aShortIDs[i] = static_cast<sal_uInt16>(pGlyphIds[i]); if (!aShortIDs[i]) if (nNotDef < 0)
nNotDef = i;
}
// nNotDef glyph must be in pos 0 => swap glyphids if (nNotDef != 0)
{ if (nNotDef < 0)
{ if (nGlyphCount == 256)
{
SAL_WARN("vcl.fonts", "too many glyphs for subsetting"); returnfalse;
}
nNotDef = nGlyphCount++;
}
if (nTableSize < 6)
{ #if OSL_DEBUG_LEVEL > 1
SAL_WARN("vcl.fonts", "O_name table too small."); #endif return;
}
sal_uInt16 n = GetUInt16(table, 2);
sal_uInt32 nStrBase = GetUInt16(table, 4); int i;
if (n == 0) return;
const sal_uInt32 remaining_table_size = nTableSize-6; const sal_uInt32 nMinRecordSize = 12; const sal_uInt32 nMaxRecords = remaining_table_size / nMinRecordSize; if (n > nMaxRecords)
{
SAL_WARN("vcl.fonts", "Parsing error in " << OUString::createFromAscii(ttf->fileName()) << ": " << nMaxRecords << " max possible entries, but " <<
n << " claimed, truncating");
n = nMaxRecords;
}
nr.resize(n);
for (i = 0; i < n; i++) {
sal_uInt32 nLargestFixedOffsetPos = 6 + 10 + 12 * i;
sal_uInt32 nMinSize = nLargestFixedOffsetPos + sizeof(sal_uInt16); if (nMinSize > nTableSize)
{
SAL_WARN( "vcl.fonts", "Font " << OUString::createFromAscii(ttf->fileName()) << " claimed to have "
<< n << " name records, but only space for " << i); break;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.