<!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>[173521] 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/173521">173521</a></dd>
<dt>Author</dt> <dd>mmaxfield@apple.com</dd>
<dt>Date</dt> <dd>2014-09-11 11:29:43 -0700 (Thu, 11 Sep 2014)</dd>
</dl>
<h3>Log Message</h3>
<pre>Initial implementation of SVG to OTF font converter
https://bugs.webkit.org/show_bug.cgi?id=136688
Patch by Myles C. Maxfield <mmaxfield@apple.com> <litherum@gmail.com> on 2014-09-11
Reviewed by Darin Adler.
This patch creates a new file and new function which turns an SVG font into an OpenType font.
This function is not very full featured, and as such is never called. However, it works with
fonts that only support simple horizontal basic multilingual plane codepoints. It has only
been tested on OS X, and does not have any automated tests. This patch is only jumping off
point, laying the basic groundwork for a more robust font converter.
The goal of this patch is to eventually allow us to delete the codepath with which we draw
text using SVG fonts. Using a native codepath instead will allow for better performance,
security, and code clarity. In my initial benchmarks, using this converter instead of the
SVG codepath is at least a 100x speed improvement (after running for 4 hours I stepped
away from my machine).
No new tests because the function is not ready to be called (yet!)
Initial implementation of SVG to OTF font converter
https://bugs.webkit.org/show_bug.cgi?id=136688
* WebCore.xcodeproj/project.pbxproj: Add new file.
* svg/SVGPathBuilder.h: Single function. Takes a SVGFontElement and returns a byte
* svg/SVGToOTFFontConversion.cpp: Added.
(WebCore::write32): Write a big-endian Int32.
(WebCore::write16): Write a big-endian Int16.
(WebCore::overwrite32): Overwrite an existing Int32 in a vector (instead of appending)
(WebCore::SVGToOTFFontConverter::GlyphData::GlyphData): Internal class to hold state during
function calls.
(WebCore::roundDownToPowerOfTwo):
(WebCore::SVGToOTFFontConverter::appendCMAPTable): See function name.
(WebCore::SVGToOTFFontConverter::appendHEADTable): Ditto.
(WebCore::clampTo): Used to clamp data types to fit into other data types.
(WebCore::SVGToOTFFontConverter::appendHHEATable): See function name.
(WebCore::SVGToOTFFontConverter::appendHMTXTable): Ditto.
(WebCore::SVGToOTFFontConverter::appendMAXPTable): Ditto.
(WebCore::SVGToOTFFontConverter::appendNAMETable): Ditto.
(WebCore::SVGToOTFFontConverter::appendOS2Table): Ditto.
(WebCore::SVGToOTFFontConverter::appendPOSTTable): Ditto.
(WebCore::isValidStringForCFF): The CFF font format only allows strings with particular
characters in them.
(WebCore::appendCFFValidString): Append a string assuming it's valid to append.
(WebCore::SVGToOTFFontConverter::appendCFFTable): See function name.
(WebCore::SVGToOTFFontConverter::appendVORGTable): Ditto.
(WebCore::writeCFFEncodedNumber): CFF has this wacky encoding scheme for encoding and
decoding numbers.
(WebCore::CFFBuilder::CFFBuilder): Subclass of SVGPathBuilder, which is used for building
CFF CharStrings (which are glyph path representations).
(WebCore::CFFBuilder::updateForConstituentPoint): Invoked for every point in a path, does
things like updates glyph bounding box information.
(WebCore::CFFBuilder::boundingBox): Getter.
(WebCore::transcodeGlyphPaths): SVG path data string -> CFF CharString
(WebCore::SVGToOTFFontConverter::SVGToOTFFontConverter): Generate internal state ahead of
time before generating any tables.
(WebCore::isFourByteAligned):
(WebCore::calculateChecksum): OTF files have checksums.
(WebCore::SVGToOTFFontConverter::appendTable): Invoked for each entry in the table of
contents of the OTF font.
(WebCore::SVGToOTFFontConverter::convertSVGToOTFFont): Write out the tables.
(WebCore::convertSVGToOTFFont): Entry point.
* svg/SVGToOTFFontConversion.h: Added.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreWebCorexcodeprojprojectpbxproj">trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceWebCoresvgSVGPathBuilderh">trunk/Source/WebCore/svg/SVGPathBuilder.h</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoresvgSVGToOTFFontConversioncpp">trunk/Source/WebCore/svg/SVGToOTFFontConversion.cpp</a></li>
<li><a href="#trunkSourceWebCoresvgSVGToOTFFontConversionh">trunk/Source/WebCore/svg/SVGToOTFFontConversion.h</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (173520 => 173521)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2014-09-11 18:24:17 UTC (rev 173520)
+++ trunk/Source/WebCore/ChangeLog        2014-09-11 18:29:43 UTC (rev 173521)
</span><span class="lines">@@ -1,3 +1,67 @@
</span><ins>+2014-09-11 Myles C. Maxfield <mmaxfield@apple.com> <litherum@gmail.com>
+
+ Initial implementation of SVG to OTF font converter
+ https://bugs.webkit.org/show_bug.cgi?id=136688
+
+ Reviewed by Darin Adler.
+
+ This patch creates a new file and new function which turns an SVG font into an OpenType font.
+ This function is not very full featured, and as such is never called. However, it works with
+ fonts that only support simple horizontal basic multilingual plane codepoints. It has only
+ been tested on OS X, and does not have any automated tests. This patch is only jumping off
+ point, laying the basic groundwork for a more robust font converter.
+
+ The goal of this patch is to eventually allow us to delete the codepath with which we draw
+ text using SVG fonts. Using a native codepath instead will allow for better performance,
+ security, and code clarity. In my initial benchmarks, using this converter instead of the
+ SVG codepath is at least a 100x speed improvement (after running for 4 hours I stepped
+ away from my machine).
+
+ No new tests because the function is not ready to be called (yet!)
+ Initial implementation of SVG to OTF font converter
+ https://bugs.webkit.org/show_bug.cgi?id=136688
+
+ * WebCore.xcodeproj/project.pbxproj: Add new file.
+ * svg/SVGPathBuilder.h: Single function. Takes a SVGFontElement and returns a byte
+ * svg/SVGToOTFFontConversion.cpp: Added.
+ (WebCore::write32): Write a big-endian Int32.
+ (WebCore::write16): Write a big-endian Int16.
+ (WebCore::overwrite32): Overwrite an existing Int32 in a vector (instead of appending)
+ (WebCore::SVGToOTFFontConverter::GlyphData::GlyphData): Internal class to hold state during
+ function calls.
+ (WebCore::roundDownToPowerOfTwo):
+ (WebCore::SVGToOTFFontConverter::appendCMAPTable): See function name.
+ (WebCore::SVGToOTFFontConverter::appendHEADTable): Ditto.
+ (WebCore::clampTo): Used to clamp data types to fit into other data types.
+ (WebCore::SVGToOTFFontConverter::appendHHEATable): See function name.
+ (WebCore::SVGToOTFFontConverter::appendHMTXTable): Ditto.
+ (WebCore::SVGToOTFFontConverter::appendMAXPTable): Ditto.
+ (WebCore::SVGToOTFFontConverter::appendNAMETable): Ditto.
+ (WebCore::SVGToOTFFontConverter::appendOS2Table): Ditto.
+ (WebCore::SVGToOTFFontConverter::appendPOSTTable): Ditto.
+ (WebCore::isValidStringForCFF): The CFF font format only allows strings with particular
+ characters in them.
+ (WebCore::appendCFFValidString): Append a string assuming it's valid to append.
+ (WebCore::SVGToOTFFontConverter::appendCFFTable): See function name.
+ (WebCore::SVGToOTFFontConverter::appendVORGTable): Ditto.
+ (WebCore::writeCFFEncodedNumber): CFF has this wacky encoding scheme for encoding and
+ decoding numbers.
+ (WebCore::CFFBuilder::CFFBuilder): Subclass of SVGPathBuilder, which is used for building
+ CFF CharStrings (which are glyph path representations).
+ (WebCore::CFFBuilder::updateForConstituentPoint): Invoked for every point in a path, does
+ things like updates glyph bounding box information.
+ (WebCore::CFFBuilder::boundingBox): Getter.
+ (WebCore::transcodeGlyphPaths): SVG path data string -> CFF CharString
+ (WebCore::SVGToOTFFontConverter::SVGToOTFFontConverter): Generate internal state ahead of
+ time before generating any tables.
+ (WebCore::isFourByteAligned):
+ (WebCore::calculateChecksum): OTF files have checksums.
+ (WebCore::SVGToOTFFontConverter::appendTable): Invoked for each entry in the table of
+ contents of the OTF font.
+ (WebCore::SVGToOTFFontConverter::convertSVGToOTFFont): Write out the tables.
+ (WebCore::convertSVGToOTFFont): Entry point.
+ * svg/SVGToOTFFontConversion.h: Added.
+
</ins><span class="cx"> 2014-09-11 Jer Noble <jer.noble@apple.com>
</span><span class="cx">
</span><span class="cx"> [EME] REGRESSION(??): test media/encrypted-media/encrypted-media-v2-syntax.html is failing
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (173520 => 173521)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2014-09-11 18:24:17 UTC (rev 173520)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2014-09-11 18:29:43 UTC (rev 173521)
</span><span class="lines">@@ -952,6 +952,8 @@
</span><span class="cx">                 1CB4214C0AF2B2CA0085AD91 /* DOMHTMLElementInternal.h in Copy Generated Headers */ = {isa = PBXBuildFile; fileRef = 85E711580AC5D5340053270F /* DOMHTMLElementInternal.h */; };
</span><span class="cx">                 1CB4214D0AF2B2CA0085AD91 /* DOMRangeInternal.h in Copy Generated Headers */ = {isa = PBXBuildFile; fileRef = 8538F05A0AD722F1006A81D1 /* DOMRangeInternal.h */; };
</span><span class="cx">                 1CCA732210ADD44A00FD440D /* DOMHTMLInputElementPrivate.h in Copy Generated Headers */ = {isa = PBXBuildFile; fileRef = 1CCA732110ADD43E00FD440D /* DOMHTMLInputElementPrivate.h */; };
</span><ins>+                1CCDF5BD1990332400BCEBAD /* SVGToOTFFontConversion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CCDF5BB1990332400BCEBAD /* SVGToOTFFontConversion.cpp */; };
+                1CCDF5BE1990332400BCEBAD /* SVGToOTFFontConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CCDF5BC1990332400BCEBAD /* SVGToOTFFontConversion.h */; };
</ins><span class="cx">                 1CE83AC30ADAFFD7009354F6 /* DeleteButtonController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C4C8EFF0AD85D87009475CE /* DeleteButtonController.cpp */; };
</span><span class="cx">                 1CE83AC40ADAFFD8009354F6 /* DeleteButton.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C4C8F630AD8655D009475CE /* DeleteButton.cpp */; };
</span><span class="cx">                 1CF6BDFA0E9BB26A0025E1CD /* ObjCEventListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CF6BDF80E9BB26A0025E1CD /* ObjCEventListener.h */; };
</span><span class="lines">@@ -7895,6 +7897,8 @@
</span><span class="cx">                 1CAF347F0A6C405200ABE06E /* WebScriptObject.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebScriptObject.mm; sourceTree = "<group>"; };
</span><span class="cx">                 1CAF34800A6C405200ABE06E /* WebScriptObjectPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebScriptObjectPrivate.h; sourceTree = "<group>"; };
</span><span class="cx">                 1CCA732110ADD43E00FD440D /* DOMHTMLInputElementPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMHTMLInputElementPrivate.h; sourceTree = "<group>"; };
</span><ins>+                1CCDF5BB1990332400BCEBAD /* SVGToOTFFontConversion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SVGToOTFFontConversion.cpp; sourceTree = "<group>"; };
+                1CCDF5BC1990332400BCEBAD /* SVGToOTFFontConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVGToOTFFontConversion.h; sourceTree = "<group>"; };
</ins><span class="cx">                 1CD0B6200AABDB5000D0A3FF /* PublicDOMInterfaces.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PublicDOMInterfaces.h; sourceTree = "<group>"; };
</span><span class="cx">                 1CDD45E40BA9C84600F90147 /* DebugRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = DebugRelease.xcconfig; sourceTree = "<group>"; };
</span><span class="cx">                 1CDD45E50BA9C84600F90147 /* WebCore.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = WebCore.xcconfig; sourceTree = "<group>"; };
</span><span class="lines">@@ -20167,6 +20171,8 @@
</span><span class="cx">                                 B2E4EC950D00C22B00432643 /* SVGZoomEvent.h */,
</span><span class="cx">                                 B2E4EC960D00C22B00432643 /* SVGZoomEvent.idl */,
</span><span class="cx">                                 B28C6A260D00C44800334AA4 /* xlinkattrs.in */,
</span><ins>+                                1CCDF5BB1990332400BCEBAD /* SVGToOTFFontConversion.cpp */,
+                                1CCDF5BC1990332400BCEBAD /* SVGToOTFFontConversion.h */,
</ins><span class="cx">                         );
</span><span class="cx">                         path = svg;
</span><span class="cx">                         sourceTree = "<group>";
</span><span class="lines">@@ -24969,6 +24975,7 @@
</span><span class="cx">                                 B2FA3D970AB75A6F000E5AC4 /* JSSVGFilterElement.h in Headers */,
</span><span class="cx">                                 B27B28260CEF0C0700D39D54 /* JSSVGFontElement.h in Headers */,
</span><span class="cx">                                 A83B79050CCAFF15000B0825 /* JSSVGFontFaceElement.h in Headers */,
</span><ins>+                                1CCDF5BE1990332400BCEBAD /* SVGToOTFFontConversion.h in Headers */,
</ins><span class="cx">                                 A83B79000CCAFF15000B0825 /* JSSVGFontFaceFormatElement.h in Headers */,
</span><span class="cx">                                 A83B79020CCAFF15000B0825 /* JSSVGFontFaceNameElement.h in Headers */,
</span><span class="cx">                                 A83B78FE0CCAFF15000B0825 /* JSSVGFontFaceSrcElement.h in Headers */,
</span><span class="lines">@@ -26973,6 +26980,7 @@
</span><span class="cx">                                 FD5686C913AC180200B69C68 /* AsyncAudioDecoder.cpp in Sources */,
</span><span class="cx">                                 E1CDE92015018ED000862CC5 /* AsyncFileStream.cpp in Sources */,
</span><span class="cx">                                 0FFD4D6018651FA300512F6E /* AsyncScrollingCoordinator.cpp in Sources */,
</span><ins>+                                1CCDF5BD1990332400BCEBAD /* SVGToOTFFontConversion.cpp in Sources */,
</ins><span class="cx">                                 A8C4A80E09D563270003AC8D /* Attr.cpp in Sources */,
</span><span class="cx">                                 FD629EA4154B47160006D026 /* AudioBasicInspectorNode.cpp in Sources */,
</span><span class="cx">                                 FD315FF612B0267600C1A359 /* AudioBasicProcessorNode.cpp in Sources */,
</span></span></pre></div>
<a id="trunkSourceWebCoresvgSVGPathBuilderh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/svg/SVGPathBuilder.h (173520 => 173521)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/svg/SVGPathBuilder.h        2014-09-11 18:24:17 UTC (rev 173520)
+++ trunk/Source/WebCore/svg/SVGPathBuilder.h        2014-09-11 18:29:43 UTC (rev 173521)
</span><span class="lines">@@ -37,6 +37,9 @@
</span><span class="cx">
</span><span class="cx"> void setCurrentPath(Path* path) { m_path = path; }
</span><span class="cx">
</span><ins>+protected:
+ FloatPoint m_current;
+
</ins><span class="cx"> private:
</span><span class="cx"> virtual void incrementPathSegmentCount() override { }
</span><span class="cx"> virtual bool continueConsuming() override { return true; }
</span><span class="lines">@@ -57,7 +60,6 @@
</span><span class="cx"> virtual void arcTo(float, float, float, bool, bool, const FloatPoint&, PathCoordinateMode) override { ASSERT_NOT_REACHED(); }
</span><span class="cx">
</span><span class="cx"> Path* m_path;
</span><del>- FloatPoint m_current;
</del><span class="cx"> };
</span><span class="cx">
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoresvgSVGToOTFFontConversioncpp"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/svg/SVGToOTFFontConversion.cpp (0 => 173521)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/svg/SVGToOTFFontConversion.cpp         (rev 0)
+++ trunk/Source/WebCore/svg/SVGToOTFFontConversion.cpp        2014-09-11 18:29:43 UTC (rev 173521)
</span><span class="lines">@@ -0,0 +1,831 @@
</span><ins>+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SVGToOTFFontConversion.h"
+
+#include "CSSStyleDeclaration.h"
+#include "ElementChildIterator.h"
+#include "SVGFontElement.h"
+#include "SVGFontFaceElement.h"
+#include "SVGGlyphElement.h"
+#include "SVGPathBuilder.h"
+#include "SVGPathParser.h"
+#include "SVGPathStringSource.h"
+
+namespace WebCore {
+
+static inline void write32(Vector<char>& vector, uint32_t value)
+{
+ vector.append(value >> 24);
+ vector.append(value >> 16);
+ vector.append(value >> 8);
+ vector.append(value);
+}
+
+static inline void write16(Vector<char>& vector, uint16_t value)
+{
+ vector.append(value >> 8);
+ vector.append(value);
+}
+
+static inline void overwrite32(Vector<char>& vector, unsigned location, uint32_t value)
+{
+ ASSERT(vector.size() >= location + 4);
+ *(vector.data() + location) = value >> 24;
+ *(vector.data() + location + 1) = value >> 16;
+ *(vector.data() + location + 2) = value >> 8;
+ *(vector.data() + location + 3) = value;
+}
+
+class SVGToOTFFontConverter {
+public:
+ SVGToOTFFontConverter(const SVGFontElement&);
+ Vector<char> convertSVGToOTFFont();
+
+private:
+ typedef uint16_t SID; // String ID
+ struct GlyphData {
+ GlyphData(Vector<char> charString, const SVGGlyphElement* glyphElement, float advance, FloatRect boundingBox, uint16_t codepoint)
+ : boundingBox(boundingBox)
+ , charString(charString)
+ , glyphElement(glyphElement)
+ , advance(advance)
+ , codepoint(codepoint)
+ {
+ }
+ FloatRect boundingBox;
+ Vector<char> charString;
+ const SVGGlyphElement* glyphElement;
+ float advance;
+ uint16_t codepoint;
+ };
+
+ static const size_t kSNFTHeaderSize = 12;
+ static const size_t kDirectoryEntrySize = 16;
+
+ typedef void (SVGToOTFFontConverter::*FontAppendingFunction)(Vector<char> &) const;
+ void appendTable(const char identifier[4], Vector<char>&, FontAppendingFunction);
+ void appendCMAPTable(Vector<char>&) const;
+ void appendHEADTable(Vector<char>&) const;
+ void appendHHEATable(Vector<char>&) const;
+ void appendHMTXTable(Vector<char>&) const;
+ void appendMAXPTable(Vector<char>&) const;
+ void appendNAMETable(Vector<char>&) const;
+ void appendOS2Table(Vector<char>&) const;
+ void appendPOSTTable(Vector<char>&) const;
+ void appendCFFTable(Vector<char>&) const;
+ void appendVORGTable(Vector<char>&) const;
+
+ Vector<GlyphData> m_glyphs;
+ FloatRect m_boundingBox;
+ const SVGFontElement& m_fontElement;
+ const SVGFontFaceElement* m_fontFaceElement;
+ String m_fontFamily;
+ float m_advanceWidthMax;
+ float m_minRightSideBearing;
+ int m_tablesAppendedCount;
+ char m_weight;
+ bool m_italic;
+};
+
+static uint16_t roundDownToPowerOfTwo(uint16_t x)
+{
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ return (x >> 1) + 1;
+}
+
+void SVGToOTFFontConverter::appendCMAPTable(Vector<char>& result) const
+{
+ auto startingOffset = result.size();
+ write16(result, 0);
+ write16(result, 1); // Number subtables
+
+ write16(result, 0); // Unicode
+ write16(result, 3); // Unicode version 2.2+
+ write32(result, result.size() - startingOffset + sizeof(uint32_t)); // Byte offset of subtable
+
+ // Braindead scheme: One segment for each character
+ ASSERT(m_glyphs.size() < 0xFFFF);
+ uint16_t segCount = m_glyphs.size() + 1;
+ write16(result, 4); // Format: Only support the Basic Multilingual Plane for now
+ write16(result, 22 + m_glyphs.size() * 2); // length
+ write16(result, 0); // Language independent
+ write16(result, 2 * segCount);
+ uint16_t originalSearchRange = roundDownToPowerOfTwo(segCount);
+ uint16_t searchRange = 2 * originalSearchRange;
+ write16(result, searchRange);
+ uint16_t entrySelector = 0;
+ while (originalSearchRange >>= 1)
+ ++entrySelector;
+ write16(result, entrySelector);
+ write16(result, (2 * segCount) - searchRange);
+
+ for (const auto& glyph : m_glyphs)
+ write16(result, glyph.codepoint); // EndCode
+ write16(result, 0xFFFF); // "To ensure that the search will terminate, the final endCode value must be 0xFFFF."
+ write16(result, 0); // Reserved
+ for (const auto& glyph : m_glyphs)
+ write16(result, glyph.codepoint); // startCode
+ write16(result, 0xFFFF);
+ for (unsigned i = 0; i < m_glyphs.size(); ++i) {
+ // Note that this value can be "negative," but that is okay because wrapping is defined and expected here
+ write16(result, static_cast<uint16_t>(i) - m_glyphs[i].codepoint); // idDelta
+ }
+ write16(result, 1);
+ for (unsigned i = 0; i < m_glyphs.size(); ++i)
+ write16(result, 0); // idRangeOffset
+ write16(result, 0);
+}
+
+void SVGToOTFFontConverter::appendHEADTable(Vector<char>& result) const
+{
+ write32(result, 0x00010000); // Version
+ write32(result, 0x00010000); // Revision
+ write32(result, 0); // Checksum adjustment
+ // Magic number. "Set to 0x5F0F3CF5"
+ result.append(0x5F);
+ result.append(0x0F);
+ result.append(0x3C);
+ result.append(-0x0B); // Wraparound
+ write16(result, (1 << 9) | 1);
+
+ uint16_t unitsPerEm = m_fontFaceElement ? m_fontFaceElement->unitsPerEm() : 0;
+
+ write16(result, unitsPerEm);
+ write32(result, 0); // First half of creation date
+ write32(result, 0); // Last half of creation date
+ write32(result, 0); // First half of modification date
+ write32(result, 0); // Last half of modification date
+ write16(result, std::numeric_limits<int16_t>::min()); // Minimum X
+ write16(result, std::numeric_limits<int16_t>::min()); // Minimum Y
+ write16(result, std::numeric_limits<int16_t>::max()); // Maximum X
+ write16(result, std::numeric_limits<int16_t>::max()); // Maximum Y
+ write16(result, (m_italic ? 1 << 1 : 0) | (m_weight >= 7 ? 1 : 0));
+ write16(result, 3); // Smallest readable size in pixels
+ write16(result, 0); // Might contain LTR or RTL glyphs
+ write16(result, 0); // Short offsets in the 'loca' table. However, OTF fonts don't have a 'loca' table so this is irrelevant
+ write16(result, 0); // Glyph data format
+}
+
+// Assumption: T2 can hold every value that a T1 can hold
+template <typename T1, typename T2>
+static inline T1 clampTo(T2 x)
+{
+ x = std::min(x, static_cast<T2>(std::numeric_limits<T1>::max()));
+ x = std::max(x, static_cast<T2>(std::numeric_limits<T1>::min()));
+ return static_cast<T1>(x);
+}
+
+void SVGToOTFFontConverter::appendHHEATable(Vector<char>& result) const
+{
+ uint16_t unitsPerEm = 0;
+ int16_t ascent = std::numeric_limits<int16_t>::max();
+ int16_t descent = std::numeric_limits<int16_t>::max();
+ if (m_fontFaceElement) {
+ unitsPerEm = m_fontFaceElement->unitsPerEm();
+ ascent = m_fontFaceElement->ascent();
+ descent = m_fontFaceElement->descent();
+ }
+
+ write32(result, 0x00010000); // Version
+ write16(result, ascent);
+ write16(result, descent);
+ write16(result, 0); // 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
+ // 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); // Current format
+ write16(result, m_glyphs.size()); // Number of advance widths in HMTX table
+}
+
+void SVGToOTFFontConverter::appendHMTXTable(Vector<char>& result) const
+{
+ for (const auto& glyph : m_glyphs) {
+ write16(result, clampTo<uint16_t, float>(glyph.advance));
+ write16(result, clampTo<int16_t, float>(glyph.boundingBox.x()));
+ }
+}
+
+void SVGToOTFFontConverter::appendMAXPTable(Vector<char>& result) const
+{
+ write32(result, 0x00010000); // Version
+ write16(result, m_glyphs.size());
+ write16(result, 0xFFFF); // Maximum number of points in non-compound glyph
+ write16(result, 0xFFFF); // Maximum number of contours in non-compound glyph
+ write16(result, 0xFFFF); // Maximum number of points in compound glyph
+ write16(result, 0xFFFF); // Maximum number of contours in compound glyph
+ write16(result, 2); // Maximum number of zones
+ write16(result, 0); // Maximum number of points used in zone 0
+ write16(result, 0); // Maximum number of storage area locations
+ write16(result, 0); // Maximum number of function definitions
+ write16(result, 0); // Maximum number of instruction definitions
+ write16(result, 0); // Maximum stack depth
+ write16(result, 0); // Maximum size of instructions
+ write16(result, m_glyphs.size()); // Maximum number of glyphs referenced at top level
+ write16(result, 0); // No compound glyphs
+}
+
+void SVGToOTFFontConverter::appendNAMETable(Vector<char>& result) const
+{
+ write16(result, 0); // Format selector
+ write16(result, 1); // Number of name records in table
+ write16(result, 18); // Offset in bytes to the beginning of name character strings
+
+ write16(result, 0); // Unicode
+ write16(result, 3); // Unicode version 2.0 or later
+ write16(result, 0); // Language
+ write16(result, 1); // Name identifier. 1 = Font family
+ write16(result, m_fontFamily.length());
+ write16(result, 0); // Offset into name data
+
+ for (unsigned i = 0; i < m_fontFamily.length(); ++i)
+ write16(result, m_fontFamily[i]);
+}
+
+void SVGToOTFFontConverter::appendOS2Table(Vector<char>& result) const
+{
+ // 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;
+ auto& attribute = m_fontElement.fastGetAttribute(SVGNames::horiz_adv_xAttr);
+ bool ok = true;
+ int value = attribute.toInt(&ok);
+ if (ok)
+ averageAdvance = clampTo<int16_t, int>(value);
+
+ write16(result, 0); // Version
+ write16(result, averageAdvance);
+ write16(result, m_weight); // Weight class
+ write16(result, 5); // Width class
+ write16(result, 0); // Protected font
+ // WebKit handles these superscripts and subscripts
+ write16(result, 0); // Subscript X Size
+ write16(result, 0); // Subscript Y Size
+ write16(result, 0); // Subscript X Offset
+ write16(result, 0); // Subscript Y Offset
+ write16(result, 0); // Superscript X Size
+ write16(result, 0); // Superscript Y Size
+ write16(result, 0); // Superscript X Offset
+ write16(result, 0); // Superscript Y Offset
+ write16(result, 0); // Strikeout width
+ write16(result, 0); // Strikeout Position
+ write16(result, 0); // No classification
+
+ Vector<unsigned char> specifiedPanose;
+ if (m_fontFaceElement) {
+ const auto& attribute = m_fontFaceElement->fastGetAttribute(SVGNames::panose_1Attr);
+ Vector<String> split;
+ String(attribute).split(" ", split);
+ if (split.size() == 10) {
+ for (const auto& s : split) {
+ bool ok = true;
+ int value = s.toInt(&ok);
+ if (!ok || value < 0 || value > 0xFF) {
+ specifiedPanose.clear();
+ break;
+ }
+ specifiedPanose.append(static_cast<unsigned char>(value));
+ }
+ }
+ }
+
+ if (specifiedPanose.size() == 10) {
+ for (char c : specifiedPanose)
+ result.append(c);
+ } else {
+ for (int i = 0; i < 10; ++i)
+ result.append(0); // PANOSE: Any
+ }
+
+ for (int i = 0; i < 4; ++i)
+ write32(result, 0); // "Bit assignments are pending. Set to 0"
+ write32(result, 0x544B4257); // Font Vendor. "WBKT"
+ write16(result, (m_weight >= 7 ? 1 << 5 : 0) | (m_italic ? 1 : 0)); // Font Patterns.
+ write16(result, m_glyphs[0].codepoint); // First unicode index
+ write16(result, m_glyphs[m_glyphs.size() - 1].codepoint); // Last unicode index
+}
+
+void SVGToOTFFontConverter::appendPOSTTable(Vector<char>& result) const
+{
+ write32(result, 0x00030000); // Format. Printing is undefined
+ write32(result, 0); // Italic angle in degrees
+ write16(result, 0); // Underline position
+ write16(result, 0); // Underline thickness
+ write32(result, 0); // Monospaced
+ write32(result, 0); // "Minimum memory usage when a TrueType font is downloaded as a Type 42 font"
+ write32(result, 0); // "Maximum memory usage when a TrueType font is downloaded as a Type 42 font"
+ write32(result, 0); // "Minimum memory usage when a TrueType font is downloaded as a Type 1 font"
+ write32(result, 0); // "Maximum memory usage when a TrueType font is downloaded as a Type 1 font"
+}
+
+static bool isValidStringForCFF(const String& string)
+{
+ for (unsigned i = 0; i < string.length(); ++i) {
+ if (string[i] < 33 || string[i] > 126)
+ return false;
+ }
+ return true;
+}
+
+static void appendCFFValidString(Vector<char>& output, const String& string)
+{
+ ASSERT(isValidStringForCFF(string));
+ for (unsigned i = 0; i < string.length(); ++i)
+ output.append(string[i]);
+}
+
+void SVGToOTFFontConverter::appendCFFTable(Vector<char>& result) const
+{
+ auto startingOffset = result.size();
+ // Header
+ result.append(1); // Major version
+ result.append(0); // Minor version
+ result.append(4); // Header size
+ result.append(4); // Offsets within CFF table are 4 bytes long
+
+ // Name INDEX
+ String fontName;
+ if (m_fontFaceElement) {
+ // FIXME: fontFamily() here might not be quite what I want
+ String potentialFontName = m_fontFamily;
+ if (isValidStringForCFF(potentialFontName))
+ fontName = potentialFontName;
+ }
+ write16(result, 1); // INDEX contains 1 element
+ result.append(4); // Offsets in this INDEX are 4 bytes long
+ write32(result, 1); // 1-index offset of name data
+ write32(result, fontName.length() + 1); // 1-index offset just past end of name data
+ appendCFFValidString(result, fontName);
+
+ String weight;
+ if (m_fontFaceElement) {
+ auto& potentialWeight = m_fontFaceElement->fastGetAttribute(SVGNames::font_weightAttr);
+ if (isValidStringForCFF(potentialWeight))
+ weight = potentialWeight;
+ }
+
+ const char operand32Bit = 29;
+ const char fullNameKey = 2;
+ const char familyNameKey = 3;
+ const char weightKey = 4;
+ const char fontBBoxKey = 5;
+ const char charsetIndexKey = 15;
+ const char charstringsIndexKey = 17;
+ const uint32_t userDefinedStringStartIndex = 391;
+ const unsigned sizeOfTopIndex = 45 + (weight.isEmpty() ? 0 : 6);
+
+ // Top DICT INDEX.
+ write16(result, 1); // INDEX contains 1 element
+ result.append(4); // Offsets in this INDEX are 4 bytes long
+ write32(result, 1); // 1-index offset of DICT data
+ write32(result, 1 + sizeOfTopIndex); // 1-index offset just past end of DICT data
+
+ // DICT information
+#if !ASSERT_DISABLED
+ unsigned topDictStart = result.size();
+#endif
+ result.append(operand32Bit);
+ write32(result, userDefinedStringStartIndex);
+ result.append(fullNameKey);
+ result.append(operand32Bit);
+ write32(result, userDefinedStringStartIndex);
+ result.append(familyNameKey);
+ if (!weight.isEmpty()) {
+ result.append(operand32Bit);
+ write32(result, userDefinedStringStartIndex + 1);
+ result.append(weightKey);
+ }
+ result.append(operand32Bit);
+ write32(result, clampTo<int32_t, float>(m_boundingBox.x()));
+ result.append(operand32Bit);
+ write32(result, clampTo<int32_t, float>(m_boundingBox.maxX()));
+ result.append(operand32Bit);
+ write32(result, clampTo<int32_t, float>(m_boundingBox.y()));
+ result.append(operand32Bit);
+ write32(result, clampTo<int32_t, float>(m_boundingBox.maxY()));
+ result.append(fontBBoxKey);
+ result.append(operand32Bit);
+ unsigned charsetOffsetLocation = result.size();
+ write32(result, 0); // Offset of Charset info. Will be overwritten later.
+ result.append(charsetIndexKey);
+ result.append(operand32Bit);
+ unsigned charstringsOffsetLocation = result.size();
+ write32(result, 0); // Offset of CharStrings INDEX. Will be overwritten later.
+ result.append(charstringsIndexKey);
+ ASSERT(result.size() == topDictStart + sizeOfTopIndex);
+
+ // String INDEX
+ write16(result, 1 + (weight.isEmpty() ? 0 : 1)); // Number of elements in INDEX
+ result.append(4); // Offsets in this INDEX are 4 bytes long
+ uint32_t offset = 1;
+ write32(result, offset);
+ offset += fontName.length();
+ write32(result, offset);
+ if (!weight.isEmpty()) {
+ offset += weight.length();
+ write32(result, offset);
+ }
+ appendCFFValidString(result, fontName);
+ appendCFFValidString(result, weight);
+
+ write16(result, 0); // Empty subroutine INDEX
+
+ // Charset info
+ overwrite32(result, charsetOffsetLocation, result.size() - startingOffset);
+ result.append(0);
+ for (unsigned i = 1; i < m_glyphs.size(); ++i)
+ write16(result, i);
+
+ // CharStrings INDEX
+ overwrite32(result, charstringsOffsetLocation, result.size() - startingOffset);
+ write16(result, m_glyphs.size());
+ result.append(4); // Offsets in this INDEX are 4 bytes long
+ offset = 1;
+ write32(result, offset);
+ for (auto& glyph : m_glyphs) {
+ offset += glyph.charString.size();
+ write32(result, offset);
+ }
+ for (auto& glyph : m_glyphs)
+ result.appendVector(glyph.charString);
+}
+
+void SVGToOTFFontConverter::appendVORGTable(Vector<char>& result) const
+{
+ write16(result, 1); // Major version
+ write16(result, 0); // Minor version
+
+ // 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;
+ }
+ write16(result, defaultVerticalOriginY);
+
+ Vector<std::pair<uint16_t, int16_t>> origins;
+ for (uint16_t i = 0; i < m_glyphs.size(); ++i) {
+ if (m_glyphs[i].glyphElement) {
+ auto& attribute = m_glyphs[i].glyphElement->fastGetAttribute(SVGNames::vert_origin_yAttr);
+ if (attribute != nullAtom && attribute.is8Bit()) {
+ bool ok;
+ int16_t verticalOriginY = attribute.toInt(&ok);
+ if (ok && verticalOriginY)
+ origins.append(std::make_pair(i, verticalOriginY));
+ }
+ }
+ }
+ write16(result, origins.size());
+
+ for (const auto& p : origins) {
+ write16(result, p.first);
+ write16(result, p.second);
+ }
+}
+
+static void writeCFFEncodedNumber(Vector<char>& vector, float number)
+{
+ int raw = number * powf(2, 16);
+ vector.append(-1); // 0xFF
+ write32(vector, raw);
+}
+
+static const char rLineTo = 0x05;
+static const char rrCurveTo = 0x08;
+static const char endChar = 0x0e;
+static const char rMoveTo = 0x15;
+
+class CFFBuilder : public SVGPathBuilder {
+public:
+ CFFBuilder(Vector<char>& cffData, float width)
+ : m_cffData(cffData)
+ , m_firstPoint(true)
+ {
+ // FIXME: We probably want the initial moveto to use horiz-origin-x and horiz-origin-y, unless we're vertical
+ writeCFFEncodedNumber(m_cffData, width);
+ writeCFFEncodedNumber(m_cffData, 0);
+ writeCFFEncodedNumber(m_cffData, 0);
+ m_cffData.append(rMoveTo);
+ }
+
+ void updateForConstituentPoint(FloatPoint x)
+ {
+ if (m_firstPoint)
+ m_boundingBox = FloatRect(x, FloatSize());
+ else
+ m_boundingBox.extend(x);
+ m_firstPoint = false;
+ }
+
+ void moveTo(const FloatPoint& targetPoint, bool closed, PathCoordinateMode mode) override
+ {
+ FloatPoint destination = mode == AbsoluteCoordinates ? targetPoint : m_current + targetPoint;
+ updateForConstituentPoint(destination);
+ FloatSize delta = destination - m_current;
+
+ if (closed && m_cffData.size())
+ closePath();
+
+ writeCFFEncodedNumber(m_cffData, delta.width());
+ writeCFFEncodedNumber(m_cffData, delta.height());
+ m_cffData.append(rMoveTo);
+
+ m_current = destination;
+ m_startingPoint = m_current;
+ }
+
+ void lineTo(const FloatPoint& targetPoint, PathCoordinateMode mode) override
+ {
+ FloatPoint destination = mode == AbsoluteCoordinates ? targetPoint : m_current + targetPoint;
+ updateForConstituentPoint(destination);
+ FloatSize delta = destination - m_current;
+
+ writeCFFEncodedNumber(m_cffData, delta.width());
+ writeCFFEncodedNumber(m_cffData, delta.height());
+ m_cffData.append(rLineTo);
+
+ m_current = destination;
+ }
+
+ void curveToCubic(const FloatPoint& point1, const FloatPoint& point2, const FloatPoint& targetPoint, PathCoordinateMode mode) override
+ {
+ // FIXME: This can be made way faster
+ FloatPoint destination1 = point1;
+ FloatPoint destination2 = point2;
+ FloatPoint destination3 = targetPoint;
+ if (mode == RelativeCoordinates) {
+ destination1 += m_current;
+ destination2 += m_current;
+ destination3 += m_current;
+ }
+ updateForConstituentPoint(destination1);
+ updateForConstituentPoint(destination2);
+ updateForConstituentPoint(destination3);
+ FloatSize delta3 = destination3 - destination2;
+ FloatSize delta2 = destination2 - destination1;
+ FloatSize delta1 = destination1 - m_current;
+
+ writeCFFEncodedNumber(m_cffData, delta1.width());
+ writeCFFEncodedNumber(m_cffData, delta1.height());
+ writeCFFEncodedNumber(m_cffData, delta2.width());
+ writeCFFEncodedNumber(m_cffData, delta2.height());
+ writeCFFEncodedNumber(m_cffData, delta3.width());
+ writeCFFEncodedNumber(m_cffData, delta3.height());
+ m_cffData.append(rrCurveTo);
+
+ m_current = destination3;
+ }
+
+ void closePath() override
+ {
+ if (m_current != m_startingPoint)
+ lineTo(m_startingPoint, AbsoluteCoordinates);
+ }
+
+ FloatRect boundingBox()
+ {
+ return m_boundingBox;
+ }
+
+private:
+ Vector<char>& m_cffData;
+ FloatPoint m_startingPoint;
+ FloatRect m_boundingBox;
+ bool m_firstPoint;
+};
+
+static Vector<char> transcodeGlyphPaths(float width, const SVGGlyphElement& glyphElement, FloatRect& boundingBox)
+{
+ Vector<char> result;
+ const auto& dAttribute = glyphElement.fastGetAttribute(SVGNames::dAttr);
+ if (dAttribute.isEmpty()) {
+ writeCFFEncodedNumber(result, width);
+ writeCFFEncodedNumber(result, 0);
+ writeCFFEncodedNumber(result, 0);
+ result.append(rMoveTo);
+ result.append(endChar);
+ return result;
+ }
+
+ CFFBuilder builder(result, width);
+ SVGPathStringSource source(dAttribute);
+ SVGPathParser parser;
+ parser.setCurrentSource(&source);
+ parser.setCurrentConsumer(&builder);
+
+ bool ok = parser.parsePathDataFromSource(NormalizedParsing);
+ parser.cleanup();
+
+ if (!ok)
+ result.clear();
+
+ boundingBox = builder.boundingBox();
+
+ result.append(endChar);
+ return result;
+}
+
+SVGToOTFFontConverter::SVGToOTFFontConverter(const SVGFontElement& fontElement)
+ : m_fontElement(fontElement)
+ , m_fontFaceElement(childrenOfType<SVGFontFaceElement>(m_fontElement).first())
+ , m_advanceWidthMax(0)
+ , m_minRightSideBearing(std::numeric_limits<float>::max())
+ , m_tablesAppendedCount(0)
+ , m_weight(5)
+ , m_italic(false)
+{
+ // 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);
+ // Only support Basic Multilingual Plane w/o ligatures for now
+ if (unicodeAttribute.length() == 1) {
+ float effectiveAdvance = 0;
+ auto& advanceAttribute = glyph.fastGetAttribute(SVGNames::horiz_adv_xAttr);
+ if (!advanceAttribute.isEmpty()) {
+ bool ok = true;
+ float advance = advanceAttribute.toFloat(&ok);
+ if (ok) {
+ effectiveAdvance = advance;
+ m_advanceWidthMax = std::max(m_advanceWidthMax, advance);
+ }
+ }
+
+ 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;
+
+ m_glyphs.append(GlyphData(path, &glyph, effectiveAdvance, glyphBoundingBox, unicodeAttribute[0]));
+ }
+ }
+ std::sort(m_glyphs.begin(), m_glyphs.end(), [](const GlyphData& data1, const GlyphData& data2) {
+ return data1.codepoint < data2.codepoint;
+ });
+
+ // FIXME: Handle commas
+ if (m_fontFaceElement) {
+ auto& fontWeightAttribute = m_fontFaceElement->fastGetAttribute(SVGNames::font_weightAttr);
+ Vector<String> split;
+ fontWeightAttribute.string().split(" ", split);
+ for (const auto& segment : split) {
+ if (segment == "bold")
+ m_weight = 7;
+ bool ok = true;
+ int value = segment.toInt(&ok);
+ if (ok && value >= 0 && value < 1000)
+ m_weight = value / 100;
+ }
+ const auto& fontStyleAttribute = m_fontFaceElement->fastGetAttribute(SVGNames::font_weightAttr);
+ split.clear();
+ String(fontStyleAttribute).split(" ", split);
+ for (const auto& s : split) {
+ if (s == "italic" || s == "oblique")
+ m_italic = true;
+ }
+ }
+
+ // Might not be quite what I want
+ if (m_fontFaceElement)
+ m_fontFamily = m_fontFaceElement->fontFamily();
+}
+
+static inline bool isFourByteAligned(size_t x)
+{
+ return !(x & sizeof(uint32_t)-1);
+}
+
+static uint32_t calculateChecksum(const Vector<char>& table, size_t startingOffset, size_t endingOffset)
+{
+ ASSERT(isFourByteAligned(endingOffset - startingOffset));
+ uint32_t sum = 0;
+ for (; startingOffset < endingOffset; startingOffset += 4) {
+ // The spec is unclear whether this is a little-endian sum or a big-endian sum. Choose little endian.
+ sum += (static_cast<unsigned char>(table[startingOffset + 3]) << 24)
+ | (static_cast<unsigned char>(table[startingOffset + 2]) << 16)
+ | (static_cast<unsigned char>(table[startingOffset + 1]) << 8)
+ | static_cast<unsigned char>(table[startingOffset]);
+ }
+ return sum;
+}
+
+void SVGToOTFFontConverter::appendTable(const char identifier[4], Vector<char>& output, FontAppendingFunction appendingFunction)
+{
+ size_t offset = output.size();
+ ASSERT(isFourByteAligned(offset));
+ (this->*appendingFunction)(output);
+ size_t unpaddedSize = output.size() - offset;
+ while (!isFourByteAligned(output.size()))
+ output.append(0);
+ ASSERT(isFourByteAligned(output.size()));
+ size_t directoryEntryOffset = kSNFTHeaderSize + m_tablesAppendedCount * kDirectoryEntrySize;
+ output[directoryEntryOffset] = identifier[0];
+ output[directoryEntryOffset + 1] = identifier[1];
+ output[directoryEntryOffset + 2] = identifier[2];
+ output[directoryEntryOffset + 3] = identifier[3];
+ overwrite32(output, directoryEntryOffset + 4, calculateChecksum(output, offset, output.size()));
+ overwrite32(output, directoryEntryOffset + 8, offset);
+ overwrite32(output, directoryEntryOffset + 12, unpaddedSize);
+ ++m_tablesAppendedCount;
+}
+
+Vector<char> SVGToOTFFontConverter::convertSVGToOTFFont()
+{
+ Vector<char> result;
+ if (m_glyphs.size() > 0xFFFF || !m_glyphs.size())
+ return result;
+
+ uint16_t numTables = 10;
+ uint16_t roundedNumTables = roundDownToPowerOfTwo(numTables);
+ uint16_t searchRange = roundedNumTables * 16;
+ uint16_t entrySelector = 0;
+ while (roundedNumTables >>= 1)
+ ++entrySelector;
+
+ result.append('O');
+ result.append('T');
+ result.append('T');
+ result.append('O');
+ write16(result, numTables);
+ write16(result, searchRange);
+ write16(result, entrySelector);
+ write16(result, numTables * 16 - searchRange);
+
+ ASSERT(result.size() == kSNFTHeaderSize);
+
+ // Leave space for the Directory Entries
+ for (size_t i = 0; i < kDirectoryEntrySize * numTables; ++i)
+ result.append(0);
+
+ // FIXME: Implement more tables, like vhea and vmtx (and kern!)
+ appendTable("CFF ", result, &SVGToOTFFontConverter::appendCFFTable);
+ appendTable("OS/2", result, &SVGToOTFFontConverter::appendOS2Table);
+ appendTable("VORG", result, &SVGToOTFFontConverter::appendVORGTable);
+ appendTable("cmap", result, &SVGToOTFFontConverter::appendCMAPTable);
+ auto headTableOffset = result.size();
+ appendTable("head", result, &SVGToOTFFontConverter::appendHEADTable);
+ appendTable("hhea", result, &SVGToOTFFontConverter::appendHHEATable);
+ appendTable("hmtx", result, &SVGToOTFFontConverter::appendHMTXTable);
+ appendTable("maxp", result, &SVGToOTFFontConverter::appendMAXPTable);
+ appendTable("name", result, &SVGToOTFFontConverter::appendNAMETable);
+ appendTable("post", result, &SVGToOTFFontConverter::appendPOSTTable);
+
+ ASSERT(numTables == m_tablesAppendedCount);
+
+ // checkSumAdjustment: "To compute: set it to 0, calculate the checksum for the 'head' table and put it in the table directory,
+ // sum the entire font as uint32, then store B1B0AFBA - sum. The checksum for the 'head' table will now be wrong. That is OK."
+ uint32_t checksumAdjustment = 0xB1B0AFBAU - calculateChecksum(result, 0, result.size());
+ overwrite32(result, headTableOffset + 8, checksumAdjustment);
+
+ return result;
+}
+
+Vector<char> convertSVGToOTFFont(const SVGFontElement& element)
+{
+ return SVGToOTFFontConverter(element).convertSVGToOTFFont();
+}
+
+}
</ins></span></pre></div>
<a id="trunkSourceWebCoresvgSVGToOTFFontConversionh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/svg/SVGToOTFFontConversion.h (0 => 173521)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/svg/SVGToOTFFontConversion.h         (rev 0)
+++ trunk/Source/WebCore/svg/SVGToOTFFontConversion.h        2014-09-11 18:29:43 UTC (rev 173521)
</span><span class="lines">@@ -0,0 +1,39 @@
</span><ins>+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SVGToOTFFontConversion_h
+#define SVGToOTFFontConversion_h
+
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class SVGFontElement;
+
+Vector<char> convertSVGToOTFFont(const SVGFontElement&);
+
+}
+
+#endif
</ins></span></pre>
</div>
</div>
</body>
</html>