/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: */ |