/* -*- 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/.
*/
#pragma once
#include <font/TTFStructure.hxx>
#include <vcl/font/FontDataContainer.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
namespace font
{
/** Handles reading of the name table */
class NameTableHandler
{
private:
FontDataContainer const& mrFontDataContainer;
const TableDirectoryEntry* mpTableDirectoryEntry;
const char* mpNameTablePointer;
const NameTable* mpNameTable;
sal_uInt16 mnNumberOfRecords;
const char* getTablePointer(const TableDirectoryEntry* pEntry, size_t nEntrySize)
{
size_t nSize = mrFontDataContainer.size();
if (pEntry->offset > nSize)
{
SAL_WARN("vcl.fonts", "Table offset beyond end of available data");
return nullptr;
}
if (nEntrySize > nSize - pEntry->offset)
{
SAL_WARN("vcl.fonts", "Insufficient available data for table entry");
return nullptr;
}
return mrFontDataContainer.getPointer() + pEntry->offset;
}
public:
NameTableHandler(FontDataContainer const& rFontDataContainer,
const TableDirectoryEntry* pTableDirectoryEntry)
: mrFontDataContainer(rFontDataContainer)
, mpTableDirectoryEntry(pTableDirectoryEntry)
, mpNameTablePointer(getTablePointer(mpTableDirectoryEntry, sizeof(NameTable)))
, mpNameTable(reinterpret_cast<const NameTable*>(mpNameTablePointer))
, mnNumberOfRecords(0)
{
if (mpNameTable)
{
mnNumberOfRecords = mpNameTable->nCount;
const char* pEnd = mrFontDataContainer.getPointer() + mrFontDataContainer.size();
const char* pStart = mpNameTablePointer + sizeof(NameTable);
size_t nAvailableData = pEnd - pStart;
size_t nMaxRecordsPossible = nAvailableData / sizeof(NameRecord);
if (mnNumberOfRecords > nMaxRecordsPossible)
{
SAL_WARN("vcl.fonts", "Font claimed to have "
<< mnNumberOfRecords
<< " name records, but only space for "
<< nMaxRecordsPossible);
mnNumberOfRecords = nMaxRecordsPossible;
}
}
}
sal_uInt32 getTableOffset() { return mpTableDirectoryEntry->offset; }
const NameTable* getNameTable() { return mpNameTable; }
/** Number of tables */
sal_uInt16 getNumberOfRecords() { return mnNumberOfRecords; }
/** Get a name table record for index */
const NameRecord* getNameRecord(sal_uInt32 index)
{
const char* pPointer = mpNameTablePointer + sizeof(NameTable);
pPointer += sizeof(NameRecord) * index;
return reinterpret_cast<const NameRecord*>(pPointer);
}
/** Get offset to english unicode string
*
* See: https://learn.microsoft.com/en-us/typography/opentype/spec/name#name-ids
*/
bool findEnglishUnicodeNameOffset(font::NameID eNameID, sal_uInt64& rOffset,
sal_uInt16& rLength)
{
rOffset = 0;
rLength = 0;
for (sal_uInt16 n = 0, nCount = getNumberOfRecords(); n < nCount; n++)
{
const font::NameRecord* pNameRecord = getNameRecord(n);
if (pNameRecord->nPlatformID == 3 // Windows
&& pNameRecord->nEncodingID == 1 // Unicode BMP
&& pNameRecord->nLanguageID == 0x0409 // en-us
&& pNameRecord->nNameID == sal_uInt16(eNameID))
{
rLength = pNameRecord->nLength;
rOffset = getTableOffset() + getNameTable()->nStorageOffset
+ pNameRecord->nStringOffset;
return true;
}
}
return false;
}
};
/** Handles reading of table entries */
class TableEntriesHandler
{
private:
FontDataContainer const& mrFontDataContainer;
const char* mpFirstPosition;
sal_uInt16 mnNumberOfTables;
const char* getTablePointer(const TableDirectoryEntry* pEntry, size_t nEntrySize)
{
size_t nSize = mrFontDataContainer.size();
if (pEntry->offset > nSize)
{
SAL_WARN("vcl.fonts", "Table offset beyond end of available data");
return nullptr;
}
if (nEntrySize > nSize - pEntry->offset)
{
SAL_WARN("vcl.fonts", "Insufficient available data for table entry");
return nullptr;
}
return mrFontDataContainer.getPointer() + pEntry->offset;
}
public:
TableEntriesHandler(FontDataContainer const& rFontDataContainer)
: mrFontDataContainer(rFontDataContainer)
{
const char* pData = mrFontDataContainer.getPointer();
assert(mrFontDataContainer.size() >= sizeof(TableDirectory));
mpFirstPosition = pData + sizeof(TableDirectory);
const TableDirectory* pDirectory = reinterpret_cast<const TableDirectory*>(pData);
mnNumberOfTables = pDirectory->nNumberOfTables;
size_t nAvailableData = mrFontDataContainer.size() - sizeof(TableDirectory);
size_t nMaxRecordsPossible = nAvailableData / sizeof(TableDirectoryEntry);
if (mnNumberOfTables > nMaxRecordsPossible)
{
SAL_WARN("vcl.fonts", "Font claimed to have " << mnNumberOfTables
<< " table records, but only space for "
<< nMaxRecordsPossible);
mnNumberOfTables = nMaxRecordsPossible;
}
}
const TableDirectoryEntry* getEntry(sal_uInt32 nTag)
{
for (sal_uInt32 i = 0; i < mnNumberOfTables; i++)
{
const char* pPosition = mpFirstPosition + sizeof(TableDirectoryEntry) * i;
const auto* pEntry = reinterpret_cast<const TableDirectoryEntry*>(pPosition);
if (nTag == pEntry->tag)
return pEntry;
}
return nullptr;
}
const OS2Table* getOS2Table()
{
const auto* pEntry = getEntry(T_OS2);
if (!pEntry)
return nullptr;
return reinterpret_cast<const OS2Table*>(getTablePointer(pEntry, sizeof(OS2Table)));
}
const HeadTable* getHeadTable()
{
const auto* pEntry = getEntry(T_head);
if (!pEntry)
return nullptr;
return reinterpret_cast<const HeadTable*>(getTablePointer(pEntry, sizeof(HeadTable)));
}
const NameTable* getNameTable()
{
const auto* pEntry = getEntry(T_name);
if (!pEntry)
return nullptr;
return reinterpret_cast<const NameTable*>(getTablePointer(pEntry, sizeof(NameTable)));
}
std::unique_ptr<NameTableHandler> getNameTableHandler()
{
const auto* pEntry = getEntry(T_name);
if (!pEntry)
return nullptr;
return std::unique_ptr<NameTableHandler>(new NameTableHandler(mrFontDataContainer, pEntry));
}
};
/** Entry point handler for the TTF Font */
class TTFFont
{
private:
FontDataContainer const& mrFontDataContainer;
const char* getTablePointer(const TableDirectoryEntry* pEntry)
{
return mrFontDataContainer.getPointer() + pEntry->offset;
}
public:
TTFFont(FontDataContainer const& rFontDataContainer)
: mrFontDataContainer(rFontDataContainer)
{
}
std::unique_ptr<TableEntriesHandler> getTableEntriesHandler()
{
size_t nSize = mrFontDataContainer.size();
if (nSize < sizeof(TableDirectory))
{
SAL_WARN("vcl.fonts", "Font Data shorter than a TableDirectory");
return nullptr;
}
return std::make_unique<TableEntriesHandler>(mrFontDataContainer);
}
/** Gets the string from a name table */
OUString getNameTableString(sal_uInt64 nOffset, sal_uInt16 nLength)
{
size_t nSize = mrFontDataContainer.size();
if (nOffset > nSize)
{
SAL_WARN("vcl.fonts", "String offset beyond end of available data");
return OUString();
}
if (nLength > nSize - nOffset)
{
SAL_WARN("vcl.fonts", "Insufficient available data for string entry");
return OUString();
}
const auto* pString = reinterpret_cast<const o3tl::sal_uInt16_BE*>(
mrFontDataContainer.getPointer() + nOffset);
OUStringBuffer aStringBuffer;
for (sal_uInt16 i = 0; i < (nLength / 2); i++)
aStringBuffer.append(sal_Unicode(pString[i]));
return aStringBuffer.makeStringAndClear();
}
};
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'append' is required to be utilized.