[WTF] Import std::optional reference implementation as WTF::Optional
[WebKit-https.git] / Source / WebCore / svg / SVGToOTFFontConversion.cpp
1 /*
2  * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "SVGToOTFFontConversion.h"
28
29 #if ENABLE(SVG_FONTS)
30
31 #include "CSSStyleDeclaration.h"
32 #include "ElementChildIterator.h"
33 #include "Glyph.h"
34 #include "SVGFontElement.h"
35 #include "SVGFontFaceElement.h"
36 #include "SVGGlyphElement.h"
37 #include "SVGHKernElement.h"
38 #include "SVGMissingGlyphElement.h"
39 #include "SVGPathParser.h"
40 #include "SVGPathStringSource.h"
41 #include "SVGVKernElement.h"
42
43 namespace WebCore {
44
45 template <typename V>
46 static inline void append32(V& result, uint32_t value)
47 {
48     result.append(value >> 24);
49     result.append(value >> 16);
50     result.append(value >> 8);
51     result.append(value);
52 }
53
54 class SVGToOTFFontConverter {
55 public:
56     SVGToOTFFontConverter(const SVGFontElement&);
57     bool convertSVGToOTFFont();
58
59     Vector<char> releaseResult()
60     {
61         return WTFMove(m_result);
62     }
63
64     bool error() const
65     {
66         return m_error;
67     }
68
69 private:
70     struct GlyphData {
71         GlyphData(Vector<char>&& charString, const SVGGlyphElement* glyphElement, float horizontalAdvance, float verticalAdvance, FloatRect boundingBox, const String& codepoints)
72             : boundingBox(boundingBox)
73             , charString(charString)
74             , codepoints(codepoints)
75             , glyphElement(glyphElement)
76             , horizontalAdvance(horizontalAdvance)
77             , verticalAdvance(verticalAdvance)
78         {
79         }
80         FloatRect boundingBox;
81         Vector<char> charString;
82         String codepoints;
83         const SVGGlyphElement* glyphElement;
84         float horizontalAdvance;
85         float verticalAdvance;
86     };
87
88     class Placeholder {
89     public:
90         Placeholder(SVGToOTFFontConverter& converter, size_t baseOfOffset)
91             : m_converter(converter)
92             , m_baseOfOffset(baseOfOffset)
93             , m_location(m_converter.m_result.size())
94         {
95             m_converter.append16(0);
96         }
97
98         Placeholder(Placeholder&& other)
99             : m_converter(other.m_converter)
100             , m_baseOfOffset(other.m_baseOfOffset)
101             , m_location(other.m_location)
102 #if !ASSERT_DISABLED
103             , m_active(other.m_active)
104 #endif
105         {
106 #if !ASSERT_DISABLED
107             other.m_active = false;
108 #endif
109         }
110
111         void populate()
112         {
113             ASSERT(m_active);
114             size_t delta = m_converter.m_result.size() - m_baseOfOffset;
115             ASSERT(delta < std::numeric_limits<uint16_t>::max());
116             m_converter.overwrite16(m_location, delta);
117 #if !ASSERT_DISABLED
118             m_active = false;
119 #endif
120         }
121
122         ~Placeholder()
123         {
124             ASSERT(!m_active);
125         }
126
127     private:
128         SVGToOTFFontConverter& m_converter;
129         const size_t m_baseOfOffset;
130         const size_t m_location;
131 #if !ASSERT_DISABLED
132         bool m_active = { true };
133 #endif
134     };
135
136     struct KerningData {
137         KerningData(uint16_t glyph1, uint16_t glyph2, int16_t adjustment)
138             : glyph1(glyph1)
139             , glyph2(glyph2)
140             , adjustment(adjustment)
141         {
142         }
143         uint16_t glyph1;
144         uint16_t glyph2;
145         int16_t adjustment;
146     };
147
148     Placeholder placeholder(size_t baseOfOffset)
149     {
150         return Placeholder(*this, baseOfOffset);
151     }
152
153     void append32(uint32_t value)
154     {
155         WebCore::append32(m_result, value);
156     }
157
158     void append32BitCode(const char code[4])
159     {
160         m_result.append(code[0]);
161         m_result.append(code[1]);
162         m_result.append(code[2]);
163         m_result.append(code[3]);
164     }
165
166     void append16(uint16_t value)
167     {
168         m_result.append(value >> 8);
169         m_result.append(value);
170     }
171
172     void grow(size_t delta)
173     {
174         m_result.grow(m_result.size() + delta);
175     }
176
177     void overwrite32(unsigned location, uint32_t value)
178     {
179         ASSERT(m_result.size() >= location + 4);
180         m_result[location] = value >> 24;
181         m_result[location + 1] = value >> 16;
182         m_result[location + 2] = value >> 8;
183         m_result[location + 3] = value;
184     }
185
186     void overwrite16(unsigned location, uint16_t value)
187     {
188         ASSERT(m_result.size() >= location + 2);
189         m_result[location] = value >> 8;
190         m_result[location + 1] = value;
191     }
192
193     static const size_t headerSize = 12;
194     static const size_t directoryEntrySize = 16;
195
196     uint32_t calculateChecksum(size_t startingOffset, size_t endingOffset) const;
197
198     void processGlyphElement(const SVGElement& glyphOrMissingGlyphElement, const SVGGlyphElement*, float defaultHorizontalAdvance, float defaultVerticalAdvance, const String& codepoints, std::optional<FloatRect>& boundingBox);
199
200     typedef void (SVGToOTFFontConverter::*FontAppendingFunction)();
201     void appendTable(const char identifier[4], FontAppendingFunction);
202     void appendFormat12CMAPTable(const Vector<std::pair<UChar32, Glyph>>& codepointToGlyphMappings);
203     void appendFormat4CMAPTable(const Vector<std::pair<UChar32, Glyph>>& codepointToGlyphMappings);
204     void appendCMAPTable();
205     void appendGSUBTable();
206     void appendHEADTable();
207     void appendHHEATable();
208     void appendHMTXTable();
209     void appendVHEATable();
210     void appendVMTXTable();
211     void appendKERNTable();
212     void appendMAXPTable();
213     void appendNAMETable();
214     void appendOS2Table();
215     void appendPOSTTable();
216     void appendCFFTable();
217     void appendVORGTable();
218
219     void appendLigatureGlyphs();
220     static bool compareCodepointsLexicographically(const GlyphData&, const GlyphData&);
221
222     void appendValidCFFString(const String&);
223
224     Vector<char> transcodeGlyphPaths(float width, const SVGElement& glyphOrMissingGlyphElement, std::optional<FloatRect>& boundingBox) const;
225
226     void addCodepointRanges(const UnicodeRanges&, HashSet<Glyph>& glyphSet) const;
227     void addCodepoints(const HashSet<String>& codepoints, HashSet<Glyph>& glyphSet) const;
228     void addGlyphNames(const HashSet<String>& glyphNames, HashSet<Glyph>& glyphSet) const;
229     void addKerningPair(Vector<KerningData>&, const SVGKerningPair&) const;
230     template<typename T> size_t appendKERNSubtable(bool (T::*buildKerningPair)(SVGKerningPair&) const, uint16_t coverage);
231     size_t finishAppendingKERNSubtable(Vector<KerningData>, uint16_t coverage);
232
233     void appendLigatureSubtable(size_t subtableRecordLocation);
234     void appendArabicReplacementSubtable(size_t subtableRecordLocation, const char arabicForm[]);
235     void appendScriptSubtable(unsigned featureCount);
236     Vector<Glyph, 1> glyphsForCodepoint(UChar32) const;
237     Glyph firstGlyph(const Vector<Glyph, 1>&, UChar32) const;
238
239     template<typename T> T scaleUnitsPerEm(T value) const
240     {
241         return value * s_outputUnitsPerEm / m_inputUnitsPerEm;
242     }
243
244     Vector<GlyphData> m_glyphs;
245     HashMap<String, Glyph> m_glyphNameToIndexMap; // SVG 1.1: "It is recommended that glyph names be unique within a font."
246     HashMap<String, Vector<Glyph, 1>> m_codepointsToIndicesMap;
247     Vector<char> m_result;
248     Vector<char, 17> m_emptyGlyphCharString;
249     FloatRect m_boundingBox;
250     const SVGFontElement& m_fontElement;
251     const SVGFontFaceElement* m_fontFaceElement;
252     const SVGMissingGlyphElement* m_missingGlyphElement;
253     String m_fontFamily;
254     float m_advanceWidthMax;
255     float m_advanceHeightMax;
256     float m_minRightSideBearing;
257     static const unsigned s_outputUnitsPerEm = 1000;
258     unsigned m_inputUnitsPerEm;
259     int m_lineGap;
260     int m_xHeight;
261     int m_capHeight;
262     int m_ascent;
263     int m_descent;
264     unsigned m_featureCountGSUB;
265     unsigned m_tablesAppendedCount;
266     char m_weight;
267     bool m_italic;
268     bool m_error { false };
269 };
270
271 static uint16_t roundDownToPowerOfTwo(uint16_t x)
272 {
273     x |= x >> 1;
274     x |= x >> 2;
275     x |= x >> 4;
276     x |= x >> 8;
277     return (x >> 1) + 1;
278 }
279
280 static uint16_t integralLog2(uint16_t x)
281 {
282     uint16_t result = 0;
283     while (x >>= 1)
284         ++result;
285     return result;
286 }
287
288 void SVGToOTFFontConverter::appendFormat12CMAPTable(const Vector<std::pair<UChar32, Glyph>>& mappings)
289 {
290     // Braindead scheme: One segment for each character
291     ASSERT(m_glyphs.size() < 0xFFFF);
292     auto subtableLocation = m_result.size();
293     append32(12 << 16); // Format 12
294     append32(0); // Placeholder for byte length
295     append32(0); // Language independent
296     append32(0); // Placeholder for nGroups
297     for (auto& mapping : mappings) {
298         append32(mapping.first); // startCharCode
299         append32(mapping.first); // endCharCode
300         append32(mapping.second); // startGlyphCode
301     }
302     overwrite32(subtableLocation + 4, m_result.size() - subtableLocation);
303     overwrite32(subtableLocation + 12, mappings.size());
304 }
305
306 void SVGToOTFFontConverter::appendFormat4CMAPTable(const Vector<std::pair<UChar32, Glyph>>& bmpMappings)
307 {
308     auto subtableLocation = m_result.size();
309     append16(4); // Format 4
310     append16(0); // Placeholder for length in bytes
311     append16(0); // Language independent
312     uint16_t segCount = bmpMappings.size() + 1;
313     append16(clampTo<uint16_t>(2 * segCount)); // segCountX2: "2 x segCount"
314     uint16_t originalSearchRange = roundDownToPowerOfTwo(segCount);
315     uint16_t searchRange = clampTo<uint16_t>(2 * originalSearchRange); // searchRange: "2 x (2**floor(log2(segCount)))"
316     append16(searchRange);
317     append16(integralLog2(originalSearchRange)); // entrySelector: "log2(searchRange/2)"  
318     append16(clampTo<uint16_t>((2 * segCount) - searchRange)); // rangeShift: "2 x segCount - searchRange"  
319
320     // Ending character codes
321     for (auto& mapping : bmpMappings)
322         append16(mapping.first); // startCharCode
323     append16(0xFFFF);
324
325     append16(0); // reserved
326
327     // Starting character codes
328     for (auto& mapping : bmpMappings)
329         append16(mapping.first); // startCharCode
330     append16(0xFFFF);
331
332     // idDelta
333     for (auto& mapping : bmpMappings)
334         append16(static_cast<uint16_t>(mapping.second) - static_cast<uint16_t>(mapping.first)); // startCharCode
335     append16(0x0001);
336
337     // idRangeOffset
338     for (size_t i = 0; i < bmpMappings.size(); ++i)
339         append16(0); // startCharCode
340     append16(0);
341
342     // Fonts strive to hold 2^16 glyphs, but with the current encoding scheme, we write 8 bytes per codepoint into this subtable.
343     // Because the size of this subtable must be represented as a 16-bit number, we are limiting the number of glyphs we support to 2^13.
344     // FIXME: If we hit this limit in the wild, use a more compact encoding scheme for this subtable.
345     overwrite16(subtableLocation + 2, clampTo<uint16_t>(m_result.size() - subtableLocation));
346 }
347
348 void SVGToOTFFontConverter::appendCMAPTable()
349 {
350     auto startingOffset = m_result.size();
351     append16(0);
352     append16(3); // Number of subtables
353
354     append16(0); // Unicode
355     append16(3); // Unicode version 2.2+
356     append32(28); // Byte offset of subtable
357
358     append16(3); // Microsoft
359     append16(1); // Unicode BMP
360     auto format4OffsetLocation = m_result.size();
361     append32(0); // Byte offset of subtable
362
363     append16(3); // Microsoft
364     append16(10); // Unicode
365     append32(28); // Byte offset of subtable
366
367     Vector<std::pair<UChar32, Glyph>> mappings;
368     UChar32 previousCodepoint = std::numeric_limits<UChar32>::max();
369     for (size_t i = 0; i < m_glyphs.size(); ++i) {
370         auto& glyph = m_glyphs[i];
371         UChar32 codepoint;
372         auto codePoints = StringView(glyph.codepoints).codePoints();
373         auto iterator = codePoints.begin();
374         if (iterator == codePoints.end())
375             codepoint = 0;
376         else {
377             codepoint = *iterator;
378             ++iterator;
379             // Don't map ligatures here.
380             if (iterator != codePoints.end() || codepoint == previousCodepoint)
381                 continue;
382         }
383
384         mappings.append(std::make_pair(codepoint, Glyph(i)));
385         previousCodepoint = codepoint;
386     }
387
388     appendFormat12CMAPTable(mappings);
389
390     Vector<std::pair<UChar32, Glyph>> bmpMappings;
391     for (auto& mapping : mappings) {
392         if (mapping.first < 0x10000)
393             bmpMappings.append(mapping);
394     }
395     overwrite32(format4OffsetLocation, m_result.size() - startingOffset);
396     appendFormat4CMAPTable(bmpMappings);
397 }
398
399 void SVGToOTFFontConverter::appendHEADTable()
400 {
401     append32(0x00010000); // Version
402     append32(0x00010000); // Revision
403     append32(0); // Checksum placeholder; to be overwritten by the caller.
404     append32(0x5F0F3CF5); // Magic number.
405     append16((1 << 9) | 1);
406
407     append16(s_outputUnitsPerEm);
408     append32(0); // First half of creation date
409     append32(0); // Last half of creation date
410     append32(0); // First half of modification date
411     append32(0); // Last half of modification date
412     append16(clampTo<int16_t>(m_boundingBox.x()));
413     append16(clampTo<int16_t>(m_boundingBox.y()));
414     append16(clampTo<int16_t>(m_boundingBox.maxX()));
415     append16(clampTo<int16_t>(m_boundingBox.maxY()));
416     append16((m_italic ? 1 << 1 : 0) | (m_weight >= 7 ? 1 : 0));
417     append16(3); // Smallest readable size in pixels
418     append16(0); // Might contain LTR or RTL glyphs
419     append16(0); // Short offsets in the 'loca' table. However, CFF fonts don't have a 'loca' table so this is irrelevant
420     append16(0); // Glyph data format
421 }
422
423 // Assumption: T2 can hold every value that a T1 can hold.
424 template<typename T1, typename T2> static inline T1 clampTo(T2 x)
425 {
426     x = std::min(x, static_cast<T2>(std::numeric_limits<T1>::max()));
427     x = std::max(x, static_cast<T2>(std::numeric_limits<T1>::min()));
428     return static_cast<T1>(x);
429 }
430
431 void SVGToOTFFontConverter::appendHHEATable()
432 {
433     append32(0x00010000); // Version
434     append16(clampTo<int16_t>(m_ascent));
435     append16(clampTo<int16_t>(-m_descent));
436     // WebKit SVG font rendering has hard coded the line gap to be 1/10th of the font size since 2008 (see r29719).
437     append16(clampTo<int16_t>(m_lineGap));
438     append16(clampTo<uint16_t>(m_advanceWidthMax));
439     append16(clampTo<int16_t>(m_boundingBox.x())); // Minimum left side bearing
440     append16(clampTo<int16_t>(m_minRightSideBearing)); // Minimum right side bearing
441     append16(clampTo<int16_t>(m_boundingBox.maxX())); // X maximum extent
442     // Since WebKit draws the caret and ignores the following values, it doesn't matter what we set them to.
443     append16(1); // Vertical caret
444     append16(0); // Vertical caret
445     append16(0); // "Set value to 0 for non-slanted fonts"
446     append32(0); // Reserved
447     append32(0); // Reserved
448     append16(0); // Current format
449     append16(m_glyphs.size()); // Number of advance widths in HMTX table
450 }
451
452 void SVGToOTFFontConverter::appendHMTXTable()
453 {
454     for (auto& glyph : m_glyphs) {
455         append16(clampTo<uint16_t>(glyph.horizontalAdvance));
456         append16(clampTo<int16_t>(glyph.boundingBox.x()));
457     }
458 }
459
460 void SVGToOTFFontConverter::appendMAXPTable()
461 {
462     append32(0x00010000); // Version
463     append16(m_glyphs.size());
464     append16(0xFFFF); // Maximum number of points in non-compound glyph
465     append16(0xFFFF); // Maximum number of contours in non-compound glyph
466     append16(0xFFFF); // Maximum number of points in compound glyph
467     append16(0xFFFF); // Maximum number of contours in compound glyph
468     append16(2); // Maximum number of zones
469     append16(0); // Maximum number of points used in zone 0
470     append16(0); // Maximum number of storage area locations
471     append16(0); // Maximum number of function definitions
472     append16(0); // Maximum number of instruction definitions
473     append16(0); // Maximum stack depth
474     append16(0); // Maximum size of instructions
475     append16(m_glyphs.size()); // Maximum number of glyphs referenced at top level
476     append16(0); // No compound glyphs
477 }
478
479 void SVGToOTFFontConverter::appendNAMETable()
480 {
481     append16(0); // Format selector
482     append16(1); // Number of name records in table
483     append16(18); // Offset in bytes to the beginning of name character strings
484
485     append16(0); // Unicode
486     append16(3); // Unicode version 2.0 or later
487     append16(0); // Language
488     append16(1); // Name identifier. 1 = Font family
489     append16(m_fontFamily.length() * 2);
490     append16(0); // Offset into name data
491
492     for (auto codeUnit : StringView(m_fontFamily).codeUnits())
493         append16(codeUnit);
494 }
495
496 void SVGToOTFFontConverter::appendOS2Table()
497 {
498     int16_t averageAdvance = s_outputUnitsPerEm;
499     bool ok;
500     int value = m_fontElement.attributeWithoutSynchronization(SVGNames::horiz_adv_xAttr).toInt(&ok);
501     if (!ok && m_missingGlyphElement)
502         value = m_missingGlyphElement->attributeWithoutSynchronization(SVGNames::horiz_adv_xAttr).toInt(&ok);
503     value = scaleUnitsPerEm(value);
504     if (ok)
505         averageAdvance = clampTo<int16_t>(value);
506
507     append16(2); // Version
508     append16(clampTo<int16_t>(averageAdvance));
509     append16(clampTo<uint16_t>(m_weight)); // Weight class
510     append16(5); // Width class
511     append16(0); // Protected font
512     // WebKit handles these superscripts and subscripts
513     append16(0); // Subscript X Size
514     append16(0); // Subscript Y Size
515     append16(0); // Subscript X Offset
516     append16(0); // Subscript Y Offset
517     append16(0); // Superscript X Size
518     append16(0); // Superscript Y Size
519     append16(0); // Superscript X Offset
520     append16(0); // Superscript Y Offset
521     append16(0); // Strikeout width
522     append16(0); // Strikeout Position
523     append16(0); // No classification
524
525     unsigned numPanoseBytes = 0;
526     const unsigned panoseSize = 10;
527     char panoseBytes[panoseSize];
528     if (m_fontFaceElement) {
529         Vector<String> segments;
530         m_fontFaceElement->attributeWithoutSynchronization(SVGNames::panose_1Attr).string().split(' ', segments);
531         if (segments.size() == panoseSize) {
532             for (auto& segment : segments) {
533                 bool ok;
534                 int value = segment.toInt(&ok);
535                 if (ok && value >= 0 && value <= 0xFF)
536                     panoseBytes[numPanoseBytes++] = value;
537             }
538         }
539     }
540     if (numPanoseBytes != panoseSize)
541         memset(panoseBytes, 0, panoseSize);
542     m_result.append(panoseBytes, panoseSize);
543
544     for (int i = 0; i < 4; ++i)
545         append32(0); // "Bit assignments are pending. Set to 0"
546     append32(0x544B4257); // Font Vendor. "WBKT"
547     append16((m_weight >= 7 ? 1 << 5 : 0) | (m_italic ? 1 : 0)); // Font Patterns.
548     append16(0); // First unicode index
549     append16(0xFFFF); // Last unicode index
550     append16(clampTo<int16_t>(m_ascent)); // Typographical ascender
551     append16(clampTo<int16_t>(-m_descent)); // Typographical descender
552     append16(clampTo<int16_t>(m_lineGap)); // Typographical line gap
553     append16(clampTo<uint16_t>(m_ascent)); // Windows-specific ascent
554     append16(clampTo<uint16_t>(m_descent)); // Windows-specific descent
555     append32(0xFF10FC07); // Bitmask for supported codepages (Part 1). Report all pages as supported.
556     append32(0x0000FFFF); // Bitmask for supported codepages (Part 2). Report all pages as supported.
557     append16(clampTo<int16_t>(m_xHeight)); // x-height
558     append16(clampTo<int16_t>(m_capHeight)); // Cap-height
559     append16(0); // Default char
560     append16(' '); // Break character
561     append16(3); // Maximum context needed to perform font features
562     append16(3); // Smallest optical point size
563     append16(0xFFFF); // Largest optical point size
564 }
565
566 void SVGToOTFFontConverter::appendPOSTTable()
567 {
568     append32(0x00030000); // Format. Printing is undefined
569     append32(0); // Italic angle in degrees
570     append16(0); // Underline position
571     append16(0); // Underline thickness
572     append32(0); // Monospaced
573     append32(0); // "Minimum memory usage when a TrueType font is downloaded as a Type 42 font"
574     append32(0); // "Maximum memory usage when a TrueType font is downloaded as a Type 42 font"
575     append32(0); // "Minimum memory usage when a TrueType font is downloaded as a Type 1 font"
576     append32(0); // "Maximum memory usage when a TrueType font is downloaded as a Type 1 font"
577 }
578
579 static bool isValidStringForCFF(const String& string)
580 {
581     for (auto c : StringView(string).codeUnits()) {
582         if (c < 33 || c > 126)
583             return false;
584     }
585     return true;
586 }
587
588 void SVGToOTFFontConverter::appendValidCFFString(const String& string)
589 {
590     ASSERT(isValidStringForCFF(string));
591     for (auto c : StringView(string).codeUnits())
592         m_result.append(c);
593 }
594
595 void SVGToOTFFontConverter::appendCFFTable()
596 {
597     auto startingOffset = m_result.size();
598
599     // Header
600     m_result.append(1); // Major version
601     m_result.append(0); // Minor version
602     m_result.append(4); // Header size
603     m_result.append(4); // Offsets within CFF table are 4 bytes long
604
605     // Name INDEX
606     String fontName;
607     if (m_fontFaceElement) {
608         // FIXME: fontFamily() here might not be quite what we want.
609         String potentialFontName = m_fontFamily;
610         if (isValidStringForCFF(potentialFontName))
611             fontName = potentialFontName;
612     }
613     append16(1); // INDEX contains 1 element
614     m_result.append(4); // Offsets in this INDEX are 4 bytes long
615     append32(1); // 1-index offset of name data
616     append32(fontName.length() + 1); // 1-index offset just past end of name data
617     appendValidCFFString(fontName);
618
619     String weight;
620     if (m_fontFaceElement) {
621         auto& potentialWeight = m_fontFaceElement->attributeWithoutSynchronization(SVGNames::font_weightAttr);
622         if (isValidStringForCFF(potentialWeight))
623             weight = potentialWeight;
624     }
625
626     bool hasWeight = !weight.isNull();
627
628     const char operand32Bit = 29;
629     const char fullNameKey = 2;
630     const char familyNameKey = 3;
631     const char weightKey = 4;
632     const char fontBBoxKey = 5;
633     const char charsetIndexKey = 15;
634     const char charstringsIndexKey = 17;
635     const char privateDictIndexKey = 18;
636     const uint32_t userDefinedStringStartIndex = 391;
637     const unsigned sizeOfTopIndex = 56 + (hasWeight ? 6 : 0);
638
639     // Top DICT INDEX.
640     append16(1); // INDEX contains 1 element
641     m_result.append(4); // Offsets in this INDEX are 4 bytes long
642     append32(1); // 1-index offset of DICT data
643     append32(1 + sizeOfTopIndex); // 1-index offset just past end of DICT data
644
645     // DICT information
646 #if !ASSERT_DISABLED
647     unsigned topDictStart = m_result.size();
648 #endif
649     m_result.append(operand32Bit);
650     append32(userDefinedStringStartIndex);
651     m_result.append(fullNameKey);
652     m_result.append(operand32Bit);
653     append32(userDefinedStringStartIndex);
654     m_result.append(familyNameKey);
655     if (hasWeight) {
656         m_result.append(operand32Bit);
657         append32(userDefinedStringStartIndex + 2);
658         m_result.append(weightKey);
659     }
660     m_result.append(operand32Bit);
661     append32(clampTo<int32_t>(m_boundingBox.x()));
662     m_result.append(operand32Bit);
663     append32(clampTo<int32_t>(m_boundingBox.y()));
664     m_result.append(operand32Bit);
665     append32(clampTo<int32_t>(m_boundingBox.width()));
666     m_result.append(operand32Bit);
667     append32(clampTo<int32_t>(m_boundingBox.height()));
668     m_result.append(fontBBoxKey);
669     m_result.append(operand32Bit);
670     unsigned charsetOffsetLocation = m_result.size();
671     append32(0); // Offset of Charset info. Will be overwritten later.
672     m_result.append(charsetIndexKey);
673     m_result.append(operand32Bit);
674     unsigned charstringsOffsetLocation = m_result.size();
675     append32(0); // Offset of CharStrings INDEX. Will be overwritten later.
676     m_result.append(charstringsIndexKey);
677     m_result.append(operand32Bit);
678     append32(0); // 0-sized private dict
679     m_result.append(operand32Bit);
680     append32(0); // no location for private dict
681     m_result.append(privateDictIndexKey); // Private dict size and offset
682     ASSERT(m_result.size() == topDictStart + sizeOfTopIndex);
683
684     // String INDEX
685     String unknownCharacter = ASCIILiteral("UnknownChar");
686     append16(2 + (hasWeight ? 1 : 0)); // Number of elements in INDEX
687     m_result.append(4); // Offsets in this INDEX are 4 bytes long
688     uint32_t offset = 1;
689     append32(offset);
690     offset += fontName.length();
691     append32(offset);
692     offset += unknownCharacter.length();
693     append32(offset);
694     if (hasWeight) {
695         offset += weight.length();
696         append32(offset);
697     }
698     appendValidCFFString(fontName);
699     appendValidCFFString(unknownCharacter);
700     appendValidCFFString(weight);
701
702     append16(0); // Empty subroutine INDEX
703
704     // Charset info
705     overwrite32(charsetOffsetLocation, m_result.size() - startingOffset);
706     m_result.append(0);
707     for (Glyph i = 1; i < m_glyphs.size(); ++i)
708         append16(userDefinedStringStartIndex + 1);
709
710     // CharStrings INDEX
711     overwrite32(charstringsOffsetLocation, m_result.size() - startingOffset);
712     append16(m_glyphs.size());
713     m_result.append(4); // Offsets in this INDEX are 4 bytes long
714     offset = 1;
715     append32(offset);
716     for (auto& glyph : m_glyphs) {
717         offset += glyph.charString.size();
718         append32(offset);
719     }
720     for (auto& glyph : m_glyphs)
721         m_result.appendVector(glyph.charString);
722 }
723
724 Glyph SVGToOTFFontConverter::firstGlyph(const Vector<Glyph, 1>& v, UChar32 codepoint) const
725 {
726 #if ASSERT_DISABLED
727     UNUSED_PARAM(codepoint);
728 #endif
729     ASSERT(!v.isEmpty());
730     if (v.isEmpty())
731         return 0;
732 #if !ASSERT_DISABLED
733     auto codePoints = StringView(m_glyphs[v[0]].codepoints).codePoints();
734     auto codePointsIterator = codePoints.begin();
735     ASSERT(codePointsIterator != codePoints.end());
736     ASSERT(codepoint = *codePointsIterator);
737 #endif
738     return v[0];
739 }
740
741 void SVGToOTFFontConverter::appendLigatureSubtable(size_t subtableRecordLocation)
742 {
743     typedef std::pair<Vector<Glyph, 3>, Glyph> LigaturePair;
744     Vector<LigaturePair> ligaturePairs;
745     for (Glyph glyphIndex = 0; glyphIndex < m_glyphs.size(); ++glyphIndex) {
746         ligaturePairs.append(LigaturePair(Vector<Glyph, 3>(), glyphIndex));
747         Vector<Glyph, 3>& ligatureGlyphs = ligaturePairs.last().first;
748         auto codePoints = StringView(m_glyphs[glyphIndex].codepoints).codePoints();
749         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=138592 This needs to be done in codepoint space, not glyph space
750         for (auto codePoint : codePoints)
751             ligatureGlyphs.append(firstGlyph(glyphsForCodepoint(codePoint), codePoint));
752         if (ligatureGlyphs.size() < 2)
753             ligaturePairs.removeLast();
754     }
755     if (ligaturePairs.size() > std::numeric_limits<uint16_t>::max())
756         ligaturePairs.clear();
757     std::sort(ligaturePairs.begin(), ligaturePairs.end(), [](auto& lhs, auto& rhs) {
758         return lhs.first[0] < rhs.first[0];
759     });
760     Vector<size_t> overlappingFirstGlyphSegmentLengths;
761     if (!ligaturePairs.isEmpty()) {
762         Glyph previousFirstGlyph = ligaturePairs[0].first[0];
763         size_t segmentStart = 0;
764         for (size_t i = 0; i < ligaturePairs.size(); ++i) {
765             auto& ligaturePair = ligaturePairs[i];
766             if (ligaturePair.first[0] != previousFirstGlyph) {
767                 overlappingFirstGlyphSegmentLengths.append(i - segmentStart);
768                 segmentStart = i;
769                 previousFirstGlyph = ligaturePairs[0].first[0];
770             }
771         }
772         overlappingFirstGlyphSegmentLengths.append(ligaturePairs.size() - segmentStart);
773     }
774
775     overwrite16(subtableRecordLocation + 6, m_result.size() - subtableRecordLocation);
776     auto subtableLocation = m_result.size();
777     append16(1); // Format 1
778     append16(0); // Placeholder for offset to coverage table, relative to beginning of substitution table
779     append16(ligaturePairs.size()); // Number of LigatureSet tables
780     grow(overlappingFirstGlyphSegmentLengths.size() * 2); // Placeholder for offset to LigatureSet table
781
782     Vector<size_t> ligatureSetTableLocations;
783     for (size_t i = 0; i < overlappingFirstGlyphSegmentLengths.size(); ++i) {
784         overwrite16(subtableLocation + 6 + 2 * i, m_result.size() - subtableLocation);
785         ligatureSetTableLocations.append(m_result.size());
786         append16(overlappingFirstGlyphSegmentLengths[i]); // LigatureCount
787         grow(overlappingFirstGlyphSegmentLengths[i] * 2); // Placeholder for offset to Ligature table
788     }
789     ASSERT(ligatureSetTableLocations.size() == overlappingFirstGlyphSegmentLengths.size());
790
791     size_t ligaturePairIndex = 0;
792     for (size_t i = 0; i < overlappingFirstGlyphSegmentLengths.size(); ++i) {
793         for (size_t j = 0; j < overlappingFirstGlyphSegmentLengths[i]; ++j) {
794             overwrite16(ligatureSetTableLocations[i] + 2 + 2 * j, m_result.size() - ligatureSetTableLocations[i]);
795             auto& ligaturePair = ligaturePairs[ligaturePairIndex];
796             append16(ligaturePair.second);
797             append16(ligaturePair.first.size());
798             for (size_t k = 1; k < ligaturePair.first.size(); ++k)
799                 append16(ligaturePair.first[k]);
800             ++ligaturePairIndex;
801         }
802     }
803     ASSERT(ligaturePairIndex == ligaturePairs.size());
804
805     // Coverage table
806     overwrite16(subtableLocation + 2, m_result.size() - subtableLocation);
807     append16(1); // CoverageFormat
808     append16(ligatureSetTableLocations.size()); // GlyphCount
809     ligaturePairIndex = 0;
810     for (auto segmentLength : overlappingFirstGlyphSegmentLengths) {
811         auto& ligaturePair = ligaturePairs[ligaturePairIndex];
812         ASSERT(ligaturePair.first.size() > 1);
813         append16(ligaturePair.first[0]);
814         ligaturePairIndex += segmentLength;
815     }
816 }
817
818 void SVGToOTFFontConverter::appendArabicReplacementSubtable(size_t subtableRecordLocation, const char arabicForm[])
819 {
820     Vector<std::pair<Glyph, Glyph>> arabicFinalReplacements;
821     for (auto& pair : m_codepointsToIndicesMap) {
822         for (auto glyphIndex : pair.value) {
823             auto& glyph = m_glyphs[glyphIndex];
824             if (glyph.glyphElement && equalIgnoringASCIICase(glyph.glyphElement->attributeWithoutSynchronization(SVGNames::arabic_formAttr), arabicForm))
825                 arabicFinalReplacements.append(std::make_pair(pair.value[0], glyphIndex));
826         }
827     }
828     if (arabicFinalReplacements.size() > std::numeric_limits<uint16_t>::max())
829         arabicFinalReplacements.clear();
830
831     overwrite16(subtableRecordLocation + 6, m_result.size() - subtableRecordLocation);
832     auto subtableLocation = m_result.size();
833     append16(2); // Format 2
834     Placeholder toCoverageTable = placeholder(subtableLocation);
835     append16(arabicFinalReplacements.size()); // GlyphCount
836     for (auto& pair : arabicFinalReplacements)
837         append16(pair.second);
838
839     toCoverageTable.populate();
840     append16(1); // CoverageFormat
841     append16(arabicFinalReplacements.size()); // GlyphCount
842     for (auto& pair : arabicFinalReplacements)
843         append16(pair.first);
844 }
845
846 void SVGToOTFFontConverter::appendScriptSubtable(unsigned featureCount)
847 {
848     auto dfltScriptTableLocation = m_result.size();
849     append16(0); // Placeholder for offset of default language system table, relative to beginning of Script table
850     append16(0); // Number of following language system tables
851
852     // LangSys table
853     overwrite16(dfltScriptTableLocation, m_result.size() - dfltScriptTableLocation);
854     append16(0); // LookupOrder "= NULL ... reserved"
855     append16(0xFFFF); // No features are required
856     append16(featureCount); // Number of FeatureIndex values
857     for (uint16_t i = 0; i < featureCount; ++i)
858         append16(m_featureCountGSUB++); // Features indices
859 }
860
861 void SVGToOTFFontConverter::appendGSUBTable()
862 {
863     auto tableLocation = m_result.size();
864     auto headerSize = 10;
865
866     append32(0x00010000); // Version
867     append16(headerSize); // Offset to ScriptList
868     Placeholder toFeatureList = placeholder(tableLocation);
869     Placeholder toLookupList = placeholder(tableLocation);
870     ASSERT(tableLocation + headerSize == m_result.size());
871
872     // ScriptList
873     auto scriptListLocation = m_result.size();
874     append16(2); // Number of ScriptRecords
875     append32BitCode("DFLT");
876     append16(0); // Placeholder for offset of Script table, relative to beginning of ScriptList
877     append32BitCode("arab");
878     append16(0); // Placeholder for offset of Script table, relative to beginning of ScriptList
879
880     overwrite16(scriptListLocation + 6, m_result.size() - scriptListLocation);
881     appendScriptSubtable(1);
882     overwrite16(scriptListLocation + 12, m_result.size() - scriptListLocation);
883     appendScriptSubtable(4);
884
885     const unsigned featureCount = 5;
886
887     // FeatureList
888     toFeatureList.populate();
889     auto featureListLocation = m_result.size();
890     size_t featureListSize = 2 + 6 * featureCount;
891     size_t featureTableSize = 6;
892     append16(featureCount); // FeatureCount
893     append32BitCode("liga");
894     append16(featureListSize + featureTableSize * 0); // Offset of feature table, relative to beginning of FeatureList table
895     append32BitCode("fina");
896     append16(featureListSize + featureTableSize * 1); // Offset of feature table, relative to beginning of FeatureList table
897     append32BitCode("medi");
898     append16(featureListSize + featureTableSize * 2); // Offset of feature table, relative to beginning of FeatureList table
899     append32BitCode("init");
900     append16(featureListSize + featureTableSize * 3); // Offset of feature table, relative to beginning of FeatureList table
901     append32BitCode("rlig");
902     append16(featureListSize + featureTableSize * 4); // Offset of feature table, relative to beginning of FeatureList table
903     ASSERT_UNUSED(featureListLocation, featureListLocation + featureListSize == m_result.size());
904
905     for (unsigned i = 0; i < featureCount; ++i) {
906         auto featureTableStart = m_result.size();
907         append16(0); // FeatureParams "= NULL ... reserved"
908         append16(1); // LookupCount
909         append16(i); // LookupListIndex
910         ASSERT_UNUSED(featureTableStart, featureTableStart + featureTableSize == m_result.size());
911     }
912
913     // LookupList
914     toLookupList.populate();
915     auto lookupListLocation = m_result.size();
916     append16(featureCount); // LookupCount
917     for (unsigned i = 0; i < featureCount; ++i)
918         append16(0); // Placeholder for offset to feature table, relative to beginning of LookupList
919     size_t subtableRecordLocations[featureCount];
920     for (unsigned i = 0; i < featureCount; ++i) {
921         subtableRecordLocations[i] = m_result.size();
922         overwrite16(lookupListLocation + 2 + 2 * i, m_result.size() - lookupListLocation);
923         switch (i) {
924         case 4:
925             append16(3); // Type 3: "Replace one glyph with one of many glyphs"
926             break;
927         case 0:
928             append16(4); // Type 4: "Replace multiple glyphs with one glyph"
929             break;
930         default:
931             append16(1); // Type 1: "Replace one glyph with one glyph"
932             break;
933         }
934         append16(0); // LookupFlag
935         append16(1); // SubTableCount
936         append16(0); // Placeholder for offset to subtable, relative to beginning of Lookup table
937     }
938
939     appendLigatureSubtable(subtableRecordLocations[0]);
940     appendArabicReplacementSubtable(subtableRecordLocations[1], "terminal");
941     appendArabicReplacementSubtable(subtableRecordLocations[2], "medial");
942     appendArabicReplacementSubtable(subtableRecordLocations[3], "initial");
943
944     // Manually append empty "rlig" subtable
945     overwrite16(subtableRecordLocations[4] + 6, m_result.size() - subtableRecordLocations[4]);
946     append16(1); // Format 1
947     append16(6); // offset to coverage table, relative to beginning of substitution table
948     append16(0); // AlternateSetCount
949     append16(1); // CoverageFormat
950     append16(0); // GlyphCount
951 }
952
953 void SVGToOTFFontConverter::appendVORGTable()
954 {
955     append16(1); // Major version
956     append16(0); // Minor version
957
958     bool ok;
959     int defaultVerticalOriginY = m_fontElement.attributeWithoutSynchronization(SVGNames::vert_origin_yAttr).toInt(&ok);
960     if (!ok && m_missingGlyphElement)
961         defaultVerticalOriginY = m_missingGlyphElement->attributeWithoutSynchronization(SVGNames::vert_origin_yAttr).toInt();
962     defaultVerticalOriginY = scaleUnitsPerEm(defaultVerticalOriginY);
963     append16(clampTo<int16_t>(defaultVerticalOriginY));
964
965     auto tableSizeOffset = m_result.size();
966     append16(0); // Place to write table size.
967     for (Glyph i = 0; i < m_glyphs.size(); ++i) {
968         if (auto* glyph = m_glyphs[i].glyphElement) {
969             if (int verticalOriginY = glyph->attributeWithoutSynchronization(SVGNames::vert_origin_yAttr).toInt()) {
970                 append16(i);
971                 append16(clampTo<int16_t>(scaleUnitsPerEm(verticalOriginY)));
972             }
973         }
974     }
975     ASSERT(!((m_result.size() - tableSizeOffset - 2) % 4));
976     overwrite16(tableSizeOffset, (m_result.size() - tableSizeOffset - 2) / 4);
977 }
978
979 void SVGToOTFFontConverter::appendVHEATable()
980 {
981     float height = m_ascent + m_descent;
982     append32(0x00011000); // Version
983     append16(clampTo<int16_t>(height / 2)); // Vertical typographic ascender (vertical baseline to the right)
984     append16(clampTo<int16_t>(-static_cast<int>(height / 2))); // Vertical typographic descender
985     append16(clampTo<int16_t>(s_outputUnitsPerEm / 10)); // Vertical typographic line gap
986     // FIXME: m_unitsPerEm is almost certainly not correct
987     append16(clampTo<int16_t>(m_advanceHeightMax));
988     append16(clampTo<int16_t>(s_outputUnitsPerEm - m_boundingBox.maxY())); // Minimum top side bearing
989     append16(clampTo<int16_t>(m_boundingBox.y())); // Minimum bottom side bearing
990     append16(clampTo<int16_t>(s_outputUnitsPerEm - m_boundingBox.y())); // Y maximum extent
991     // Since WebKit draws the caret and ignores the following values, it doesn't matter what we set them to.
992     append16(1); // Vertical caret
993     append16(0); // Vertical caret
994     append16(0); // "Set value to 0 for non-slanted fonts"
995     append32(0); // Reserved
996     append32(0); // Reserved
997     append16(0); // "Set to 0"
998     append16(m_glyphs.size()); // Number of advance heights in VMTX table
999 }
1000
1001 void SVGToOTFFontConverter::appendVMTXTable()
1002 {
1003     for (auto& glyph : m_glyphs) {
1004         append16(clampTo<uint16_t>(glyph.verticalAdvance));
1005         append16(clampTo<int16_t>(s_outputUnitsPerEm - glyph.boundingBox.maxY())); // top side bearing
1006     }
1007 }
1008
1009 static String codepointToString(UChar32 codepoint)
1010 {
1011     UChar buffer[2];
1012     uint8_t length = 0;
1013     UBool error = false;
1014     U16_APPEND(buffer, length, 2, codepoint, error);
1015     return error ? String() : String(buffer, length);
1016 }
1017
1018 Vector<Glyph, 1> SVGToOTFFontConverter::glyphsForCodepoint(UChar32 codepoint) const
1019 {
1020     return m_codepointsToIndicesMap.get(codepointToString(codepoint));
1021 }
1022
1023 void SVGToOTFFontConverter::addCodepointRanges(const UnicodeRanges& unicodeRanges, HashSet<Glyph>& glyphSet) const
1024 {
1025     for (auto& unicodeRange : unicodeRanges) {
1026         for (auto codepoint = unicodeRange.first; codepoint <= unicodeRange.second; ++codepoint) {
1027             for (auto index : glyphsForCodepoint(codepoint))
1028                 glyphSet.add(index);
1029         }
1030     }
1031 }
1032
1033 void SVGToOTFFontConverter::addCodepoints(const HashSet<String>& codepoints, HashSet<Glyph>& glyphSet) const
1034 {
1035     for (auto& codepointString : codepoints) {
1036         for (auto index : m_codepointsToIndicesMap.get(codepointString))
1037             glyphSet.add(index);
1038     }
1039 }
1040
1041 void SVGToOTFFontConverter::addGlyphNames(const HashSet<String>& glyphNames, HashSet<Glyph>& glyphSet) const
1042 {
1043     for (auto& glyphName : glyphNames) {
1044         if (Glyph glyph = m_glyphNameToIndexMap.get(glyphName))
1045             glyphSet.add(glyph);
1046     }
1047 }
1048
1049 void SVGToOTFFontConverter::addKerningPair(Vector<KerningData>& data, const SVGKerningPair& kerningPair) const
1050 {
1051     HashSet<Glyph> glyphSet1;
1052     HashSet<Glyph> glyphSet2;
1053
1054     addCodepointRanges(kerningPair.unicodeRange1, glyphSet1);
1055     addCodepointRanges(kerningPair.unicodeRange2, glyphSet2);
1056     addGlyphNames(kerningPair.glyphName1, glyphSet1);
1057     addGlyphNames(kerningPair.glyphName2, glyphSet2);
1058     addCodepoints(kerningPair.unicodeName1, glyphSet1);
1059     addCodepoints(kerningPair.unicodeName2, glyphSet2);
1060
1061     // FIXME: Use table format 2 so we don't have to append each of these one by one.
1062     for (auto& glyph1 : glyphSet1) {
1063         for (auto& glyph2 : glyphSet2)
1064             data.append(KerningData(glyph1, glyph2, clampTo<int16_t>(-scaleUnitsPerEm(kerningPair.kerning))));
1065     }
1066 }
1067
1068 template<typename T> inline size_t SVGToOTFFontConverter::appendKERNSubtable(bool (T::*buildKerningPair)(SVGKerningPair&) const, uint16_t coverage)
1069 {
1070     Vector<KerningData> kerningData;
1071     for (auto& element : childrenOfType<T>(m_fontElement)) {
1072         SVGKerningPair kerningPair;
1073         if ((element.*buildKerningPair)(kerningPair))
1074             addKerningPair(kerningData, kerningPair);
1075     }
1076     return finishAppendingKERNSubtable(WTFMove(kerningData), coverage);
1077 }
1078
1079 size_t SVGToOTFFontConverter::finishAppendingKERNSubtable(Vector<KerningData> kerningData, uint16_t coverage)
1080 {
1081     std::sort(kerningData.begin(), kerningData.end(), [](auto& a, auto& b) {
1082         return a.glyph1 < b.glyph1 || (a.glyph1 == b.glyph1 && a.glyph2 < b.glyph2);
1083     });
1084
1085     size_t sizeOfKerningDataTable = 14 + 6 * kerningData.size();
1086     if (sizeOfKerningDataTable > std::numeric_limits<uint16_t>::max()) {
1087         kerningData.clear();
1088         sizeOfKerningDataTable = 14;
1089     }
1090
1091     append16(0); // Version of subtable
1092     append16(sizeOfKerningDataTable); // Length of this subtable
1093     append16(coverage); // Table coverage bitfield
1094
1095     uint16_t roundedNumKerningPairs = roundDownToPowerOfTwo(kerningData.size());
1096
1097     append16(kerningData.size());
1098     append16(roundedNumKerningPairs * 6); // searchRange: "The largest power of two less than or equal to the value of nPairs, multiplied by the size in bytes of an entry in the table."
1099     append16(integralLog2(roundedNumKerningPairs)); // entrySelector: "log2 of the largest power of two less than or equal to the value of nPairs."
1100     append16((kerningData.size() - roundedNumKerningPairs) * 6); // rangeShift: "The value of nPairs minus the largest power of two less than or equal to nPairs,
1101                                                                         // and then multiplied by the size in bytes of an entry in the table."
1102
1103     for (auto& kerningDataElement : kerningData) {
1104         append16(kerningDataElement.glyph1);
1105         append16(kerningDataElement.glyph2);
1106         append16(kerningDataElement.adjustment);
1107     }
1108
1109     return sizeOfKerningDataTable;
1110 }
1111
1112 void SVGToOTFFontConverter::appendKERNTable()
1113 {
1114     append16(0); // Version
1115     append16(2); // Number of subtables
1116
1117 #if !ASSERT_DISABLED
1118     auto subtablesOffset = m_result.size();
1119 #endif
1120
1121     size_t sizeOfHorizontalSubtable = appendKERNSubtable<SVGHKernElement>(&SVGHKernElement::buildHorizontalKerningPair, 1);
1122     ASSERT_UNUSED(sizeOfHorizontalSubtable, subtablesOffset + sizeOfHorizontalSubtable == m_result.size());
1123     size_t sizeOfVerticalSubtable = appendKERNSubtable<SVGVKernElement>(&SVGVKernElement::buildVerticalKerningPair, 0);
1124     ASSERT_UNUSED(sizeOfVerticalSubtable, subtablesOffset + sizeOfHorizontalSubtable + sizeOfVerticalSubtable == m_result.size());
1125
1126 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED <= 101000
1127     // Work around a bug in Apple's font parser by adding some padding bytes. <rdar://problem/18401901>
1128     for (int i = 0; i < 6; ++i)
1129         m_result.append(0);
1130 #endif
1131 }
1132
1133 template <typename V>
1134 static void writeCFFEncodedNumber(V& vector, float number)
1135 {
1136     vector.append(0xFF);
1137     // Convert to 16.16 fixed-point
1138     append32(vector, clampTo<int32_t>(number * 0x10000));
1139 }
1140
1141 static const char rLineTo = 0x05;
1142 static const char rrCurveTo = 0x08;
1143 static const char endChar = 0x0e;
1144 static const char rMoveTo = 0x15;
1145
1146 class CFFBuilder final : public SVGPathConsumer {
1147 public:
1148     CFFBuilder(Vector<char>& cffData, float width, FloatPoint origin, float unitsPerEmScalar)
1149         : m_cffData(cffData)
1150         , m_unitsPerEmScalar(unitsPerEmScalar)
1151     {
1152         writeCFFEncodedNumber(m_cffData, std::floor(width)); // hmtx table can't encode fractional FUnit values, and the CFF table needs to agree with hmtx.
1153         writeCFFEncodedNumber(m_cffData, origin.x());
1154         writeCFFEncodedNumber(m_cffData, origin.y());
1155         m_cffData.append(rMoveTo);
1156     }
1157
1158     std::optional<FloatRect> boundingBox() const
1159     {
1160         return m_boundingBox;
1161     }
1162
1163 private:
1164     void updateBoundingBox(FloatPoint point)
1165     {
1166         if (!m_boundingBox) {
1167             m_boundingBox = FloatRect(point, FloatSize());
1168             return;
1169         }
1170         m_boundingBox.value().extend(point);
1171     }
1172
1173     void writePoint(FloatPoint destination)
1174     {
1175         updateBoundingBox(destination);
1176
1177         FloatSize delta = destination - m_current;
1178         writeCFFEncodedNumber(m_cffData, delta.width());
1179         writeCFFEncodedNumber(m_cffData, delta.height());
1180
1181         m_current = destination;
1182     }
1183
1184     void moveTo(const FloatPoint& targetPoint, bool closed, PathCoordinateMode mode) final
1185     {
1186         if (closed && !m_cffData.isEmpty())
1187             closePath();
1188
1189         FloatPoint scaledTargetPoint = FloatPoint(targetPoint.x() * m_unitsPerEmScalar, targetPoint.y() * m_unitsPerEmScalar);
1190         FloatPoint destination = mode == AbsoluteCoordinates ? scaledTargetPoint : m_current + scaledTargetPoint;
1191
1192         writePoint(destination);
1193         m_cffData.append(rMoveTo);
1194
1195         m_startingPoint = m_current;
1196     }
1197
1198     void unscaledLineTo(const FloatPoint& targetPoint)
1199     {
1200         writePoint(targetPoint);
1201         m_cffData.append(rLineTo);
1202     }
1203
1204     void lineTo(const FloatPoint& targetPoint, PathCoordinateMode mode) final
1205     {
1206         FloatPoint scaledTargetPoint = FloatPoint(targetPoint.x() * m_unitsPerEmScalar, targetPoint.y() * m_unitsPerEmScalar);
1207         FloatPoint destination = mode == AbsoluteCoordinates ? scaledTargetPoint : m_current + scaledTargetPoint;
1208
1209         unscaledLineTo(destination);
1210     }
1211
1212     void curveToCubic(const FloatPoint& point1, const FloatPoint& point2, const FloatPoint& point3, PathCoordinateMode mode) final
1213     {
1214         FloatPoint scaledPoint1 = FloatPoint(point1.x() * m_unitsPerEmScalar, point1.y() * m_unitsPerEmScalar);
1215         FloatPoint scaledPoint2 = FloatPoint(point2.x() * m_unitsPerEmScalar, point2.y() * m_unitsPerEmScalar);
1216         FloatPoint scaledPoint3 = FloatPoint(point3.x() * m_unitsPerEmScalar, point3.y() * m_unitsPerEmScalar);
1217
1218         if (mode == RelativeCoordinates) {
1219             scaledPoint1 += m_current;
1220             scaledPoint2 += m_current;
1221             scaledPoint3 += m_current;
1222         }
1223
1224         writePoint(scaledPoint1);
1225         writePoint(scaledPoint2);
1226         writePoint(scaledPoint3);
1227         m_cffData.append(rrCurveTo);
1228     }
1229
1230     void closePath() final
1231     {
1232         if (m_current != m_startingPoint)
1233             unscaledLineTo(m_startingPoint);
1234     }
1235
1236     void incrementPathSegmentCount() final { }
1237     bool continueConsuming() final { return true; }
1238
1239     void lineToHorizontal(float, PathCoordinateMode) final { ASSERT_NOT_REACHED(); }
1240     void lineToVertical(float, PathCoordinateMode) final { ASSERT_NOT_REACHED(); }
1241     void curveToCubicSmooth(const FloatPoint&, const FloatPoint&, PathCoordinateMode) final { ASSERT_NOT_REACHED(); }
1242     void curveToQuadratic(const FloatPoint&, const FloatPoint&, PathCoordinateMode) final { ASSERT_NOT_REACHED(); }
1243     void curveToQuadraticSmooth(const FloatPoint&, PathCoordinateMode) final { ASSERT_NOT_REACHED(); }
1244     void arcTo(float, float, float, bool, bool, const FloatPoint&, PathCoordinateMode) final { ASSERT_NOT_REACHED(); }
1245
1246     Vector<char>& m_cffData;
1247     FloatPoint m_startingPoint;
1248     FloatPoint m_current;
1249     std::optional<FloatRect> m_boundingBox;
1250     float m_unitsPerEmScalar;
1251 };
1252
1253 Vector<char> SVGToOTFFontConverter::transcodeGlyphPaths(float width, const SVGElement& glyphOrMissingGlyphElement, std::optional<FloatRect>& boundingBox) const
1254 {
1255     Vector<char> result;
1256
1257     auto& dAttribute = glyphOrMissingGlyphElement.attributeWithoutSynchronization(SVGNames::dAttr);
1258     if (dAttribute.isEmpty()) {
1259         writeCFFEncodedNumber(result, width);
1260         writeCFFEncodedNumber(result, 0);
1261         writeCFFEncodedNumber(result, 0);
1262         result.append(rMoveTo);
1263         result.append(endChar);
1264         return result;
1265     }
1266
1267     // FIXME: If we are vertical, use vert_origin_x and vert_origin_y
1268     bool ok;
1269     float horizontalOriginX = scaleUnitsPerEm(glyphOrMissingGlyphElement.attributeWithoutSynchronization(SVGNames::horiz_origin_xAttr).toFloat(&ok));
1270     if (!ok && m_fontFaceElement)
1271         horizontalOriginX = scaleUnitsPerEm(m_fontFaceElement->horizontalOriginX());
1272     float horizontalOriginY = scaleUnitsPerEm(glyphOrMissingGlyphElement.attributeWithoutSynchronization(SVGNames::horiz_origin_yAttr).toFloat(&ok));
1273     if (!ok && m_fontFaceElement)
1274         horizontalOriginY = scaleUnitsPerEm(m_fontFaceElement->horizontalOriginY());
1275
1276     CFFBuilder builder(result, width, FloatPoint(horizontalOriginX, horizontalOriginY), static_cast<float>(s_outputUnitsPerEm) / m_inputUnitsPerEm);
1277     SVGPathStringSource source(dAttribute);
1278
1279     ok = SVGPathParser::parse(source, builder);
1280     if (!ok)
1281         return { };
1282
1283     boundingBox = builder.boundingBox();
1284
1285     result.append(endChar);
1286     return result;
1287 }
1288
1289 void SVGToOTFFontConverter::processGlyphElement(const SVGElement& glyphOrMissingGlyphElement, const SVGGlyphElement* glyphElement, float defaultHorizontalAdvance, float defaultVerticalAdvance, const String& codepoints, std::optional<FloatRect>& boundingBox)
1290 {
1291     bool ok;
1292     float horizontalAdvance = scaleUnitsPerEm(glyphOrMissingGlyphElement.attributeWithoutSynchronization(SVGNames::horiz_adv_xAttr).toFloat(&ok));
1293     if (!ok)
1294         horizontalAdvance = defaultHorizontalAdvance;
1295     m_advanceWidthMax = std::max(m_advanceWidthMax, horizontalAdvance);
1296     float verticalAdvance = scaleUnitsPerEm(glyphOrMissingGlyphElement.attributeWithoutSynchronization(SVGNames::vert_adv_yAttr).toFloat(&ok));
1297     if (!ok)
1298         verticalAdvance = defaultVerticalAdvance;
1299     m_advanceHeightMax = std::max(m_advanceHeightMax, verticalAdvance);
1300
1301     std::optional<FloatRect> glyphBoundingBox;
1302     auto path = transcodeGlyphPaths(horizontalAdvance, glyphOrMissingGlyphElement, glyphBoundingBox);
1303     if (!path.size()) {
1304         // It's better to use a fallback font rather than use a font without all its glyphs.
1305         m_error = true;
1306     }
1307     if (!boundingBox)
1308         boundingBox = glyphBoundingBox;
1309     else if (glyphBoundingBox)
1310         boundingBox.value().unite(glyphBoundingBox.value());
1311     if (glyphBoundingBox)
1312         m_minRightSideBearing = std::min(m_minRightSideBearing, horizontalAdvance - glyphBoundingBox.value().maxX());
1313
1314     m_glyphs.append(GlyphData(WTFMove(path), glyphElement, horizontalAdvance, verticalAdvance, glyphBoundingBox.value_or(FloatRect()), codepoints));
1315 }
1316
1317 void SVGToOTFFontConverter::appendLigatureGlyphs()
1318 {
1319     HashSet<UChar32> ligatureCodepoints;
1320     HashSet<UChar32> nonLigatureCodepoints;
1321     for (auto& glyph : m_glyphs) {
1322         auto codePoints = StringView(glyph.codepoints).codePoints();
1323         auto codePointsIterator = codePoints.begin();
1324         if (codePointsIterator == codePoints.end())
1325             continue;
1326         UChar32 codepoint = *codePointsIterator;
1327         ++codePointsIterator;
1328         if (codePointsIterator == codePoints.end())
1329             nonLigatureCodepoints.add(codepoint);
1330         else {
1331             ligatureCodepoints.add(codepoint);
1332             for (; codePointsIterator != codePoints.end(); ++codePointsIterator)
1333                 ligatureCodepoints.add(*codePointsIterator);
1334         }
1335     }
1336
1337     for (auto codepoint : nonLigatureCodepoints)
1338         ligatureCodepoints.remove(codepoint);
1339     for (auto codepoint : ligatureCodepoints) {
1340         auto codepoints = codepointToString(codepoint);
1341         if (!codepoints.isNull())
1342             m_glyphs.append(GlyphData(Vector<char>(m_emptyGlyphCharString), nullptr, s_outputUnitsPerEm, s_outputUnitsPerEm, FloatRect(), codepoints));
1343     }
1344 }
1345
1346 bool SVGToOTFFontConverter::compareCodepointsLexicographically(const GlyphData& data1, const GlyphData& data2)
1347 {
1348     auto codePoints1 = StringView(data1.codepoints).codePoints();
1349     auto codePoints2 = StringView(data2.codepoints).codePoints();
1350     auto iterator1 = codePoints1.begin();
1351     auto iterator2 = codePoints2.begin();
1352     while (iterator1 != codePoints1.end() && iterator2 != codePoints2.end()) {
1353         UChar32 codepoint1, codepoint2;
1354         codepoint1 = *iterator1;
1355         codepoint2 = *iterator2;
1356
1357         if (codepoint1 < codepoint2)
1358             return true;
1359         if (codepoint1 > codepoint2)
1360             return false;
1361
1362         ++iterator1;
1363         ++iterator2;
1364     }
1365
1366     if (iterator1 == codePoints1.end() && iterator2 == codePoints2.end()) {
1367         bool firstIsIsolated = data1.glyphElement && equalLettersIgnoringASCIICase(data1.glyphElement->attributeWithoutSynchronization(SVGNames::arabic_formAttr), "isolated");
1368         bool secondIsIsolated = data2.glyphElement && equalLettersIgnoringASCIICase(data2.glyphElement->attributeWithoutSynchronization(SVGNames::arabic_formAttr), "isolated");
1369         return firstIsIsolated && !secondIsIsolated;
1370     }
1371     return iterator1 == codePoints1.end();
1372 }
1373
1374 static void populateEmptyGlyphCharString(Vector<char, 17>& o, unsigned unitsPerEm)
1375 {
1376     writeCFFEncodedNumber(o, unitsPerEm);
1377     writeCFFEncodedNumber(o, 0);
1378     writeCFFEncodedNumber(o, 0);
1379     o.append(rMoveTo);
1380     o.append(endChar);
1381 }
1382
1383 SVGToOTFFontConverter::SVGToOTFFontConverter(const SVGFontElement& fontElement)
1384     : m_fontElement(fontElement)
1385     , m_fontFaceElement(childrenOfType<SVGFontFaceElement>(m_fontElement).first())
1386     , m_missingGlyphElement(childrenOfType<SVGMissingGlyphElement>(m_fontElement).first())
1387     , m_advanceWidthMax(0)
1388     , m_advanceHeightMax(0)
1389     , m_minRightSideBearing(std::numeric_limits<float>::max())
1390     , m_featureCountGSUB(0)
1391     , m_tablesAppendedCount(0)
1392     , m_weight(5)
1393     , m_italic(false)
1394 {
1395     if (!m_fontFaceElement) {
1396         m_inputUnitsPerEm = 1;
1397         m_ascent = s_outputUnitsPerEm;
1398         m_descent = 1;
1399         m_xHeight = s_outputUnitsPerEm;
1400         m_capHeight = m_ascent;
1401     } else {
1402         m_inputUnitsPerEm = m_fontFaceElement->unitsPerEm();
1403         m_ascent = scaleUnitsPerEm(m_fontFaceElement->ascent());
1404         m_descent = scaleUnitsPerEm(m_fontFaceElement->descent());
1405         m_xHeight = scaleUnitsPerEm(m_fontFaceElement->xHeight());
1406         m_capHeight = scaleUnitsPerEm(m_fontFaceElement->capHeight());
1407
1408         // Some platforms, including OS X, use 0 ascent and descent to mean that the platform should synthesize
1409         // a value based on a heuristic. However, SVG fonts can legitimately have 0 for ascent or descent.
1410         // Specifing a single FUnit gets us as close to 0 as we can without triggering the synthesis.
1411         if (!m_ascent)
1412             m_ascent = 1;
1413         if (!m_descent)
1414             m_descent = 1;
1415     }
1416
1417     float defaultHorizontalAdvance = m_fontFaceElement ? scaleUnitsPerEm(m_fontFaceElement->horizontalAdvanceX()) : 0;
1418     float defaultVerticalAdvance = m_fontFaceElement ? scaleUnitsPerEm(m_fontFaceElement->verticalAdvanceY()) : 0;
1419
1420     m_lineGap = s_outputUnitsPerEm / 10;
1421
1422     populateEmptyGlyphCharString(m_emptyGlyphCharString, s_outputUnitsPerEm);
1423
1424     std::optional<FloatRect> boundingBox;
1425     if (m_missingGlyphElement)
1426         processGlyphElement(*m_missingGlyphElement, nullptr, defaultHorizontalAdvance, defaultVerticalAdvance, String(), boundingBox);
1427     else {
1428         m_glyphs.append(GlyphData(Vector<char>(m_emptyGlyphCharString), nullptr, s_outputUnitsPerEm, s_outputUnitsPerEm, FloatRect(), String()));
1429         boundingBox = FloatRect(0, 0, s_outputUnitsPerEm, s_outputUnitsPerEm);
1430     }
1431
1432     for (auto& glyphElement : childrenOfType<SVGGlyphElement>(m_fontElement)) {
1433         auto& unicodeAttribute = glyphElement.attributeWithoutSynchronization(SVGNames::unicodeAttr);
1434         if (!unicodeAttribute.isEmpty()) // If we can never actually trigger this glyph, ignore it completely
1435             processGlyphElement(glyphElement, &glyphElement, defaultHorizontalAdvance, defaultVerticalAdvance, unicodeAttribute, boundingBox);
1436     }
1437
1438     m_boundingBox = boundingBox.value_or(FloatRect());
1439
1440 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED <= 101000
1441     // <rdar://problem/20086223> Cocoa has a bug where glyph bounding boxes are not correctly respected for frustum culling. Work around this by
1442     // inflating the font's bounding box
1443     m_boundingBox.extend(FloatPoint(0, 0));
1444 #endif
1445
1446     appendLigatureGlyphs();
1447
1448     if (m_glyphs.size() > std::numeric_limits<Glyph>::max()) {
1449         m_glyphs.clear();
1450         return;
1451     }
1452
1453     std::sort(m_glyphs.begin(), m_glyphs.end(), &compareCodepointsLexicographically);
1454
1455     for (Glyph i = 0; i < m_glyphs.size(); ++i) {
1456         GlyphData& glyph = m_glyphs[i];
1457         if (glyph.glyphElement) {
1458             auto& glyphName = glyph.glyphElement->attributeWithoutSynchronization(SVGNames::glyph_nameAttr);
1459             if (!glyphName.isNull())
1460                 m_glyphNameToIndexMap.add(glyphName, i);
1461         }
1462         if (m_codepointsToIndicesMap.isValidKey(glyph.codepoints)) {
1463             auto& glyphVector = m_codepointsToIndicesMap.add(glyph.codepoints, Vector<Glyph>()).iterator->value;
1464             // Prefer isolated arabic forms
1465             if (glyph.glyphElement && equalLettersIgnoringASCIICase(glyph.glyphElement->attributeWithoutSynchronization(SVGNames::arabic_formAttr), "isolated"))
1466                 glyphVector.insert(0, i);
1467             else
1468                 glyphVector.append(i);
1469         }
1470     }
1471
1472     // FIXME: Handle commas.
1473     if (m_fontFaceElement) {
1474         Vector<String> segments;
1475         m_fontFaceElement->attributeWithoutSynchronization(SVGNames::font_weightAttr).string().split(' ', segments);
1476         for (auto& segment : segments) {
1477             if (equalLettersIgnoringASCIICase(segment, "bold")) {
1478                 m_weight = 7;
1479                 break;
1480             }
1481             bool ok;
1482             int value = segment.toInt(&ok);
1483             if (ok && value >= 0 && value < 1000) {
1484                 m_weight = (value + 50) / 100;
1485                 break;
1486             }
1487         }
1488         m_fontFaceElement->attributeWithoutSynchronization(SVGNames::font_styleAttr).string().split(' ', segments);
1489         for (auto& segment : segments) {
1490             if (equalLettersIgnoringASCIICase(segment, "italic") || equalLettersIgnoringASCIICase(segment, "oblique")) {
1491                 m_italic = true;
1492                 break;
1493             }
1494         }
1495     }
1496
1497     if (m_fontFaceElement)
1498         m_fontFamily = m_fontFaceElement->fontFamily();
1499 }
1500
1501 static inline bool isFourByteAligned(size_t x)
1502 {
1503     return !(x & 3);
1504 }
1505
1506 uint32_t SVGToOTFFontConverter::calculateChecksum(size_t startingOffset, size_t endingOffset) const
1507 {
1508     ASSERT(isFourByteAligned(endingOffset - startingOffset));
1509     uint32_t sum = 0;
1510     for (size_t offset = startingOffset; offset < endingOffset; offset += 4) {
1511         sum += static_cast<unsigned char>(m_result[offset + 3])
1512             | (static_cast<unsigned char>(m_result[offset + 2]) << 8)
1513             | (static_cast<unsigned char>(m_result[offset + 1]) << 16)
1514             | (static_cast<unsigned char>(m_result[offset]) << 24);
1515     }
1516     return sum;
1517 }
1518
1519 void SVGToOTFFontConverter::appendTable(const char identifier[4], FontAppendingFunction appendingFunction)
1520 {
1521     size_t offset = m_result.size();
1522     ASSERT(isFourByteAligned(offset));
1523     (this->*appendingFunction)();
1524     size_t unpaddedSize = m_result.size() - offset;
1525     while (!isFourByteAligned(m_result.size()))
1526         m_result.append(0);
1527     ASSERT(isFourByteAligned(m_result.size()));
1528     size_t directoryEntryOffset = headerSize + m_tablesAppendedCount * directoryEntrySize;
1529     m_result[directoryEntryOffset] = identifier[0];
1530     m_result[directoryEntryOffset + 1] = identifier[1];
1531     m_result[directoryEntryOffset + 2] = identifier[2];
1532     m_result[directoryEntryOffset + 3] = identifier[3];
1533     overwrite32(directoryEntryOffset + 4, calculateChecksum(offset, m_result.size()));
1534     overwrite32(directoryEntryOffset + 8, offset);
1535     overwrite32(directoryEntryOffset + 12, unpaddedSize);
1536     ++m_tablesAppendedCount;
1537 }
1538
1539 bool SVGToOTFFontConverter::convertSVGToOTFFont()
1540 {
1541     if (m_glyphs.isEmpty())
1542         return false;
1543
1544     uint16_t numTables = 14;
1545     uint16_t roundedNumTables = roundDownToPowerOfTwo(numTables);
1546     uint16_t searchRange = roundedNumTables * 16; // searchRange: "(Maximum power of 2 <= numTables) x 16."
1547
1548     m_result.append('O');
1549     m_result.append('T');
1550     m_result.append('T');
1551     m_result.append('O');
1552     append16(numTables);
1553     append16(searchRange);
1554     append16(integralLog2(roundedNumTables)); // entrySelector: "Log2(maximum power of 2 <= numTables)."
1555     append16(numTables * 16 - searchRange); // rangeShift: "NumTables x 16-searchRange."
1556
1557     ASSERT(m_result.size() == headerSize);
1558
1559     // Leave space for the directory entries.
1560     for (size_t i = 0; i < directoryEntrySize * numTables; ++i)
1561         m_result.append(0);
1562
1563     appendTable("CFF ", &SVGToOTFFontConverter::appendCFFTable);
1564     appendTable("GSUB", &SVGToOTFFontConverter::appendGSUBTable);
1565     appendTable("OS/2", &SVGToOTFFontConverter::appendOS2Table);
1566     appendTable("VORG", &SVGToOTFFontConverter::appendVORGTable);
1567     appendTable("cmap", &SVGToOTFFontConverter::appendCMAPTable);
1568     auto headTableOffset = m_result.size();
1569     appendTable("head", &SVGToOTFFontConverter::appendHEADTable);
1570     appendTable("hhea", &SVGToOTFFontConverter::appendHHEATable);
1571     appendTable("hmtx", &SVGToOTFFontConverter::appendHMTXTable);
1572     appendTable("kern", &SVGToOTFFontConverter::appendKERNTable);
1573     appendTable("maxp", &SVGToOTFFontConverter::appendMAXPTable);
1574     appendTable("name", &SVGToOTFFontConverter::appendNAMETable);
1575     appendTable("post", &SVGToOTFFontConverter::appendPOSTTable);
1576     appendTable("vhea", &SVGToOTFFontConverter::appendVHEATable);
1577     appendTable("vmtx", &SVGToOTFFontConverter::appendVMTXTable);
1578
1579     ASSERT(numTables == m_tablesAppendedCount);
1580
1581     // checksumAdjustment: "To compute: set it to 0, calculate the checksum for the 'head' table and put it in the table directory,
1582     // sum the entire font as uint32, then store B1B0AFBA - sum. The checksum for the 'head' table will now be wrong. That is OK."
1583     overwrite32(headTableOffset + 8, 0xB1B0AFBAU - calculateChecksum(0, m_result.size()));
1584     return true;
1585 }
1586
1587 std::optional<Vector<char>> convertSVGToOTFFont(const SVGFontElement& element)
1588 {
1589     SVGToOTFFontConverter converter(element);
1590     if (converter.error())
1591         return std::nullopt;
1592     if (!converter.convertSVGToOTFFont())
1593         return std::nullopt;
1594     return converter.releaseResult();
1595 }
1596
1597 }
1598
1599 #endif // ENABLE(SVG_FONTS)