/src/libreoffice/vcl/inc/font/TTFReader.hxx
Line | Count | Source (jump to first uncovered line) |
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 | 391 | { |
32 | 391 | size_t nSize = mrFontDataContainer.size(); |
33 | 391 | if (pEntry->offset > nSize) |
34 | 32 | { |
35 | 32 | SAL_WARN("vcl.fonts", "Table offset beyond end of available data"); |
36 | 32 | return nullptr; |
37 | 32 | } |
38 | 359 | if (nEntrySize > nSize - pEntry->offset) |
39 | 5 | { |
40 | 5 | SAL_WARN("vcl.fonts", "Insufficient available data for table entry"); |
41 | 5 | return nullptr; |
42 | 5 | } |
43 | 354 | return mrFontDataContainer.getPointer() + pEntry->offset; |
44 | 359 | } |
45 | | |
46 | | public: |
47 | | NameTableHandler(FontDataContainer const& rFontDataContainer, |
48 | | const TableDirectoryEntry* pTableDirectoryEntry) |
49 | 391 | : mrFontDataContainer(rFontDataContainer) |
50 | 391 | , mpTableDirectoryEntry(pTableDirectoryEntry) |
51 | 391 | , mpNameTablePointer(getTablePointer(mpTableDirectoryEntry, sizeof(NameTable))) |
52 | 391 | , mpNameTable(reinterpret_cast<const NameTable*>(mpNameTablePointer)) |
53 | 391 | , mnNumberOfRecords(0) |
54 | 391 | { |
55 | 391 | if (mpNameTable) |
56 | 354 | { |
57 | 354 | mnNumberOfRecords = mpNameTable->nCount; |
58 | | |
59 | 354 | const char* pEnd = mrFontDataContainer.getPointer() + mrFontDataContainer.size(); |
60 | 354 | const char* pStart = mpNameTablePointer + sizeof(NameTable); |
61 | 354 | size_t nAvailableData = pEnd - pStart; |
62 | 354 | size_t nMaxRecordsPossible = nAvailableData / sizeof(NameRecord); |
63 | 354 | if (mnNumberOfRecords > nMaxRecordsPossible) |
64 | 313 | { |
65 | 313 | SAL_WARN("vcl.fonts", "Font claimed to have " |
66 | 313 | << mnNumberOfRecords |
67 | 313 | << " name records, but only space for " |
68 | 313 | << nMaxRecordsPossible); |
69 | 313 | mnNumberOfRecords = nMaxRecordsPossible; |
70 | 313 | } |
71 | 354 | } |
72 | 391 | } |
73 | | |
74 | 297 | sal_uInt32 getTableOffset() { return mpTableDirectoryEntry->offset; } |
75 | | |
76 | 297 | const NameTable* getNameTable() { return mpNameTable; } |
77 | | |
78 | | /** Number of tables */ |
79 | 1.56k | sal_uInt16 getNumberOfRecords() { return mnNumberOfRecords; } |
80 | | |
81 | | /** Get a name table record for index */ |
82 | | const NameRecord* getNameRecord(sal_uInt32 index) |
83 | 2.06M | { |
84 | 2.06M | const char* pPointer = mpNameTablePointer + sizeof(NameTable); |
85 | 2.06M | pPointer += sizeof(NameRecord) * index; |
86 | 2.06M | return reinterpret_cast<const NameRecord*>(pPointer); |
87 | 2.06M | } |
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.56k | { |
96 | 1.56k | rOffset = 0; |
97 | 1.56k | rLength = 0; |
98 | | |
99 | 2.06M | for (sal_uInt16 n = 0, nCount = getNumberOfRecords(); n < nCount; n++) |
100 | 2.06M | { |
101 | 2.06M | const font::NameRecord* pNameRecord = getNameRecord(n); |
102 | | |
103 | 2.06M | if (pNameRecord->nPlatformID == 3 // Windows |
104 | 2.06M | && pNameRecord->nEncodingID == 1 // Unicode BMP |
105 | 2.06M | && pNameRecord->nLanguageID == 0x0409 // en-us |
106 | 2.06M | && pNameRecord->nNameID == sal_uInt16(eNameID)) |
107 | 297 | { |
108 | 297 | rLength = pNameRecord->nLength; |
109 | 297 | rOffset = getTableOffset() + getNameTable()->nStorageOffset |
110 | 297 | + pNameRecord->nStringOffset; |
111 | 297 | return true; |
112 | 297 | } |
113 | 2.06M | } |
114 | 1.26k | return false; |
115 | 1.56k | } |
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 | 181 | { |
128 | 181 | size_t nSize = mrFontDataContainer.size(); |
129 | 181 | if (pEntry->offset > nSize) |
130 | 61 | { |
131 | 61 | SAL_WARN("vcl.fonts", "Table offset beyond end of available data"); |
132 | 61 | return nullptr; |
133 | 61 | } |
134 | 120 | if (nEntrySize > nSize - pEntry->offset) |
135 | 33 | { |
136 | 33 | SAL_WARN("vcl.fonts", "Insufficient available data for table entry"); |
137 | 33 | return nullptr; |
138 | 33 | } |
139 | 87 | return mrFontDataContainer.getPointer() + pEntry->offset; |
140 | 120 | } |
141 | | |
142 | | public: |
143 | | TableEntriesHandler(FontDataContainer const& rFontDataContainer) |
144 | 760 | : mrFontDataContainer(rFontDataContainer) |
145 | 760 | { |
146 | 760 | const char* pData = mrFontDataContainer.getPointer(); |
147 | 760 | assert(mrFontDataContainer.size() >= sizeof(TableDirectory)); |
148 | 760 | mpFirstPosition = pData + sizeof(TableDirectory); |
149 | | |
150 | 760 | const TableDirectory* pDirectory = reinterpret_cast<const TableDirectory*>(pData); |
151 | 760 | mnNumberOfTables = pDirectory->nNumberOfTables; |
152 | | |
153 | 760 | size_t nAvailableData = mrFontDataContainer.size() - sizeof(TableDirectory); |
154 | 760 | size_t nMaxRecordsPossible = nAvailableData / sizeof(TableDirectoryEntry); |
155 | 760 | if (mnNumberOfTables > nMaxRecordsPossible) |
156 | 650 | { |
157 | 650 | SAL_WARN("vcl.fonts", "Font claimed to have " << mnNumberOfTables |
158 | 650 | << " table records, but only space for " |
159 | 650 | << nMaxRecordsPossible); |
160 | 650 | mnNumberOfTables = nMaxRecordsPossible; |
161 | 650 | } |
162 | 760 | } |
163 | | |
164 | | const TableDirectoryEntry* getEntry(sal_uInt32 nTag) |
165 | 2.28k | { |
166 | 2.94M | for (sal_uInt32 i = 0; i < mnNumberOfTables; i++) |
167 | 2.93M | { |
168 | 2.93M | const char* pPosition = mpFirstPosition + sizeof(TableDirectoryEntry) * i; |
169 | 2.93M | const auto* pEntry = reinterpret_cast<const TableDirectoryEntry*>(pPosition); |
170 | | |
171 | 2.93M | if (nTag == pEntry->tag) |
172 | 572 | return pEntry; |
173 | 2.93M | } |
174 | 1.70k | return nullptr; |
175 | 2.28k | } |
176 | | |
177 | | const OS2Table* getOS2Table() |
178 | 760 | { |
179 | 760 | const auto* pEntry = getEntry(T_OS2); |
180 | 760 | if (!pEntry) |
181 | 675 | return nullptr; |
182 | 85 | return reinterpret_cast<const OS2Table*>(getTablePointer(pEntry, sizeof(OS2Table))); |
183 | 760 | } |
184 | | |
185 | | const HeadTable* getHeadTable() |
186 | 760 | { |
187 | 760 | const auto* pEntry = getEntry(T_head); |
188 | 760 | if (!pEntry) |
189 | 664 | return nullptr; |
190 | 96 | return reinterpret_cast<const HeadTable*>(getTablePointer(pEntry, sizeof(HeadTable))); |
191 | 760 | } |
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 | 760 | { |
203 | 760 | const auto* pEntry = getEntry(T_name); |
204 | 760 | if (!pEntry) |
205 | 369 | return nullptr; |
206 | | |
207 | 391 | return std::unique_ptr<NameTableHandler>(new NameTableHandler(mrFontDataContainer, pEntry)); |
208 | 760 | } |
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 | 767 | : mrFontDataContainer(rFontDataContainer) |
225 | 767 | { |
226 | 767 | } |
227 | | |
228 | | std::unique_ptr<TableEntriesHandler> getTableEntriesHandler() |
229 | 767 | { |
230 | 767 | size_t nSize = mrFontDataContainer.size(); |
231 | 767 | if (nSize < sizeof(TableDirectory)) |
232 | 7 | { |
233 | 7 | SAL_WARN("vcl.fonts", "Font Data shorter than a TableDirectory"); |
234 | 7 | return nullptr; |
235 | 7 | } |
236 | 760 | return std::make_unique<TableEntriesHandler>(mrFontDataContainer); |
237 | 767 | } |
238 | | |
239 | | /** Gets the string from a name table */ |
240 | | OUString getNameTableString(sal_uInt64 nOffset, sal_uInt16 nLength) |
241 | 297 | { |
242 | 297 | size_t nSize = mrFontDataContainer.size(); |
243 | 297 | if (nOffset > nSize) |
244 | 55 | { |
245 | 55 | SAL_WARN("vcl.fonts", "String offset beyond end of available data"); |
246 | 55 | return OUString(); |
247 | 55 | } |
248 | 242 | if (nLength > nSize - nOffset) |
249 | 47 | { |
250 | 47 | SAL_WARN("vcl.fonts", "Insufficient available data for string entry"); |
251 | 47 | return OUString(); |
252 | 47 | } |
253 | 195 | const auto* pString = reinterpret_cast<const o3tl::sal_uInt16_BE*>( |
254 | 195 | mrFontDataContainer.getPointer() + nOffset); |
255 | 195 | OUStringBuffer aStringBuffer; |
256 | 445k | for (sal_uInt16 i = 0; i < (nLength / 2); i++) |
257 | 445k | aStringBuffer.append(sal_Unicode(pString[i])); |
258 | 195 | return aStringBuffer.makeStringAndClear(); |
259 | 242 | } |
260 | | }; |
261 | | } |
262 | | |
263 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |