Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/inc/font/TTFReader.hxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 */
9
10
#pragma once
11
12
#include <font/TTFStructure.hxx>
13
#include <vcl/font/FontDataContainer.hxx>
14
#include <rtl/ustrbuf.hxx>
15
#include <sal/log.hxx>
16
17
namespace font
18
{
19
/** Handles reading of the name table */
20
class NameTableHandler
21
{
22
private:
23
    FontDataContainer const& mrFontDataContainer;
24
25
    const TableDirectoryEntry* mpTableDirectoryEntry;
26
    const char* mpNameTablePointer;
27
    const NameTable* mpNameTable;
28
    sal_uInt16 mnNumberOfRecords;
29
30
    const char* getTablePointer(const TableDirectoryEntry* pEntry, size_t nEntrySize)
31
452
    {
32
452
        size_t nSize = mrFontDataContainer.size();
33
452
        if (pEntry->offset > nSize)
34
30
        {
35
30
            SAL_WARN("vcl.fonts", "Table offset beyond end of available data");
36
30
            return nullptr;
37
30
        }
38
422
        if (nEntrySize > nSize - pEntry->offset)
39
4
        {
40
4
            SAL_WARN("vcl.fonts", "Insufficient available data for table entry");
41
4
            return nullptr;
42
4
        }
43
418
        return mrFontDataContainer.getPointer() + pEntry->offset;
44
422
    }
45
46
public:
47
    NameTableHandler(FontDataContainer const& rFontDataContainer,
48
                     const TableDirectoryEntry* pTableDirectoryEntry)
49
452
        : mrFontDataContainer(rFontDataContainer)
50
452
        , mpTableDirectoryEntry(pTableDirectoryEntry)
51
452
        , mpNameTablePointer(getTablePointer(mpTableDirectoryEntry, sizeof(NameTable)))
52
452
        , mpNameTable(reinterpret_cast<const NameTable*>(mpNameTablePointer))
53
452
        , mnNumberOfRecords(0)
54
452
    {
55
452
        if (mpNameTable)
56
418
        {
57
418
            mnNumberOfRecords = mpNameTable->nCount;
58
59
418
            const char* pEnd = mrFontDataContainer.getPointer() + mrFontDataContainer.size();
60
418
            const char* pStart = mpNameTablePointer + sizeof(NameTable);
61
418
            size_t nAvailableData = pEnd - pStart;
62
418
            size_t nMaxRecordsPossible = nAvailableData / sizeof(NameRecord);
63
418
            if (mnNumberOfRecords > nMaxRecordsPossible)
64
364
            {
65
364
                SAL_WARN("vcl.fonts", "Font claimed to have "
66
364
                                          << mnNumberOfRecords
67
364
                                          << " name records, but only space for "
68
364
                                          << nMaxRecordsPossible);
69
364
                mnNumberOfRecords = nMaxRecordsPossible;
70
364
            }
71
418
        }
72
452
    }
73
74
484
    sal_uInt32 getTableOffset() { return mpTableDirectoryEntry->offset; }
75
76
484
    const NameTable* getNameTable() { return mpNameTable; }
77
78
    /** Number of tables */
79
1.80k
    sal_uInt16 getNumberOfRecords() { return mnNumberOfRecords; }
80
81
    /** Get a name table record for index */
82
    const NameRecord* getNameRecord(sal_uInt32 index)
83
2.24M
    {
84
2.24M
        const char* pPointer = mpNameTablePointer + sizeof(NameTable);
85
2.24M
        pPointer += sizeof(NameRecord) * index;
86
2.24M
        return reinterpret_cast<const NameRecord*>(pPointer);
87
2.24M
    }
88
89
    /** Get offset to english unicode string
90
     *
91
     * See: https://learn.microsoft.com/en-us/typography/opentype/spec/name#name-ids
92
     */
93
    bool findEnglishUnicodeNameOffset(font::NameID eNameID, sal_uInt64& rOffset,
94
                                      sal_uInt16& rLength)
95
1.80k
    {
96
1.80k
        rOffset = 0;
97
1.80k
        rLength = 0;
98
99
2.24M
        for (sal_uInt16 n = 0, nCount = getNumberOfRecords(); n < nCount; n++)
100
2.24M
        {
101
2.24M
            const font::NameRecord* pNameRecord = getNameRecord(n);
102
103
2.24M
            if (pNameRecord->nPlatformID == 3 // Windows
104
21.0k
                && pNameRecord->nEncodingID == 1 // Unicode BMP
105
17.4k
                && pNameRecord->nLanguageID == 0x0409 // en-us
106
6.71k
                && pNameRecord->nNameID == sal_uInt16(eNameID))
107
484
            {
108
484
                rLength = pNameRecord->nLength;
109
484
                rOffset = getTableOffset() + getNameTable()->nStorageOffset
110
484
                          + pNameRecord->nStringOffset;
111
484
                return true;
112
484
            }
113
2.24M
        }
114
1.32k
        return false;
115
1.80k
    }
116
};
117
118
/** Handles reading of table entries */
119
class TableEntriesHandler
120
{
121
private:
122
    FontDataContainer const& mrFontDataContainer;
123
    const char* mpFirstPosition;
124
    sal_uInt16 mnNumberOfTables;
125
126
    const char* getTablePointer(const TableDirectoryEntry* pEntry, size_t nEntrySize)
127
174
    {
128
174
        size_t nSize = mrFontDataContainer.size();
129
174
        if (pEntry->offset > nSize)
130
55
        {
131
55
            SAL_WARN("vcl.fonts", "Table offset beyond end of available data");
132
55
            return nullptr;
133
55
        }
134
119
        if (nEntrySize > nSize - pEntry->offset)
135
34
        {
136
34
            SAL_WARN("vcl.fonts", "Insufficient available data for table entry");
137
34
            return nullptr;
138
34
        }
139
85
        return mrFontDataContainer.getPointer() + pEntry->offset;
140
119
    }
141
142
public:
143
    TableEntriesHandler(FontDataContainer const& rFontDataContainer)
144
762
        : mrFontDataContainer(rFontDataContainer)
145
762
    {
146
762
        const char* pData = mrFontDataContainer.getPointer();
147
762
        assert(mrFontDataContainer.size() >= sizeof(TableDirectory));
148
762
        mpFirstPosition = pData + sizeof(TableDirectory);
149
150
762
        const TableDirectory* pDirectory = reinterpret_cast<const TableDirectory*>(pData);
151
762
        mnNumberOfTables = pDirectory->nNumberOfTables;
152
153
762
        size_t nAvailableData = mrFontDataContainer.size() - sizeof(TableDirectory);
154
762
        size_t nMaxRecordsPossible = nAvailableData / sizeof(TableDirectoryEntry);
155
762
        if (mnNumberOfTables > nMaxRecordsPossible)
156
611
        {
157
611
            SAL_WARN("vcl.fonts", "Font claimed to have " << mnNumberOfTables
158
611
                                                          << " table records, but only space for "
159
611
                                                          << nMaxRecordsPossible);
160
611
            mnNumberOfTables = nMaxRecordsPossible;
161
611
        }
162
762
    }
163
164
    const TableDirectoryEntry* getEntry(sal_uInt32 nTag)
165
2.28k
    {
166
2.47M
        for (sal_uInt32 i = 0; i < mnNumberOfTables; i++)
167
2.46M
        {
168
2.46M
            const char* pPosition = mpFirstPosition + sizeof(TableDirectoryEntry) * i;
169
2.46M
            const auto* pEntry = reinterpret_cast<const TableDirectoryEntry*>(pPosition);
170
171
2.46M
            if (nTag == pEntry->tag)
172
626
                return pEntry;
173
2.46M
        }
174
1.66k
        return nullptr;
175
2.28k
    }
176
177
    const OS2Table* getOS2Table()
178
762
    {
179
762
        const auto* pEntry = getEntry(T_OS2);
180
762
        if (!pEntry)
181
681
            return nullptr;
182
81
        return reinterpret_cast<const OS2Table*>(getTablePointer(pEntry, sizeof(OS2Table)));
183
762
    }
184
185
    const HeadTable* getHeadTable()
186
762
    {
187
762
        const auto* pEntry = getEntry(T_head);
188
762
        if (!pEntry)
189
669
            return nullptr;
190
93
        return reinterpret_cast<const HeadTable*>(getTablePointer(pEntry, sizeof(HeadTable)));
191
762
    }
192
193
    const NameTable* getNameTable()
194
0
    {
195
0
        const auto* pEntry = getEntry(T_name);
196
0
        if (!pEntry)
197
0
            return nullptr;
198
0
        return reinterpret_cast<const NameTable*>(getTablePointer(pEntry, sizeof(NameTable)));
199
0
    }
200
201
    std::unique_ptr<NameTableHandler> getNameTableHandler()
202
762
    {
203
762
        const auto* pEntry = getEntry(T_name);
204
762
        if (!pEntry)
205
310
            return nullptr;
206
207
452
        return std::unique_ptr<NameTableHandler>(new NameTableHandler(mrFontDataContainer, pEntry));
208
762
    }
209
};
210
211
/** Entry point handler for the TTF Font */
212
class TTFFont
213
{
214
private:
215
    FontDataContainer const& mrFontDataContainer;
216
217
    const char* getTablePointer(const TableDirectoryEntry* pEntry)
218
0
    {
219
0
        return mrFontDataContainer.getPointer() + pEntry->offset;
220
0
    }
221
222
public:
223
    TTFFont(FontDataContainer const& rFontDataContainer)
224
770
        : mrFontDataContainer(rFontDataContainer)
225
770
    {
226
770
    }
227
228
    std::unique_ptr<TableEntriesHandler> getTableEntriesHandler()
229
770
    {
230
770
        size_t nSize = mrFontDataContainer.size();
231
770
        if (nSize < sizeof(TableDirectory))
232
8
        {
233
8
            SAL_WARN("vcl.fonts", "Font Data shorter than a TableDirectory");
234
8
            return nullptr;
235
8
        }
236
762
        return std::make_unique<TableEntriesHandler>(mrFontDataContainer);
237
770
    }
238
239
    /** Gets the string from a name table */
240
    OUString getNameTableString(sal_uInt64 nOffset, sal_uInt16 nLength)
241
484
    {
242
484
        size_t nSize = mrFontDataContainer.size();
243
484
        if (nOffset > nSize)
244
104
        {
245
104
            SAL_WARN("vcl.fonts", "String offset beyond end of available data");
246
104
            return OUString();
247
104
        }
248
380
        if (nLength > nSize - nOffset)
249
59
        {
250
59
            SAL_WARN("vcl.fonts", "Insufficient available data for string entry");
251
59
            return OUString();
252
59
        }
253
321
        const auto* pString = reinterpret_cast<const o3tl::sal_uInt16_BE*>(
254
321
            mrFontDataContainer.getPointer() + nOffset);
255
321
        OUStringBuffer aStringBuffer;
256
471k
        for (sal_uInt16 i = 0; i < (nLength / 2); i++)
257
471k
            aStringBuffer.append(sal_Unicode(pString[i]));
258
321
        return aStringBuffer.makeStringAndClear();
259
380
    }
260
};
261
}
262
263
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */