<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[173852] trunk/Source/WebCore</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/173852">173852</a></dd>
<dt>Author</dt> <dd>mmaxfield@apple.com</dd>
<dt>Date</dt> <dd>2014-09-22 15:08:02 -0700 (Mon, 22 Sep 2014)</dd>
</dl>
<h3>Log Message</h3>
<pre>Implement 'vhea', 'vmtx', and 'kern' tables in SVG -> OTF converter
https://bugs.webkit.org/show_bug.cgi?id=136971
Reviewed by Darin Adler.
This patch fills in the tables related to vertical metrics and kerning. The vertical metrics tables are
fairly straightforward. On the other hand, the 'kern' table has subtables which can be in a variety
of formats; however, according to Microsoft, Windows only allows one particular format, which doesn't
allow for ranges of glyphs to be described and therefore might lead to a large 'kern' table size. In
the interest of platform agnosticism I have implemented this particular format, and have not actually
witnessed any extremely large 'kern' tables in any of the fonts that I have looked at.
I also added basic support for the horiz-origin-x and horiz-origin-y SVG properties, though support
might not be perfect based on my testing.
Regarding testing, after this patch, almost all of our SVG fonts tests pass (barring tiny rebaselining;
the platform font system calculates slightly different advances than we did. These differences are tiny
and expected from such a dramatic shift in text handling engines.) There are around two dozen failing
tests left that look like they are actually indiciative of code problems.
* svg/SVGFontElement.cpp:
(WebCore::SVGFontElement::ensureGlyphCache): Refactor to accept new signature of
SVG*KernElement::build*KerningPair().
* svg/SVGHKernElement.cpp:
(WebCore::SVGHKernElement::buildHorizontalKerningPair): Refactor to allow for intermediate data to be
returned, which the SVG -> OTF font converter can use.
* svg/SVGHKernElement.h: New signature for buildHorizontalKerningPair().
* svg/SVGToOTFFontConversion.cpp:
(WebCore::SVGToOTFFontConverter::GlyphData::GlyphData): Save the glyph's vertical advance along with
the glyph's horizontal advance.
(WebCore::SVGToOTFFontConverter::KerningData::KerningData): This data represents a record in one of
the two 'kern' subtables, and as such needs to be sorted.
(WebCore::SVGToOTFFontConverter::KerningData::operator<): Sort comparator.
(WebCore::SVGToOTFFontConverter::appendHEADTable): Save the unitsPerEm locally to save a conditional.
(WebCore::SVGToOTFFontConverter::appendHHEATable): Ditto.
(WebCore::SVGToOTFFontConverter::appendHMTXTable): Ditto. Also use the horizontal advance as opposed to
the vertical advance.
(WebCore::SVGToOTFFontConverter::appendOS2Table): Save the unitsPerEm locally to save a conditional.
Also if the average advance isn't found in the font, use the missing glyph's advance. If there is no
missing glyph, bail and use the unitsPerEm.
(WebCore::SVGToOTFFontConverter::appendVORGTable): If the default vertical origin isn't found in the
font, use the missing glyphs's vertical origin. If there is no missing glyph, bail and use 0.
(WebCore::SVGToOTFFontConverter::appendVHEATable): Parallel of 'hhea' table, except for vertical info.
(WebCore::SVGToOTFFontConverter::appendVMTXTable): Parallel of 'hmtx' table, except for vertical info.
(WebCore::SVGToOTFFontConverter::appendKerningHelperFields): Computes and appends 4 fields that font
parsers need in order to do a binary search on the kern records.
(WebCore::SVGToOTFFontConverter::addCodepointRanges): For each codepoint in the ranges, look up its
representative glyph and append it to a set.
(WebCore::SVGToOTFFontConverter::addCodepoints): For each codepoint in the range, look up its
representative glyph and append it to a set.
(WebCore::SVGToOTFFontConverter::addGlyphNames): For each glyph name, look up its representative glyph
and append it to a set.
(WebCore::SVGToOTFFontConverter::computeKerningData): Call SVG*KernElement::build*KerningPair() and
iterate through all the data it returned, building up records into a Vector.
(WebCore::SVGToOTFFontConverter::appendKERNTable): Go through the vector that computeKerningData()
returned and actually write out the two subtables. One is for horizontal kerning and the other is for
vertical kerning.
(WebCore::CFFBuilder::CFFBuilder): Use the position of the glyph origin to perform the initial moveto
command.
(WebCore::transcodeGlyphPaths): Get the glyph's origin and pass it to the CFFBuilder.
(WebCore::SVGToOTFFontConverter::appendGlyphData): Templated overloading so that the missing glyph
gets a null SVGGlyphElement pointer but the rest of the glyphs get a non-null one.
(WebCore::SVGToOTFFontConverter::processGlyphElement): Called on both SVGGlyphElements and
SVGMissingGlyphElements, this pulls out information from the element and computs its path information.
(WebCore::SVGToOTFFontConverter::SVGToOTFFontConverter): Use the new templated version of
processGlyphElement so we can get the same glyph parsing behavior on SVGMissingGlyphElements. In
addition, populate two maps for the kerning table creation function to use.
(WebCore::SVGToOTFFontConverter::convertSVGToOTFFont): Append new tables.
* svg/SVGVKernElement.cpp:
(WebCore::SVGVKernElement::buildVerticalKerningPair): Refactor to allow for intermediate data to be
returned, which the SVG -> OTF font converter can use.
* svg/SVGVKernElement.h: New signature for buildVerticalKerningPair().</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoresvgSVGFontElementcpp">trunk/Source/WebCore/svg/SVGFontElement.cpp</a></li>
<li><a href="#trunkSourceWebCoresvgSVGHKernElementcpp">trunk/Source/WebCore/svg/SVGHKernElement.cpp</a></li>
<li><a href="#trunkSourceWebCoresvgSVGHKernElementh">trunk/Source/WebCore/svg/SVGHKernElement.h</a></li>
<li><a href="#trunkSourceWebCoresvgSVGToOTFFontConversioncpp">trunk/Source/WebCore/svg/SVGToOTFFontConversion.cpp</a></li>
<li><a href="#trunkSourceWebCoresvgSVGVKernElementcpp">trunk/Source/WebCore/svg/SVGVKernElement.cpp</a></li>
<li><a href="#trunkSourceWebCoresvgSVGVKernElementh">trunk/Source/WebCore/svg/SVGVKernElement.h</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (173851 => 173852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2014-09-22 22:00:49 UTC (rev 173851)
+++ trunk/Source/WebCore/ChangeLog        2014-09-22 22:08:02 UTC (rev 173852)
</span><span class="lines">@@ -1,3 +1,78 @@
</span><ins>+2014-09-19 Myles C. Maxfield <mmaxfield@apple.com>
+
+ Implement 'vhea', 'vmtx', and 'kern' tables in SVG -> OTF converter
+ https://bugs.webkit.org/show_bug.cgi?id=136971
+
+ Reviewed by Darin Adler.
+
+ This patch fills in the tables related to vertical metrics and kerning. The vertical metrics tables are
+ fairly straightforward. On the other hand, the 'kern' table has subtables which can be in a variety
+ of formats; however, according to Microsoft, Windows only allows one particular format, which doesn't
+ allow for ranges of glyphs to be described and therefore might lead to a large 'kern' table size. In
+ the interest of platform agnosticism I have implemented this particular format, and have not actually
+ witnessed any extremely large 'kern' tables in any of the fonts that I have looked at.
+
+ I also added basic support for the horiz-origin-x and horiz-origin-y SVG properties, though support
+ might not be perfect based on my testing.
+
+ Regarding testing, after this patch, almost all of our SVG fonts tests pass (barring tiny rebaselining;
+ the platform font system calculates slightly different advances than we did. These differences are tiny
+ and expected from such a dramatic shift in text handling engines.) There are around two dozen failing
+ tests left that look like they are actually indiciative of code problems.
+
+ * svg/SVGFontElement.cpp:
+ (WebCore::SVGFontElement::ensureGlyphCache): Refactor to accept new signature of
+ SVG*KernElement::build*KerningPair().
+ * svg/SVGHKernElement.cpp:
+ (WebCore::SVGHKernElement::buildHorizontalKerningPair): Refactor to allow for intermediate data to be
+ returned, which the SVG -> OTF font converter can use.
+ * svg/SVGHKernElement.h: New signature for buildHorizontalKerningPair().
+ * svg/SVGToOTFFontConversion.cpp:
+ (WebCore::SVGToOTFFontConverter::GlyphData::GlyphData): Save the glyph's vertical advance along with
+ the glyph's horizontal advance.
+ (WebCore::SVGToOTFFontConverter::KerningData::KerningData): This data represents a record in one of
+ the two 'kern' subtables, and as such needs to be sorted.
+ (WebCore::SVGToOTFFontConverter::KerningData::operator<): Sort comparator.
+ (WebCore::SVGToOTFFontConverter::appendHEADTable): Save the unitsPerEm locally to save a conditional.
+ (WebCore::SVGToOTFFontConverter::appendHHEATable): Ditto.
+ (WebCore::SVGToOTFFontConverter::appendHMTXTable): Ditto. Also use the horizontal advance as opposed to
+ the vertical advance.
+ (WebCore::SVGToOTFFontConverter::appendOS2Table): Save the unitsPerEm locally to save a conditional.
+ Also if the average advance isn't found in the font, use the missing glyph's advance. If there is no
+ missing glyph, bail and use the unitsPerEm.
+ (WebCore::SVGToOTFFontConverter::appendVORGTable): If the default vertical origin isn't found in the
+ font, use the missing glyphs's vertical origin. If there is no missing glyph, bail and use 0.
+ (WebCore::SVGToOTFFontConverter::appendVHEATable): Parallel of 'hhea' table, except for vertical info.
+ (WebCore::SVGToOTFFontConverter::appendVMTXTable): Parallel of 'hmtx' table, except for vertical info.
+ (WebCore::SVGToOTFFontConverter::appendKerningHelperFields): Computes and appends 4 fields that font
+ parsers need in order to do a binary search on the kern records.
+ (WebCore::SVGToOTFFontConverter::addCodepointRanges): For each codepoint in the ranges, look up its
+ representative glyph and append it to a set.
+ (WebCore::SVGToOTFFontConverter::addCodepoints): For each codepoint in the range, look up its
+ representative glyph and append it to a set.
+ (WebCore::SVGToOTFFontConverter::addGlyphNames): For each glyph name, look up its representative glyph
+ and append it to a set.
+ (WebCore::SVGToOTFFontConverter::computeKerningData): Call SVG*KernElement::build*KerningPair() and
+ iterate through all the data it returned, building up records into a Vector.
+ (WebCore::SVGToOTFFontConverter::appendKERNTable): Go through the vector that computeKerningData()
+ returned and actually write out the two subtables. One is for horizontal kerning and the other is for
+ vertical kerning.
+ (WebCore::CFFBuilder::CFFBuilder): Use the position of the glyph origin to perform the initial moveto
+ command.
+ (WebCore::transcodeGlyphPaths): Get the glyph's origin and pass it to the CFFBuilder.
+ (WebCore::SVGToOTFFontConverter::appendGlyphData): Templated overloading so that the missing glyph
+ gets a null SVGGlyphElement pointer but the rest of the glyphs get a non-null one.
+ (WebCore::SVGToOTFFontConverter::processGlyphElement): Called on both SVGGlyphElements and
+ SVGMissingGlyphElements, this pulls out information from the element and computs its path information.
+ (WebCore::SVGToOTFFontConverter::SVGToOTFFontConverter): Use the new templated version of
+ processGlyphElement so we can get the same glyph parsing behavior on SVGMissingGlyphElements. In
+ addition, populate two maps for the kerning table creation function to use.
+ (WebCore::SVGToOTFFontConverter::convertSVGToOTFFont): Append new tables.
+ * svg/SVGVKernElement.cpp:
+ (WebCore::SVGVKernElement::buildVerticalKerningPair): Refactor to allow for intermediate data to be
+ returned, which the SVG -> OTF font converter can use.
+ * svg/SVGVKernElement.h: New signature for buildVerticalKerningPair().
+
</ins><span class="cx"> 2014-09-22 Alexey Proskuryakov <ap@apple.com>
</span><span class="cx">
</span><span class="cx"> WebSocket crash when a connection is closed from server side
</span></span></pre></div>
<a id="trunkSourceWebCoresvgSVGFontElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/svg/SVGFontElement.cpp (173851 => 173852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/svg/SVGFontElement.cpp        2014-09-22 22:00:49 UTC (rev 173851)
+++ trunk/Source/WebCore/svg/SVGFontElement.cpp        2014-09-22 22:08:02 UTC (rev 173852)
</span><span class="lines">@@ -133,10 +133,14 @@
</span><span class="cx"> ligatures.append(unicode.string());
</span><span class="cx"> } else if (isSVGHKernElement(child)) {
</span><span class="cx"> SVGHKernElement& hkern = toSVGHKernElement(child);
</span><del>- hkern.buildHorizontalKerningPair(m_horizontalKerningMap);
</del><ins>+ SVGKerningPair kerningPair;
+ if (hkern.buildHorizontalKerningPair(kerningPair))
+ m_horizontalKerningMap.insert(kerningPair);
</ins><span class="cx"> } else if (isSVGVKernElement(child)) {
</span><span class="cx"> SVGVKernElement& vkern = toSVGVKernElement(child);
</span><del>- vkern.buildVerticalKerningPair(m_verticalKerningMap);
</del><ins>+ SVGKerningPair kerningPair;
+ if (vkern.buildVerticalKerningPair(kerningPair))
+ m_verticalKerningMap.insert(kerningPair);
</ins><span class="cx"> } else if (isSVGMissingGlyphElement(child) && !firstMissingGlyphElement)
</span><span class="cx"> firstMissingGlyphElement = &toSVGMissingGlyphElement(child);
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoresvgSVGHKernElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/svg/SVGHKernElement.cpp (173851 => 173852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/svg/SVGHKernElement.cpp        2014-09-22 22:00:49 UTC (rev 173851)
+++ trunk/Source/WebCore/svg/SVGHKernElement.cpp        2014-09-22 22:08:02 UTC (rev 173852)
</span><span class="lines">@@ -59,23 +59,24 @@
</span><span class="cx"> SVGElement::removedFrom(rootParent);
</span><span class="cx"> }
</span><span class="cx">
</span><del>-void SVGHKernElement::buildHorizontalKerningPair(SVGKerningMap& kerningMap)
</del><ins>+bool SVGHKernElement::buildHorizontalKerningPair(SVGKerningPair& kerningPair) const
</ins><span class="cx"> {
</span><span class="cx"> String u1 = fastGetAttribute(SVGNames::u1Attr);
</span><span class="cx"> String g1 = fastGetAttribute(SVGNames::g1Attr);
</span><span class="cx"> String u2 = fastGetAttribute(SVGNames::u2Attr);
</span><span class="cx"> String g2 = fastGetAttribute(SVGNames::g2Attr);
</span><span class="cx"> if ((u1.isEmpty() && g1.isEmpty()) || (u2.isEmpty() && g2.isEmpty()))
</span><del>- return;
</del><ins>+ return false;
</ins><span class="cx">
</span><del>- SVGKerningPair kerningPair;
</del><span class="cx"> if (parseGlyphName(g1, kerningPair.glyphName1)
</span><span class="cx"> && parseGlyphName(g2, kerningPair.glyphName2)
</span><span class="cx"> && parseKerningUnicodeString(u1, kerningPair.unicodeRange1, kerningPair.unicodeName1)
</span><span class="cx"> && parseKerningUnicodeString(u2, kerningPair.unicodeRange2, kerningPair.unicodeName2)) {
</span><del>- kerningPair.kerning = fastGetAttribute(SVGNames::kAttr).string().toFloat();
- kerningMap.insert(kerningPair);
</del><ins>+ bool ok = false;
+ kerningPair.kerning = fastGetAttribute(SVGNames::kAttr).string().toFloat(&ok);
+ return ok;
</ins><span class="cx"> }
</span><ins>+ return false;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoresvgSVGHKernElementh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/svg/SVGHKernElement.h (173851 => 173852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/svg/SVGHKernElement.h        2014-09-22 22:00:49 UTC (rev 173851)
+++ trunk/Source/WebCore/svg/SVGHKernElement.h        2014-09-22 22:08:02 UTC (rev 173852)
</span><span class="lines">@@ -31,7 +31,7 @@
</span><span class="cx"> public:
</span><span class="cx"> static PassRefPtr<SVGHKernElement> create(const QualifiedName&, Document&);
</span><span class="cx">
</span><del>- void buildHorizontalKerningPair(SVGKerningMap&);
</del><ins>+ bool buildHorizontalKerningPair(SVGKerningPair& kerningPair) const;
</ins><span class="cx">
</span><span class="cx"> private:
</span><span class="cx"> SVGHKernElement(const QualifiedName&, Document&);
</span></span></pre></div>
<a id="trunkSourceWebCoresvgSVGToOTFFontConversioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/svg/SVGToOTFFontConversion.cpp (173851 => 173852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/svg/SVGToOTFFontConversion.cpp        2014-09-22 22:00:49 UTC (rev 173851)
+++ trunk/Source/WebCore/svg/SVGToOTFFontConversion.cpp        2014-09-22 22:08:02 UTC (rev 173852)
</span><span class="lines">@@ -31,9 +31,12 @@
</span><span class="cx"> #include "SVGFontElement.h"
</span><span class="cx"> #include "SVGFontFaceElement.h"
</span><span class="cx"> #include "SVGGlyphElement.h"
</span><ins>+#include "SVGHKernElement.h"
+#include "SVGMissingGlyphElement.h"
</ins><span class="cx"> #include "SVGPathBuilder.h"
</span><span class="cx"> #include "SVGPathParser.h"
</span><span class="cx"> #include "SVGPathStringSource.h"
</span><ins>+#include "SVGVKernElement.h"
</ins><span class="cx">
</span><span class="cx"> namespace WebCore {
</span><span class="cx">
</span><span class="lines">@@ -67,31 +70,59 @@
</span><span class="cx">
</span><span class="cx"> private:
</span><span class="cx"> typedef uint16_t SID; // String ID
</span><ins>+ typedef UChar Codepoint; // FIXME: Only support BMP for now
</ins><span class="cx"> struct GlyphData {
</span><del>- GlyphData(Vector<char> charString, const SVGGlyphElement* glyphElement, float advance, FloatRect boundingBox, uint16_t codepoint)
</del><ins>+ GlyphData(Vector<char> charString, const SVGGlyphElement* glyphElement, float horizontalAdvance, float verticalAdvance, FloatRect boundingBox, Codepoint codepoint)
</ins><span class="cx"> : boundingBox(boundingBox)
</span><span class="cx"> , charString(charString)
</span><span class="cx"> , glyphElement(glyphElement)
</span><del>- , advance(advance)
</del><ins>+ , horizontalAdvance(horizontalAdvance)
+ , verticalAdvance(verticalAdvance)
</ins><span class="cx"> , codepoint(codepoint)
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> FloatRect boundingBox;
</span><span class="cx"> Vector<char> charString;
</span><span class="cx"> const SVGGlyphElement* glyphElement;
</span><del>- float advance;
- uint16_t codepoint;
</del><ins>+ float horizontalAdvance;
+ float verticalAdvance;
+ Codepoint codepoint;
</ins><span class="cx"> };
</span><span class="cx">
</span><ins>+ struct KerningData {
+ KerningData(uint16_t glyph1, uint16_t glyph2, int16_t adjustment)
+ : glyph1(glyph1)
+ , glyph2(glyph2)
+ , adjustment(adjustment)
+ {
+ }
+ bool operator<(const KerningData& other) const
+ {
+ return glyph1 < other.glyph1 || (glyph1 == other.glyph1 && glyph2 < other.glyph2);
+ }
+ uint16_t glyph1;
+ uint16_t glyph2;
+ int16_t adjustment;
+ };
+
</ins><span class="cx"> static const size_t kSNFTHeaderSize = 12;
</span><span class="cx"> static const size_t kDirectoryEntrySize = 16;
</span><span class="cx">
</span><ins>+
+ template <typename T>
+ void appendGlyphData(const Vector<char>& path, const T* element, float horizontalAdvance, float verticalAdvance, const FloatRect& boundingBox, Codepoint codepoint);
+ template <typename T>
+ void processGlyphElement(const T& element, float defaultHorizontalAdvance, float defaultVerticalAdvance, Codepoint, bool& initialGlyph);
+
</ins><span class="cx"> typedef void (SVGToOTFFontConverter::*FontAppendingFunction)(Vector<char> &) const;
</span><span class="cx"> void appendTable(const char identifier[4], Vector<char>&, FontAppendingFunction);
</span><span class="cx"> void appendCMAPTable(Vector<char>&) const;
</span><span class="cx"> void appendHEADTable(Vector<char>&) const;
</span><span class="cx"> void appendHHEATable(Vector<char>&) const;
</span><span class="cx"> void appendHMTXTable(Vector<char>&) const;
</span><ins>+ void appendVHEATable(Vector<char>&) const;
+ void appendVMTXTable(Vector<char>&) const;
+ void appendKERNTable(Vector<char>&) const;
</ins><span class="cx"> void appendMAXPTable(Vector<char>&) const;
</span><span class="cx"> void appendNAMETable(Vector<char>&) const;
</span><span class="cx"> void appendOS2Table(Vector<char>&) const;
</span><span class="lines">@@ -99,13 +130,26 @@
</span><span class="cx"> void appendCFFTable(Vector<char>&) const;
</span><span class="cx"> void appendVORGTable(Vector<char>&) const;
</span><span class="cx">
</span><ins>+ void addCodepointRanges(const UnicodeRanges&, HashSet<uint16_t>& glyphSet) const;
+ void addCodepoints(const HashSet<String>& codepoints, HashSet<uint16_t>& glyphSet) const;
+ void addGlyphNames(const HashSet<String>& glyphNames, HashSet<uint16_t>& glyphSet) const;
+ template <typename T>
+ Vector<KerningData> computeKerningData(bool (T::*buildKerningPair)(SVGKerningPair&) const) const;
+ template <typename T>
+ size_t appendKERNSubtable(Vector<char>& result, bool (T::*buildKerningPair)(SVGKerningPair&) const, uint16_t coverage) const;
+
</ins><span class="cx"> Vector<GlyphData> m_glyphs;
</span><ins>+ HashMap<String, Glyph> m_glyphNameToIndexMap; // SVG 1.1: "It is recommended that glyph names be unique within a font."
+ HashMap<Codepoint, Glyph> m_codepointToIndexMap; // FIXME: There might be many glyphs that map to a single codepoint.
</ins><span class="cx"> FloatRect m_boundingBox;
</span><span class="cx"> const SVGFontElement& m_fontElement;
</span><span class="cx"> const SVGFontFaceElement* m_fontFaceElement;
</span><ins>+ const SVGMissingGlyphElement* m_missingGlyphElement;
</ins><span class="cx"> String m_fontFamily;
</span><span class="cx"> float m_advanceWidthMax;
</span><ins>+ float m_advanceHeightMax;
</ins><span class="cx"> float m_minRightSideBearing;
</span><ins>+ unsigned m_unitsPerEm;
</ins><span class="cx"> int m_tablesAppendedCount;
</span><span class="cx"> char m_weight;
</span><span class="cx"> bool m_italic;
</span><span class="lines">@@ -120,6 +164,13 @@
</span><span class="cx"> return (x >> 1) + 1;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+static uint16_t integralLog2(uint16_t x) {
+ uint16_t result = 0;
+ while (x >>= 1)
+ ++result;
+ return result;
+}
+
</ins><span class="cx"> void SVGToOTFFontConverter::appendCMAPTable(Vector<char>& result) const
</span><span class="cx"> {
</span><span class="cx"> auto startingOffset = result.size();
</span><span class="lines">@@ -136,21 +187,18 @@
</span><span class="cx"> write16(result, 4); // Format: Only support the Basic Multilingual Plane for now
</span><span class="cx"> write16(result, 22 + m_glyphs.size() * 2); // length
</span><span class="cx"> write16(result, 0); // Language independent
</span><del>- write16(result, 2 * segCount);
</del><ins>+ write16(result, 2 * segCount); // segCountX2: "2 x segCount"
</ins><span class="cx"> uint16_t originalSearchRange = roundDownToPowerOfTwo(segCount);
</span><del>- uint16_t searchRange = 2 * originalSearchRange;
</del><ins>+ uint16_t searchRange = 2 * originalSearchRange; // searchRange: "2 x (2**floor(log2(segCount)))"
</ins><span class="cx"> write16(result, searchRange);
</span><del>- uint16_t entrySelector = 0;
- while (originalSearchRange >>= 1)
- ++entrySelector;
- write16(result, entrySelector);
- write16(result, (2 * segCount) - searchRange);
</del><ins>+ write16(result, integralLog2(originalSearchRange)); // entrySelector: "log2(searchRange/2)"
+ write16(result, (2 * segCount) - searchRange); // rangeShift: "2 x segCount - searchRange"
</ins><span class="cx">
</span><del>- for (const auto& glyph : m_glyphs)
</del><ins>+ for (auto& glyph : m_glyphs)
</ins><span class="cx"> write16(result, glyph.codepoint); // EndCode
</span><span class="cx"> write16(result, 0xFFFF); // "To ensure that the search will terminate, the final endCode value must be 0xFFFF."
</span><span class="cx"> write16(result, 0); // Reserved
</span><del>- for (const auto& glyph : m_glyphs)
</del><ins>+ for (auto& glyph : m_glyphs)
</ins><span class="cx"> write16(result, glyph.codepoint); // startCode
</span><span class="cx"> write16(result, 0xFFFF);
</span><span class="cx"> for (unsigned i = 0; i < m_glyphs.size(); ++i) {
</span><span class="lines">@@ -175,9 +223,7 @@
</span><span class="cx"> result.append(-0x0B); // Wraparound
</span><span class="cx"> write16(result, (1 << 9) | 1);
</span><span class="cx">
</span><del>- uint16_t unitsPerEm = m_fontFaceElement ? m_fontFaceElement->unitsPerEm() : 0;
-
- write16(result, unitsPerEm);
</del><ins>+ write16(result, m_unitsPerEm);
</ins><span class="cx"> write32(result, 0); // First half of creation date
</span><span class="cx"> write32(result, 0); // Last half of creation date
</span><span class="cx"> write32(result, 0); // First half of modification date
</span><span class="lines">@@ -204,11 +250,9 @@
</span><span class="cx">
</span><span class="cx"> void SVGToOTFFontConverter::appendHHEATable(Vector<char>& result) const
</span><span class="cx"> {
</span><del>- uint16_t unitsPerEm = 0;
</del><span class="cx"> int16_t ascent = std::numeric_limits<int16_t>::max();
</span><span class="cx"> int16_t descent = std::numeric_limits<int16_t>::max();
</span><span class="cx"> if (m_fontFaceElement) {
</span><del>- unitsPerEm = m_fontFaceElement->unitsPerEm();
</del><span class="cx"> ascent = m_fontFaceElement->ascent();
</span><span class="cx"> descent = m_fontFaceElement->descent();
</span><span class="cx"> }
</span><span class="lines">@@ -227,11 +271,11 @@
</span><span class="cx"> write16(result, descent);
</span><span class="cx"> // WebKit's SVG codepath hardcodes the line gap to be 1/10th of the font size (see r29719). Matching that
</span><span class="cx"> // allows us to have consistent renderings between the two paths.
</span><del>- write16(result, unitsPerEm / 10); // Line gap
- write16(result, clampTo<uint16_t, float>(m_advanceWidthMax));
- write16(result, clampTo<int16_t, float>(m_boundingBox.x())); // Minimum left side bearing
- write16(result, clampTo<int16_t, float>(m_minRightSideBearing)); // Minimum right side bearing
- write16(result, clampTo<int16_t, float>(m_boundingBox.maxX())); // X maximum extent
</del><ins>+ write16(result, m_unitsPerEm / 10); // Line gap
+ write16(result, clampTo<uint16_t>(m_advanceWidthMax));
+ write16(result, clampTo<int16_t>(m_boundingBox.x())); // Minimum left side bearing
+ write16(result, clampTo<int16_t>(m_minRightSideBearing)); // Minimum right side bearing
+ write16(result, clampTo<int16_t>(m_boundingBox.maxX())); // X maximum extent
</ins><span class="cx"> // WebKit draws the caret
</span><span class="cx"> write16(result, 1); // Vertical caret
</span><span class="cx"> write16(result, 0); // Vertical caret
</span><span class="lines">@@ -244,9 +288,9 @@
</span><span class="cx">
</span><span class="cx"> void SVGToOTFFontConverter::appendHMTXTable(Vector<char>& result) const
</span><span class="cx"> {
</span><del>- for (const auto& glyph : m_glyphs) {
- write16(result, clampTo<uint16_t, float>(glyph.advance));
- write16(result, clampTo<int16_t, float>(glyph.boundingBox.x()));
</del><ins>+ for (auto& glyph : m_glyphs) {
+ write16(result, clampTo<uint16_t>(glyph.horizontalAdvance));
+ write16(result, clampTo<int16_t>(glyph.boundingBox.x()));
</ins><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -288,14 +332,17 @@
</span><span class="cx">
</span><span class="cx"> void SVGToOTFFontConverter::appendOS2Table(Vector<char>& result) const
</span><span class="cx"> {
</span><del>- // FIXME: We can look at the missing glyph info for this
- uint16_t unitsPerEm = m_fontFaceElement ? m_fontFaceElement->unitsPerEm() : 0;
- int16_t averageAdvance = unitsPerEm / 2;
</del><ins>+ int16_t averageAdvance = m_unitsPerEm;
</ins><span class="cx"> auto& attribute = m_fontElement.fastGetAttribute(SVGNames::horiz_adv_xAttr);
</span><del>- bool ok = true;
</del><ins>+ bool ok;
</ins><span class="cx"> int value = attribute.toInt(&ok);
</span><span class="cx"> if (ok)
</span><del>- averageAdvance = clampTo<int16_t, int>(value);
</del><ins>+ averageAdvance = clampTo<int16_t>(value);
+ else if (m_missingGlyphElement) {
+ int value = m_missingGlyphElement->fastGetAttribute(SVGNames::horiz_adv_xAttr).toInt(&ok);
+ if (ok)
+ averageAdvance = clampTo<int16_t>(value);
+ }
</ins><span class="cx">
</span><span class="cx"> write16(result, 0); // Version
</span><span class="cx"> write16(result, averageAdvance);
</span><span class="lines">@@ -317,11 +364,11 @@
</span><span class="cx">
</span><span class="cx"> Vector<unsigned char> specifiedPanose;
</span><span class="cx"> if (m_fontFaceElement) {
</span><del>- const auto& attribute = m_fontFaceElement->fastGetAttribute(SVGNames::panose_1Attr);
</del><ins>+ auto& attribute = m_fontFaceElement->fastGetAttribute(SVGNames::panose_1Attr);
</ins><span class="cx"> Vector<String> split;
</span><span class="cx"> String(attribute).split(" ", split);
</span><span class="cx"> if (split.size() == 10) {
</span><del>- for (const auto& s : split) {
</del><ins>+ for (auto& s : split) {
</ins><span class="cx"> bool ok = true;
</span><span class="cx"> int value = s.toInt(&ok);
</span><span class="cx"> if (!ok || value < 0 || value > 0xFF) {
</span><span class="lines">@@ -440,13 +487,13 @@
</span><span class="cx"> result.append(weightKey);
</span><span class="cx"> }
</span><span class="cx"> result.append(operand32Bit);
</span><del>- write32(result, clampTo<int32_t, float>(m_boundingBox.x()));
</del><ins>+ write32(result, clampTo<int32_t>(m_boundingBox.x()));
</ins><span class="cx"> result.append(operand32Bit);
</span><del>- write32(result, clampTo<int32_t, float>(m_boundingBox.maxX()));
</del><ins>+ write32(result, clampTo<int32_t>(m_boundingBox.maxX()));
</ins><span class="cx"> result.append(operand32Bit);
</span><del>- write32(result, clampTo<int32_t, float>(m_boundingBox.y()));
</del><ins>+ write32(result, clampTo<int32_t>(m_boundingBox.y()));
</ins><span class="cx"> result.append(operand32Bit);
</span><del>- write32(result, clampTo<int32_t, float>(m_boundingBox.maxY()));
</del><ins>+ write32(result, clampTo<int32_t>(m_boundingBox.maxY()));
</ins><span class="cx"> result.append(fontBBoxKey);
</span><span class="cx"> result.append(operand32Bit);
</span><span class="cx"> unsigned charsetOffsetLocation = result.size();
</span><span class="lines">@@ -499,15 +546,10 @@
</span><span class="cx"> write16(result, 1); // Major version
</span><span class="cx"> write16(result, 0); // Minor version
</span><span class="cx">
</span><del>- // FIXME: We can use the missing glyph info for this
- int16_t defaultVerticalOriginY = 0;
- auto& attribute = m_fontElement.fastGetAttribute(SVGNames::vert_origin_yAttr);
- if (attribute != nullAtom && attribute.is8Bit()) {
- bool ok;
- int verticalOriginY = attribute.toInt(&ok);
- if (ok && verticalOriginY)
- defaultVerticalOriginY = verticalOriginY;
- }
</del><ins>+ bool ok;
+ int16_t defaultVerticalOriginY = clampTo<int16_t>(m_fontElement.fastGetAttribute(SVGNames::vert_origin_yAttr).toInt(&ok));
+ if (!ok && m_missingGlyphElement)
+ defaultVerticalOriginY = clampTo<int16_t>(m_missingGlyphElement->fastGetAttribute(SVGNames::vert_origin_yAttr).toInt());
</ins><span class="cx"> write16(result, defaultVerticalOriginY);
</span><span class="cx">
</span><span class="cx"> Vector<std::pair<uint16_t, int16_t>> origins;
</span><span class="lines">@@ -524,12 +566,164 @@
</span><span class="cx"> }
</span><span class="cx"> write16(result, origins.size());
</span><span class="cx">
</span><del>- for (const auto& p : origins) {
</del><ins>+ for (auto& p : origins) {
</ins><span class="cx"> write16(result, p.first);
</span><span class="cx"> write16(result, p.second);
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+void SVGToOTFFontConverter::appendVHEATable(Vector<char>& result) const
+{
+ write32(result, 0x00011000); // Version
+ write16(result, m_unitsPerEm / 2); // Vertical typographic ascender (vertical baseline to the right)
+ write16(result, clampTo<int16_t>(-static_cast<int>(m_unitsPerEm / 2))); // Vertical typographic descender
+ write16(result, m_unitsPerEm / 10); // Vertical typographic line gap
+ write16(result, clampTo<int16_t>(m_advanceHeightMax));
+ write16(result, clampTo<int16_t>(m_unitsPerEm - m_boundingBox.maxY())); // Minimum top side bearing
+ write16(result, clampTo<int16_t>(m_boundingBox.y())); // Minimum bottom side bearing
+ write16(result, clampTo<int16_t>(m_unitsPerEm - m_boundingBox.y())); // Y maximum extent
+ // WebKit draws the caret
+ write16(result, 1); // Vertical caret
+ write16(result, 0); // Vertical caret
+ write16(result, 0); // "Set value to 0 for non-slanted fonts"
+ write32(result, 0); // Reserved
+ write32(result, 0); // Reserved
+ write16(result, 0); // "Set to 0"
+ write16(result, m_glyphs.size()); // Number of advance heights in VMTX table
+}
+
+void SVGToOTFFontConverter::appendVMTXTable(Vector<char>& result) const
+{
+ for (auto& glyph : m_glyphs) {
+ write16(result, clampTo<uint16_t>(glyph.verticalAdvance));
+ write16(result, clampTo<int16_t>(m_unitsPerEm - glyph.boundingBox.maxY())); // top side bearing
+ }
+}
+
+void SVGToOTFFontConverter::addCodepointRanges(const UnicodeRanges& unicodeRanges, HashSet<Glyph>& glyphSet) const
+{
+ for (auto& unicodeRange : unicodeRanges) {
+ for (auto codepoint = unicodeRange.first; codepoint < unicodeRange.second; ++codepoint) {
+ if (!codepoint || codepoint >= std::numeric_limits<Codepoint>::max())
+ continue;
+ auto iterator = m_codepointToIndexMap.find(codepoint);
+ if (iterator != m_codepointToIndexMap.end())
+ glyphSet.add(iterator->value);
+ }
+ }
+}
+
+void SVGToOTFFontConverter::addCodepoints(const HashSet<String>& codepoints, HashSet<Glyph>& glyphSet) const
+{
+ for (auto& codepointString : codepoints) {
+ auto codepointStringLength = codepointString.length();
+ unsigned i = 0;
+ while (i < codepointStringLength) {
+ // FIXME: Canonicalization might be necessary
+ UChar32 codepoint;
+ if (codepointString.is8Bit())
+ codepoint = codepointString.characters8()[i++];
+ else
+ U16_NEXT(codepointString.characters16(), i, codepointStringLength, codepoint);
+
+ if (!codepoint || codepoint >= std::numeric_limits<Codepoint>::max())
+ continue;
+ auto indexIter = m_codepointToIndexMap.find(codepoint);
+ if (indexIter != m_codepointToIndexMap.end())
+ glyphSet.add(indexIter->value);
+ }
+ }
+}
+
+void SVGToOTFFontConverter::addGlyphNames(const HashSet<String>& glyphNames, HashSet<uint16_t>& glyphSet) const
+{
+ for (auto& glyphName : glyphNames) {
+ auto indexIter = m_glyphNameToIndexMap.find(glyphName);
+ if (indexIter != m_glyphNameToIndexMap.end())
+ glyphSet.add(indexIter->value);
+ }
+}
+
+template <typename T>
+auto SVGToOTFFontConverter::computeKerningData(bool (T::*buildKerningPair)(SVGKerningPair&) const) const -> Vector<KerningData>
+{
+ Vector<KerningData> result;
+
+ for (auto& kernElement : childrenOfType<T>(m_fontElement)) {
+ SVGKerningPair kerningPair;
+ if ((kernElement.*buildKerningPair)(kerningPair)) {
+ HashSet<Glyph> glyphSet1;
+ HashSet<Glyph> glyphSet2;
+
+ addCodepointRanges(kerningPair.unicodeRange1, glyphSet1);
+ addCodepointRanges(kerningPair.unicodeRange2, glyphSet2);
+ addGlyphNames(kerningPair.glyphName1, glyphSet1);
+ addGlyphNames(kerningPair.glyphName2, glyphSet2);
+ addCodepoints(kerningPair.unicodeName1, glyphSet1);
+ addCodepoints(kerningPair.unicodeName2, glyphSet1);
+
+ // FIXME: Use table format 2 so we don't have to append each of these one by one
+ for (auto& glyph1 : glyphSet1) {
+ for (auto& glyph2 : glyphSet2)
+ result.append(KerningData(glyph1, glyph2, clampTo<int16_t>(-kerningPair.kerning)));
+ }
+ }
+ }
+
+ return result;
+}
+
+template <typename T>
+size_t SVGToOTFFontConverter::appendKERNSubtable(Vector<char>& result, bool (T::*buildKerningPair)(SVGKerningPair&) const, uint16_t coverage) const
+{
+ Vector<KerningData> kerningData = computeKerningData<T>(buildKerningPair);
+ std::sort(kerningData.begin(), kerningData.end());
+ size_t sizeOfKerningDataTable = 14 + 6 * kerningData.size();
+ if (sizeOfKerningDataTable > std::numeric_limits<uint16_t>::max()) {
+ kerningData.clear();
+ sizeOfKerningDataTable = 14;
+ }
+
+ write16(result, 0); // Version of subtable
+ write16(result, sizeOfKerningDataTable); // Length of this subtable
+ write16(result, coverage); // Table coverage bitfield
+
+ uint16_t roundedNumKerningPairs = roundDownToPowerOfTwo(kerningData.size());
+
+ write16(result, kerningData.size());
+ write16(result, 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."
+ write16(result, integralLog2(roundedNumKerningPairs)); // entrySelector: "log2 of the largest power of two less than or equal to the value of nPairs."
+ write16(result, (kerningData.size() - roundedNumKerningPairs) * 6); // rangeShift: "The value of nPairs minus the largest power of two less than or equal to nPairs,
+ // and then multiplied by the size in bytes of an entry in the table."
+
+ for (auto& kerningData : kerningData) {
+ write16(result, kerningData.glyph1);
+ write16(result, kerningData.glyph2);
+ write16(result, kerningData.adjustment);
+ }
+
+ return sizeOfKerningDataTable;
+}
+
+void SVGToOTFFontConverter::appendKERNTable(Vector<char>& result) const
+{
+ write16(result, 0); // Version
+ write16(result, 2); // Number of subtables
+
+#if !ASSERT_DISABLED
+ auto subtablesOffset = result.size();
+#endif
+
+ size_t sizeOfHorizontalSubtable = appendKERNSubtable<SVGHKernElement>(result, &SVGHKernElement::buildHorizontalKerningPair, 1);
+ ASSERT_UNUSED(sizeOfHorizontalSubtable, subtablesOffset + sizeOfHorizontalSubtable == result.size());
+ size_t sizeOfVerticalSubtable = appendKERNSubtable<SVGVKernElement>(result, &SVGVKernElement::buildVerticalKerningPair, 0);
+ ASSERT_UNUSED(sizeOfVerticalSubtable, subtablesOffset + sizeOfHorizontalSubtable + sizeOfVerticalSubtable == result.size());
+
+ // Work around a bug in Apple's font parser by adding some padding bytes. <rdar://problem/18401901>
+ for (int i = 0; i < 6; ++i)
+ result.append(0);
+}
+
</ins><span class="cx"> static void writeCFFEncodedNumber(Vector<char>& vector, float number)
</span><span class="cx"> {
</span><span class="cx"> int raw = number * powf(2, 16);
</span><span class="lines">@@ -544,14 +738,13 @@
</span><span class="cx">
</span><span class="cx"> class CFFBuilder : public SVGPathBuilder {
</span><span class="cx"> public:
</span><del>- CFFBuilder(Vector<char>& cffData, float width)
</del><ins>+ CFFBuilder(Vector<char>& cffData, float width, FloatPoint origin)
</ins><span class="cx"> : m_cffData(cffData)
</span><span class="cx"> , m_firstPoint(true)
</span><span class="cx"> {
</span><del>- // FIXME: We probably want the initial moveto to use horiz-origin-x and horiz-origin-y, unless we're vertical
</del><span class="cx"> writeCFFEncodedNumber(m_cffData, width);
</span><del>- writeCFFEncodedNumber(m_cffData, 0);
- writeCFFEncodedNumber(m_cffData, 0);
</del><ins>+ writeCFFEncodedNumber(m_cffData, origin.x());
+ writeCFFEncodedNumber(m_cffData, origin.y());
</ins><span class="cx"> m_cffData.append(rMoveTo);
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -641,10 +834,11 @@
</span><span class="cx"> bool m_firstPoint;
</span><span class="cx"> };
</span><span class="cx">
</span><del>-static Vector<char> transcodeGlyphPaths(float width, const SVGGlyphElement& glyphElement, FloatRect& boundingBox)
</del><ins>+template <typename T>
+static Vector<char> transcodeGlyphPaths(float width, const T& glyphElement, FloatRect& boundingBox)
</ins><span class="cx"> {
</span><span class="cx"> Vector<char> result;
</span><del>- const auto& dAttribute = glyphElement.fastGetAttribute(SVGNames::dAttr);
</del><ins>+ auto& dAttribute = glyphElement.fastGetAttribute(SVGNames::dAttr);
</ins><span class="cx"> if (dAttribute.isEmpty()) {
</span><span class="cx"> writeCFFEncodedNumber(result, width);
</span><span class="cx"> writeCFFEncodedNumber(result, 0);
</span><span class="lines">@@ -654,13 +848,22 @@
</span><span class="cx"> return result;
</span><span class="cx"> }
</span><span class="cx">
</span><del>- CFFBuilder builder(result, width);
</del><ins>+ // FIXME: If we are vertical, use vert_origin_x and vert_origin_y
+ bool ok;
+ float horizontalOriginX = glyphElement.fastGetAttribute(SVGNames::horiz_origin_xAttr).toFloat(&ok);
+ if (!ok)
+ horizontalOriginX = 0;
+ float horizontalOriginY = glyphElement.fastGetAttribute(SVGNames::horiz_origin_yAttr).toFloat(&ok);
+ if (!ok)
+ horizontalOriginY = 0;
+
+ CFFBuilder builder(result, width, FloatPoint(horizontalOriginX, horizontalOriginY));
</ins><span class="cx"> SVGPathStringSource source(dAttribute);
</span><span class="cx"> SVGPathParser parser;
</span><span class="cx"> parser.setCurrentSource(&source);
</span><span class="cx"> parser.setCurrentConsumer(&builder);
</span><span class="cx">
</span><del>- bool ok = parser.parsePathDataFromSource(NormalizedParsing);
</del><ins>+ ok = parser.parsePathDataFromSource(NormalizedParsing);
</ins><span class="cx"> parser.cleanup();
</span><span class="cx">
</span><span class="cx"> if (!ok)
</span><span class="lines">@@ -672,69 +875,119 @@
</span><span class="cx"> return result;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+template <typename T>
+void SVGToOTFFontConverter::appendGlyphData(const Vector<char>& path, const T*, float horizontalAdvance, float verticalAdvance, const FloatRect& boundingBox, Codepoint codepoint)
+{
+ m_glyphs.append(GlyphData(path, nullptr, horizontalAdvance, verticalAdvance, boundingBox, codepoint));
+}
+
+template <>
+void SVGToOTFFontConverter::appendGlyphData(const Vector<char>& path, const SVGGlyphElement* element, float horizontalAdvance, float verticalAdvance, const FloatRect& boundingBox, Codepoint codepoint)
+{
+ m_glyphs.append(GlyphData(path, element, horizontalAdvance, verticalAdvance, boundingBox, codepoint));
+}
+
+template <typename T>
+void SVGToOTFFontConverter::processGlyphElement(const T& element, float defaultHorizontalAdvance, float defaultVerticalAdvance, Codepoint codepoint, bool& initialGlyph)
+{
+ bool ok;
+ float horizontalAdvance = element.fastGetAttribute(SVGNames::horiz_adv_xAttr).toFloat(&ok);
+ if (!ok)
+ horizontalAdvance = defaultHorizontalAdvance;
+ m_advanceWidthMax = std::max(m_advanceWidthMax, horizontalAdvance);
+ float verticalAdvance = element.fastGetAttribute(SVGNames::vert_adv_yAttr).toFloat(&ok);
+ if (!ok)
+ verticalAdvance = defaultVerticalAdvance;
+ m_advanceHeightMax = std::max(m_advanceHeightMax, verticalAdvance);
+
+ FloatRect glyphBoundingBox;
+ auto path = transcodeGlyphPaths(horizontalAdvance, element, glyphBoundingBox);
+ if (initialGlyph)
+ m_boundingBox = glyphBoundingBox;
+ else
+ m_boundingBox.unite(glyphBoundingBox);
+ m_minRightSideBearing = std::min(m_minRightSideBearing, horizontalAdvance - glyphBoundingBox.maxX());
+ initialGlyph = false;
+
+ appendGlyphData(path, &element, horizontalAdvance, verticalAdvance, glyphBoundingBox, codepoint);
+}
+
</ins><span class="cx"> SVGToOTFFontConverter::SVGToOTFFontConverter(const SVGFontElement& fontElement)
</span><span class="cx"> : m_fontElement(fontElement)
</span><span class="cx"> , m_fontFaceElement(childrenOfType<SVGFontFaceElement>(m_fontElement).first())
</span><ins>+ , m_missingGlyphElement(childrenOfType<SVGMissingGlyphElement>(m_fontElement).first())
</ins><span class="cx"> , m_advanceWidthMax(0)
</span><ins>+ , m_advanceHeightMax(0)
</ins><span class="cx"> , m_minRightSideBearing(std::numeric_limits<float>::max())
</span><ins>+ , m_unitsPerEm(0)
</ins><span class="cx"> , m_tablesAppendedCount(0)
</span><span class="cx"> , m_weight(5)
</span><span class="cx"> , m_italic(false)
</span><span class="cx"> {
</span><del>- bool ok = true;
- float defaultAdvance = fontElement.fastGetAttribute(SVGNames::horiz_adv_xAttr).toFloat(&ok);
- if (!ok)
- defaultAdvance = 0;
- m_advanceWidthMax = std::max(m_advanceWidthMax, defaultAdvance);
</del><ins>+ float defaultHorizontalAdvance = m_fontFaceElement ? m_fontFaceElement->horizontalAdvanceX() : 0;
+ float defaultVerticalAdvance = m_fontFaceElement ? m_fontFaceElement->verticalAdvanceY() : 0;
+ bool initialGlyph = true;
</ins><span class="cx">
</span><del>- // FIXME: Use the missingGlyph info
- Vector<char, 1> notdefCharString;
- notdefCharString.append(endChar);
- m_glyphs.append(GlyphData(notdefCharString, nullptr, m_fontFaceElement ? m_fontFaceElement->unitsPerEm() : 0, FloatRect(), 0));
- bool initialGlyph = true;
- for (auto& glyph : childrenOfType<SVGGlyphElement>(m_fontElement)) {
- auto& unicodeAttribute = glyph.fastGetAttribute(SVGNames::unicodeAttr);
</del><ins>+ if (m_fontFaceElement)
+ m_unitsPerEm = m_fontFaceElement->unitsPerEm();
+
+ if (m_missingGlyphElement)
+ processGlyphElement(*m_missingGlyphElement, defaultHorizontalAdvance, defaultVerticalAdvance, 0, initialGlyph);
+ else {
+ Vector<char> notdefCharString;
+ writeCFFEncodedNumber(notdefCharString, m_unitsPerEm);
+ writeCFFEncodedNumber(notdefCharString, 0);
+ writeCFFEncodedNumber(notdefCharString, 0);
+ notdefCharString.append(rMoveTo);
+ notdefCharString.append(endChar);
+ m_glyphs.append(GlyphData(notdefCharString, nullptr, m_unitsPerEm, m_unitsPerEm, FloatRect(), 0));
+ }
+
+ for (auto& glyphElement : childrenOfType<SVGGlyphElement>(m_fontElement)) {
+ auto& unicodeAttribute = glyphElement.fastGetAttribute(SVGNames::unicodeAttr);
</ins><span class="cx"> // Only support Basic Multilingual Plane w/o ligatures for now
</span><del>- if (unicodeAttribute.length() == 1) {
- bool ok = true;
- float effectiveAdvance = glyph.fastGetAttribute(SVGNames::horiz_adv_xAttr).toFloat(&ok);
- if (!ok)
- effectiveAdvance = defaultAdvance;
- m_advanceWidthMax = std::max(m_advanceWidthMax, effectiveAdvance);
</del><ins>+ if (unicodeAttribute.length() == 1)
+ processGlyphElement(glyphElement, defaultHorizontalAdvance, defaultVerticalAdvance, unicodeAttribute[0], initialGlyph);
+ }
</ins><span class="cx">
</span><del>- FloatRect glyphBoundingBox;
- const auto& path = transcodeGlyphPaths(effectiveAdvance, glyph, glyphBoundingBox);
- if (initialGlyph)
- m_boundingBox = glyphBoundingBox;
- else
- m_boundingBox.unite(glyphBoundingBox);
- m_minRightSideBearing = std::min(m_minRightSideBearing, effectiveAdvance - glyphBoundingBox.maxX());
- initialGlyph = false;
</del><ins>+ if (m_glyphs.size() > std::numeric_limits<Glyph>::max()) {
+ // This will break all sorts of things. Bail early instead.
+ m_glyphs.clear();
+ return;
+ }
</ins><span class="cx">
</span><del>- m_glyphs.append(GlyphData(path, &glyph, effectiveAdvance, glyphBoundingBox, unicodeAttribute[0]));
- }
- }
</del><span class="cx"> std::sort(m_glyphs.begin(), m_glyphs.end(), [](const GlyphData& data1, const GlyphData& data2) {
</span><span class="cx"> return data1.codepoint < data2.codepoint;
</span><span class="cx"> });
</span><span class="cx">
</span><ins>+ for (Glyph i = 0; i < m_glyphs.size(); ++i) {
+ GlyphData& glyph = m_glyphs[i];
+ if (glyph.glyphElement) {
+ auto& glyphName = glyph.glyphElement->fastGetAttribute(SVGNames::glyph_nameAttr);
+ if (!glyphName.isEmpty())
+ m_glyphNameToIndexMap.add(glyphName, i);
+ }
+ if (glyph.codepoint)
+ m_codepointToIndexMap.add(glyph.codepoint, i);
+ }
+
</ins><span class="cx"> // FIXME: Handle commas
</span><span class="cx"> if (m_fontFaceElement) {
</span><span class="cx"> auto& fontWeightAttribute = m_fontFaceElement->fastGetAttribute(SVGNames::font_weightAttr);
</span><span class="cx"> Vector<String> split;
</span><span class="cx"> fontWeightAttribute.string().split(" ", split);
</span><del>- for (const auto& segment : split) {
</del><ins>+ for (auto& segment : split) {
</ins><span class="cx"> if (segment == "bold")
</span><span class="cx"> m_weight = 7;
</span><del>- bool ok = true;
</del><ins>+ bool ok;
</ins><span class="cx"> int value = segment.toInt(&ok);
</span><span class="cx"> if (ok && value >= 0 && value < 1000)
</span><span class="cx"> m_weight = value / 100;
</span><span class="cx"> }
</span><del>- const auto& fontStyleAttribute = m_fontFaceElement->fastGetAttribute(SVGNames::font_weightAttr);
</del><ins>+ auto& fontStyleAttribute = m_fontFaceElement->fastGetAttribute(SVGNames::font_weightAttr);
</ins><span class="cx"> split.clear();
</span><span class="cx"> String(fontStyleAttribute).split(" ", split);
</span><del>- for (const auto& s : split) {
</del><ins>+ for (auto& s : split) {
</ins><span class="cx"> if (s == "italic" || s == "oblique")
</span><span class="cx"> m_italic = true;
</span><span class="cx"> }
</span><span class="lines">@@ -787,15 +1040,12 @@
</span><span class="cx"> Vector<char> SVGToOTFFontConverter::convertSVGToOTFFont()
</span><span class="cx"> {
</span><span class="cx"> Vector<char> result;
</span><del>- if (m_glyphs.size() > 0xFFFF || !m_glyphs.size())
</del><ins>+ if (!m_glyphs.size())
</ins><span class="cx"> return result;
</span><span class="cx">
</span><del>- uint16_t numTables = 10;
</del><ins>+ uint16_t numTables = 13;
</ins><span class="cx"> uint16_t roundedNumTables = roundDownToPowerOfTwo(numTables);
</span><del>- uint16_t searchRange = roundedNumTables * 16;
- uint16_t entrySelector = 0;
- while (roundedNumTables >>= 1)
- ++entrySelector;
</del><ins>+ uint16_t searchRange = roundedNumTables * 16; // searchRange: "(Maximum power of 2 <= numTables) x 16."
</ins><span class="cx">
</span><span class="cx"> result.append('O');
</span><span class="cx"> result.append('T');
</span><span class="lines">@@ -803,8 +1053,8 @@
</span><span class="cx"> result.append('O');
</span><span class="cx"> write16(result, numTables);
</span><span class="cx"> write16(result, searchRange);
</span><del>- write16(result, entrySelector);
- write16(result, numTables * 16 - searchRange);
</del><ins>+ write16(result, integralLog2(roundedNumTables)); // entrySelector: "Log2(maximum power of 2 <= numTables)."
+ write16(result, numTables * 16 - searchRange); // rangeShift: "NumTables x 16-searchRange."
</ins><span class="cx">
</span><span class="cx"> ASSERT(result.size() == kSNFTHeaderSize);
</span><span class="cx">
</span><span class="lines">@@ -821,9 +1071,12 @@
</span><span class="cx"> appendTable("head", result, &SVGToOTFFontConverter::appendHEADTable);
</span><span class="cx"> appendTable("hhea", result, &SVGToOTFFontConverter::appendHHEATable);
</span><span class="cx"> appendTable("hmtx", result, &SVGToOTFFontConverter::appendHMTXTable);
</span><ins>+ appendTable("kern", result, &SVGToOTFFontConverter::appendKERNTable);
</ins><span class="cx"> appendTable("maxp", result, &SVGToOTFFontConverter::appendMAXPTable);
</span><span class="cx"> appendTable("name", result, &SVGToOTFFontConverter::appendNAMETable);
</span><span class="cx"> appendTable("post", result, &SVGToOTFFontConverter::appendPOSTTable);
</span><ins>+ appendTable("vhea", result, &SVGToOTFFontConverter::appendVHEATable);
+ appendTable("vmtx", result, &SVGToOTFFontConverter::appendVMTXTable);
</ins><span class="cx">
</span><span class="cx"> ASSERT(numTables == m_tablesAppendedCount);
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCoresvgSVGVKernElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/svg/SVGVKernElement.cpp (173851 => 173852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/svg/SVGVKernElement.cpp        2014-09-22 22:00:49 UTC (rev 173851)
+++ trunk/Source/WebCore/svg/SVGVKernElement.cpp        2014-09-22 22:08:02 UTC (rev 173852)
</span><span class="lines">@@ -60,23 +60,24 @@
</span><span class="cx"> SVGElement::removedFrom(rootParent);
</span><span class="cx"> }
</span><span class="cx">
</span><del>-void SVGVKernElement::buildVerticalKerningPair(SVGKerningMap& kerningMap)
</del><ins>+bool SVGVKernElement::buildVerticalKerningPair(SVGKerningPair& kerningPair) const
</ins><span class="cx"> {
</span><span class="cx"> String u1 = fastGetAttribute(SVGNames::u1Attr);
</span><span class="cx"> String g1 = fastGetAttribute(SVGNames::g1Attr);
</span><span class="cx"> String u2 = fastGetAttribute(SVGNames::u2Attr);
</span><span class="cx"> String g2 = fastGetAttribute(SVGNames::g2Attr);
</span><span class="cx"> if ((u1.isEmpty() && g1.isEmpty()) || (u2.isEmpty() && g2.isEmpty()))
</span><del>- return;
</del><ins>+ return false;
</ins><span class="cx">
</span><del>- SVGKerningPair kerningPair;
</del><span class="cx"> if (parseGlyphName(g1, kerningPair.glyphName1)
</span><span class="cx"> && parseGlyphName(g2, kerningPair.glyphName2)
</span><span class="cx"> && parseKerningUnicodeString(u1, kerningPair.unicodeRange1, kerningPair.unicodeName1)
</span><span class="cx"> && parseKerningUnicodeString(u2, kerningPair.unicodeRange2, kerningPair.unicodeName2)) {
</span><del>- kerningPair.kerning = fastGetAttribute(SVGNames::kAttr).string().toFloat();
- kerningMap.insert(kerningPair);
</del><ins>+ bool ok = false;
+ kerningPair.kerning = fastGetAttribute(SVGNames::kAttr).string().toFloat(&ok);
+ return ok;
</ins><span class="cx"> }
</span><ins>+ return false;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoresvgSVGVKernElementh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/svg/SVGVKernElement.h (173851 => 173852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/svg/SVGVKernElement.h        2014-09-22 22:00:49 UTC (rev 173851)
+++ trunk/Source/WebCore/svg/SVGVKernElement.h        2014-09-22 22:08:02 UTC (rev 173852)
</span><span class="lines">@@ -30,7 +30,7 @@
</span><span class="cx"> public:
</span><span class="cx"> static PassRefPtr<SVGVKernElement> create(const QualifiedName&, Document&);
</span><span class="cx">
</span><del>- void buildVerticalKerningPair(SVGKerningMap&);
</del><ins>+ bool buildVerticalKerningPair(SVGKerningPair& kerningPair) const;
</ins><span class="cx">
</span><span class="cx"> private:
</span><span class="cx"> SVGVKernElement(const QualifiedName&, Document&);
</span></span></pre>
</div>
</div>
</body>
</html>