merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 12 Jan 2017 10:14:43 +0100
changeset 374061 97d6f73643940256c0eb61e384c49bf6f6c49847
parent 373974 a2425e6d32feebaa6a0ae749be2d888fc3d11e92 (current diff)
parent 374060 a2e8ab4165a1da0982aa222489511dfc76b73292 (diff)
child 374062 1289b60bd5ed221f6713497fcdc8167ae3896916
child 374073 2958164fb64c31948de13ec74af8a0fda009c99b
child 374134 311a9929ce5baae9f383eacc8109b60f6f472987
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone53.0a1
first release with
nightly linux32
97d6f7364394 / 53.0a1 / 20170112030301 / files
nightly linux64
97d6f7364394 / 53.0a1 / 20170112030301 / files
nightly mac
97d6f7364394 / 53.0a1 / 20170112030301 / files
nightly win32
97d6f7364394 / 53.0a1 / 20170112030301 / files
nightly win64
97d6f7364394 / 53.0a1 / 20170112030301 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
devtools/client/aboutdebugging/test/browser_addons_reload.js
devtools/client/inspector/rules/rules.js
devtools/server/actors/tab.js
devtools/shared/content-observer.js
devtools/shared/webconsole/network-monitor.js
devtools/shared/webconsole/server-logger-monitor.js
devtools/shared/webconsole/server-logger.js
dom/animation/KeyframeEffectReadOnly.h
gfx/layers/client/CompositableChild.cpp
gfx/layers/client/CompositableChild.h
gfx/layers/ipc/PCompositable.ipdl
mobile/android/base/java/org/mozilla/gecko/home/activitystream/StreamItem.java
mobile/android/base/java/org/mozilla/gecko/home/activitystream/StreamRecyclerAdapter.java
modules/libpref/init/all.js
toolkit/components/telemetry/Histograms.json
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -3,16 +3,17 @@
 module.exports = {
   // When adding items to this file please check for effects on sub-directories.
   "plugins": [
     "mozilla"
   ],
   "rules": {
     "mozilla/import-globals": "warn",
     "mozilla/no-import-into-var-and-global": "error",
+    "mozilla/no-useless-parameters": "error",
 
     // No (!foo in bar) or (!object instanceof Class)
     "no-unsafe-negation": "error",
   },
   "env": {
     "es6": true
   },
   "parserOptions": {
--- a/addon-sdk/source/lib/sdk/preferences/event-target.js
+++ b/addon-sdk/source/lib/sdk/preferences/event-target.js
@@ -51,11 +51,11 @@ function onChange(subject, topic, name) 
   }
 }
 
 function destroy() {
   off(this);
 
   // stop listening to preference changes
   let branch = prefTargetNS(this).branch;
-  branch.removeObserver('', prefTargetNS(this).observer, false);
+  branch.removeObserver('', prefTargetNS(this).observer);
   prefTargetNS(this).observer = null;
 }
--- a/addon-sdk/source/test/leak/leak-utils.js
+++ b/addon-sdk/source/test/leak/leak-utils.js
@@ -55,17 +55,17 @@ exports.asyncWindowLeakTest = function*(
   }
   Services.obs.addObserver(windowObserver, "domwindowopened", false);
 
   // Execute the body of the test.
   let testLoader = yield asyncTestFunc(assert);
 
   // Stop tracking new windows and attempt to GC any resources allocated
   // by the test body.
-  Services.obs.removeObserver(windowObserver, "domwindowopened", false);
+  Services.obs.removeObserver(windowObserver, "domwindowopened");
   yield gc();
 
   // Check to see if any of the windows we saw survived the GC.  We consider
   // these leaks.
   assert.ok(weakWindows.length > 0, "should see at least one new window");
   for (let i = 0; i < weakWindows.length; ++i) {
     assert.equal(weakWindows[i].get(), null, "window " + i + " should be GC'd");
   }
--- a/b2g/components/SafeMode.jsm
+++ b/b2g/components/SafeMode.jsm
@@ -40,17 +40,17 @@ this.SafeMode = {
         return Promise.resolve();
       }
     } catch(e) { debug("No current mode available!"); }
 
     // Wait for the preference to toggle.
     return new Promise((aResolve, aReject) => {
       let observer = function(aSubject, aTopic, aData) {
         if (Services.prefs.getCharPref(kSafeModePref)) {
-          Services.prefs.removeObserver(kSafeModePref, observer, false);
+          Services.prefs.removeObserver(kSafeModePref, observer);
           aResolve();
         }
       }
 
       Services.prefs.addObserver(kSafeModePref, observer, false);
     });
   },
 
--- a/browser/base/content/browser-devedition.js
+++ b/browser/base/content/browser-devedition.js
@@ -119,18 +119,18 @@ var DevEdition = {
     } else if (!deveditionThemeEnabled && wasEnabled) {
       this.styleSheet.sheet.disabled = true;
       this.refreshBrowserDisplay();
     }
   },
 
   uninit() {
     Services.prefs.removeObserver(this._devtoolsThemePrefName, this);
-    Services.obs.removeObserver(this, "lightweight-theme-styling-update", false);
-    Services.obs.removeObserver(this, "lightweight-theme-window-updated", false);
+    Services.obs.removeObserver(this, "lightweight-theme-styling-update");
+    Services.obs.removeObserver(this, "lightweight-theme-window-updated");
     if (this.styleSheet) {
       this.styleSheet.removeEventListener("load", this);
     }
     this.styleSheet = null;
   }
 };
 
 // If the DevEdition theme is going to be applied in gBrowserInit.onLoad,
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -1407,17 +1407,17 @@ var BookmarkingUI = {
         updatePlacesContextMenu(shouldHidePrefUI);
       }
     };
 
     let onBookmarksMenuHidden = event => {
       if (event.target == event.currentTarget) {
         updatePlacesContextMenu(true);
 
-        Services.prefs.removeObserver(this.RECENTLY_BOOKMARKED_PREF, prefObserver, false);
+        Services.prefs.removeObserver(this.RECENTLY_BOOKMARKED_PREF, prefObserver);
         PlacesUtils.bookmarks.removeObserver(this._recentlyBookmarkedObserver);
         this._recentlyBookmarkedObserver = null;
         if (placesContextMenu) {
           placesContextMenu.removeEventListener("popupshowing", onPlacesContextMenuShowing);
         }
         bookmarksMenu.removeEventListener("popuphidden", onBookmarksMenuHidden);
       }
     };
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -7771,16 +7771,19 @@ var TabContextMenu = {
     this.contextTab = aPopupMenu.triggerNode.localName == "tab" ?
                       aPopupMenu.triggerNode : gBrowser.selectedTab;
     let disabled = gBrowser.tabs.length == 1;
 
     var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
     for (let menuItem of menuItems)
       menuItem.disabled = disabled;
 
+    if (this.contextTab.hasAttribute("customizemode"))
+      document.getElementById("context_openTabInWindow").disabled = true;
+
     if (AppConstants.E10S_TESTING_ONLY) {
       menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-remote");
       for (let menuItem of menuItems) {
         menuItem.hidden = !gMultiProcessBrowser;
         if (menuItem.id == "context_openNonRemoteWindow") {
           menuItem.disabled = !!parseInt(this.contextTab.getAttribute("usercontextid"));
         }
       }
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -5020,19 +5020,19 @@
           // window ID. We switched to a monotonic counter as Date.now() lead
           // to random failures because of colliding IDs.
           return "panel-" + outerID + "-" + (++this._uniquePanelIDCounter);
         ]]></body>
       </method>
 
       <destructor>
         <![CDATA[
-          Services.obs.removeObserver(this, "live-resize-start", false);
-          Services.obs.removeObserver(this, "live-resize-end", false);
-          Services.obs.removeObserver(this, "contextual-identity-updated", false);
+          Services.obs.removeObserver(this, "live-resize-start");
+          Services.obs.removeObserver(this, "live-resize-end");
+          Services.obs.removeObserver(this, "contextual-identity-updated");
 
           for (let tab of this.tabs) {
             let browser = tab.linkedBrowser;
             if (browser.registeredOpenURI) {
               this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI,
                                                        browser.getAttribute("usercontextid") || 0);
               delete browser.registeredOpenURI;
             }
--- a/browser/base/content/test/general/browser_bug435325.js
+++ b/browser/base/content/test/general/browser_bug435325.js
@@ -47,17 +47,17 @@ function checkPage(data) {
 
   // Re-enable the proxy so example.com is resolved to localhost, rather than
   // the actual example.com.
   Services.prefs.setIntPref("network.proxy.type", proxyPrefValue);
 
   Services.obs.addObserver(function observer(aSubject, aTopic) {
     ok(!Services.io.offline, "After clicking the Try Again button, we're back " +
                              "online.");
-    Services.obs.removeObserver(observer, "network:offline-status-changed", false);
+    Services.obs.removeObserver(observer, "network:offline-status-changed");
     finish();
   }, "network:offline-status-changed", false);
 
   ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
     content.document.getElementById("errorTryAgain").click();
   });
 }
 
--- a/browser/base/content/test/general/browser_sanitize-timespans.js
+++ b/browser/base/content/test/general/browser_sanitize-timespans.js
@@ -16,17 +16,17 @@ Cc["@mozilla.org/moz/jssubscript-loader;
 var Sanitizer = tempScope.Sanitizer;
 
 var FormHistory = (Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory;
 var Downloads = (Components.utils.import("resource://gre/modules/Downloads.jsm", {})).Downloads;
 
 function promiseFormHistoryRemoved() {
   let deferred = Promise.defer();
   Services.obs.addObserver(function onfh() {
-    Services.obs.removeObserver(onfh, "satchel-storage-changed", false);
+    Services.obs.removeObserver(onfh, "satchel-storage-changed");
     deferred.resolve();
   }, "satchel-storage-changed", false);
   return deferred.promise;
 }
 
 function promiseDownloadRemoved(list) {
   let deferred = Promise.defer();
 
--- a/browser/base/content/test/general/test_offlineNotification.html
+++ b/browser/base/content/test/general/test_offlineNotification.html
@@ -35,18 +35,18 @@ window.addEventListener("message", funct
     is(event.data, "success", "Child was successfully cached.");
 
     if (++numFinished == 3) {
       // Clean up after ourself
       var pm = Cc["@mozilla.org/permissionmanager;1"].
                getService(SpecialPowers.Ci.nsIPermissionManager);
       var ioService = Cc["@mozilla.org/network/io-service;1"]
                         .getService(SpecialPowers.Ci.nsIIOService);
-      var uri1 = ioService.newURI(frames.testFrame.location, null, null);
-      var uri2 = ioService.newURI(frames.testFrame3.location, null, null);
+      var uri1 = ioService.newURI(frames.testFrame.location);
+      var uri2 = ioService.newURI(frames.testFrame3.location);
 
       var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
                   .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
       var principal1 = ssm.createCodebasePrincipal(uri1, {});
       var principal2 = ssm.createCodebasePrincipal(uri2, {});
 
       pm.removeFromPrincipal(principal1, "offline-app");
       pm.removeFromPrincipal(principal2, "offline-app");
--- a/browser/components/controlcenter/content/panel.inc.xul
+++ b/browser/components/controlcenter/content/panel.inc.xul
@@ -21,25 +21,21 @@
       <!-- Security Section -->
       <hbox id="identity-popup-security" class="identity-popup-section">
         <vbox id="identity-popup-security-content" flex="1">
           <label class="plain">
             <label class="identity-popup-headline host"></label>
             <label class="identity-popup-headline hostless" crop="end"/>
           </label>
           <description class="identity-popup-connection-not-secure"
-                       value="&identity.connectionNotSecure;"
-                       when-connection="not-secure secure-cert-user-overridden"/>
+                       when-connection="not-secure secure-cert-user-overridden">&identity.connectionNotSecure;</description>
           <description class="identity-popup-connection-secure"
-                       value="&identity.connectionSecure;"
-                       when-connection="secure secure-ev"/>
-          <description value="&identity.connectionInternal;"
-                       when-connection="chrome"/>
-          <description value="&identity.connectionFile;"
-                       when-connection="file"/>
+                       when-connection="secure secure-ev">&identity.connectionSecure;</description>
+          <description when-connection="chrome">&identity.connectionInternal;</description>
+          <description when-connection="file">&identity.connectionFile;</description>
 
           <vbox id="identity-popup-security-descriptions">
             <description class="identity-popup-warning-gray"
                          when-mixedcontent="active-blocked">&identity.activeBlocked;</description>
             <description class="identity-popup-warning-yellow"
                          when-mixedcontent="passive-loaded">&identity.passiveLoaded;</description>
             <description when-mixedcontent="active-loaded">&identity.activeLoaded;</description>
             <description class="identity-popup-warning-yellow"
@@ -100,21 +96,19 @@
     <!-- Security SubView -->
     <panelview id="identity-popup-securityView" flex="1">
       <vbox id="identity-popup-securityView-header">
         <label class="plain">
           <label class="identity-popup-headline host"></label>
           <label class="identity-popup-headline hostless" crop="end"/>
         </label>
         <description class="identity-popup-connection-not-secure"
-                     value="&identity.connectionNotSecure;"
-                     when-connection="not-secure secure-cert-user-overridden"/>
+                     when-connection="not-secure secure-cert-user-overridden">&identity.connectionNotSecure;</description>
         <description class="identity-popup-connection-secure"
-                     value="&identity.connectionSecure;"
-                     when-connection="secure secure-ev"/>
+                     when-connection="secure secure-ev">&identity.connectionSecure;</description>
       </vbox>
 
       <vbox id="identity-popup-securityView-body" flex="1">
         <!-- (EV) Certificate Information -->
         <description id="identity-popup-content-verified-by"
                      when-connection="secure-ev">&identity.connectionVerified2;</description>
         <description id="identity-popup-content-owner"
                      when-connection="secure-ev"
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -444,17 +444,17 @@ CustomizeMode.prototype = {
     this._transitioning = true;
 
     Task.spawn(function*() {
       yield this.depopulatePalette();
 
       yield this._doTransition(false);
       this.removeLWTStyling();
 
-      Services.obs.removeObserver(this, "lightweight-theme-window-updated", false);
+      Services.obs.removeObserver(this, "lightweight-theme-window-updated");
 
       if (this.browser.selectedTab == gTab) {
         if (gTab.linkedBrowser.currentURI.spec == "about:blank") {
           closeGlobalTab();
         } else {
           unregisterGlobalTab();
         }
       }
--- a/browser/components/customizableui/test/browser_934951_zoom_in_toolbar.js
+++ b/browser/components/customizableui/test/browser_934951_zoom_in_toolbar.js
@@ -55,22 +55,22 @@ add_task(function*() {
   });
   is(parseInt(zoomResetButton.label, 10), 110, "Zoom is still 110% for about:mozilla");
   FullZoom.reset();
 });
 
 function promiseObserverNotification(aObserver) {
   let deferred = Promise.defer();
   function notificationCallback(e) {
-    Services.obs.removeObserver(notificationCallback, aObserver, false);
+    Services.obs.removeObserver(notificationCallback, aObserver);
     clearTimeout(timeoutId);
     deferred.resolve();
   }
   let timeoutId = setTimeout(() => {
-    Services.obs.removeObserver(notificationCallback, aObserver, false);
+    Services.obs.removeObserver(notificationCallback, aObserver);
     deferred.reject("Notification '" + aObserver + "' did not happen within 20 seconds.");
   }, kTimeoutInMS);
   Services.obs.addObserver(notificationCallback, aObserver, false);
   return deferred.promise;
 }
 
 function promiseTabSelect() {
   let deferred = Promise.defer();
--- a/browser/components/extensions/ext-sessions.js
+++ b/browser/components/extensions/ext-sessions.js
@@ -6,17 +6,17 @@ Cu.import("resource://gre/modules/Extens
 var {
   promiseObserved,
   SingletonEventManager,
 } = ExtensionUtils;
 
 XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
                                   "resource:///modules/sessionstore/SessionStore.jsm");
 
-const ssOnChangedTopic = "sessionstore-closed-objects-changed";
+const SS_ON_CLOSED_OBJECTS_CHANGED = "sessionstore-closed-objects-changed";
 
 function getRecentlyClosed(maxResults, extension) {
   let recentlyClosed = [];
 
   // Get closed windows
   let closedWindowData = SessionStore.getClosedWindowData(false);
   for (let window of closedWindowData) {
     recentlyClosed.push({
@@ -57,16 +57,17 @@ function createSession(restored, extensi
 extensions.registerSchemaAPI("sessions", "addon_parent", context => {
   let {extension} = context;
   return {
     sessions: {
       getRecentlyClosed: function(filter) {
         let maxResults = filter.maxResults == undefined ? this.MAX_SESSION_RESULTS : filter.maxResults;
         return Promise.resolve(getRecentlyClosed(maxResults, extension));
       },
+
       restore: function(sessionId) {
         let session, closedId;
         if (sessionId) {
           closedId = sessionId;
           session = SessionStore.undoCloseById(closedId);
         } else if (SessionStore.lastClosedObjectType == "window") {
           // If the most recently closed object is a window, just undo closing the most recent window.
           session = SessionStore.undoCloseWindow(0);
@@ -85,38 +86,22 @@ extensions.registerSchemaAPI("sessions",
           recentlyClosedTabs.sort((a, b) => b.closedAt - a.closedAt);
 
           // Use the closedId of the most recently closed tab to restore it.
           closedId = recentlyClosedTabs[0].closedId;
           session = SessionStore.undoCloseById(closedId);
         }
         return createSession(session, extension, closedId);
       },
+
       onChanged: new SingletonEventManager(context, "sessions.onChanged", fire => {
-        let listenerCount = 0;
-
-        let observer = {
-          observe: function() {
-            this.emit("changed");
-          },
-        };
-        EventEmitter.decorate(observer);
-
-        let listener = (event) => {
+        let observer = () => {
           context.runSafe(fire);
         };
 
-        observer.on("changed", listener);
-        listenerCount++;
-        if (listenerCount == 1) {
-          Services.obs.addObserver(observer, ssOnChangedTopic, false);
-        }
+        Services.obs.addObserver(observer, SS_ON_CLOSED_OBJECTS_CHANGED, false);
         return () => {
-          observer.off("changed", listener);
-          listenerCount -= 1;
-          if (!listenerCount) {
-            Services.obs.removeObserver(observer, ssOnChangedTopic);
-          }
+          Services.obs.removeObserver(observer, SS_ON_CLOSED_OBJECTS_CHANGED);
         };
       }).api(),
     },
   };
 });
--- a/browser/components/newtab/NewTabPrefsProvider.jsm
+++ b/browser/components/newtab/NewTabPrefsProvider.jsm
@@ -93,17 +93,17 @@ PrefsProvider.prototype = {
   init() {
     for (let pref of gPrefsMap.keys()) {
       Services.prefs.addObserver(pref, this, false);
     }
   },
 
   uninit() {
     for (let pref of gPrefsMap.keys()) {
-      Services.prefs.removeObserver(pref, this, false);
+      Services.prefs.removeObserver(pref, this);
     }
   }
 };
 
 /**
  * Singleton that serves as the default new tab pref provider for the grid.
  */
 const gPrefs = new PrefsProvider();
--- a/browser/components/newtab/NewTabSearchProvider.jsm
+++ b/browser/components/newtab/NewTabSearchProvider.jsm
@@ -51,17 +51,17 @@ SearchProvider.prototype = {
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
     Ci.nsISupportsWeakReference
   ]),
 
   uninit() {
     try {
-      Services.obs.removeObserver(this, CURRENT_ENGINE, true);
+      Services.obs.removeObserver(this, CURRENT_ENGINE);
     } catch (e) {
       Cu.reportError(e);
     }
   },
 
   get searchSuggestionUIStrings() {
     return ContentSearch.searchSuggestionUIStrings;
   },
--- a/browser/components/originattributes/test/browser/browser_favicon_firstParty.js
+++ b/browser/components/originattributes/test/browser/browser_favicon_firstParty.js
@@ -37,17 +37,17 @@ function clearAllPlacesFavicons() {
   let faviconService = Cc["@mozilla.org/browser/favicon-service;1"]
                           .getService(Ci.nsIFaviconService);
 
   return new Promise(resolve => {
     let observer = {
       observe(aSubject, aTopic, aData) {
         if (aTopic === "places-favicons-expired") {
           resolve();
-          Services.obs.removeObserver(observer, "places-favicons-expired", false);
+          Services.obs.removeObserver(observer, "places-favicons-expired");
         }
       }
     };
 
     Services.obs.addObserver(observer, "places-favicons-expired", false);
     faviconService.expireAllFavicons();
   });
 }
@@ -97,17 +97,17 @@ function observeFavicon(aFirstPartyDomai
           let faviconCookie = httpChannel.getRequestHeader("cookie");
 
           is(faviconCookie, aExpectedCookie, "The cookie of the favicon loading is correct.");
         } else {
           ok(false, "Received unexpected topic: ", aTopic);
         }
 
         if (faviconReqXUL && faviconReqPlaces) {
-          Services.obs.removeObserver(observer, "http-on-modify-request", false);
+          Services.obs.removeObserver(observer, "http-on-modify-request");
           resolve();
         }
       }
     };
 
     Services.obs.addObserver(observer, "http-on-modify-request", false);
   });
 }
@@ -127,18 +127,18 @@ function waitOnFaviconResponse(aFaviconU
           }
 
           let result = {
             topic: aTopic,
             firstPartyDomain: loadInfo.originAttributes.firstPartyDomain
           };
 
           resolve(result);
-          Services.obs.removeObserver(observer, "http-on-examine-response", false);
-          Services.obs.removeObserver(observer, "http-on-examine-cached-response", false);
+          Services.obs.removeObserver(observer, "http-on-examine-response");
+          Services.obs.removeObserver(observer, "http-on-examine-cached-response");
         }
       }
     };
 
     Services.obs.addObserver(observer, "http-on-examine-response", false);
     Services.obs.addObserver(observer, "http-on-examine-cached-response", false);
   });
 }
--- a/browser/components/originattributes/test/browser/browser_favicon_userContextId.js
+++ b/browser/components/originattributes/test/browser/browser_favicon_userContextId.js
@@ -37,17 +37,17 @@ function clearAllPlacesFavicons() {
   let faviconService = Cc["@mozilla.org/browser/favicon-service;1"]
                           .getService(Ci.nsIFaviconService);
 
   return new Promise(resolve => {
     let observer = {
       observe(aSubject, aTopic, aData) {
         if (aTopic === "places-favicons-expired") {
           resolve();
-          Services.obs.removeObserver(observer, "places-favicons-expired", false);
+          Services.obs.removeObserver(observer, "places-favicons-expired");
         }
       }
     };
 
     Services.obs.addObserver(observer, "places-favicons-expired", false);
     faviconService.expireAllFavicons();
   });
 }
@@ -191,17 +191,17 @@ function* doTest(aTestPage, aFaviconHost
 
   // Reset the observer for observing requests for the work container.
   observer.reset(USER_CONTEXT_ID_WORK, cookies[1], pageURI, aFaviconURL);
   tabInfo = yield openTabInUserContext(aTestPage, USER_CONTEXT_ID_WORK);
 
   // Waiting for favicon requests are all made.
   yield observer.promise;
 
-  Services.obs.removeObserver(observer, "http-on-modify-request", false);
+  Services.obs.removeObserver(observer, "http-on-modify-request");
 
   yield BrowserTestUtils.removeTab(tabInfo.tab);
 }
 
 add_task(function* setup() {
   // Make sure userContext is enabled.
   yield SpecialPowers.pushPrefEnv({"set": [
       ["privacy.userContext.enabled", true]
--- a/browser/components/preferences/in-content/search.js
+++ b/browser/components/preferences/in-content/search.js
@@ -36,17 +36,17 @@ var gSearchPane = {
     window.addEventListener("command", this, false);
     window.addEventListener("dragstart", this, false);
     window.addEventListener("keypress", this, false);
     window.addEventListener("select", this, false);
     window.addEventListener("blur", this, true);
 
     Services.obs.addObserver(this, "browser-search-engine-modified", false);
     window.addEventListener("unload", () => {
-      Services.obs.removeObserver(this, "browser-search-engine-modified", false);
+      Services.obs.removeObserver(this, "browser-search-engine-modified");
     });
 
     this._initAutocomplete();
 
     let suggestsPref =
       document.getElementById("browser.search.suggest.enabled");
     suggestsPref.addEventListener("change", () => {
       this.updateSuggestsCheckbox();
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js
@@ -30,17 +30,17 @@ function clearAllPlacesFavicons() {
   let faviconService = Cc["@mozilla.org/browser/favicon-service;1"]
                           .getService(Ci.nsIFaviconService);
 
   return new Promise(resolve => {
     let observer = {
       observe(aSubject, aTopic, aData) {
         if (aTopic === "places-favicons-expired") {
           resolve();
-          Services.obs.removeObserver(observer, "places-favicons-expired", false);
+          Services.obs.removeObserver(observer, "places-favicons-expired");
         }
       }
     };
 
     Services.obs.addObserver(observer, "places-favicons-expired", false);
     faviconService.expireAllFavicons();
   });
 }
@@ -100,17 +100,17 @@ function observeFavicon(aIsPrivate, aExp
 
           is(faviconCookie, aExpectedCookie, "The cookie of the favicon loading is correct.");
         } else {
           ok(false, "Received unexpected topic: ", aTopic);
         }
 
         if (faviconReqXUL && faviconReqPlaces) {
           resolve();
-          Services.obs.removeObserver(observer, "http-on-modify-request", false);
+          Services.obs.removeObserver(observer, "http-on-modify-request");
         }
       }
     };
 
     Services.obs.addObserver(observer, "http-on-modify-request", false);
   });
 }
 
@@ -129,18 +129,18 @@ function waitOnFaviconResponse(aFaviconU
           }
 
           let result = {
             topic: aTopic,
             privateBrowsingId: loadInfo.originAttributes.privateBrowsingId
           };
 
           resolve(result);
-          Services.obs.removeObserver(observer, "http-on-examine-response", false);
-          Services.obs.removeObserver(observer, "http-on-examine-cached-response", false);
+          Services.obs.removeObserver(observer, "http-on-examine-response");
+          Services.obs.removeObserver(observer, "http-on-examine-cached-response");
         }
       }
     };
 
     Services.obs.addObserver(observer, "http-on-examine-response", false);
     Services.obs.addObserver(observer, "http-on-examine-cached-response", false);
   });
 }
--- a/browser/components/uitour/test/browser_UITour_detach_tab.js
+++ b/browser/components/uitour/test/browser_UITour_detach_tab.js
@@ -27,17 +27,17 @@ function test() {
 var tests = [
   taskify(function* test_move_tab_to_new_window() {
     const myDocIdentifier = "Hello, I'm a unique expando to identify this document.";
 
     let highlight = document.getElementById("UITourHighlight");
     let windowDestroyedDeferred = Promise.defer();
     let onDOMWindowDestroyed = (aWindow) => {
       if (gContentWindow && aWindow == gContentWindow) {
-        Services.obs.removeObserver(onDOMWindowDestroyed, "dom-window-destroyed", false);
+        Services.obs.removeObserver(onDOMWindowDestroyed, "dom-window-destroyed");
         windowDestroyedDeferred.resolve();
       }
     };
 
     let browserStartupDeferred = Promise.defer();
     Services.obs.addObserver(function onBrowserDelayedStartup(aWindow) {
       Services.obs.removeObserver(onBrowserDelayedStartup, "browser-delayed-startup-finished");
       browserStartupDeferred.resolve(aWindow);
--- a/browser/extensions/pdfjs/content/PdfJs.jsm
+++ b/browser/extensions/pdfjs/content/PdfJs.jsm
@@ -192,21 +192,21 @@ var PdfJs = {
       this._ensureRegistered();
     } else {
       this._ensureUnregistered();
     }
   },
 
   uninit: function uninit() {
     if (this._initialized) {
-      Services.prefs.removeObserver(PREF_DISABLED, this, false);
-      Services.prefs.removeObserver(PREF_DISABLED_PLUGIN_TYPES, this, false);
-      Services.obs.removeObserver(this, TOPIC_PDFJS_HANDLER_CHANGED, false);
-      Services.obs.removeObserver(this, TOPIC_PLUGINS_LIST_UPDATED, false);
-      Services.obs.removeObserver(this, TOPIC_PLUGIN_INFO_UPDATED, false);
+      Services.prefs.removeObserver(PREF_DISABLED, this);
+      Services.prefs.removeObserver(PREF_DISABLED_PLUGIN_TYPES, this);
+      Services.obs.removeObserver(this, TOPIC_PDFJS_HANDLER_CHANGED);
+      Services.obs.removeObserver(this, TOPIC_PLUGINS_LIST_UPDATED);
+      Services.obs.removeObserver(this, TOPIC_PLUGIN_INFO_UPDATED);
       this._initialized = false;
     }
     this._ensureUnregistered();
   },
 
   _migrate: function migrate() {
     const VERSION = 2;
     var currentVersion = getIntPref(PREF_MIGRATION_VERSION, 0);
--- a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
+++ b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
@@ -105,17 +105,17 @@ var PdfjsChromeUtils = {
                                        this);
 
       this._mmg.removeMessageListener('PDFJS:Parent:displayWarning', this);
 
       this._mmg.removeMessageListener('PDFJS:Parent:addEventListener', this);
       this._mmg.removeMessageListener('PDFJS:Parent:removeEventListener', this);
       this._mmg.removeMessageListener('PDFJS:Parent:updateControlState', this);
 
-      Services.obs.removeObserver(this, 'quit-application', false);
+      Services.obs.removeObserver(this, 'quit-application');
 
       this._mmg = null;
       this._ppmm = null;
     }
   },
 
   /*
    * Called by the main module when preference changes are picked up
--- a/browser/modules/BrowserUsageTelemetry.jsm
+++ b/browser/modules/BrowserUsageTelemetry.jsm
@@ -207,17 +207,17 @@ let URICountListener = {
 };
 
 let urlbarListener = {
   init() {
     Services.obs.addObserver(this, AUTOCOMPLETE_ENTER_TEXT_TOPIC, true);
   },
 
   uninit() {
-    Services.obs.removeObserver(this, AUTOCOMPLETE_ENTER_TEXT_TOPIC, true);
+    Services.obs.removeObserver(this, AUTOCOMPLETE_ENTER_TEXT_TOPIC);
   },
 
   observe(subject, topic, data) {
     switch (topic) {
       case AUTOCOMPLETE_ENTER_TEXT_TOPIC:
         this._handleURLBarTelemetry(subject.QueryInterface(Ci.nsIAutoCompleteInput));
         break;
     }
@@ -295,19 +295,19 @@ let BrowserUsageTelemetry = {
     Services.telemetry.scalarSetMaximum(MAX_TAB_COUNT_SCALAR_NAME, counts.tabCount);
     Services.telemetry.scalarSetMaximum(MAX_WINDOW_COUNT_SCALAR_NAME, counts.winCount);
 
     // Reset the URI counter.
     URICountListener.reset();
   },
 
   uninit() {
-    Services.obs.removeObserver(this, DOMWINDOW_OPENED_TOPIC, false);
-    Services.obs.removeObserver(this, TELEMETRY_SUBSESSIONSPLIT_TOPIC, false);
-    Services.obs.removeObserver(this, WINDOWS_RESTORED_TOPIC, false);
+    Services.obs.removeObserver(this, DOMWINDOW_OPENED_TOPIC);
+    Services.obs.removeObserver(this, TELEMETRY_SUBSESSIONSPLIT_TOPIC);
+    Services.obs.removeObserver(this, WINDOWS_RESTORED_TOPIC);
     urlbarListener.uninit();
   },
 
   observe(subject, topic, data) {
     switch (topic) {
       case WINDOWS_RESTORED_TOPIC:
         this._setupAfterRestore();
         break;
--- a/browser/modules/ContentCrashHandlers.jsm
+++ b/browser/modules/ContentCrashHandlers.jsm
@@ -904,19 +904,19 @@ this.PluginCrashReporter = {
     this.crashReports = new Map();
 
     Services.obs.addObserver(this, "plugin-crashed", false);
     Services.obs.addObserver(this, "gmp-plugin-crash", false);
     Services.obs.addObserver(this, "profile-after-change", false);
   },
 
   uninit() {
-    Services.obs.removeObserver(this, "plugin-crashed", false);
-    Services.obs.removeObserver(this, "gmp-plugin-crash", false);
-    Services.obs.removeObserver(this, "profile-after-change", false);
+    Services.obs.removeObserver(this, "plugin-crashed");
+    Services.obs.removeObserver(this, "gmp-plugin-crash");
+    Services.obs.removeObserver(this, "profile-after-change");
     this.initialized = false;
   },
 
   observe(subject, topic, data) {
     switch (topic) {
       case "plugin-crashed": {
         let propertyBag = subject;
         if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
--- a/browser/modules/test/browser_urlBar_zoom.js
+++ b/browser/modules/test/browser_urlBar_zoom.js
@@ -55,19 +55,19 @@ add_task(function* asyncCleanup() {
     ok(!document.getElementById("zoom-controls"), "Customizable zoom widget removed from toolbar");
   }
 
 });
 
 function promiseObserverNotification(aObserver) {
   let deferred = Promise.defer();
   function notificationCallback(e) {
-    Services.obs.removeObserver(notificationCallback, aObserver, false);
+    Services.obs.removeObserver(notificationCallback, aObserver);
     clearTimeout(timeoutId);
     deferred.resolve();
   }
   let timeoutId = setTimeout(() => {
-    Services.obs.removeObserver(notificationCallback, aObserver, false);
+    Services.obs.removeObserver(notificationCallback, aObserver);
     deferred.reject("Notification '" + aObserver + "' did not happen within 20 seconds.");
   }, kTimeoutInMS);
   Services.obs.addObserver(notificationCallback, aObserver, false);
   return deferred.promise;
 }
--- a/browser/themes/shared/newtab/newTab.inc.css
+++ b/browser/themes/shared/newtab/newTab.inc.css
@@ -112,26 +112,26 @@ body.compact .newtab-cell {
 
 .newtab-cell:empty {
   outline: 2px dashed #c1c1c1;
   outline-offset: -2px;
   -moz-outline-radius: var(--cell-corner-radius);
 }
 
 /* SITES */
-body:not(.compact) .newtab-site {
+.newtab-site {
   border-radius: var(--cell-corner-radius);
   box-shadow: 0 1px 3px #c1c1c1;
   text-decoration: none;
   transition-property: top, left, opacity, box-shadow, background-color;
 }
 
-body:not(.compact) .newtab-cell:not([ignorehover]) .newtab-control:hover ~ .newtab-link,
-body:not(.compact) .newtab-cell:not([ignorehover]) .newtab-link:hover,
-body:not(.compact) .newtab-site[dragged] {
+.newtab-cell:not([ignorehover]) .newtab-control:hover ~ .newtab-link,
+.newtab-cell:not([ignorehover]) .newtab-link:hover,
+.newtab-site[dragged] {
   border: 2px solid white;
   box-shadow: 0 0 6px 1px #add6ff;
   margin: -2px;
 }
 
 .newtab-site[dragged] {
   transition-property: box-shadow, background-color;
   background-color: rgb(242,242,242);
--- a/build/autoconf/jemalloc.m4
+++ b/build/autoconf/jemalloc.m4
@@ -86,19 +86,16 @@ if test "$MOZ_BUILD_APP" != js -o -n "$J
     # their mozconfig.
     if test "$_MSC_VER"; then
        ac_configure_args="$ac_configure_args CFLAGS="
     fi
 
     # Force disable DSS support in jemalloc.
     ac_configure_args="$ac_configure_args ac_cv_func_sbrk=false"
 
-    # Force disable hugepage support in jemalloc.
-    ac_configure_args="$ac_configure_args je_cv_thp=no"
-
     # Make Linux builds munmap freed chunks instead of recycling them.
     ac_configure_args="$ac_configure_args --enable-munmap"
 
     # Disable cache oblivious behavior that appears to have a performance
     # impact on Firefox.
     ac_configure_args="$ac_configure_args --disable-cache-oblivious"
 
     if ! test -e memory/jemalloc; then
--- a/build/clang-plugin/Utils.h
+++ b/build/clang-plugin/Utils.h
@@ -179,16 +179,17 @@ inline bool isIgnoredPathForImplicitCtor
   SourceLocation Loc = Declaration->getLocation();
   const SourceManager &SM = Declaration->getASTContext().getSourceManager();
   SmallString<1024> FileName = SM.getFilename(Loc);
   llvm::sys::fs::make_absolute(FileName);
   llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName),
                                     End = llvm::sys::path::rend(FileName);
   for (; Begin != End; ++Begin) {
     if (Begin->compare_lower(StringRef("skia")) == 0 ||
+        Begin->compare_lower(StringRef("sfntly")) == 0 ||
         Begin->compare_lower(StringRef("angle")) == 0 ||
         Begin->compare_lower(StringRef("harfbuzz")) == 0 ||
         Begin->compare_lower(StringRef("hunspell")) == 0 ||
         Begin->compare_lower(StringRef("scoped_ptr.h")) == 0 ||
         Begin->compare_lower(StringRef("graphite2")) == 0 ||
         Begin->compare_lower(StringRef("icu")) == 0 ||
         Begin->compare_lower(StringRef("libcubeb")) == 0 ||
         Begin->compare_lower(StringRef("libstagefright")) == 0 ||
@@ -236,16 +237,17 @@ inline bool isIgnoredPathForSprintfLiter
         Begin->compare_lower(StringRef("chromium")) == 0 ||
         Begin->compare_lower(StringRef("crashreporter")) == 0 ||
         Begin->compare_lower(StringRef("google-breakpad")) == 0 ||
         Begin->compare_lower(StringRef("harfbuzz")) == 0 ||
         Begin->compare_lower(StringRef("libstagefright")) == 0 ||
         Begin->compare_lower(StringRef("mtransport")) == 0 ||
         Begin->compare_lower(StringRef("protobuf")) == 0 ||
         Begin->compare_lower(StringRef("skia")) == 0 ||
+        Begin->compare_lower(StringRef("sfntly")) == 0 ||
         // Gtest uses snprintf as GTEST_SNPRINTF_ with sizeof
         Begin->compare_lower(StringRef("testing")) == 0) {
       return true;
     }
     if (Begin->compare_lower(StringRef("webrtc")) == 0) {
       // Ignore trunk/webrtc, but not media/webrtc
       ++Begin;
       return Begin != End && Begin->compare_lower(StringRef("trunk")) == 0;
--- a/build/mozconfig.win-common
+++ b/build/mozconfig.win-common
@@ -9,9 +9,11 @@ if [ "x$IS_NIGHTLY" = "xyes" ]; then
   MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
 fi
 
 # Some builds (eg: Mulet) don't want the installer, so only set this if it
 # hasn't already been set.
 MOZ_AUTOMATION_INSTALLER=${MOZ_AUTOMATION_INSTALLER-1}
 
 export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=c:/builds/crash-stats-api.token
-export MAKECAB=$topsrcdir/makecab.exe
+
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+export MAKECAB=$TOOLTOOL_DIR/makecab.exe
--- a/devtools/client/aboutdebugging/test/browser_addons_reload.js
+++ b/devtools/client/aboutdebugging/test/browser_addons_reload.js
@@ -117,17 +117,17 @@ add_task(function* reloadButtonReloadsAd
 
   const reloadButton = getReloadButton(document, ADDON_NAME);
   is(reloadButton.disabled, false, "Reload button should not be disabled");
   is(reloadButton.title, "", "Reload button should not have a tooltip");
   const onInstalled = promiseAddonEvent("onInstalled");
 
   const onBootstrapInstallCalled = new Promise(done => {
     Services.obs.addObserver(function listener() {
-      Services.obs.removeObserver(listener, ADDON_NAME, false);
+      Services.obs.removeObserver(listener, ADDON_NAME);
       info("Add-on was re-installed: " + ADDON_NAME);
       done();
     }, ADDON_NAME, false);
   });
 
   reloadButton.click();
 
   const [reloadedAddon] = yield onInstalled;
--- a/devtools/client/inspector/rules/rules.js
+++ b/devtools/client/inspector/rules/rules.js
@@ -1084,21 +1084,21 @@ CssRuleView.prototype = {
       if (seenPseudoElement && !seenNormalElement && !rule.pseudoElement) {
         seenNormalElement = true;
         let div = this.styleDocument.createElementNS(HTML_NS, "div");
         div.className = this._getRuleViewHeaderClassName();
         div.textContent = this.selectedElementLabel;
         this.element.appendChild(div);
       }
 
-      let inheritedSource = rule.inheritedSource;
+      let inheritedSource = rule.inherited;
       if (inheritedSource && inheritedSource !== lastInheritedSource) {
         let div = this.styleDocument.createElementNS(HTML_NS, "div");
         div.className = this._getRuleViewHeaderClassName();
-        div.textContent = inheritedSource;
+        div.textContent = rule.inheritedSource;
         lastInheritedSource = inheritedSource;
         this.element.appendChild(div);
       }
 
       if (!seenPseudoElement && rule.pseudoElement) {
         seenPseudoElement = true;
         container = this.createExpandableContainer(this.pseudoElementLabel,
                                                    true);
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -145,16 +145,17 @@ skip-if = (os == "win" && debug) # bug 9
 [browser_rules_grid-highlighter-on-navigate.js]
 [browser_rules_grid-toggle_01.js]
 [browser_rules_grid-toggle_02.js]
 [browser_rules_grid-toggle_03.js]
 [browser_rules_guessIndentation.js]
 [browser_rules_inherited-properties_01.js]
 [browser_rules_inherited-properties_02.js]
 [browser_rules_inherited-properties_03.js]
+[browser_rules_inherited-properties_04.js]
 [browser_rules_inline-source-map.js]
 [browser_rules_invalid.js]
 [browser_rules_invalid-source-map.js]
 [browser_rules_keybindings.js]
 [browser_rules_keyframes-rule_01.js]
 [browser_rules_keyframes-rule_02.js]
 [browser_rules_keyframeLineNumbers.js]
 [browser_rules_lineNumbers.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_inherited-properties_04.js
@@ -0,0 +1,32 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that different inherited properties sections are created for rules
+// inherited from several elements of the same type.
+
+const TEST_URI = `
+  <div style="cursor:pointer">
+    A
+    <div style="cursor:pointer">
+      B<a>Cursor</a>
+    </div>
+  </div>
+`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("a", inspector);
+  yield elementStyleInherit(inspector, view);
+});
+
+function* elementStyleInherit(inspector, view) {
+  let gutters = view.element.querySelectorAll(".theme-gutter");
+  is(gutters.length, 2,
+    "Gutters should contains 2 sections");
+  ok(gutters[0].textContent, "Inherited from div");
+  ok(gutters[1].textContent, "Inherited from div");
+}
--- a/devtools/client/inspector/rules/test/doc_author-sheet.html
+++ b/devtools/client/inspector/rules/test/doc_author-sheet.html
@@ -11,17 +11,17 @@
   </style>
 
   <script>
     "use strict";
     var gIOService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
                                   .getService(SpecialPowers.Ci.nsIIOService);
 
     var style = "data:text/css,a { background-color: seagreen; }";
-    var uri = gIOService.newURI(style, null, null);
+    var uri = gIOService.newURI(style);
     var windowUtils = SpecialPowers.wrap(window)
         .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
         .getInterface(SpecialPowers.Ci.nsIDOMWindowUtils);
     windowUtils.loadSheet(uri, windowUtils.AUTHOR_SHEET);
   </script>
 
 </head>
 <body>
--- a/devtools/client/inspector/shared/test/doc_author-sheet.html
+++ b/devtools/client/inspector/shared/test/doc_author-sheet.html
@@ -9,17 +9,17 @@
     }
   </style>
   <script>
     "use strict";
     var gIOService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
                                   .getService(SpecialPowers.Ci.nsIIOService);
 
     var style = "data:text/css,div { background-color: seagreen; }";
-    var uri = gIOService.newURI(style, null, null);
+    var uri = gIOService.newURI(style);
     var windowUtils = SpecialPowers.wrap(window)
                       .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
                       .getInterface(SpecialPowers.Ci.nsIDOMWindowUtils);
     windowUtils.loadSheet(uri, windowUtils.AUTHOR_SHEET);
   </script>
 </head>
 <body>
   <div id="target"> the ocean </div>
--- a/devtools/client/scratchpad/scratchpad.js
+++ b/devtools/client/scratchpad/scratchpad.js
@@ -2461,18 +2461,17 @@ var CloseObserver = {
   uninit: function CO_uninit()
   {
     // Will throw exception if removeObserver is called twice.
     if (this._uninited) {
       return;
     }
 
     this._uninited = true;
-    Services.obs.removeObserver(this, "browser-lastwindow-close-requested",
-                                false);
+    Services.obs.removeObserver(this, "browser-lastwindow-close-requested");
   },
 };
 
 XPCOMUtils.defineLazyGetter(Scratchpad, "strings", function () {
   return Services.strings.createBundle(SCRATCHPAD_L10N);
 });
 
 addEventListener("load", Scratchpad.onLoad.bind(Scratchpad), false);
--- a/devtools/client/scratchpad/test/browser_scratchpad_browser_last_window_closing.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_browser_last_window_closing.js
@@ -69,11 +69,11 @@ var CloseObserver = {
     aSubject.QueryInterface(Ci.nsISupportsPRBool);
     let message = this.expectedValue ? "close" : "stay open";
     ok(this.expectedValue === aSubject.data, "Expected browser to " + message);
     aSubject.data = true;
   },
 
   uninit: function ()
   {
-    Services.obs.removeObserver(this, "browser-lastwindow-close-requested", false);
+    Services.obs.removeObserver(this, "browser-lastwindow-close-requested");
   },
 };
--- a/devtools/client/webide/content/prefs.js
+++ b/devtools/client/webide/content/prefs.js
@@ -29,17 +29,17 @@ window.addEventListener("load", function
 }, true);
 
 window.addEventListener("unload", function onUnload() {
   window.removeEventListener("unload", onUnload);
   let inputs = document.querySelectorAll("[data-pref]");
   for (let i of inputs) {
     let pref = i.dataset.pref;
     i.removeEventListener("change", SaveForm, false);
-    Services.prefs.removeObserver(pref, FillForm, false);
+    Services.prefs.removeObserver(pref, FillForm);
   }
 }, true);
 
 function CloseUI() {
   window.parent.UI.openProject();
 }
 
 function ShowAddons() {
--- a/devtools/server/actors/storage.js
+++ b/devtools/server/actors/storage.js
@@ -164,17 +164,17 @@ StorageActors.defaults = function (typeN
       this.onWindowDestroyed = this.onWindowDestroyed.bind(this);
       events.on(this.storageActor, "window-ready", this.onWindowReady);
       events.on(this.storageActor, "window-destroyed", this.onWindowDestroyed);
     },
 
     destroy() {
       if (observationTopics) {
         observationTopics.forEach((observationTopic) => {
-          Services.obs.removeObserver(this, observationTopic, false);
+          Services.obs.removeObserver(this, observationTopic);
         });
       }
       events.off(this.storageActor, "window-ready", this.onWindowReady);
       events.off(this.storageActor, "window-destroyed", this.onWindowDestroyed);
 
       this.hostVsStores.clear();
 
       protocol.Actor.prototype.destroy.call(this);
@@ -889,17 +889,17 @@ var cookieHelpers = {
   },
 
   addCookieObservers() {
     Services.obs.addObserver(cookieHelpers, "cookie-changed", false);
     return null;
   },
 
   removeCookieObservers() {
-    Services.obs.removeObserver(cookieHelpers, "cookie-changed", false);
+    Services.obs.removeObserver(cookieHelpers, "cookie-changed");
     return null;
   },
 
   observe(subject, topic, data) {
     if (!subject) {
       return;
     }
 
@@ -2456,18 +2456,18 @@ let StorageActor = protocol.ActorClassWi
     this.destroyed = false;
     this.boundUpdate = {};
   },
 
   destroy() {
     clearTimeout(this.batchTimer);
     this.batchTimer = null;
     // Remove observers
-    Services.obs.removeObserver(this, "content-document-global-created", false);
-    Services.obs.removeObserver(this, "inner-window-destroyed", false);
+    Services.obs.removeObserver(this, "content-document-global-created");
+    Services.obs.removeObserver(this, "inner-window-destroyed");
     this.destroyed = true;
     if (this.parentActor.browser) {
       this.parentActor.browser.removeEventListener("pageshow", this.onPageChange, true);
       this.parentActor.browser.removeEventListener("pagehide", this.onPageChange, true);
     }
     // Destroy the registered store types
     for (let actor of this.childActorPool.values()) {
       actor.destroy();
--- a/devtools/server/actors/tab.js
+++ b/devtools/server/actors/tab.js
@@ -1493,17 +1493,17 @@ function DebuggerProgressListener(tabAct
 DebuggerProgressListener.prototype = {
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIWebProgressListener,
     Ci.nsISupportsWeakReference,
     Ci.nsISupports,
   ]),
 
   destroy() {
-    Services.obs.removeObserver(this, "inner-window-destroyed", false);
+    Services.obs.removeObserver(this, "inner-window-destroyed");
     this._knownWindowIDs.clear();
     this._knownWindowIDs = null;
   },
 
   watch(docShell) {
     // Add the docshell to the watched set. We're actually adding the window,
     // because docShell objects are not wrappercached and would be rejected
     // by the WeakSet.
--- a/devtools/server/actors/webextension-inspected-window.js
+++ b/devtools/server/actors/webextension-inspected-window.js
@@ -161,17 +161,17 @@ CustomizedReload.prototype = {
   stop(error) {
     if (this.stopped) {
       return;
     }
 
     this.docShell.removeProgressListener(this);
 
     if (this.injectedScript) {
-      Services.obs.removeObserver(this, "document-element-inserted", false);
+      Services.obs.removeObserver(this, "document-element-inserted");
     }
 
     // Reset the customized user agent.
     if (this.userAgent && this.docShell.customUserAgent == this.userAgent) {
       this.docShell.customUserAgent = null;
     }
 
     if (error) {
--- a/devtools/server/actors/worker.js
+++ b/devtools/server/actors/worker.js
@@ -297,17 +297,17 @@ protocol.ActorClassWithSpec(serviceWorke
       // - In non-e10s: registrations always have at least one worker, if the worker is
       // active, the registration is active.
       active: isE10s ? true : !!activeWorker
     };
   },
 
   destroy() {
     protocol.Actor.prototype.destroy.call(this);
-    Services.obs.removeObserver(this, PushService.subscriptionModifiedTopic, false);
+    Services.obs.removeObserver(this, PushService.subscriptionModifiedTopic);
     this._registration.removeListener(this);
     this._registration = null;
     if (this._pushSubscriptionActor) {
       this._pushSubscriptionActor.destroy();
     }
     this._pushSubscriptionActor = null;
 
     this._installingWorker.destroy();
--- a/devtools/server/tests/unit/test_register_actor.js
+++ b/devtools/server/tests/unit/test_register_actor.js
@@ -87,17 +87,17 @@ function test_lazy_api() {
   }
   function onRequest(aResponse) {
     do_check_eq(aResponse, "world");
 
     // Finally, the actor is loaded on the first request being made to it
     do_check_true(isActorLoaded);
     do_check_true(isActorInstanciated);
 
-    Services.obs.removeObserver(onActorEvent, "actor", false);
+    Services.obs.removeObserver(onActorEvent, "actor");
     client.close().then(() => run_next_test());
   }
 }
 
 function cleanup() {
   DebuggerServer.destroy();
 
   // Check that all actors are unregistered on server destruction
--- a/devtools/shared/content-observer.js
+++ b/devtools/shared/content-observer.js
@@ -33,19 +33,19 @@ ContentObserver.prototype = {
       this._onInnerWindowDestroyed, "inner-window-destroyed", false);
   },
 
   /**
    * Stops listening for the required observer messages.
    */
   stopListening: function () {
     Services.obs.removeObserver(
-      this._onContentGlobalCreated, "content-document-global-created", false);
+      this._onContentGlobalCreated, "content-document-global-created");
     Services.obs.removeObserver(
-      this._onInnerWindowDestroyed, "inner-window-destroyed", false);
+      this._onInnerWindowDestroyed, "inner-window-destroyed");
   },
 
   /**
    * Fired immediately after a web content document window has been set up.
    */
   _onContentGlobalCreated: function (subject, topic, data) {
     if (subject == this._contentWindow) {
       events.emit(this, "global-created", subject);
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -1484,17 +1484,17 @@ NetworkMonitor.prototype = {
   destroy: function () {
     if (Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT) {
       gActivityDistributor.removeObserver(this);
       Services.obs.removeObserver(this._httpResponseExaminer,
                                   "http-on-examine-response");
       Services.obs.removeObserver(this._httpResponseExaminer,
                                   "http-on-examine-cached-response");
       Services.obs.removeObserver(this._httpModifyExaminer,
-                                  "http-on-modify-request", false);
+                                  "http-on-modify-request");
     }
 
     Services.obs.removeObserver(this._serviceWorkerRequest,
                                 "service-worker-synthesized-response");
 
     this.interceptedChannels.clear();
     this.openRequests = {};
     this.openResponses = {};
--- a/devtools/shared/webconsole/server-logger-monitor.js
+++ b/devtools/shared/webconsole/server-logger-monitor.js
@@ -117,17 +117,17 @@ var ServerLoggerMonitor = {
     let size = this.targets.size;
     trace.log("ServerLoggerMonitor.onDetachChild; size: ", size, target);
 
     // If this is the last child process attached, unregister
     // the global HTTP observer.
     if (!size) {
       trace.log("ServerLoggerMonitor.onDetachChild; Remove HTTP Observer");
       Services.obs.removeObserver(this.onExamineResponse,
-        "http-on-examine-response", false);
+        "http-on-examine-response");
     }
   },
 
   // HTTP Observer
 
   onExamineResponse: makeInfallible(function (subject, topic) {
     let httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
 
--- a/devtools/shared/webconsole/server-logger.js
+++ b/devtools/shared/webconsole/server-logger.js
@@ -96,17 +96,17 @@ var ServerLoggingListener = Class({
    */
   detach: makeInfallible(function () {
     trace.log("ServerLoggingListener.detach; ", this.owner.actorID);
 
     if (DebuggerServer.isInChildProcess) {
       this.detachParentProcess();
     } else {
       Services.obs.removeObserver(this.onExamineResponse,
-        "http-on-examine-response", false);
+        "http-on-examine-response");
     }
   }),
 
   // Parent Child Relationship
 
   attachParentProcess: function () {
     trace.log("ServerLoggingListener.attachParentProcess;");
 
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -343,16 +343,44 @@ KeyframeEffectReadOnly::CompositeValue(
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown compisite operation type");
       break;
   }
   return StyleAnimationValue();
 }
 
 StyleAnimationValue
+KeyframeEffectReadOnly::GetUnderlyingStyle(
+  nsCSSPropertyID aProperty,
+  const RefPtr<AnimValuesStyleRule>& aAnimationRule)
+{
+  StyleAnimationValue result;
+
+  if (aAnimationRule->HasValue(aProperty)) {
+    // If we have already composed style for the property, we use the style
+    // as the underlying style.
+    DebugOnly<bool> success = aAnimationRule->GetValue(aProperty, result);
+    MOZ_ASSERT(success, "AnimValuesStyleRule::GetValue should not fail");
+  } else {
+    // If we are composing with composite operation that is not 'replace'
+    // and we have not composed style for the property yet, we have to get
+    // the base style for the property.
+    RefPtr<nsStyleContext> styleContext = GetTargetStyleContext();
+    result = EffectCompositor::GetBaseStyle(aProperty,
+                                            styleContext,
+                                            *mTarget->mElement,
+                                            mTarget->mPseudoType);
+    MOZ_ASSERT(!result.IsNull(), "The base style should be set");
+    SetNeedsBaseStyle(aProperty);
+  }
+
+  return result;
+}
+
+StyleAnimationValue
 KeyframeEffectReadOnly::CompositeValue(
   nsCSSPropertyID aProperty,
   const RefPtr<AnimValuesStyleRule>& aAnimationRule,
   const StyleAnimationValue& aValueToComposite,
   CompositeOperation aCompositeOperation)
 {
   MOZ_ASSERT(mTarget, "CompositeValue should be called with target element");
 
@@ -369,33 +397,17 @@ KeyframeEffectReadOnly::CompositeValue(
   if (mDocument->IsStyledByServo()) {
     return result;
   }
 
   MOZ_ASSERT(!aValueToComposite.IsNull() ||
              aCompositeOperation == CompositeOperation::Add,
              "InputValue should be null only if additive composite");
 
-  if (aAnimationRule->HasValue(aProperty)) {
-    // If we have already composed style for the property, we use the style
-    // as the underlying style.
-    DebugOnly<bool> success = aAnimationRule->GetValue(aProperty, result);
-    MOZ_ASSERT(success, "AnimValuesStyleRule::GetValue should not fail");
-  } else {
-    // If we are composing with composite operation that is not 'replace'
-    // and we have not composed style for the property yet, we have to get
-    // the base style for the property.
-    RefPtr<nsStyleContext> styleContext = GetTargetStyleContext();
-    result = EffectCompositor::GetBaseStyle(aProperty,
-                                            styleContext,
-                                            *mTarget->mElement,
-                                            mTarget->mPseudoType);
-    MOZ_ASSERT(!result.IsNull(), "The base style should be set");
-    SetNeedsBaseStyle(aProperty);
-  }
+  result = GetUnderlyingStyle(aProperty, aAnimationRule);
 
   return CompositeValue(aProperty,
                         aValueToComposite,
                         result,
                         aCompositeOperation);
 }
 
 void
@@ -528,24 +540,27 @@ KeyframeEffectReadOnly::ComposeStyle(
     // Iteration composition for accumulate
     if (mEffectOptions.mIterationComposite ==
           IterationCompositeOperation::Accumulate &&
         computedTiming.mCurrentIteration > 0) {
       const AnimationPropertySegment& lastSegment =
         prop.mSegments.LastElement();
       // FIXME: Bug 1293492: Add a utility function to calculate both of
       // below StyleAnimationValues.
+      StyleAnimationValue lastValue = lastSegment.mToValue.IsNull()
+        ? GetUnderlyingStyle(prop.mProperty, aStyleRule)
+        : lastSegment.mToValue;
       fromValue =
         StyleAnimationValue::Accumulate(prop.mProperty,
-                                        lastSegment.mToValue,
+                                        lastValue,
                                         Move(fromValue),
                                         computedTiming.mCurrentIteration);
       toValue =
         StyleAnimationValue::Accumulate(prop.mProperty,
-                                        lastSegment.mToValue,
+                                        lastValue,
                                         Move(toValue),
                                         computedTiming.mCurrentIteration);
     }
 
     // Special handling for zero-length segments
     if (segment->mToKey == segment->mFromKey) {
       if (computedTiming.mProgress.Value() < 0) {
         aStyleRule->AddValue(prop.mProperty, Move(fromValue));
--- a/dom/animation/KeyframeEffectReadOnly.h
+++ b/dom/animation/KeyframeEffectReadOnly.h
@@ -421,16 +421,21 @@ protected:
   // |aAnimationRule|, uses the base value for the property recorded on the
   // target element's EffectSet.
   StyleAnimationValue CompositeValue(
     nsCSSPropertyID aProperty,
     const RefPtr<AnimValuesStyleRule>& aAnimationRule,
     const StyleAnimationValue& aValueToComposite,
     CompositeOperation aCompositeOperation);
 
+  // Returns underlying style animation value for |aProperty|.
+  StyleAnimationValue GetUnderlyingStyle(
+    nsCSSPropertyID aProperty,
+    const RefPtr<AnimValuesStyleRule>& aAnimationRule);
+
   // Set a bit in mNeedsBaseStyleSet if |aProperty| can be run on the
   // compositor.
   void SetNeedsBaseStyle(nsCSSPropertyID aProperty);
 
   // Ensure the base styles is available for any properties that can be run on
   // the compositor and which are not includes in |aPropertiesToSkip|.
   void EnsureBaseStylesForCompositor(
     const nsCSSPropertyIDSet& aPropertiesToSkip);
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/crashtests/1325193-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<meta charset="UTF-8">
+<script>
+function boom(){
+  o_0.animate([{"perspective": "262ex"}, {}], {duration: 1070, iterationStart: 68, iterationComposite: "accumulate"});
+  o_0.animate([{"color": "white", "flex": "inherit"}, {"color": "red", "flex": "66% "}], 3439);
+  o_0.animate([{"font": "icon"}], 1849);
+  setTimeout(function() {
+    document.documentElement.classList.remove("reftest-wait");
+  }, 500);
+}
+document.addEventListener("DOMContentLoaded", boom);
+</script>
+</head>
+<body><details id=o_0><q></q></details></body>
+</html>
--- a/dom/animation/test/crashtests/crashtests.list
+++ b/dom/animation/test/crashtests/crashtests.list
@@ -11,8 +11,9 @@ asserts-if(stylo,4-10) pref(dom.animatio
 asserts-if(stylo,5) pref(dom.animations-api.core.enabled,true) load 1278485-1.html # bug 1324691
 asserts-if(stylo,31) pref(dom.animations-api.core.enabled,true) load 1277272-1.html # bug 1324694
 asserts-if(stylo,2) pref(dom.animations-api.core.enabled,true) load 1290535-1.html # bug 1324690
 pref(dom.animations-api.core.enabled,true) load 1304886-1.html
 pref(dom.animations-api.core.enabled,true) load 1322382-1.html
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1322291-1.html # bug 1323733
 asserts-if(stylo,0-5) pref(dom.animations-api.core.enabled,true) load 1323114-1.html # bug 1324690
 asserts-if(stylo,0-5) pref(dom.animations-api.core.enabled,true) load 1323114-2.html # bug 1324690
+skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1325193-1.html # bug 1311257
--- a/dom/animation/test/style/file_missing-keyframe-on-compositor.html
+++ b/dom/animation/test/style/file_missing-keyframe-on-compositor.html
@@ -485,11 +485,31 @@ promise_test(t => {
       SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
     assert_matrix_equals(transform, 'matrix(1, 0, 0, 1, 150, 0)',
       'Transform value for animation with positive delay should be composed ' +
       'onto the base style');
   });
 }, 'Transform value for animation with no keyframe at offset 0 and with ' +
    'positive delay');
 
+promise_test(t => {
+  useTestRefreshMode(t);
+
+  var div = addDiv(t, { style: 'transform: translateX(100px)' });
+
+  div.animate([{ offset: 0, transform: 'translateX(200px)'}],
+              { duration: 100 * MS_PER_SEC,
+                iterationStart: 1,
+                iterationComposite: 'accumulate' });
+
+  return waitForPaintsFlushed().then(() => {
+    var transform =
+      SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
+    assert_matrix_equals(transform, 'matrix(1, 0, 0, 1, 300, 0)',
+      'Transform value for animation with no keyframe at offset 1 and its ' +
+      'iterationComposite is accumulate');
+  });
+}, 'Transform value for animation with no keyframe at offset 1 and its ' +
+   'iterationComposite is accumulate');
+
 done();
 </script>
 </body>
--- a/dom/animation/test/style/file_missing-keyframe.html
+++ b/dom/animation/test/style/file_missing-keyframe.html
@@ -80,15 +80,29 @@ test(t => {
   div.animate([{ offset: 0, marginLeft: '200px' }], 100 * MS_PER_SEC);
 
   div.getAnimations().forEach(animation => {
     animation.currentTime = 50 * MS_PER_SEC;
   });
                                                  // (200px + 200px) * 0.5
   assert_equals(getComputedStyle(div).marginLeft, '200px',
                 'The margin-left value at 50% should be additive value of ' +
-                'of the transition and animation');
+                'the transition and animation');
 }, 'margin-left value at 50% for an animation with no keyframe at offset 1 ' +
    'is that of transition');
 
+test(t => {
+  var div = addDiv(t, { style: 'margin-left: 100px' });
+
+  var animation = div.animate([{ offset: 0, marginLeft: '200px' }],
+                              { duration: 100 * MS_PER_SEC,
+                                iterationStart: 1,
+                                iterationComposite: 'accumulate' });
+
+  assert_equals(getComputedStyle(div).marginLeft, '300px',
+                'The margin-left value should be additive value of the ' +
+                'accumulation of the initial value onto the base value ');
+}, 'margin-left value for an animation with no keyframe at offset 1 and its ' +
+   'iterationComposite is accumulate');
+
 done();
 </script>
 </body>
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1996,18 +1996,27 @@ Element::IsLabelable() const
 
 bool
 Element::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
 {
   return false;
 }
 
 DeclarationBlock*
-Element::GetInlineStyleDeclaration()
+Element::GetInlineStyleDeclaration() const
 {
+  if (!MayHaveStyle()) {
+    return nullptr;
+  }
+  const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style);
+
+  if (attrVal && attrVal->Type() == nsAttrValue::eCSSDeclaration) {
+    return attrVal->GetCSSDeclarationValue();
+  }
+
   return nullptr;
 }
 
 nsresult
 Element::SetInlineStyleDeclaration(DeclarationBlock* aDeclaration,
                                    const nsAString* aSerialized,
                                    bool aNotify)
 {
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -262,17 +262,17 @@ public:
   /**
    * Clear all style state locks on this element.
    */
   void ClearStyleStateLocks();
 
   /**
    * Get the inline style declaration, if any, for this element.
    */
-  virtual DeclarationBlock* GetInlineStyleDeclaration();
+  DeclarationBlock* GetInlineStyleDeclaration() const;
 
   /**
    * Set the inline style declaration for this element. This will send
    * an appropriate AttributeChanged notification if aNotify is true.
    */
   virtual nsresult SetInlineStyleDeclaration(DeclarationBlock* aDeclaration,
                                              const nsAString* aSerialized,
                                              bool aNotify);
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -6743,18 +6743,16 @@ nsContentUtils::IsPatternMatching(nsAStr
                                   nsIDocument* aDocument)
 {
   NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
 
   AutoJSAPI jsapi;
   jsapi.Init();
   JSContext* cx = jsapi.cx();
 
-  MOZ_RELEASE_ASSERT(js::AllowGCBarriers(cx), "IsPatternMatching can enter the JS engine during painting. See bug 1310335.");
-
   // We can use the junk scope here, because we're just using it for
   // regexp evaluation, not actual script execution.
   JSAutoCompartment ac(cx, xpc::UnprivilegedJunkScope());
 
   // The pattern has to match the entire value.
   aPattern.Insert(NS_LITERAL_STRING("^(?:"), 0);
   aPattern.AppendLiteral(")$");
 
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -4021,17 +4021,20 @@ NS_IMETHODIMP
 nsDOMWindowUtils::ForceUseCounterFlush(nsIDOMNode *aNode)
 {
   NS_ENSURE_ARG_POINTER(aNode);
 
   if (nsCOMPtr<nsIDocument> doc = do_QueryInterface(aNode)) {
     mozilla::css::ImageLoader* loader = doc->StyleImageLoader();
     loader->FlushUseCounters();
 
-    static_cast<nsDocument*>(doc.get())->ReportUseCounters();
+    // Flush the document and any external documents that it depends on.
+    const auto reportKind
+      = nsDocument::UseCounterReportKind::eIncludeExternalResources;
+    static_cast<nsDocument*>(doc.get())->ReportUseCounters(reportKind);
     return NS_OK;
   }
 
   if (nsCOMPtr<nsIContent> content = do_QueryInterface(aNode)) {
     if (HTMLImageElement* img = HTMLImageElement::FromContent(content)) {
       img->FlushUseCounters();
       return NS_OK;
     }
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -12299,26 +12299,39 @@ MightBeAboutOrChromeScheme(nsIURI* aURI)
   MOZ_ASSERT(aURI);
   bool isAbout = true;
   bool isChrome = true;
   aURI->SchemeIs("about", &isAbout);
   aURI->SchemeIs("chrome", &isChrome);
   return isAbout || isChrome;
 }
 
-void
-nsDocument::ReportUseCounters()
+static bool
+ReportExternalResourceUseCounters(nsIDocument* aDocument, void* aData)
+{
+  const auto reportKind
+    = nsDocument::UseCounterReportKind::eIncludeExternalResources;
+  static_cast<nsDocument*>(aDocument)->ReportUseCounters(reportKind);
+  return true;
+}
+
+void
+nsDocument::ReportUseCounters(UseCounterReportKind aKind)
 {
   static const bool sDebugUseCounters = false;
   if (mReportedUseCounters) {
     return;
   }
 
   mReportedUseCounters = true;
 
+  if (aKind == UseCounterReportKind::eIncludeExternalResources) {
+    EnumerateExternalResources(ReportExternalResourceUseCounters, nullptr);
+  }
+
   if (Telemetry::HistogramUseCounterCount > 0 &&
       (IsContentDocument() || IsResourceDoc())) {
     nsCOMPtr<nsIURI> uri;
     NodePrincipal()->GetURI(getter_AddRefs(uri));
     if (!uri || MightBeAboutOrChromeScheme(uri)) {
       return;
     }
 
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -769,17 +769,29 @@ public:
   virtual void SetValueMissingState(const nsAString& aName, bool aValue) override;
 
   // for radio group
   nsRadioGroupStruct* GetRadioGroup(const nsAString& aName) const;
   nsRadioGroupStruct* GetOrCreateRadioGroup(const nsAString& aName);
 
   virtual nsViewportInfo GetViewportInfo(const mozilla::ScreenIntSize& aDisplaySize) override;
 
-  void ReportUseCounters();
+  enum class UseCounterReportKind {
+    // Flush the document's use counters only; the use counters for any
+    // external resource documents will be flushed when the external
+    // resource documents themselves are destroyed.
+    eDefault,
+
+    // Flush use counters for the document and for its external resource
+    // documents. (Should only be necessary for tests, where we need
+    // flushing to happen synchronously and deterministically.)
+    eIncludeExternalResources,
+  };
+
+  void ReportUseCounters(UseCounterReportKind aKind = UseCounterReportKind::eDefault);
 
   virtual void AddIntersectionObserver(
     mozilla::dom::DOMIntersectionObserver* aObserver) override;
   virtual void RemoveIntersectionObserver(
     mozilla::dom::DOMIntersectionObserver* aObserver) override;
   virtual void UpdateIntersectionObservations() override;
   virtual void ScheduleIntersectionObserverNotification() override;
   virtual void NotifyIntersectionObservers() override;
--- a/dom/base/nsStyledElement.cpp
+++ b/dom/base/nsStyledElement.cpp
@@ -88,31 +88,16 @@ nsStyledElement::SetInlineStyleDeclarati
     static_cast<uint8_t>(nsIDOMMutationEvent::MODIFICATION) :
     static_cast<uint8_t>(nsIDOMMutationEvent::ADDITION);
 
   return SetAttrAndNotify(kNameSpaceID_None, nsGkAtoms::style, nullptr,
                           oldValue, attrValue, modType, hasListeners,
                           aNotify, kDontCallAfterSetAttr);
 }
 
-DeclarationBlock*
-nsStyledElement::GetInlineStyleDeclaration()
-{
-  if (!MayHaveStyle()) {
-    return nullptr;
-  }
-  const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style);
-
-  if (attrVal && attrVal->Type() == nsAttrValue::eCSSDeclaration) {
-    return attrVal->GetCSSDeclarationValue();
-  }
-
-  return nullptr;
-}
-
 // ---------------------------------------------------------------
 // Others and helpers
 
 nsICSSDeclaration*
 nsStyledElement::Style()
 {
   Element::nsDOMSlots *slots = DOMSlots();
 
--- a/dom/base/nsStyledElement.h
+++ b/dom/base/nsStyledElement.h
@@ -39,17 +39,16 @@ protected:
 
 public:
   // We don't want to implement AddRef/Release because that would add an extra
   // function call for those on pretty much all elements.  But we do need QI, so
   // we can QI to nsStyledElement.
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
 
   // Element interface methods
-  virtual mozilla::DeclarationBlock* GetInlineStyleDeclaration() override;
   virtual nsresult SetInlineStyleDeclaration(mozilla::DeclarationBlock* aDeclaration,
                                              const nsAString* aSerialized,
                                              bool aNotify) override;
 
   nsICSSDeclaration* Style();
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_STYLED_ELEMENT_IID)
 
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -729,16 +729,18 @@ support-files = file_receiveMessage.html
 [test_range_bounds.html]
 skip-if = toolkit == 'android'
 [test_reentrant_flush.html]
 skip-if = toolkit == 'android'
 [test_referrer_redirect.html]
 [test_root_iframe.html]
 [test_screen_orientation.html]
 [test_script_loader_crossorigin_data_url.html]
+[test_selection_with_anon_trees.html]
+skip-if = toolkit == 'android'
 [test_setInterval_uncatchable_exception.html]
 skip-if = debug == false
 [test_settimeout_extra_arguments.html]
 [test_settimeout_inner.html]
 [test_setTimeoutWith0.html]
 [test_setting_opener.html]
 [test_simplecontentpolicy.html]
 skip-if = e10s # Bug 1156489.
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_selection_with_anon_trees.html
@@ -0,0 +1,445 @@
+<!DOCTYPE>
+<html>
+<head>
+<title>selection over trees of anonymous nodes</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<style type="text/css">
+@font-face {
+  font-family: Ahem;
+  src: url("Ahem.ttf");
+}
+* { font-family: Ahem; font-size: 20px; }
+</style>
+
+</head>
+<body>
+
+<div id="test1">
+aaaaa aaaaa aaaaaaa aaaaaaaa aaaaaaaa
+<table>
+  <tbody>
+    <tr>
+      <td>
+        <input value="1111">
+      </td>
+    </tr>
+  <tbody>
+</table>
+bbbbbbbb
+<br>
+<br>
+</div>
+
+<div id="test2">
+ccccc ccccccc cccccccccccc ccccccccc
+<div>
+  <input value="2222">
+</div>
+dddddd
+</div>
+
+<div id="test3">
+aaaaa
+<br>
+<input type="button"><br>
+<br>
+<input type="button"><br>
+BBBBB BB<br>
+<br>
+</div>
+
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+const kIsWin = navigator.platform.indexOf("Win") == 0;
+
+function test()
+{
+  function clear(w)
+  {
+    var sel = (w ? w : window).getSelection();
+    sel.removeAllRanges();
+  }
+  function doneTest(e)
+  {
+    // We hide the elements we're done with so that later tests
+    // are inside the rather narrow iframe mochitest gives us.
+    // It matters for synthesizeMouse event tests.
+    e.style.display = 'none';
+    e.offsetHeight;
+  }
+  function show(e) { e.style.display = ''; }
+
+  function dragSelectWithKey(e, clickCount, shiftKey, pointsArray)
+  {
+    const kMove = { type:"mousemove", shiftKey:shiftKey };
+    let pt = pointsArray[0];
+    let x = pt[0];
+    let y = pt[1];
+    synthesizeMouse(e, x, y, { type:"mousedown", clickCount:clickCount, shiftKey:shiftKey });
+    pointsArray.forEach(function(pt) {
+      let x = pt[0];
+      let y = pt[1];
+      synthesizeMouse(e, x, y, kMove);
+    });
+    pt = pointsArray[pointsArray.length - 1];
+    x = pt[0];
+    y = pt[1];
+    synthesizeMouse(e, x, y, kMove);
+    synthesizeMouse(e, x, y, { type:"mouseup", shiftKey:shiftKey });
+  }
+
+  function shiftDragSelect(e, clickCount, pointsArray)
+  {
+    dragSelectWithKey(e, clickCount, true, pointsArray);
+  }
+  function dragSelect(e, clickCount, pointsArray)
+  {
+    dragSelectWithKey(e, clickCount, false, pointsArray);
+  }
+
+  function shiftClick(e, x, y)
+  {
+    synthesizeMouse(e, x, y, { type: "mousedown", shiftKey: true });
+    synthesizeMouse(e, x, y, { type: "mouseup", shiftKey: true });
+  }
+
+  function click(e, x, y)
+  {
+    synthesizeMouse(e, x, y, { type: "mousedown" });
+    synthesizeMouse(e, x, y, { type: "mouseup" });
+  }
+
+  function repeatShiftKey(k, n) {
+    for (let i = 0; i < n; ++i) {
+      synthesizeKey(k, { shiftKey:true });
+    }
+  }
+
+  function init(arr, e)
+  {
+    clear();
+    var sel = window.getSelection();
+    for (i = 0; i < arr.length; ++i) {
+      var data = arr[i];
+      var r = new Range()
+      r.setStart(node(e, data[0]), data[1]);
+      r.setEnd(node(e, data[2]), data[3]);
+      sel.addRange(r);
+    }
+  }
+
+  function NL(s) { return s.replace(/(\r\n|\n\r|\r)/g, '\n'); }
+
+  function checkText(text, e)
+  {
+    var sel = window.getSelection();
+    is(NL(sel.toString()), text, e.id + ": selected text")
+  }
+
+  function checkRangeText(text, index)
+  {
+    var r = window.getSelection().getRangeAt(index);
+    is(NL(r.toString()), text, e.id + ": range["+index+"].toString()")
+  }
+
+  function node(e, arg)
+  {
+    if (typeof arg == "number")
+      return arg == -1 ? e : e.childNodes[arg];
+    return arg;
+  }
+
+  function checkRangeCount(n, e)
+  {
+    var sel = window.getSelection();
+    is(sel.rangeCount, n, e.id + ": Selection range count");
+  }
+
+  function checkRange(r, expected, e) {
+    is(r.startContainer, node(e, expected[0]), e.id + ": range["+i+"].startContainer");
+    is(r.startOffset, expected[1], e.id + ": range["+i+"].startOffset");
+    is(r.endContainer, node(e, expected[2]), e.id + ": range["+i+"].endContainer");
+    is(r.endOffset, expected[3], e.id + ": range["+i+"].endOffset");
+  }
+
+  function checkRanges(arr, e)
+  {
+    let sel = window.getSelection();
+    checkRangeCount(arr.length, e);
+    for (i = 0; i < arr.length; ++i) {
+      let expected = arr[i];
+      let r = sel.getRangeAt(i);
+      checkRange(r, expected, e);
+    }
+  }
+
+  // ======================================================
+  // ================== dragSelect tests ==================
+  // ======================================================
+
+  // Downward word-select with SHIFT
+  clear();
+  var e = document.getElementById('test1');
+  shiftDragSelect(e, 2, [[20,5], [30,5], [250,5], [250,20], [250,50], [60,110], [10,110]]);
+  checkText('aaaaa aaaaa aaaaaaa aaaaaaaa aaaaaaaa\nbbbbbbbb ', e);
+  checkRanges([[0,1,-1,3]], e);
+
+  // Downward word-select without SHIFT
+  clear();
+  dragSelect(e, 2, [[20,5], [30,5], [250,5], [250,20], [250,50], [60,110], [10,110]]);
+  checkText('aaaaa aaaaa aaaaaaa aaaaaaaa aaaaaaaa\nbbbbbbbb ', e);
+  checkRanges([[0,1,-1,3]], e);
+
+  // Downward char-select with SHIFT
+  clear();
+  click(e, 1, 1);
+  shiftDragSelect(e, 1, [[30,5], [250,5], [250,20], [250,50], [60,110], [20,110]]);
+  checkText('aaaaa aaaaa aaaaaaa aaaaaaaa aaaaaaaa\nb', e);
+  checkRanges([[0,1,2,2]], e);
+
+  // Downward char-select without SHIFT
+  clear();
+  dragSelect(e, 1, [[40,5], [60,70], [30,110]]);
+  checkText('aaa aaaaa aaaaaaa aaaaaaaa aaaaaaaa\nb', e);
+  checkRanges([[0,3,2,2]], e);
+
+  // Upward word-select with SHIFT
+  clear();
+  shiftDragSelect(e, 2, [[20,110], [60,110], [250,50], [250,20], [250,5], [30,5], [30,5]]);
+  checkText('aaaaa aaaaa aaaaaaa aaaaaaaa aaaaaaaa\nbbbbbbbb ', e);
+  checkRanges([[0,1,-1,3]], e);
+
+  // Upward word-select without SHIFT
+  clear();
+  dragSelect(e, 2, [[20,110], [60,110], [250,50], [250,20], [250,5], [30,5], [30,5]]);
+  checkText('aaaaa aaaaa aaaaaaa aaaaaaaa aaaaaaaa\nbbbbbbbb ', e);
+  checkRanges([[0,1,-1,3]], e);
+
+  // Upward char-select with SHIFT
+  clear();
+  click(e, 100, 110);
+  shiftDragSelect(e, 1, [[20,110], [250,50], [250,20], [250,5], [30,5]]);
+  checkText('aaaa aaaaa aaaaaaa aaaaaaaa aaaaaaaa\nbbbbb', e);
+  checkRanges([[0,2,2,6]], e);
+
+  // Upward char-select without SHIFT
+  clear();
+  dragSelect(e, 1, [[30,110], [60,110], [40,5]]);
+  checkText('aaa aaaaa aaaaaaa aaaaaaaa aaaaaaaa\nb', e);
+  checkRanges([[0,3,2,2]], e);
+
+  doneTest(e);
+
+  // Downward word-select with SHIFT
+  var e = document.getElementById('test2');
+  shiftDragSelect(e, 2, [[20,5], [30,5], [250,5], [250,20], [250,50], [60,100], [10,100]]);
+  checkText('ccccc ccccccc cccccccccccc ccccccccc\ndddddd', e);
+  checkRanges([[0,1,2,7]], e);
+
+  // Downward word-select without SHIFT
+  clear();
+  dragSelect(e, 2, [[20,5], [30,5], [250,5], [250,20], [250,50], [60,100], [10,100]]);
+  checkText('ccccc ccccccc cccccccccccc ccccccccc\ndddddd', e);
+  checkRanges([[0,1,2,7]], e);
+
+  // Downward char-select with SHIFT
+  clear();
+  click(e, 1, 1);
+  shiftDragSelect(e, 1, [[30,5], [30,5], [250,5], [250,20], [250,50], [60,100], [20,100]]);
+  checkText('ccccc ccccccc cccccccccccc ccccccccc\nd', e);
+  checkRanges([[0,1,2,2]], e);
+
+  // Downward char-select without SHIFT
+  clear();
+  dragSelect(e, 1, [[40,5], [250,5], [250,20], [250,50], [60,100], [20,100]]);
+  checkText('ccc ccccccc cccccccccccc ccccccccc\nd', e);
+  checkRanges([[0,3,2,2]], e);
+
+  // Upward word-select with SHIFT
+  var e = document.getElementById('test2');
+  shiftDragSelect(e, 2, [[10,100], [60,100], [250,50], [250,20], [250,5], [30,5], [20,5]]);
+  checkText('ccccc ccccccc cccccccccccc ccccccccc\ndddddd', e);
+  checkRanges([[0,1,2,7]], e);
+
+  // Upward word-select without SHIFT
+  clear();
+  shiftDragSelect(e, 2, [[10,100], [60,100], [250,50], [250,20], [250,5], [30,5], [20,5]]);
+  checkText('ccccc ccccccc cccccccccccc ccccccccc\ndddddd', e);
+  checkRanges([[0,1,2,7]], e);
+
+  // Upward char-select with SHIFT
+  clear();
+  click(e, 100, 100);
+  shiftDragSelect(e, 1, [[20,100], [250,50], [250,20], [250,5], [60,5]]);
+  checkText('cc ccccccc cccccccccccc ccccccccc\nddddd', e);
+  checkRanges([[0,4,2,6]], e);
+
+  // Upward char-select without SHIFT
+  clear();
+  dragSelect(e, 1, [[20,100], [60,100], [60,5]]);
+  checkText('cc ccccccc cccccccccccc ccccccccc\nd', e);
+  checkRanges([[0,4,2,2]], e);
+
+  doneTest(e);
+
+  // On Windows word-selection also selects the space after the word.
+  kSpaceAfterWord = kIsWin ? ' ' : '';
+
+  // Downward word-select with SHIFT
+  e = document.getElementById('test3');
+  clear();
+  shiftDragSelect(e, 2, [[50,5], [60,125], [10,125]]);
+  checkText('aaaaa\n\n\n\nBBBBB' + kSpaceAfterWord, e);
+  checkRanges([[0,1,-1,3], [4,0,-1,8], [9,0,10,6 + kSpaceAfterWord.length]], e);
+
+  // Downward word-select without SHIFT
+  e = document.getElementById('test3');
+  clear();
+  dragSelect(e, 2, [[40,5], [30,5], [50,5], [60,125], [10,125]]);
+  checkText('aaaaa\n\n\n\nBBBBB' + kSpaceAfterWord, e);
+  checkRanges([[0,1,-1,3], [4,0,-1,8], [9,0,10,6 + kSpaceAfterWord.length]], e);
+
+  // Downward char-select without SHIFT
+  e = document.getElementById('test3');
+  clear();
+  dragSelect(e, 1, [[60,5], [60,125], [20,125]]);
+  checkText('aa\n\n\n\nB', e);
+  checkRanges([[0,4,-1,3], [4,0,-1,8], [9,0,10,2]], e);
+
+  // Upward word-select with SHIFT
+  e = document.getElementById('test3');
+  clear();
+  shiftDragSelect(e, 2, [[10,125], [60,125], [50,5]]);
+  checkText('aaaaa\n\n\n\nBBBBB' + kSpaceAfterWord, e);
+  checkRanges([[0,1,-1,3], [4,0,-1,8], [9,0,10,6 + kSpaceAfterWord.length]], e);
+
+  // Upward word-select without SHIFT
+  e = document.getElementById('test3');
+  clear();
+  dragSelect(e, 2, [[40,125], [60,125], [50,5], [30,5], [20,5]]);
+  checkText('aaaaa\n\n\n\nBBBBB' + kSpaceAfterWord, e);
+  checkRanges([[0,1,-1,3], [4,0,-1,8], [9,0,10,6 + kSpaceAfterWord.length]], e);
+
+  // Upward char-select without SHIFT
+  e = document.getElementById('test3');
+  clear();
+  dragSelect(e, 1, [[60,125], [60,125], [50,5], [30,5], [40,5]]);
+  checkText('aaa\n\n\n\nBBB', e);
+  checkRanges([[0,3,-1,3], [4,0,-1,8], [9,0,10,4]], e);
+
+  doneTest(e);
+
+
+  // ======================================================
+  // ================== shift+click tests =================
+  // ======================================================
+
+  e = document.getElementById('test1');
+  show(e);
+
+  clear();
+  init([[0,0,0,1]], e);
+  shiftClick(e, 100, 100);
+  checkText(' aaaaa aaaaa aaaaaaa aaaaaaaa aaaaaaaa\nbbbbb', e);
+  checkRanges([[0,0,2,6]], e);
+  
+  clear();
+  init([[2,6,2,6]], e);
+  shiftClick(e, 60, 5);
+  checkText('aa aaaaa aaaaaaa aaaaaaaa aaaaaaaa\nbbbbb', e);
+  checkRanges([[0,4,2,6]], e);
+  
+  doneTest(e);
+  e = document.getElementById('test2');
+  show(e);
+
+  clear();
+  init([[0,4,0,4]], e);
+  shiftClick(e, 100, 100);
+  checkText('cc ccccccc cccccccccccc ccccccccc\nddddd', e);
+  checkRanges([[0,4,2,6]], e);
+
+  clear();
+  init([[2,6,2,6]], e);
+  shiftClick(e, 1, 5);
+  checkText('ccccc ccccccc cccccccccccc ccccccccc\nddddd', e);
+  checkRanges([[0,1,2,6]], e);
+
+  doneTest(e);
+  e = document.getElementById('test3');
+  show(e);
+
+  clear();
+  init([[0,4,0,4]], e);
+  shiftClick(e, 70, 125);
+  checkText('aa\n\n\n\nBBB', e);
+  checkRanges([[0,4,-1,3], [4,0,-1,8], [9,0,10,4]], e);
+
+  // ======================================================
+  // ================== Kbd command tests =================
+  // ======================================================
+
+  doneTest(e);
+  e = document.getElementById('test1');
+  show(e);
+
+  clear();
+  init([[0,0,0,0]], e);
+  repeatShiftKey("VK_RIGHT", 45);
+  checkText(' aaaaa aaaaa aaaaaaa aaaaaaaa aaaaaaaa\nbbb', e);
+  checkRanges([[0,0,2,4]], e);
+
+  clear();
+  init([[2,5,2,5]], e);
+  repeatShiftKey("VK_LEFT", 45);
+  checkText('aaaaa aaaaa aaaaaaa aaaaaaaa aaaaaaaa\nbbbb', e);
+  checkRanges([[0,1,2,5]], e);
+
+  doneTest(e);
+  e = document.getElementById('test2');
+  show(e);
+
+  clear();
+  init([[0,0,0,0]], e);
+  repeatShiftKey("VK_RIGHT", 45);
+  checkText(' ccccc ccccccc cccccccccccc ccccccccc\ndddd', e);
+  checkRanges([[0,0,2,5]], e);
+
+  clear();
+  init([[2,6,2,6]], e);
+  repeatShiftKey("VK_LEFT", 45);
+  checkText('ccccc ccccccc cccccccccccc ccccccccc\nddddd', e);
+  checkRanges([[0,1,2,6]], e);
+
+  doneTest(e);
+  e = document.getElementById('test3');
+  show(e);
+
+  clear();
+  init([[0,0,0,0]], e);
+  repeatShiftKey("VK_RIGHT", 15);
+  checkText(' aaaaa\n\n\n\nBBB', e);
+  checkRanges([[0,0,-1,3], [4,0,-1,8], [9,0,10,4]], e);
+
+  clear();
+  init([[10,4,10,4]], e);
+  repeatShiftKey("VK_LEFT", 10);
+  checkText('aaaa\n\n\n\nBBB', e);
+  checkRanges([[0,2,-1,3], [4,0,-1,8], [9,0,10,4]], e);
+
+  clear();
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(test);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1074,16 +1074,17 @@ WebGLContext::SetDimensions(int32_t sign
     mOptions.antialias = gl->Caps().antialias;
 
     //////
     // Initial setup.
 
     MakeContextCurrent();
 
     gl->fViewport(0, 0, mWidth, mHeight);
+    mViewportX = mViewportY = 0;
     mViewportWidth = mWidth;
     mViewportHeight = mHeight;
 
     gl->fScissor(0, 0, mWidth, mHeight);
     gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
 
     //////
     // Check everything
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1442,16 +1442,18 @@ protected:
 
     // What is supported:
     uint32_t mGLMaxColorAttachments;
     uint32_t mGLMaxDrawBuffers;
     // What we're allowing:
     uint32_t mImplMaxColorAttachments;
     uint32_t mImplMaxDrawBuffers;
 
+    uint32_t mImplMaxViewportDims[2];
+
 public:
     GLenum LastColorAttachmentEnum() const {
         return LOCAL_GL_COLOR_ATTACHMENT0 + mImplMaxColorAttachments - 1;
     }
 
     const decltype(mOptions)& Options() const { return mOptions; }
 
 protected:
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -2135,16 +2135,19 @@ void
 WebGLContext::Viewport(GLint x, GLint y, GLsizei width, GLsizei height)
 {
     if (IsContextLost())
         return;
 
     if (width < 0 || height < 0)
         return ErrorInvalidValue("viewport: negative size");
 
+    width = std::min(width, (GLsizei)mImplMaxViewportDims[0]);
+    height = std::min(height, (GLsizei)mImplMaxViewportDims[1]);
+
     MakeContextCurrent();
     gl->fViewport(x, y, width, height);
 
     mViewportX = x;
     mViewportY = y;
     mViewportWidth = width;
     mViewportHeight = height;
 }
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -564,16 +564,18 @@ WebGLContext::InitAndValidateGL(FailureR
     }
 
     mBound2DTextures.SetLength(mGLMaxTextureUnits);
     mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits);
     mBound3DTextures.SetLength(mGLMaxTextureUnits);
     mBound2DArrayTextures.SetLength(mGLMaxTextureUnits);
     mBoundSamplers.SetLength(mGLMaxTextureUnits);
 
+    gl->fGetIntegerv(LOCAL_GL_MAX_VIEWPORT_DIMS, (GLint*)mImplMaxViewportDims);
+
     ////////////////
 
     if (MinCapabilityMode()) {
         mImplMaxTextureSize = MINVALUE_GL_MAX_TEXTURE_SIZE;
         mImplMaxCubeMapTextureSize = MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE;
         mImplMaxRenderbufferSize = MINVALUE_GL_MAX_RENDERBUFFER_SIZE;
 
         mImplMax3DTextureSize = MINVALUE_GL_MAX_3D_TEXTURE_SIZE;
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -312,17 +312,16 @@ HangMonitorChild::InterruptCallback()
 
     mForcePaint = false;
   }
 
   if (forcePaint) {
     RefPtr<TabChild> tabChild = TabChild::FindTabChild(forcePaintTab);
     if (tabChild) {
       JS::AutoAssertNoGC nogc(mContext);
-      JS::AutoAssertOnBarrier nobarrier(mContext);
       tabChild->ForcePaint(forcePaintEpoch);
     }
   }
 }
 
 void
 HangMonitorChild::Shutdown()
 {
@@ -396,17 +395,16 @@ HangMonitorChild::RecvForcePaint(const T
   {
     MonitorAutoLock lock(mMonitor);
     mForcePaint = true;
     mForcePaintTab = aTabId;
     mForcePaintEpoch = aLayerObserverEpoch;
   }
 
   JS_RequestInterruptCallback(mContext);
-  JS::RequestGCInterruptCallback(mContext);
 
   return IPC_OK();
 }
 
 void
 HangMonitorChild::ClearForcePaint()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
@@ -1193,17 +1191,16 @@ CreateHangMonitorParent(ContentParent* a
 
 void
 mozilla::CreateHangMonitorChild(Endpoint<PProcessHangMonitorChild>&& aEndpoint)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   JSContext* cx = danger::GetJSContext();
   JS_AddInterruptCallback(cx, InterruptCallback);
-  JS::AddGCInterruptCallback(cx, InterruptCallback);
 
   ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
   auto* child = new HangMonitorChild(monitor);
 
   monitor->MonitorLoop()->PostTask(NewNonOwningRunnableMethod
                                    <Endpoint<PProcessHangMonitorChild>&&>(child,
                                                                           &HangMonitorChild::Bind,
                                                                           Move(aEndpoint)));
--- a/dom/media/ipc/VideoDecoderManagerChild.cpp
+++ b/dom/media/ipc/VideoDecoderManagerChild.cpp
@@ -214,22 +214,24 @@ VideoDecoderManagerChild::Readback(const
 {
   // We can't use NS_DISPATCH_SYNC here since that can spin the event
   // loop while it waits. This function can be called from JS and we
   // don't want that to happen.
   SynchronousTask task("Readback sync");
 
   RefPtr<VideoDecoderManagerChild> ref = this;
   SurfaceDescriptor sd;
-  sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([&]() {
+  if (NS_FAILED(sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([&]() {
     AutoCompleteTask complete(&task);
     if (ref->CanSend()) {
       ref->SendReadback(aSD, &sd);
     }
-  }), NS_DISPATCH_NORMAL);
+  }), NS_DISPATCH_NORMAL))) {
+    return nullptr;
+  }
 
   task.Wait();
 
   if (!IsSurfaceDescriptorValid(sd)) {
     return nullptr;
   }
 
   RefPtr<DataSourceSurface> source = GetSurfaceForDescriptor(sd);
--- a/dom/plugins/test/crashtests/crashtests.list
+++ b/dom/plugins/test/crashtests/crashtests.list
@@ -4,11 +4,11 @@ load 110650-1.html
 skip-if(!haveTestPlugin) script 539897-1.html
 asserts-if(winWidget&&browserIsRemote,0-1) skip-if(!haveTestPlugin) script 540114-1.html
 load 570884.html
 # This test relies on the reading of screenX/Y forcing a round trip to
 # the X server, which is a bad assumption for <browser remote>.
 # Plugin arch is going to change anyway with OOP content so skipping
 # this test for now is OK.
 skip-if(!haveTestPlugin||http.platform!="X11") load 598862.html
-asserts-if(stylo,20) skip-if(Android) load 626602-1.html # bug 1324663 # bug 908363
+skip-if(Android) load 626602-1.html # bug 908363
 load 752340.html
 asserts-if(stylo,1) load 843086.xhtml # bug 1324647
--- a/dom/push/test/xpcshell/head.js
+++ b/dom/push/test/xpcshell/head.js
@@ -27,17 +27,17 @@ const WEBSOCKET_CLOSE_GOING_AWAY = 1001;
 const MS_IN_ONE_DAY = 24 * 60 * 60 * 1000;
 
 var isParent = Cc['@mozilla.org/xre/runtime;1']
                  .getService(Ci.nsIXULRuntime).processType ==
                  Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
 
 // Stop and clean up after the PushService.
 Services.obs.addObserver(function observe(subject, topic, data) {
-  Services.obs.removeObserver(observe, topic, false);
+  Services.obs.removeObserver(observe, topic);
   serviceExports.PushService.uninit();
   // Occasionally, `profile-change-teardown` and `xpcom-shutdown` will fire
   // before the PushService and AlarmService finish writing to IndexedDB. This
   // causes spurious errors and crashes, so we spin the event loop to let the
   // writes finish.
   let done = false;
   setTimeout(() => done = true, 1000);
   let thread = Services.tm.mainThread;
@@ -87,17 +87,17 @@ function waterfall(...callbacks) {
  */
 function promiseObserverNotification(topic, matchFunc) {
   return new Promise((resolve, reject) => {
     Services.obs.addObserver(function observe(subject, topic, data) {
       let matches = typeof matchFunc != 'function' || matchFunc(subject, data);
       if (!matches) {
         return;
       }
-      Services.obs.removeObserver(observe, topic, false);
+      Services.obs.removeObserver(observe, topic);
       resolve({subject, data});
     }, topic, false);
   });
 }
 
 /**
  * Wraps an object in a proxy that traps property gets and returns stubs. If
  * the stub is a function, the original value will be passed as the first
--- a/dom/svg/crashtests/crashtests.list
+++ b/dom/svg/crashtests/crashtests.list
@@ -70,15 +70,15 @@ load 880544-1.svg
 load 880544-2.svg
 load 880544-3.svg
 load 880544-4.svg
 load 880544-5.svg
 load 898915-1.svg
 load 1035248-1.svg
 load 1035248-2.svg
 load 1244898-1.xhtml
-asserts-if(stylo,3) load 1250725.html # bug 1324669
+asserts-if(stylo,2) load 1250725.html # bug 1324669
 load 1267272-1.svg
 load 1282985-1.svg
 # Disabled for now due to it taking a very long time to run - bug 1259356
 #load long-clipPath-reference-chain.svg
 load zero-size-image.svg
 asserts-if(stylo,2) load 1322286.html # bug 1324669
--- a/dom/tests/browser/browser_ConsoleAPI_originAttributes.js
+++ b/dom/tests/browser/browser_ConsoleAPI_originAttributes.js
@@ -10,17 +10,17 @@ const EXPECTED_CONSOLE_MESSAGE_CONTENT =
 const ConsoleObserver = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   init() {
     Services.obs.addObserver(this, "console-api-log-event", false);
   },
 
   uninit() {
-    Services.obs.removeObserver(this, "console-api-log-event", false);
+    Services.obs.removeObserver(this, "console-api-log-event");
   },
 
   observe(aSubject, aTopic, aData) {
     if (aTopic == "console-api-log-event") {
       let consoleAPIMessage = aSubject.wrappedJSObject;
 
       is(consoleAPIMessage.arguments[0], EXPECTED_CONSOLE_MESSAGE_CONTENT,
          "the consoleAPIMessage contains the expected message");
--- a/editor/libeditor/crashtests/crashtests.list
+++ b/editor/libeditor/crashtests/crashtests.list
@@ -1,13 +1,13 @@
 load 336081-1.xhtml
 asserts-if(stylo,2-3) load 336104.html # bug 1324669
 load 382527-1.html
 load 382778-1.html
-asserts-if(stylo,1) load 402172-1.html # bug 1324663
+load 402172-1.html
 load 403965-1.xhtml
 load 407074-1.html
 load 407079-1.html
 load 407256-1.html
 load 407277-1.html
 load 414178-1.html
 load 418923-1.html
 load 420439.html
@@ -29,17 +29,17 @@ skip-if(stylo) load 513375-1.xhtml # bug
 load 535632-1.xhtml
 load 574558-1.xhtml
 load 580151-1.xhtml
 load 582138-1.xhtml
 load 612565-1.html
 load 615015-1.html
 load 615450-1.html
 load 633709.xhtml
-asserts-if(stylo,1) load 636074-1.html # bug 1324663
+load 636074-1.html
 load 639736-1.xhtml
 load 643786-1.html
 load 650572-1.html
 load 667321-1.html
 load 682650-1.html
 load 713427-1.html
 load 713427-2.xhtml
 load 716456-1.html
@@ -57,17 +57,17 @@ load 768748.html
 load 768765.html
 load 769008-1.html
 load 769967.xhtml
 needs-focus load 771749.html
 load 772282.html
 load 776323.html
 needs-focus load 793866.html
 load 1057677.html
-asserts-if(stylo,1) needs-focus load 1128787.html # bug 1324663
+needs-focus load 1128787.html
 load 1134545.html
 load 1158452.html
 load 1158651.html
 load 1244894.xhtml
 load 1264921.html
 load 1272490.html
-asserts-if(stylo,1-24) load 1317704.html # bug 1324663
+load 1317704.html
 load 1317718.html
--- a/gfx/layers/apz/test/mochitest/apz_test_utils.js
+++ b/gfx/layers/apz/test/mochitest/apz_test_utils.js
@@ -115,17 +115,17 @@ function isLayerized(elementId) {
   return false;
 }
 
 function flushApzRepaints(aCallback, aWindow = window) {
   if (!aCallback) {
     throw "A callback must be provided!";
   }
   var repaintDone = function() {
-    SpecialPowers.Services.obs.removeObserver(repaintDone, "apz-repaints-flushed", false);
+    SpecialPowers.Services.obs.removeObserver(repaintDone, "apz-repaints-flushed");
     setTimeout(aCallback, 0);
   };
   SpecialPowers.Services.obs.addObserver(repaintDone, "apz-repaints-flushed", false);
   if (SpecialPowers.getDOMWindowUtils(aWindow).flushApzRepaints()) {
     dump("Flushed APZ repaints, waiting for callback...\n");
   } else {
     dump("Flushing APZ repaints was a no-op, triggering callback directly...\n");
     repaintDone();
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -608,22 +608,26 @@ SampleValue(float aPortion, const Animat
   if (static_cast<dom::IterationCompositeOperation>
         (aAnimation.iterationComposite()) ==
           dom::IterationCompositeOperation::Accumulate &&
       aCurrentIteration > 0) {
     // FIXME: Bug 1293492: Add a utility function to calculate both of
     // below StyleAnimationValues.
     startValue =
       StyleAnimationValue::Accumulate(aAnimation.property(),
-                                      aLastValue,
+                                      aLastValue.IsNull()
+                                        ? aUnderlyingValue
+                                        : aLastValue,
                                       Move(startValue),
                                       aCurrentIteration);
     endValue =
       StyleAnimationValue::Accumulate(aAnimation.property(),
-                                      aLastValue,
+                                      aLastValue.IsNull()
+                                        ? aUnderlyingValue
+                                        : aLastValue,
                                       Move(endValue),
                                       aCurrentIteration);
   }
 
   StyleAnimationValue interpolatedValue;
   // This should never fail because we only pass transform and opacity values
   // to the compositor and they should never fail to interpolate.
   DebugOnly<bool> uncomputeResult =
--- a/gfx/moz.build
+++ b/gfx/moz.build
@@ -22,14 +22,17 @@ DIRS += [
     'ipc',
     'vr',
     'config',
 ]
 
 if CONFIG['MOZ_ENABLE_SKIA']:
     DIRS += ['skia']
 
+if CONFIG['MOZ_ENABLE_SKIA_PDF_SFNTLY'] and CONFIG['ENABLE_INTL_API']:
+     DIRS += ['sfntly/cpp/src']
+
 if CONFIG['ENABLE_TESTS']:
     DIRS += ['tests/gtest']
 
 TEST_DIRS += ['tests']
 
 SPHINX_TREES['gfx'] = 'docs'
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/README.mozilla
@@ -0,0 +1,5 @@
+This directory contains the C++ port of SFNTLY,
+from https://github.com/googlei18n/sfntly/tree/master/cpp.
+
+SFNTLY is distributed under the Apache license v2.0
+(see cpp/COPYING.txt).
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/moz.build
@@ -0,0 +1,73 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+    'sample/chromium/font_subsetter.h',
+]
+
+UNIFIED_SOURCES += [
+    'sample/chromium/font_subsetter.cc',
+    'sample/chromium/subsetter_impl.cc',
+    'sfntly/data/byte_array.cc',
+    'sfntly/data/font_data.cc',
+    'sfntly/data/font_input_stream.cc',
+    'sfntly/data/font_output_stream.cc',
+    'sfntly/data/growable_memory_byte_array.cc',
+    'sfntly/data/memory_byte_array.cc',
+    'sfntly/data/readable_font_data.cc',
+    'sfntly/data/writable_font_data.cc',
+    'sfntly/font.cc',
+    'sfntly/font_factory.cc',
+    'sfntly/port/file_input_stream.cc',
+    'sfntly/port/lock.cc',
+    'sfntly/port/memory_input_stream.cc',
+    'sfntly/port/memory_output_stream.cc',
+    'sfntly/table/bitmap/big_glyph_metrics.cc',
+    'sfntly/table/bitmap/bitmap_glyph.cc',
+    'sfntly/table/bitmap/bitmap_glyph_info.cc',
+    'sfntly/table/bitmap/bitmap_size_table.cc',
+    'sfntly/table/bitmap/composite_bitmap_glyph.cc',
+    'sfntly/table/bitmap/ebdt_table.cc',
+    'sfntly/table/bitmap/eblc_table.cc',
+    'sfntly/table/bitmap/ebsc_table.cc',
+    'sfntly/table/bitmap/glyph_metrics.cc',
+    'sfntly/table/bitmap/index_sub_table.cc',
+    'sfntly/table/bitmap/index_sub_table_format1.cc',
+    'sfntly/table/bitmap/index_sub_table_format2.cc',
+    'sfntly/table/bitmap/index_sub_table_format3.cc',
+    'sfntly/table/bitmap/index_sub_table_format4.cc',
+    'sfntly/table/bitmap/index_sub_table_format5.cc',
+    'sfntly/table/bitmap/simple_bitmap_glyph.cc',
+    'sfntly/table/bitmap/small_glyph_metrics.cc',
+    'sfntly/table/byte_array_table_builder.cc',
+    'sfntly/table/core/cmap_table.cc',
+    'sfntly/table/core/font_header_table.cc',
+    'sfntly/table/core/horizontal_device_metrics_table.cc',
+    'sfntly/table/core/horizontal_header_table.cc',
+    'sfntly/table/core/horizontal_metrics_table.cc',
+    'sfntly/table/core/maximum_profile_table.cc',
+    'sfntly/table/core/name_table.cc',
+    'sfntly/table/core/os2_table.cc',
+    'sfntly/table/font_data_table.cc',
+    'sfntly/table/generic_table_builder.cc',
+    'sfntly/table/header.cc',
+    'sfntly/table/subtable.cc',
+    'sfntly/table/table.cc',
+    'sfntly/table/table_based_table_builder.cc',
+    'sfntly/table/truetype/glyph_table.cc',
+    'sfntly/table/truetype/loca_table.cc',
+    'sfntly/tag.cc',
+    'sfntly/tools/subsetter/glyph_table_subsetter.cc',
+    'sfntly/tools/subsetter/subsetter.cc',
+    'sfntly/tools/subsetter/table_subsetter_impl.cc',
+]
+
+# We allow warnings for third-party code that can be updated from upstream.
+ALLOW_COMPILER_WARNINGS = True
+
+FINAL_LIBRARY = 'gkmedias'
+
+DEFINES['SFNTLY_NO_EXCEPTION'] = 1
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/chromium/chrome_subsetter.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#include <vector>
+#include <string>
+#include <sstream>
+
+#include "sfntly/port/type.h"
+#include "font_subsetter.h"
+
+template <typename T>
+class HexTo {
+ public:
+  explicit HexTo(const char* in) {
+    std::stringstream ss;
+    ss << std::hex << in;
+    ss >> value_;
+  }
+  operator T() const { return value_; }
+
+ private:
+  T value_;
+};
+
+bool LoadFile(const char* input_file_path, sfntly::ByteVector* input_buffer) {
+  assert(input_file_path);
+  assert(input_buffer);
+
+  FILE* input_file = NULL;
+#if defined WIN32
+  fopen_s(&input_file, input_file_path, "rb");
+#else
+  input_file = fopen(input_file_path, "rb");
+#endif
+  if (input_file == NULL) {
+    return false;
+  }
+  fseek(input_file, 0, SEEK_END);
+  size_t file_size = ftell(input_file);
+  fseek(input_file, 0, SEEK_SET);
+  input_buffer->resize(file_size);
+  size_t bytes_read = fread(&((*input_buffer)[0]), 1, file_size, input_file);
+  fclose(input_file);
+  return bytes_read == file_size;
+}
+
+bool SaveFile(const char* output_file_path, const unsigned char* output_buffer,
+              int buffer_length) {
+  int byte_count = 0;
+  if (buffer_length > 0) {
+    FILE* output_file = NULL;
+#if defined WIN32
+    fopen_s(&output_file, output_file_path, "wb");
+#else
+    output_file = fopen(output_file_path, "wb");
+#endif
+    if (output_file) {
+      byte_count = fwrite(output_buffer, 1, buffer_length, output_file);
+      fflush(output_file);
+      fclose(output_file);
+    }
+    return buffer_length == byte_count;
+  }
+  return false;
+}
+
+bool StringToGlyphId(const char* input, std::vector<unsigned int>* glyph_ids) {
+  assert(input);
+  std::string hex_csv = input;
+  size_t start = 0;
+  size_t end = hex_csv.find_first_of(",");
+  while (end != std::string::npos) {
+    glyph_ids->push_back(
+        HexTo<unsigned int>(hex_csv.substr(start, end - start).c_str()));
+    start = end + 1;
+    end = hex_csv.find_first_of(",", start);
+  }
+  glyph_ids->push_back(HexTo<unsigned int>(hex_csv.substr(start).c_str()));
+  return glyph_ids->size() > 0;
+}
+
+int main(int argc, char** argv) {
+  if (argc < 5) {
+    fprintf(stderr,
+        "Usage: %s <input path> <output path> <font name> <glyph ids>\n",
+        argv[0]);
+    fprintf(stderr, "\tGlyph ids are comma separated hex values\n");
+    fprintf(stderr, "\te.g. 20,1a,3b,4f\n");
+    return 0;
+  }
+
+  sfntly::ByteVector input_buffer;
+  if (!LoadFile(argv[1], &input_buffer)) {
+    fprintf(stderr, "ERROR: unable to load font file %s\n", argv[1]);
+    return 0;
+  }
+
+  std::vector<unsigned int> glyph_ids;
+  if (!StringToGlyphId(argv[4], &glyph_ids)) {
+    fprintf(stderr, "ERROR: unable to parse input glyph id\n");
+    return 0;
+  }
+
+  unsigned char* output_buffer = NULL;
+  int output_length =
+      SfntlyWrapper::SubsetFont(argv[3],
+                                &(input_buffer[0]),
+                                input_buffer.size(),
+                                &(glyph_ids[0]),
+                                glyph_ids.size(),
+                                &output_buffer);
+
+  int result = SaveFile(argv[2], output_buffer, output_length) ? 1 : 0;
+  delete[] output_buffer;
+  return result;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/chromium/font_subsetter.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "font_subsetter.h"
+
+#include "subsetter_impl.h"
+
+int SfntlyWrapper::SubsetFont(const char* font_name,
+                              const unsigned char* original_font,
+                              size_t font_size,
+                              const unsigned int* glyph_ids,
+                              size_t glyph_count,
+                              unsigned char** output_buffer) {
+  if (output_buffer == NULL ||
+      original_font == NULL || font_size == 0 ||
+      glyph_ids == NULL || glyph_count == 0) {
+    return 0;
+  }
+
+  sfntly::SubsetterImpl subsetter;
+  if (!subsetter.LoadFont(font_name, original_font, font_size)) {
+    return -1;  // Load error or font not found.
+  }
+
+  return subsetter.SubsetFont(glyph_ids, glyph_count, output_buffer);
+}
+
+int SfntlyWrapper::SubsetFont(int font_index,
+                              const unsigned char* original_font,
+                              size_t font_size,
+                              const unsigned int* glyph_ids,
+                              size_t glyph_count,
+                              unsigned char** output_buffer) {
+  if (output_buffer == NULL ||
+      original_font == NULL || font_size == 0 ||
+      glyph_ids == NULL || glyph_count == 0) {
+    return 0;
+  }
+
+  sfntly::SubsetterImpl subsetter;
+  if (!subsetter.LoadFont(font_index, original_font, font_size)) {
+    return -1;  // Load error or font not found.
+  }
+
+  return subsetter.SubsetFont(glyph_ids, glyph_count, output_buffer);
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/chromium/font_subsetter.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// File is originally from Chromium third_party/sfntly/src/subsetter.
+// Use as test case in sfntly so that problems can be caught in upstream early.
+#ifndef SFNTLY_CPP_SRC_TEST_FONT_SUBSETTER_H_
+#define SFNTLY_CPP_SRC_TEST_FONT_SUBSETTER_H_
+
+#include <stddef.h>
+
+class SfntlyWrapper {
+ public:
+
+  // Font subsetting API
+  //
+  // Input TTF/TTC/OTF fonts, specify the glyph IDs to subset, and the subset
+  // font is returned in |output_buffer| (caller to delete[]).  Return value is
+  // the length of output_buffer allocated.
+  //
+  // If subsetting fails, a negative value is returned.  If none of the glyph
+  // IDs specified is found, the function will return 0.
+  //
+  // |font_name|      Font name, required for TTC files.  If specified NULL,
+  //                  the first available font is selected.
+  // |original_font|  Original font file contents.
+  // |font_size|      Size of |original_font| in bytes.
+  // |glyph_ids|      Glyph IDs to subset.  If the specified glyph ID is not
+  //                  found in the font file, it will be ignored silently.
+  // |glyph_count|    Number of glyph IDs in |glyph_ids|
+  // |output_buffer|  Generated subset font.  Caller to delete[].
+  static int SubsetFont(const char* font_name,
+                        const unsigned char* original_font,
+                        size_t font_size,
+                        const unsigned int* glyph_ids,
+                        size_t glyph_count,
+                        unsigned char** output_buffer);
+
+
+  // Font subsetting API
+  //
+  // Input TTF/TTC/OTF fonts, specify the glyph IDs to subset, and the subset
+  // font is returned in |output_buffer| (caller to delete[]).  Return value is
+  // the length of output_buffer allocated.
+  //
+  // If subsetting fails, a negative value is returned.  If none of the glyph
+  // IDs specified is found, the function will return 0.
+  //
+  // |font_name|      Font index, ignored for non-TTC files, 0-indexed.
+  // |original_font|  Original font file contents.
+  // |font_size|      Size of |original_font| in bytes.
+  // |glyph_ids|      Glyph IDs to subset.  If the specified glyph ID is not
+  //                  found in the font file, it will be ignored silently.
+  // |glyph_count|    Number of glyph IDs in |glyph_ids|
+  // |output_buffer|  Generated subset font.  Caller to delete[].
+  static int SubsetFont(int font_index,
+                        const unsigned char* original_font,
+                        size_t font_size,
+                        const unsigned int* glyph_ids,
+                        size_t glyph_count,
+                        unsigned char** output_buffer);
+};
+
+#endif  // SFNTLY_CPP_SRC_TEST_FONT_SUBSETTER_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/chromium/subsetter_impl.cc
@@ -0,0 +1,803 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "subsetter_impl.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <iterator>
+#include <map>
+#include <set>
+
+#include "sfntly/table/bitmap/eblc_table.h"
+#include "sfntly/table/bitmap/ebdt_table.h"
+#include "sfntly/table/bitmap/index_sub_table.h"
+#include "sfntly/table/bitmap/index_sub_table_format1.h"
+#include "sfntly/table/bitmap/index_sub_table_format2.h"
+#include "sfntly/table/bitmap/index_sub_table_format3.h"
+#include "sfntly/table/bitmap/index_sub_table_format4.h"
+#include "sfntly/table/bitmap/index_sub_table_format5.h"
+#include "sfntly/table/core/name_table.h"
+#include "sfntly/tag.h"
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/port/memory_input_stream.h"
+#include "sfntly/port/memory_output_stream.h"
+
+#if defined U_USING_ICU_NAMESPACE
+  U_NAMESPACE_USE
+#endif
+
+namespace {
+
+using namespace sfntly;
+
+// The bitmap tables must be greater than 16KB to trigger bitmap subsetter.
+static const int BITMAP_SIZE_THRESHOLD = 16384;
+
+void ConstructName(UChar* name_part, UnicodeString* name, int32_t name_id) {
+  switch (name_id) {
+    case NameId::kFullFontName:
+      *name = name_part;
+      break;
+    case NameId::kFontFamilyName:
+    case NameId::kPreferredFamily:
+    case NameId::kWWSFamilyName: {
+      UnicodeString original = *name;
+      *name = name_part;
+      *name += original;
+      break;
+    }
+    case NameId::kFontSubfamilyName:
+    case NameId::kPreferredSubfamily:
+    case NameId::kWWSSubfamilyName:
+      *name += name_part;
+      break;
+    default:
+      // This name part is not used to construct font name (e.g. copyright).
+      // Simply ignore it.
+      break;
+  }
+}
+
+int32_t HashCode(int32_t platform_id, int32_t encoding_id, int32_t language_id,
+                 int32_t name_id) {
+  int32_t result = platform_id << 24 | encoding_id << 16 | language_id << 8;
+  if (name_id == NameId::kFullFontName) {
+    result |= 0xff;
+  } else if (name_id == NameId::kPreferredFamily ||
+             name_id == NameId::kPreferredSubfamily) {
+    result |= 0xf;
+  } else if (name_id == NameId::kWWSFamilyName ||
+             name_id == NameId::kWWSSubfamilyName) {
+    result |= 1;
+  }
+  return result;
+}
+
+bool HasName(const char* font_name, Font* font) {
+  UnicodeString font_string = UnicodeString::fromUTF8(font_name);
+  if (font_string.isEmpty())
+    return false;
+  UnicodeString regular_suffix = UnicodeString::fromUTF8(" Regular");
+  UnicodeString alt_font_string = font_string;
+  alt_font_string += regular_suffix;
+
+  typedef std::map<int32_t, UnicodeString> NameMap;
+  NameMap names;
+  NameTablePtr name_table = down_cast<NameTable*>(font->GetTable(Tag::name));
+  if (name_table == NULL) {
+    return false;
+  }
+
+  for (int32_t i = 0; i < name_table->NameCount(); ++i) {
+    switch (name_table->NameId(i)) {
+      case NameId::kFontFamilyName:
+      case NameId::kFontSubfamilyName:
+      case NameId::kFullFontName:
+      case NameId::kPreferredFamily:
+      case NameId::kPreferredSubfamily:
+      case NameId::kWWSFamilyName:
+      case NameId::kWWSSubfamilyName: {
+        UChar* name_part = name_table->Name(i);
+        if (name_part == NULL) {
+          continue;
+        }
+        int32_t hash_code = HashCode(name_table->PlatformId(i),
+                                     name_table->EncodingId(i),
+                                     name_table->LanguageId(i),
+                                     name_table->NameId(i));
+        ConstructName(name_part, &(names[hash_code]), name_table->NameId(i));
+        delete[] name_part;
+        break;
+      }
+      default:
+        break;
+    }
+  }
+
+  if (!names.empty()) {
+    for (NameMap::iterator i = names.begin(), e = names.end(); i != e; ++i) {
+      if (i->second.caseCompare(font_string, 0) == 0 ||
+          i->second.caseCompare(alt_font_string, 0) == 0) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+Font* FindFont(const char* font_name, const FontArray& font_array) {
+  if (font_array.empty() || font_array[0] == NULL) {
+    return NULL;
+  }
+
+  if (font_name && strlen(font_name)) {
+    for (FontArray::const_iterator i = font_array.begin(), e = font_array.end();
+         i != e; ++i) {
+      if (HasName(font_name, i->p_)) {
+        return i->p_;
+      }
+    }
+  }
+
+  return font_array[0].p_;
+}
+
+bool ResolveCompositeGlyphs(GlyphTable* glyph_table,
+                            LocaTable* loca_table,
+                            const unsigned int* glyph_ids,
+                            size_t glyph_count,
+                            IntegerSet* glyph_id_processed) {
+  if (glyph_table == NULL || loca_table == NULL ||
+      glyph_ids == NULL || glyph_count == 0 || glyph_id_processed == NULL) {
+    return false;
+  }
+
+  // Sort and uniquify glyph ids.
+  IntegerSet glyph_id_remaining;
+  glyph_id_remaining.insert(0);  // Always include glyph id 0.
+  for (size_t i = 0; i < glyph_count; ++i) {
+    glyph_id_remaining.insert(glyph_ids[i]);
+  }
+
+  // Identify if any given glyph id maps to a composite glyph.  If so, include
+  // the glyphs referenced by that composite glyph.
+  while (!glyph_id_remaining.empty()) {
+    IntegerSet comp_glyph_id;
+    for (IntegerSet::iterator i = glyph_id_remaining.begin(),
+                              e = glyph_id_remaining.end(); i != e; ++i) {
+      if (*i < 0 || *i >= loca_table->num_glyphs()) {
+        // Invalid glyph id, ignore.
+        continue;
+      }
+
+      int32_t length = loca_table->GlyphLength(*i);
+      if (length == 0) {
+        // Empty glyph, ignore.
+        continue;
+      }
+      int32_t offset = loca_table->GlyphOffset(*i);
+
+      GlyphPtr glyph;
+      glyph.Attach(glyph_table->GetGlyph(offset, length));
+      if (glyph == NULL) {
+        // Error finding glyph, ignore.
+        continue;
+      }
+
+      if (glyph->GlyphType() == GlyphType::kComposite) {
+        Ptr<GlyphTable::CompositeGlyph> comp_glyph =
+            down_cast<GlyphTable::CompositeGlyph*>(glyph.p_);
+        for (int32_t j = 0; j < comp_glyph->NumGlyphs(); ++j) {
+          int32_t glyph_id = comp_glyph->GlyphIndex(j);
+          if (glyph_id_processed->find(glyph_id) == glyph_id_processed->end() &&
+              glyph_id_remaining.find(glyph_id) == glyph_id_remaining.end()) {
+            comp_glyph_id.insert(comp_glyph->GlyphIndex(j));
+          }
+        }
+      }
+
+      glyph_id_processed->insert(*i);
+    }
+
+    glyph_id_remaining.clear();
+    glyph_id_remaining = comp_glyph_id;
+  }
+
+  return true;
+}
+
+bool SetupGlyfBuilders(Font::Builder* font_builder,
+                       GlyphTable* glyph_table,
+                       LocaTable* loca_table,
+                       const IntegerSet& glyph_ids) {
+  if (!font_builder || !glyph_table || !loca_table) {
+    return false;
+  }
+
+  GlyphTableBuilderPtr glyph_table_builder =
+      down_cast<GlyphTable::Builder*>(font_builder->NewTableBuilder(Tag::glyf));
+  LocaTableBuilderPtr loca_table_builder =
+      down_cast<LocaTable::Builder*>(font_builder->NewTableBuilder(Tag::loca));
+  if (glyph_table_builder == NULL || loca_table_builder == NULL) {
+    // Out of memory.
+    return false;
+  }
+
+  // Extract glyphs and setup loca list.
+  IntegerList loca_list;
+  loca_list.resize(loca_table->num_glyphs());
+  loca_list.push_back(0);
+  int32_t last_glyph_id = 0;
+  int32_t last_offset = 0;
+  GlyphTable::GlyphBuilderList* glyph_builders =
+      glyph_table_builder->GlyphBuilders();
+  for (IntegerSet::const_iterator i = glyph_ids.begin(), e = glyph_ids.end();
+                                  i != e; ++i) {
+    int32_t length = loca_table->GlyphLength(*i);
+    int32_t offset = loca_table->GlyphOffset(*i);
+
+    GlyphPtr glyph;
+    glyph.Attach(glyph_table->GetGlyph(offset, length));
+
+    // Add glyph to new glyf table.
+    ReadableFontDataPtr data = glyph->ReadFontData();
+    WritableFontDataPtr copy_data;
+    copy_data.Attach(WritableFontData::CreateWritableFontData(data->Length()));
+    data->CopyTo(copy_data);
+    GlyphBuilderPtr glyph_builder;
+    glyph_builder.Attach(glyph_table_builder->GlyphBuilder(copy_data));
+    glyph_builders->push_back(glyph_builder);
+
+    // Configure loca list.
+    for (int32_t j = last_glyph_id + 1; j <= *i; ++j) {
+      loca_list[j] = last_offset;
+    }
+    last_offset += length;
+    loca_list[*i + 1] = last_offset;
+    last_glyph_id = *i;
+  }
+  for (int32_t j = last_glyph_id + 1; j <= loca_table->num_glyphs(); ++j) {
+    loca_list[j] = last_offset;
+  }
+  loca_table_builder->SetLocaList(&loca_list);
+
+  return true;
+}
+
+bool HasOverlap(int32_t range_begin, int32_t range_end,
+                const IntegerSet& glyph_ids) {
+  if (range_begin == range_end) {
+    return glyph_ids.find(range_begin) != glyph_ids.end();
+  } else if (range_end > range_begin) {
+    IntegerSet::const_iterator left = glyph_ids.lower_bound(range_begin);
+    IntegerSet::const_iterator right = glyph_ids.lower_bound(range_end);
+    return right != left;
+  }
+  return false;
+}
+
+// Initialize builder, returns false if glyph_id subset is not covered.
+// Not thread-safe, caller to ensure object life-time.
+bool InitializeBitmapBuilder(EbdtTable::Builder* ebdt, EblcTable::Builder* eblc,
+                             const IntegerSet& glyph_ids) {
+  BitmapLocaList loca_list;
+  BitmapSizeTableBuilderList* strikes = eblc->BitmapSizeBuilders();
+
+  // Note: Do not call eblc_builder->GenerateLocaList(&loca_list) and then
+  //       ebdt_builder->SetLoca(loca_list).  For fonts like SimSun, there are
+  //       >28K glyphs inside, where a typical usage will be <1K glyphs.  Doing
+  //       the calls improperly will result in creation of >100K objects that
+  //       will be destroyed immediately, inducing significant slowness.
+  IntegerList removed_strikes;
+  for (size_t i = 0; i < strikes->size(); i++) {
+    if (!HasOverlap((*strikes)[i]->StartGlyphIndex(),
+                    (*strikes)[i]->EndGlyphIndex(), glyph_ids)) {
+      removed_strikes.push_back(i);
+      continue;
+    }
+
+    IndexSubTableBuilderList* index_builders =
+        (*strikes)[i]->IndexSubTableBuilders();
+    IntegerList removed_indexes;
+    BitmapGlyphInfoMap info_map;
+    for (size_t j = 0; j < index_builders->size(); ++j) {
+      if ((*index_builders)[j] == NULL) {
+        // Subtable is malformed, let's just skip it.
+        removed_indexes.push_back(j);
+        continue;
+      }
+      int32_t first_glyph_id = (*index_builders)[j]->first_glyph_index();
+      int32_t last_glyph_id = (*index_builders)[j]->last_glyph_index();
+      if (!HasOverlap(first_glyph_id, last_glyph_id, glyph_ids)) {
+        removed_indexes.push_back(j);
+        continue;
+      }
+      for (IntegerSet::const_iterator gid = glyph_ids.begin(),
+                                      gid_end = glyph_ids.end();
+                                      gid != gid_end; gid++) {
+        if (*gid < first_glyph_id) {
+          continue;
+        }
+        if (*gid > last_glyph_id) {
+          break;
+        }
+        BitmapGlyphInfoPtr info;
+        info.Attach((*index_builders)[j]->GlyphInfo(*gid));
+        if (info && info->length()) {  // Do not include gid without bitmap
+          info_map[*gid] = info;
+        }
+      }
+    }
+    if (!info_map.empty()) {
+      loca_list.push_back(info_map);
+    } else {
+      removed_strikes.push_back(i);  // Detected null entries.
+    }
+
+    // Remove unused index sub tables
+    for (IntegerList::reverse_iterator j = removed_indexes.rbegin(),
+                                       e = removed_indexes.rend();
+                                       j != e; j++) {
+      index_builders->erase(index_builders->begin() + *j);
+    }
+  }
+  if (removed_strikes.size() == strikes->size() || loca_list.empty()) {
+    return false;
+  }
+
+  for (IntegerList::reverse_iterator i = removed_strikes.rbegin(),
+                                     e = removed_strikes.rend(); i != e; i++) {
+    strikes->erase(strikes->begin() + *i);
+  }
+
+  if (strikes->empty()) {  // no glyph covered, can safely drop the builders.
+    return false;
+  }
+
+  ebdt->SetLoca(&loca_list);
+  ebdt->GlyphBuilders();  // Initialize the builder.
+  return true;
+}
+
+void CopyBigGlyphMetrics(BigGlyphMetrics::Builder* source,
+                         BigGlyphMetrics::Builder* target) {
+  target->SetHeight(static_cast<byte_t>(source->Height()));
+  target->SetWidth(static_cast<byte_t>(source->Width()));
+  target->SetHoriBearingX(static_cast<byte_t>(source->HoriBearingX()));
+  target->SetHoriBearingY(static_cast<byte_t>(source->HoriBearingY()));
+  target->SetHoriAdvance(static_cast<byte_t>(source->HoriAdvance()));
+  target->SetVertBearingX(static_cast<byte_t>(source->VertBearingX()));
+  target->SetVertBearingY(static_cast<byte_t>(source->VertBearingY()));
+  target->SetVertAdvance(static_cast<byte_t>(source->VertAdvance()));
+}
+
+CALLER_ATTACH IndexSubTable::Builder*
+ConstructIndexFormat4(IndexSubTable::Builder* b, const BitmapGlyphInfoMap& loca,
+                      int32_t* image_data_offset) {
+  IndexSubTableFormat4BuilderPtr builder4;
+  builder4.Attach(IndexSubTableFormat4::Builder::CreateBuilder());
+  CodeOffsetPairBuilderList offset_pairs;
+
+  size_t offset = 0;
+  int32_t lower_bound = b->first_glyph_index();
+  int32_t upper_bound = b->last_glyph_index();
+  int32_t last_gid = -1;
+  BitmapGlyphInfoMap::const_iterator i = loca.lower_bound(lower_bound);
+  BitmapGlyphInfoMap::const_iterator end = loca.end();
+  if (i != end) {
+    last_gid = i->first;
+    builder4->set_first_glyph_index(last_gid);
+    builder4->set_image_format(b->image_format());
+    builder4->set_image_data_offset(*image_data_offset);
+  }
+  for (; i != end; i++) {
+    int32_t gid = i->first;
+    if (gid > upper_bound) {
+      break;
+    }
+    offset_pairs.push_back(
+        IndexSubTableFormat4::CodeOffsetPairBuilder(gid, offset));
+    offset += i->second->length();
+    last_gid = gid;
+  }
+  offset_pairs.push_back(
+      IndexSubTableFormat4::CodeOffsetPairBuilder(-1, offset));
+  builder4->set_last_glyph_index(last_gid);
+  *image_data_offset += offset;
+  builder4->SetOffsetArray(offset_pairs);
+
+  return builder4.Detach();
+}
+
+CALLER_ATTACH IndexSubTable::Builder*
+ConstructIndexFormat5(IndexSubTable::Builder* b, const BitmapGlyphInfoMap& loca,
+                      int32_t* image_data_offset) {
+  IndexSubTableFormat5BuilderPtr new_builder;
+  new_builder.Attach(IndexSubTableFormat5::Builder::CreateBuilder());
+
+  // Copy BigMetrics
+  int32_t image_size = 0;
+  if (b->index_format() == IndexSubTable::Format::FORMAT_2) {
+    IndexSubTableFormat2BuilderPtr builder2 =
+      down_cast<IndexSubTableFormat2::Builder*>(b);
+    CopyBigGlyphMetrics(builder2->BigMetrics(), new_builder->BigMetrics());
+    image_size = builder2->ImageSize();
+  } else {
+    IndexSubTableFormat5BuilderPtr builder5 =
+      down_cast<IndexSubTableFormat5::Builder*>(b);
+    BigGlyphMetricsBuilderPtr metrics_builder;
+    CopyBigGlyphMetrics(builder5->BigMetrics(), new_builder->BigMetrics());
+    image_size = builder5->ImageSize();
+  }
+
+  IntegerList* glyph_array = new_builder->GlyphArray();
+  size_t offset = 0;
+  int32_t lower_bound = b->first_glyph_index();
+  int32_t upper_bound = b->last_glyph_index();
+  int32_t last_gid = -1;
+  BitmapGlyphInfoMap::const_iterator i = loca.lower_bound(lower_bound);
+  BitmapGlyphInfoMap::const_iterator end = loca.end();
+  if (i != end) {
+    last_gid = i->first;
+    new_builder->set_first_glyph_index(last_gid);
+    new_builder->set_image_format(b->image_format());
+    new_builder->set_image_data_offset(*image_data_offset);
+    new_builder->SetImageSize(image_size);
+  }
+  for (; i != end; i++) {
+    int32_t gid = i->first;
+    if (gid > upper_bound) {
+      break;
+    }
+    glyph_array->push_back(gid);
+    offset += i->second->length();
+    last_gid = gid;
+  }
+  new_builder->set_last_glyph_index(last_gid);
+  *image_data_offset += offset;
+  return new_builder.Detach();
+}
+
+CALLER_ATTACH IndexSubTable::Builder*
+SubsetIndexSubTable(IndexSubTable::Builder* builder,
+                    const BitmapGlyphInfoMap& loca,
+                    int32_t* image_data_offset) {
+  switch (builder->index_format()) {
+    case IndexSubTable::Format::FORMAT_1:
+    case IndexSubTable::Format::FORMAT_3:
+    case IndexSubTable::Format::FORMAT_4:
+      return ConstructIndexFormat4(builder, loca, image_data_offset);
+    case IndexSubTable::Format::FORMAT_2:
+    case IndexSubTable::Format::FORMAT_5:
+      return ConstructIndexFormat5(builder, loca, image_data_offset);
+    default:
+      assert(false);
+      break;
+  }
+  return NULL;
+}
+
+}
+
+namespace sfntly {
+
+// Not thread-safe, caller to ensure object life-time.
+void SubsetEBLC(EblcTable::Builder* eblc, const BitmapLocaList& new_loca) {
+  BitmapSizeTableBuilderList* size_builders = eblc->BitmapSizeBuilders();
+  if (size_builders == NULL) {
+    return;
+  }
+
+  int32_t image_data_offset = EbdtTable::Offset::kHeaderLength;
+  for (size_t strike = 0; strike < size_builders->size(); ++strike) {
+    IndexSubTableBuilderList* index_builders =
+        (*size_builders)[strike]->IndexSubTableBuilders();
+    for (size_t index = 0; index < index_builders->size(); ++index) {
+      IndexSubTable::Builder* new_builder_raw =
+          SubsetIndexSubTable((*index_builders)[index], new_loca[strike],
+                              &image_data_offset);
+      if (NULL != new_builder_raw) {
+        (*index_builders)[index].Attach(new_builder_raw);
+      }
+    }
+  }
+}
+
+// EBLC structure (from stuartg)
+//  header
+//  bitmapSizeTable[]
+//    one per strike
+//    holds strike metrics - sbitLineMetrics
+//    holds info about indexSubTableArray
+//  indexSubTableArray[][]
+//    one per strike and then one per indexSubTable for that strike
+//    holds info about the indexSubTable
+//    the indexSubTable entries pointed to can be of different formats
+//  indexSubTable
+//    one per indexSubTableArray entry
+//    tells how to get the glyphs
+//    may hold the glyph metrics if they are uniform for all the glyphs in range
+// Please note that the structure can also be
+//  {indexSubTableArray[], indexSubTables[]}[]
+//  This way is also legal and in fact how Microsoft fonts are laid out.
+//
+// There is nothing that says that the indexSubTableArray entries and/or the
+// indexSubTable items need to be unique. They may be shared between strikes.
+//
+// EBDT structure:
+//  header
+//  glyphs
+//    amorphous blob of data
+//    different glyphs that are only able to be figured out from the EBLC table
+//    may hold metrics - depends on the EBLC entry that pointed to them
+
+// Subsetting EBLC table (from arthurhsu)
+//  Most pages use only a fraction (hundreds or less) glyphs out of a given font
+//  (which can have >20K glyphs for CJK).  It's safe to assume that the subset
+//  font will have sparse bitmap glyphs.  So we reconstruct the EBLC table as
+//  format 4 or 5 here.
+
+enum BuildersToRemove {
+  kRemoveNone,
+  kRemoveBDAT,
+  kRemoveBDATAndEBDT,
+  kRemoveEBDT
+};
+
+int SetupBitmapBuilders(Font* font, Font::Builder* font_builder,
+                        const IntegerSet& glyph_ids) {
+  if (!font || !font_builder) {
+    return false;
+  }
+
+  // Check if bitmap table exists.
+  EbdtTablePtr ebdt_table = down_cast<EbdtTable*>(font->GetTable(Tag::EBDT));
+  EblcTablePtr eblc_table = down_cast<EblcTable*>(font->GetTable(Tag::EBLC));
+  bool use_ebdt = (ebdt_table != NULL && eblc_table != NULL);
+  if (!use_ebdt) {
+    ebdt_table = down_cast<EbdtTable*>(font->GetTable(Tag::bdat));
+    eblc_table = down_cast<EblcTable*>(font->GetTable(Tag::bloc));
+    if (ebdt_table == NULL || eblc_table == NULL) {
+      return kRemoveNone;
+    }
+  }
+
+  // If the bitmap table's size is too small, skip subsetting.
+  if (ebdt_table->DataLength() + eblc_table->DataLength() <
+      BITMAP_SIZE_THRESHOLD) {
+    return use_ebdt ? kRemoveBDAT : kRemoveNone;
+  }
+
+  // Get the builders.
+  EbdtTableBuilderPtr ebdt_table_builder = down_cast<EbdtTable::Builder*>(
+      font_builder->NewTableBuilder(use_ebdt ? Tag::EBDT : Tag::bdat,
+                                    ebdt_table->ReadFontData()));
+  EblcTableBuilderPtr eblc_table_builder = down_cast<EblcTable::Builder*>(
+      font_builder->NewTableBuilder(use_ebdt ? Tag::EBLC : Tag::bloc,
+                                    eblc_table->ReadFontData()));
+  if (ebdt_table_builder == NULL || eblc_table_builder == NULL) {
+    // Out of memory.
+    return use_ebdt ? kRemoveBDAT : kRemoveNone;
+  }
+
+  if (!InitializeBitmapBuilder(ebdt_table_builder, eblc_table_builder,
+                               glyph_ids)) {
+    // Bitmap tables do not cover the glyphs in our subset.
+    font_builder->RemoveTableBuilder(use_ebdt ? Tag::EBLC : Tag::bloc);
+    font_builder->RemoveTableBuilder(use_ebdt ? Tag::EBDT : Tag::bdat);
+    return use_ebdt ? kRemoveBDATAndEBDT : kRemoveEBDT;
+  }
+
+  BitmapLocaList new_loca;
+  ebdt_table_builder->GenerateLocaList(&new_loca);
+  SubsetEBLC(eblc_table_builder, new_loca);
+
+  return use_ebdt ? kRemoveBDAT : kRemoveNone;
+}
+
+SubsetterImpl::SubsetterImpl() {
+}
+
+SubsetterImpl::~SubsetterImpl() {
+}
+
+bool SubsetterImpl::LoadFont(int font_index,
+                             const unsigned char* original_font,
+                             size_t font_size) {
+  MemoryInputStream mis;
+  mis.Attach(original_font, font_size);
+  if (factory_ == NULL) {
+    factory_.Attach(FontFactory::GetInstance());
+  }
+
+  FontArray font_array;
+  factory_->LoadFonts(&mis, &font_array);
+  if (font_index < 0 || (size_t)font_index >= font_array.size()) {
+    return false;
+  }
+  font_ = font_array[font_index].p_;
+  return font_ != NULL;
+}
+
+bool SubsetterImpl::LoadFont(const char* font_name,
+                             const unsigned char* original_font,
+                             size_t font_size) {
+  MemoryInputStream mis;
+  mis.Attach(original_font, font_size);
+  if (factory_ == NULL) {
+    factory_.Attach(FontFactory::GetInstance());
+  }
+
+  FontArray font_array;
+  factory_->LoadFonts(&mis, &font_array);
+  font_ = FindFont(font_name, font_array);
+  if (font_ == NULL) {
+    return false;
+  }
+
+  return true;
+}
+
+int SubsetterImpl::SubsetFont(const unsigned int* glyph_ids,
+                              size_t glyph_count,
+                              unsigned char** output_buffer) {
+  if (factory_ == NULL || font_ == NULL) {
+    return -1;
+  }
+
+  // Find glyf and loca table.
+  GlyphTablePtr glyph_table =
+      down_cast<GlyphTable*>(font_->GetTable(Tag::glyf));
+  LocaTablePtr loca_table = down_cast<LocaTable*>(font_->GetTable(Tag::loca));
+  if (glyph_table == NULL || loca_table == NULL) {
+    // We are not able to subset the font.
+    return 0;
+  }
+
+  IntegerSet glyph_id_processed;
+  if (!ResolveCompositeGlyphs(glyph_table, loca_table,
+                              glyph_ids, glyph_count, &glyph_id_processed) ||
+      glyph_id_processed.empty()) {
+    return 0;
+  }
+
+  FontPtr new_font;
+  new_font.Attach(Subset(glyph_id_processed, glyph_table, loca_table));
+  if (new_font == NULL) {
+    return 0;
+  }
+
+  MemoryOutputStream output_stream;
+  factory_->SerializeFont(new_font, &output_stream);
+  int length = static_cast<int>(output_stream.Size());
+  if (length > 0) {
+    *output_buffer = new unsigned char[length];
+    memcpy(*output_buffer, output_stream.Get(), length);
+  }
+
+  return length;
+}
+
+// Long comments regarding TTF tables and PDF (from stuartg)
+//
+// According to PDF spec 1.4 (section 5.8), the following tables must be
+// present:
+//  head, hhea, loca, maxp, cvt, prep, glyf, hmtx, fpgm
+//  cmap if font is used with a simple font dict and not a CIDFont dict
+//
+// Other tables we need to keep for PDF rendering to support zoom in/out:
+//  bdat, bloc, ebdt, eblc, ebsc, gasp
+//
+// Special table:
+//  CFF - if you have this table then you shouldn't have a glyf table and this
+//        is the table with all the glyphs.  Shall skip subsetting completely
+//        since sfntly is not capable of subsetting it for now.
+//  post - extra info here for printing on PostScript printers but maybe not
+//         enough to outweigh the space taken by the names
+//
+// Tables to break apart:
+//  name - could throw away all but one language and one platform strings/ might
+//         throw away some of the name entries
+//  cmap - could strip out non-needed cmap subtables
+//       - format 4 subtable can be subsetted as well using sfntly
+//
+// Graphite tables:
+//  silf, glat, gloc, feat - should be okay to strip out
+//
+// Tables that can be discarded:
+//  OS/2 - everything here is for layout and description of the font that is
+//         elsewhere (some in the PDF objects)
+//  BASE, GDEF, GSUB, GPOS, JSTF - all used for layout
+//  kern - old style layout
+//  DSIG - this will be invalid after subsetting
+//  hdmx - layout
+//  PCLT - metadata that's not needed
+//  vmtx - layout
+//  vhea - layout
+//  VDMX
+//  VORG - not used by TT/OT - used by CFF
+//  hsty - would be surprised to see one of these - used on the Newton
+//  AAT tables - mort, morx, feat, acnt, bsin, just, lcar, fdsc, fmtx, prop,
+//               Zapf, opbd, trak, fvar, gvar, avar, cvar
+//             - these are all layout tables and once layout happens are not
+//               needed anymore
+//  LTSH - layout
+
+CALLER_ATTACH
+Font* SubsetterImpl::Subset(const IntegerSet& glyph_ids, GlyphTable* glyf,
+                            LocaTable* loca) {
+  // The const is initialized here to workaround VC bug of rendering all Tag::*
+  // as 0.  These tags represents the TTF tables that we will embed in subset
+  // font.
+  const int32_t TABLES_IN_SUBSET[] = {
+    Tag::head, Tag::hhea, Tag::loca, Tag::maxp, Tag::cvt,
+    Tag::prep, Tag::glyf, Tag::hmtx, Tag::fpgm, Tag::EBDT,
+    Tag::EBLC, Tag::EBSC, Tag::bdat, Tag::bloc, Tag::bhed,
+    Tag::cmap,  // Keep here for future tagged PDF development.
+    Tag::name,  // Keep here due to legal concerns: copyright info inside.
+  };
+
+  // Setup font builders we need.
+  FontBuilderPtr font_builder;
+  font_builder.Attach(factory_->NewFontBuilder());
+  IntegerSet remove_tags;
+
+  if (SetupGlyfBuilders(font_builder, glyf, loca, glyph_ids)) {
+    remove_tags.insert(Tag::glyf);
+    remove_tags.insert(Tag::loca);
+  }
+
+  // For old Apple bitmap fonts, they have only bdats and bhed is identical
+  // to head.  As a result, we can't remove bdat tables for those fonts.
+  int setup_result = SetupBitmapBuilders(font_, font_builder, glyph_ids);
+  if (setup_result == kRemoveBDATAndEBDT || setup_result == kRemoveEBDT) {
+    remove_tags.insert(Tag::EBDT);
+    remove_tags.insert(Tag::EBLC);
+    remove_tags.insert(Tag::EBSC);
+  }
+
+  if (setup_result == kRemoveBDAT || setup_result == kRemoveBDATAndEBDT) {
+    remove_tags.insert(Tag::bdat);
+    remove_tags.insert(Tag::bloc);
+    remove_tags.insert(Tag::bhed);
+  }
+
+  IntegerSet allowed_tags;
+  for (size_t i = 0; i < sizeof(TABLES_IN_SUBSET) / sizeof(int32_t); ++i) {
+    allowed_tags.insert(TABLES_IN_SUBSET[i]);
+  }
+
+  IntegerSet result;
+  std::set_difference(allowed_tags.begin(), allowed_tags.end(),
+                      remove_tags.begin(), remove_tags.end(),
+                      std::inserter(result, result.end()));
+  allowed_tags = result;
+
+  // Setup remaining builders.
+  for (IntegerSet::iterator i = allowed_tags.begin(), e = allowed_tags.end();
+                            i != e; ++i) {
+    Table* table = font_->GetTable(*i);
+    if (table) {
+      font_builder->NewTableBuilder(*i, table->ReadFontData());
+    }
+  }
+
+  return font_builder->Build();
+}
+
+}  // namespace sfntly
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/chromium/subsetter_impl.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// File is originally from Chromium third_party/sfntly/src/subsetter.
+// Use as test case in sfntly so that problems can be caught in upstream early.
+
+#ifndef SFNTLY_CPP_SRC_TEST_SUBSETTER_IMPL_H_
+#define SFNTLY_CPP_SRC_TEST_SUBSETTER_IMPL_H_
+
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/table/truetype/glyph_table.h"
+#include "sfntly/table/truetype/loca_table.h"
+#include "sfntly/tag.h"
+
+namespace sfntly {
+
+// Smart pointer usage in sfntly:
+//
+// sfntly carries a smart pointer implementation like COM.  Ref-countable object
+// type inherits from RefCounted<>, which have AddRef and Release just like
+// IUnknown (but no QueryInterface).  Use a Ptr<> based smart pointer to hold
+// the object so that the object ref count is handled correctly.
+//
+// class Foo : public RefCounted<Foo> {
+//  public:
+//   static Foo* CreateInstance() {
+//     Ptr<Foo> obj = new Foo();  // ref count = 1
+//     return obj.detach();
+//   }
+// };
+// typedef Ptr<Foo> FooPtr;  // common short-hand notation
+// FooPtr obj;
+// obj.attach(Foo::CreatedInstance());  // ref count = 1
+// {
+//   FooPtr obj2 = obj;  // ref count = 2
+// }  // ref count = 1, obj2 out of scope
+// obj.release();  // ref count = 0, object destroyed
+
+class SubsetterImpl {
+ public:
+  SubsetterImpl();
+  ~SubsetterImpl();
+
+  bool LoadFont(const char* font_name,
+                const unsigned char* original_font,
+                size_t font_size);
+  bool LoadFont(int font_index,
+                const unsigned char* original_font,
+                size_t font_size);
+  int SubsetFont(const unsigned int* glyph_ids,
+                 size_t glyph_count,
+                 unsigned char** output_buffer);
+
+ private:
+  CALLER_ATTACH Font* Subset(const IntegerSet& glyph_ids,
+                             GlyphTable* glyf, LocaTable* loca);
+
+  FontFactoryPtr factory_;
+  FontPtr font_;
+};
+
+}  // namespace sfntly
+
+#endif  // SFNTLY_CPP_SRC_TEST_SUBSETTER_IMPL_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subsetter/main.cc
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#if _MSC_VER > 12
+  #define _CRTDBG_MAP_ALLOC
+  #include <stdlib.h>
+  #include <crtdbg.h>
+#endif
+
+#include "sample/subsetter/subset_util.h"
+
+int main(int argc, char** argv) {
+#ifdef _CRTDBG_MAP_ALLOC
+  _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
+#endif
+
+  if (argc < 3) {
+    printf("Usage: subsetter <font file> <output file>\n");
+    return 0;
+  }
+
+  sfntly::SubsetUtil subset_util;
+  subset_util.Subset(argv[1], argv[2]);
+
+#ifdef _CRTDBG_MAP_ALLOC
+  _CrtDumpMemoryLeaks();
+#endif
+
+  return 0;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subsetter/subset_util.cc
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Remove VC++ nag on fopen.
+#define _CRT_SECURE_NO_WARNINGS
+
+#include "sample/subsetter/subset_util.h"
+
+#include <stdio.h>
+
+#include <vector>
+#include <memory>
+
+#include "sfntly/font.h"
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/port/memory_output_stream.h"
+#include "sfntly/port/type.h"
+#include "sfntly/tag.h"
+#include "sfntly/tools/subsetter/subsetter.h"
+
+namespace sfntly {
+
+SubsetUtil::SubsetUtil() {
+}
+
+SubsetUtil::~SubsetUtil() {
+}
+
+void SubsetUtil::Subset(const char *input_file_path,
+                        const char *output_file_path) {
+  UNREFERENCED_PARAMETER(output_file_path);
+  ByteVector input_buffer;
+  FILE* input_file = fopen(input_file_path, "rb");
+  if (input_file == NULL) {
+    fprintf(stderr, "file not found\n");
+    return;
+  }
+  fseek(input_file, 0, SEEK_END);
+  size_t file_size = ftell(input_file);
+  fseek(input_file, 0, SEEK_SET);
+  input_buffer.resize(file_size);
+  size_t bytes_read = fread(&(input_buffer[0]), 1, file_size, input_file);
+  UNREFERENCED_PARAMETER(bytes_read);
+  fclose(input_file);
+
+  FontFactoryPtr factory;
+  factory.Attach(FontFactory::GetInstance());
+
+  FontArray font_array;
+  factory->LoadFonts(&input_buffer, &font_array);
+  if (font_array.empty() || font_array[0] == NULL)
+    return;
+
+  IntegerList glyphs;
+  for (int32_t i = 0; i < 10; i++) {
+    glyphs.push_back(i);
+  }
+  glyphs.push_back(11);
+  glyphs.push_back(10);
+
+  Ptr<Subsetter> subsetter = new Subsetter(font_array[0], factory);
+  subsetter->SetGlyphs(&glyphs);
+  IntegerSet remove_tables;
+  remove_tables.insert(Tag::DSIG);
+  subsetter->SetRemoveTables(&remove_tables);
+
+  FontBuilderPtr font_builder;
+  font_builder.Attach(subsetter->Subset());
+
+  FontPtr new_font;
+  new_font.Attach(font_builder->Build());
+
+  // TODO(arthurhsu): glyph renumbering/Loca table
+  // TODO(arthurhsu): alter CMaps
+
+  MemoryOutputStream output_stream;
+  factory->SerializeFont(new_font, &output_stream);
+
+  FILE* output_file = fopen(output_file_path, "wb");
+  fwrite(output_stream.Get(), 1, output_stream.Size(), output_file);
+  fflush(output_file);
+  fclose(output_file);
+}
+
+}  // namespace sfntly
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subsetter/subset_util.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SAMPLE_SUBSETTER_SUBSET_UTIL_H_
+#define SFNTLY_CPP_SRC_SAMPLE_SUBSETTER_SUBSET_UTIL_H_
+
+namespace sfntly {
+
+class SubsetUtil {
+ public:
+  SubsetUtil();
+  virtual ~SubsetUtil();
+
+  void Subset(const char* input_file_path, const char* output_file_path);
+};
+
+}  // namespace sfntly
+
+#endif  // SFNTLY_CPP_SRC_SAMPLE_SUBSETTER_SUBSET_UTIL_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/character_predicate.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sfntly/port/refcount.h"
+#include "subtly/character_predicate.h"
+
+namespace subtly {
+using namespace sfntly;
+
+// AcceptRange predicate
+AcceptRange::AcceptRange(int32_t start, int32_t end)
+    : start_(start),
+      end_(end) {
+}
+
+AcceptRange::~AcceptRange() {}
+
+bool AcceptRange::operator()(int32_t character) const {
+  return start_ <= character && character <= end_;
+}
+
+// AcceptSet predicate
+AcceptSet::AcceptSet(IntegerSet* characters)
+    : characters_(characters) {
+}
+
+AcceptSet::~AcceptSet() {
+  delete characters_;
+}
+
+bool AcceptSet::operator()(int32_t character) const {
+  return characters_->find(character) != characters_->end();
+}
+
+// AcceptAll predicate
+bool AcceptAll::operator()(int32_t character) const {
+  UNREFERENCED_PARAMETER(character);
+  return true;
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/character_predicate.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_CHARACTER_PREDICATE_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_CHARACTER_PREDICATE_H_
+
+#include "sfntly/port/refcount.h"
+#include "sfntly/port/type.h"
+
+namespace subtly {
+class CharacterPredicate : virtual public sfntly::RefCount {
+ public:
+  CharacterPredicate() {}
+  virtual ~CharacterPredicate() {}
+  virtual bool operator()(int32_t character) const = 0;
+};
+
+// All characters except for those between [start, end] are rejected
+class AcceptRange : public CharacterPredicate,
+                    public sfntly::RefCounted<AcceptRange> {
+ public:
+  AcceptRange(int32_t start, int32_t end);
+  ~AcceptRange();
+  virtual bool operator()(int32_t character) const;
+
+ private:
+  int32_t start_;
+  int32_t end_;
+};
+
+// All characters in IntegerSet
+// The set is OWNED by the predicate! Do not modify it.
+// It will be freed when the predicate is destroyed.
+class AcceptSet : public CharacterPredicate,
+                  public sfntly::RefCounted<AcceptSet> {
+ public:
+  explicit AcceptSet(sfntly::IntegerSet* characters);
+  ~AcceptSet();
+  virtual bool operator()(int32_t character) const;
+
+ private:
+  sfntly::IntegerSet* characters_;
+};
+
+// All characters
+class AcceptAll : public CharacterPredicate,
+                  public sfntly::RefCounted<AcceptAll> {
+ public:
+  AcceptAll() {}
+  ~AcceptAll() {}
+  virtual bool operator()(int32_t character) const;
+};
+}
+
+#endif  // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_CHARACTER_PREDICATE_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/debug_main.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <map>
+#include <utility>
+
+#include "sfntly/font.h"
+#include "sfntly/table/core/cmap_table.h"
+#include "sfntly/tag.h"
+#include "subtly/stats.h"
+#include "subtly/subsetter.h"
+#include "subtly/utils.h"
+
+using namespace subtly;
+
+void PrintUsage(const char* program_name) {
+  fprintf(stdout, "Usage: %s <input_font_file>\n", program_name);
+}
+
+int main(int argc, const char** argv) {
+  const char* program_name = argv[0];
+  if (argc < 2) {
+    PrintUsage(program_name);
+    exit(1);
+  }
+
+  const char* input_font_path = argv[1];
+  const char* output_font_path = argv[2];
+  FontPtr font;
+  font.Attach(subtly::LoadFont(input_font_path));
+
+  int32_t original_size = TotalFontSize(font);
+  Ptr<Subsetter> subsetter = new Subsetter(font, NULL);
+  Ptr<Font> new_font;
+  new_font.Attach(subsetter->Subset());
+  if (!new_font) {
+    fprintf(stdout, "Cannot create subset.\n");
+    return 0;
+  }
+
+  subtly::SerializeFont(output_font_path, new_font);
+  subtly::PrintComparison(stdout, font, new_font);
+  int32_t new_size = TotalFontSize(new_font);
+  fprintf(stdout, "Went from %d to %d: %lf%% of original\n",
+          original_size, new_size,
+          static_cast<double>(new_size) / original_size * 100);
+  return 0;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/font_assembler.cc
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "subtly/font_assembler.h"
+
+#include <stdio.h>
+
+#include <set>
+#include <map>
+
+#include "sfntly/tag.h"
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/table/core/cmap_table.h"
+#include "sfntly/table/truetype/loca_table.h"
+#include "sfntly/table/truetype/glyph_table.h"
+#include "sfntly/table/core/maximum_profile_table.h"
+#include "sfntly/port/type.h"
+#include "sfntly/port/refcount.h"
+#include "subtly/font_info.h"
+
+namespace subtly {
+using namespace sfntly;
+
+FontAssembler::FontAssembler(FontInfo* font_info,
+                             IntegerSet* table_blacklist)
+    : table_blacklist_(table_blacklist) {
+  font_info_ = font_info;
+  Initialize();
+}
+
+FontAssembler::FontAssembler(FontInfo* font_info)
+    : table_blacklist_(NULL) {
+  font_info_ = font_info;
+  Initialize();
+}
+
+void FontAssembler::Initialize() {
+  font_factory_.Attach(sfntly::FontFactory::GetInstance());
+  font_builder_.Attach(font_factory_->NewFontBuilder());
+}
+
+CALLER_ATTACH Font* FontAssembler::Assemble() {
+  // Assemble tables we can subset.
+  if (!AssembleCMapTable() || !AssembleGlyphAndLocaTables()) {
+    return NULL;
+  }
+  // For all other tables, either include them unmodified or don't at all.
+  const TableMap* common_table_map =
+      font_info_->GetTableMap(font_info_->fonts()->begin()->first);
+  for (TableMap::const_iterator it = common_table_map->begin(),
+           e = common_table_map->end(); it != e; ++it) {
+    if (table_blacklist_
+        && table_blacklist_->find(it->first) != table_blacklist_->end()) {
+      continue;
+    }
+    font_builder_->NewTableBuilder(it->first, it->second->ReadFontData());
+  }
+  return font_builder_->Build();
+}
+
+bool FontAssembler::AssembleCMapTable() {
+  // Creating the new CMapTable and the new format 4 CMap
+  Ptr<CMapTable::Builder> cmap_table_builder =
+      down_cast<CMapTable::Builder*>
+      (font_builder_->NewTableBuilder(Tag::cmap));
+  if (!cmap_table_builder)
+    return false;
+  Ptr<CMapTable::CMapFormat4::Builder> cmap_builder =
+      down_cast<CMapTable::CMapFormat4::Builder*>
+      (cmap_table_builder->NewCMapBuilder(CMapFormat::kFormat4,
+                                          CMapTable::WINDOWS_BMP));
+  if (!cmap_builder)
+    return false;
+  // Creating the segments and the glyph id array
+  CharacterMap* chars_to_glyph_ids = font_info_->chars_to_glyph_ids();
+  SegmentList* segment_list = new SegmentList;
+  IntegerList* glyph_id_array = new IntegerList;
+  int32_t last_chararacter = -2;
+  int32_t last_offset = 0;
+  Ptr<CMapTable::CMapFormat4::Builder::Segment> current_segment;
+
+  // For simplicity, we will have one segment per contiguous range.
+  // To test the algorithm, we've replaced the original CMap with the CMap
+  // generated by this code without removing any character.
+  // Tuffy.ttf: CMap went from 3146 to 3972 bytes (1.7% to 2.17% of file)
+  // AnonymousPro.ttf: CMap went from 1524 to 1900 bytes (0.96% to 1.2%)
+  for (CharacterMap::iterator it = chars_to_glyph_ids->begin(),
+           e = chars_to_glyph_ids->end(); it != e; ++it) {
+    int32_t character = it->first;
+    int32_t glyph_id = it->second.glyph_id();
+    if (character != last_chararacter + 1) {  // new segment
+      if (current_segment != NULL) {
+        current_segment->set_end_count(last_chararacter);
+        segment_list->push_back(current_segment);
+      }
+      // start_code = character
+      // end_code = -1 (unknown for now)
+      // id_delta = 0 (we don't use id_delta for this representation)
+      // id_range_offset = last_offset (offset into the glyph_id_array)
+      current_segment =
+          new CMapTable::CMapFormat4::Builder::
+          Segment(character, -1, 0, last_offset);
+    }
+    glyph_id_array->push_back(glyph_id);
+    last_offset += DataSize::kSHORT;
+    last_chararacter = character;
+  }
+  // The last segment is still open.
+  current_segment->set_end_count(last_chararacter);
+  segment_list->push_back(current_segment);
+  // Updating the id_range_offset for every segment.
+  for (int32_t i = 0, num_segs = segment_list->size(); i < num_segs; ++i) {
+    Ptr<CMapTable::CMapFormat4::Builder::Segment> segment = segment_list->at(i);
+    segment->set_id_range_offset(segment->id_range_offset()
+                                 + (num_segs - i + 1) * DataSize::kSHORT);
+  }
+  // Adding the final, required segment.
+  current_segment =
+      new CMapTable::CMapFormat4::Builder::Segment(0xffff, 0xffff, 1, 0);
+  segment_list->push_back(current_segment);
+  // Writing the segments and glyph id array to the CMap
+  cmap_builder->set_segments(segment_list);
+  cmap_builder->set_glyph_id_array(glyph_id_array);
+  delete segment_list;
+  delete glyph_id_array;
+  return true;
+}
+
+bool FontAssembler::AssembleGlyphAndLocaTables() {
+  Ptr<LocaTable::Builder> loca_table_builder =
+      down_cast<LocaTable::Builder*>
+      (font_builder_->NewTableBuilder(Tag::loca));
+  Ptr<GlyphTable::Builder> glyph_table_builder =
+      down_cast<GlyphTable::Builder*>
+      (font_builder_->NewTableBuilder(Tag::glyf));
+
+  GlyphIdSet* resolved_glyph_ids = font_info_->resolved_glyph_ids();
+  IntegerList loca_list;
+  // Basic sanity check: all LOCA tables are of the same size
+  // This is necessary but not suficient!
+  int32_t previous_size = -1;
+  for (FontIdMap::iterator it = font_info_->fonts()->begin();
+       it != font_info_->fonts()->end(); ++it) {
+    Ptr<LocaTable> loca_table =
+        down_cast<LocaTable*>(font_info_->GetTable(it->first, Tag::loca));
+    int32_t current_size = loca_table->header_length();
+    if (previous_size != -1 && current_size != previous_size) {
+      return false;
+    }
+    previous_size = current_size;
+  }
+
+  // Assuming all fonts referenced by the FontInfo are the subsets of the same
+  // font, their loca tables should all have the same sizes.
+  // We'll just get the size of the first font's LOCA table for simplicty.
+  Ptr<LocaTable> first_loca_table =
+    down_cast<LocaTable*>
+    (font_info_->GetTable(font_info_->fonts()->begin()->first, Tag::loca));
+  int32_t num_loca_glyphs = first_loca_table->num_glyphs();
+  loca_list.resize(num_loca_glyphs);
+  loca_list.push_back(0);
+  int32_t last_glyph_id = 0;
+  int32_t last_offset = 0;
+  GlyphTable::GlyphBuilderList* glyph_builders =
+      glyph_table_builder->GlyphBuilders();
+
+  for (GlyphIdSet::iterator it = resolved_glyph_ids->begin(),
+           e = resolved_glyph_ids->end(); it != e; ++it) {
+    // Get the glyph for this resolved_glyph_id.
+    int32_t resolved_glyph_id = it->glyph_id();
+    int32_t font_id = it->font_id();
+    // Get the LOCA table for the current glyph id.
+    Ptr<LocaTable> loca_table =
+        down_cast<LocaTable*>
+        (font_info_->GetTable(font_id, Tag::loca));
+    int32_t length = loca_table->GlyphLength(resolved_glyph_id);
+    int32_t offset = loca_table->GlyphOffset(resolved_glyph_id);
+
+    // Get the GLYF table for the current glyph id.
+    Ptr<GlyphTable> glyph_table =
+        down_cast<GlyphTable*>
+        (font_info_->GetTable(font_id, Tag::glyf));
+    GlyphPtr glyph;
+    glyph.Attach(glyph_table->GetGlyph(offset, length));
+
+    // The data reference by the glyph is copied into a new glyph and
+    // added to the glyph_builders belonging to the glyph_table_builder.
+    // When Build gets called, all the glyphs will be built.
+    Ptr<ReadableFontData> data = glyph->ReadFontData();
+    Ptr<WritableFontData> copy_data;
+    copy_data.Attach(WritableFontData::CreateWritableFontData(data->Length()));
+    data->CopyTo(copy_data);
+    GlyphBuilderPtr glyph_builder;
+    glyph_builder.Attach(glyph_table_builder->GlyphBuilder(copy_data));
+    glyph_builders->push_back(glyph_builder);
+
+    // If there are missing glyphs between the last glyph_id and the
+    // current resolved_glyph_id, since the LOCA table needs to have the same
+    // size, the offset is kept the same.
+    loca_list.resize(std::max(loca_list.size(),
+                     static_cast<size_t>(resolved_glyph_id + 2)));
+    for (int32_t i = last_glyph_id + 1; i <= resolved_glyph_id; ++i)
+      loca_list[i] = last_offset;
+    last_offset += length;
+    loca_list[resolved_glyph_id + 1] = last_offset;
+    last_glyph_id = resolved_glyph_id + 1;
+  }
+  // If there are missing glyph ids, their loca entries must all point
+  // to the same offset as the last valid glyph id making them all zero length.
+  for (int32_t i = last_glyph_id + 1; i <= num_loca_glyphs; ++i)
+    loca_list[i] = last_offset;
+  loca_table_builder->SetLocaList(&loca_list);
+  return true;
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/font_assembler.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_ASSEMBLER_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_ASSEMBLER_H_
+
+#include <set>
+#include <map>
+
+#include "subtly/font_info.h"
+
+#include "sfntly/tag.h"
+#include "sfntly/font.h"
+#include "sfntly/port/type.h"
+#include "sfntly/port/refcount.h"
+#include "sfntly/table/core/cmap_table.h"
+#include "sfntly/table/truetype/glyph_table.h"
+#include "sfntly/table/truetype/loca_table.h"
+
+namespace subtly {
+// Assembles FontInfo into font builders.
+// Does not take ownership of data passed to it.
+class FontAssembler : public sfntly::RefCounted<FontAssembler> {
+ public:
+  // font_info is the FontInfo which will be used for the new font
+  // table_blacklist is used to decide which tables to exclude from the
+  // final font.
+  FontAssembler(FontInfo* font_info, sfntly::IntegerSet* table_blacklist);
+  explicit FontAssembler(FontInfo* font_info);
+  ~FontAssembler() { }
+
+  // Assemble a new font from the font info object.
+  virtual CALLER_ATTACH sfntly::Font* Assemble();
+
+  sfntly::IntegerSet* table_blacklist() const { return table_blacklist_; }
+  void set_table_blacklist(sfntly::IntegerSet* table_blacklist) {
+    table_blacklist_ = table_blacklist;
+  }
+
+ protected:
+  virtual bool AssembleCMapTable();
+  virtual bool AssembleGlyphAndLocaTables();
+
+  virtual void Initialize();
+
+ private:
+  sfntly::Ptr<FontInfo> font_info_;
+  sfntly::Ptr<sfntly::FontFactory> font_factory_;
+  sfntly::Ptr<sfntly::Font::Builder> font_builder_;
+  sfntly::IntegerSet* table_blacklist_;
+};
+}
+
+#endif  // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_ASSEMBLER_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/font_info.cc
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "subtly/font_info.h"
+
+#include <stdio.h>
+
+#include <set>
+#include <map>
+
+#include "subtly/character_predicate.h"
+
+#include "sfntly/tag.h"
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/table/core/cmap_table.h"
+#include "sfntly/table/truetype/loca_table.h"
+#include "sfntly/table/truetype/glyph_table.h"
+#include "sfntly/table/core/maximum_profile_table.h"
+#include "sfntly/port/type.h"
+#include "sfntly/port/refcount.h"
+
+namespace subtly {
+using namespace sfntly;
+/******************************************************************************
+ * GlyphId class
+ ******************************************************************************/
+GlyphId::GlyphId(int32_t glyph_id, FontId font_id)
+    : glyph_id_(glyph_id),
+      font_id_(font_id) {
+}
+
+bool GlyphId::operator==(const GlyphId& other) const {
+  return glyph_id_ == other.glyph_id();
+}
+
+bool GlyphId::operator<(const GlyphId& other) const {
+  return glyph_id_ < other.glyph_id();
+}
+
+/******************************************************************************
+ * FontInfo class
+ ******************************************************************************/
+FontInfo::FontInfo()
+    : chars_to_glyph_ids_(new CharacterMap),
+      resolved_glyph_ids_(new GlyphIdSet),
+      fonts_(new FontIdMap) {
+}
+
+FontInfo::FontInfo(CharacterMap* chars_to_glyph_ids,
+                   GlyphIdSet* resolved_glyph_ids,
+                   FontIdMap* fonts) {
+  chars_to_glyph_ids_ = new CharacterMap(chars_to_glyph_ids->begin(),
+                                         chars_to_glyph_ids->end());
+  resolved_glyph_ids_ = new GlyphIdSet(resolved_glyph_ids->begin(),
+                                       resolved_glyph_ids->end());
+  fonts_ = new FontIdMap(fonts->begin(), fonts->end());
+}
+
+FontInfo::~FontInfo() {
+  delete chars_to_glyph_ids_;
+  delete resolved_glyph_ids_;
+  delete fonts_;
+}
+
+FontDataTable* FontInfo::GetTable(FontId font_id, int32_t tag) {
+  if (!fonts_)
+    return NULL;
+  FontIdMap::iterator it = fonts_->find(font_id);
+  if (it == fonts_->end())
+    return NULL;
+  return it->second->GetTable(tag);
+}
+
+const TableMap* FontInfo::GetTableMap(FontId font_id) {
+  if (!fonts_)
+    return NULL;
+  FontIdMap::iterator it = fonts_->find(font_id);
+  if (it == fonts_->end())
+    return NULL;
+  return it->second->GetTableMap();
+}
+
+void FontInfo::set_chars_to_glyph_ids(CharacterMap* chars_to_glyph_ids) {
+  *chars_to_glyph_ids_ = *chars_to_glyph_ids;
+}
+
+void FontInfo::set_resolved_glyph_ids(GlyphIdSet* resolved_glyph_ids) {
+  *resolved_glyph_ids_ = *resolved_glyph_ids;
+}
+
+void FontInfo::set_fonts(FontIdMap* fonts) {
+  *fonts_ = *fonts;
+}
+
+/******************************************************************************
+ * FontSourcedInfoBuilder class
+ ******************************************************************************/
+FontSourcedInfoBuilder::FontSourcedInfoBuilder(Font* font, FontId font_id)
+    : font_(font),
+      font_id_(font_id),
+      predicate_(NULL) {
+  Initialize();
+}
+
+FontSourcedInfoBuilder::FontSourcedInfoBuilder(Font* font,
+                                               FontId font_id,
+                                               CharacterPredicate* predicate)
+    : font_(font),
+      font_id_(font_id),
+      predicate_(predicate) {
+  Initialize();
+}
+
+void FontSourcedInfoBuilder::Initialize() {
+  Ptr<CMapTable> cmap_table = down_cast<CMapTable*>(font_->GetTable(Tag::cmap));
+  // We prefer Windows BMP format 4 cmaps.
+  cmap_.Attach(cmap_table->GetCMap(CMapTable::WINDOWS_BMP));
+  // But if none is found,
+  if (!cmap_) {
+    return;
+  }
+  loca_table_ = down_cast<LocaTable*>(font_->GetTable(Tag::loca));
+  glyph_table_ = down_cast<GlyphTable*>(font_->GetTable(Tag::glyf));
+}
+
+CALLER_ATTACH FontInfo* FontSourcedInfoBuilder::GetFontInfo() {
+  CharacterMap* chars_to_glyph_ids = new CharacterMap;
+  bool success = GetCharacterMap(chars_to_glyph_ids);
+  if (!success) {
+    delete chars_to_glyph_ids;
+#if defined (SUBTLY_DEBUG)
+    fprintf(stderr, "Error creating character map.\n");
+#endif
+    return NULL;
+  }
+  GlyphIdSet* resolved_glyph_ids = new GlyphIdSet;
+  success = ResolveCompositeGlyphs(chars_to_glyph_ids, resolved_glyph_ids);
+  if (!success) {
+    delete chars_to_glyph_ids;
+    delete resolved_glyph_ids;
+#if defined (SUBTLY_DEBUG)
+    fprintf(stderr, "Error resolving composite glyphs.\n");
+#endif
+    return NULL;
+  }
+  Ptr<FontInfo> font_info = new FontInfo;
+  font_info->set_chars_to_glyph_ids(chars_to_glyph_ids);
+  font_info->set_resolved_glyph_ids(resolved_glyph_ids);
+  FontIdMap* font_id_map = new FontIdMap;
+  font_id_map->insert(std::make_pair(font_id_, font_));
+  font_info->set_fonts(font_id_map);
+  delete chars_to_glyph_ids;
+  delete resolved_glyph_ids;
+  delete font_id_map;
+  return font_info.Detach();
+}
+
+bool FontSourcedInfoBuilder::GetCharacterMap(CharacterMap* chars_to_glyph_ids) {
+  if (!cmap_ || !chars_to_glyph_ids)
+    return false;
+  chars_to_glyph_ids->clear();
+  CMapTable::CMap::CharacterIterator* character_iterator = cmap_->Iterator();
+  if (!character_iterator)
+    return false;
+  while (character_iterator->HasNext()) {
+    int32_t character = character_iterator->Next();
+    if (!predicate_ || (*predicate_)(character)) {
+      chars_to_glyph_ids->insert
+          (std::make_pair(character,
+                          GlyphId(cmap_->GlyphId(character), font_id_)));
+    }
+  }
+  delete character_iterator;
+  return true;
+}
+
+bool
+FontSourcedInfoBuilder::ResolveCompositeGlyphs(CharacterMap* chars_to_glyph_ids,
+                                               GlyphIdSet* resolved_glyph_ids) {
+  if (!chars_to_glyph_ids || !resolved_glyph_ids)
+    return false;
+  resolved_glyph_ids->clear();
+  resolved_glyph_ids->insert(GlyphId(0, font_id_));
+  IntegerSet* unresolved_glyph_ids = new IntegerSet;
+  // Since composite glyph elements might themselves be composite, we would need
+  // to recursively resolve the elements too. To avoid the recursion we
+  // create two sets, |unresolved_glyph_ids| for the unresolved glyphs,
+  // initially containing all the ids and |resolved_glyph_ids|, initially empty.
+  // We'll remove glyph ids from |unresolved_glyph_ids| until it is empty and,
+  // if the glyph is composite, add its elements to the unresolved set.
+  for (CharacterMap::iterator it = chars_to_glyph_ids->begin(),
+           e = chars_to_glyph_ids->end(); it != e; ++it) {
+    unresolved_glyph_ids->insert(it->second.glyph_id());
+  }
+  // As long as there are unresolved glyph ids.
+  while (!unresolved_glyph_ids->empty()) {
+    // Get the corresponding glyph.
+    int32_t glyph_id = *(unresolved_glyph_ids->begin());
+    unresolved_glyph_ids->erase(unresolved_glyph_ids->begin());
+    if (glyph_id < 0 || glyph_id > loca_table_->num_glyphs()) {
+#if defined (SUBTLY_DEBUG)
+      fprintf(stderr, "%d larger than %d or smaller than 0\n", glyph_id,
+              loca_table_->num_glyphs());
+#endif
+      continue;
+    }
+    int32_t length = loca_table_->GlyphLength(glyph_id);
+    if (length == 0) {
+#if defined (SUBTLY_DEBUG)
+      fprintf(stderr, "Zero length glyph %d\n", glyph_id);
+#endif
+      continue;
+    }
+    int32_t offset = loca_table_->GlyphOffset(glyph_id);
+    GlyphPtr glyph;
+    glyph.Attach(glyph_table_->GetGlyph(offset, length));
+    if (glyph == NULL) {
+#if defined (SUBTLY_DEBUG)
+      fprintf(stderr, "GetGlyph returned NULL for %d\n", glyph_id);
+#endif
+      continue;
+    }
+    // Mark the glyph as resolved.
+    resolved_glyph_ids->insert(GlyphId(glyph_id, font_id_));
+    // If it is composite, add all its components to the unresolved glyph set.
+    if (glyph->GlyphType() == GlyphType::kComposite) {
+      Ptr<GlyphTable::CompositeGlyph> composite_glyph =
+          down_cast<GlyphTable::CompositeGlyph*>(glyph.p_);
+      int32_t num_glyphs = composite_glyph->NumGlyphs();
+      for (int32_t i = 0; i < num_glyphs; ++i) {
+        int32_t glyph_id = composite_glyph->GlyphIndex(i);
+        if (resolved_glyph_ids->find(GlyphId(glyph_id, -1))
+            == resolved_glyph_ids->end()) {
+          unresolved_glyph_ids->insert(glyph_id);
+        }
+      }
+    }
+  }
+  delete unresolved_glyph_ids;
+  return true;
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/font_info.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_INFO_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_INFO_H_
+
+#include <map>
+#include <set>
+
+#include "sfntly/font.h"
+#include "sfntly/port/type.h"
+#include "sfntly/port/refcount.h"
+#include "sfntly/table/core/cmap_table.h"
+#include "sfntly/table/truetype/glyph_table.h"
+#include "sfntly/table/truetype/loca_table.h"
+
+namespace subtly {
+class CharacterPredicate;
+
+typedef int32_t FontId;
+typedef std::map<FontId, sfntly::Ptr<sfntly::Font> > FontIdMap;
+
+// Glyph id pair that contains the loca table glyph id as well as the
+// font id that has the glyph table this glyph belongs to.
+class GlyphId {
+ public:
+  GlyphId(int32_t glyph_id, FontId font_id);
+  ~GlyphId() {}
+
+  bool operator==(const GlyphId& other) const;
+  bool operator<(const GlyphId& other) const;
+
+  int32_t glyph_id() const { return glyph_id_; }
+  void set_glyph_id(const int32_t glyph_id) { glyph_id_ = glyph_id; }
+  FontId font_id() const { return font_id_; }
+  void set_font_id(const FontId font_id) { font_id_ = font_id; }
+
+ private:
+  int32_t glyph_id_;
+  FontId font_id_;
+};
+
+typedef std::map<int32_t, GlyphId> CharacterMap;
+typedef std::set<GlyphId> GlyphIdSet;
+
+// Font information used for FontAssembler in the construction of a new font.
+// Will make copies of character map, glyph id set and font id map.
+class FontInfo : public sfntly::RefCounted<FontInfo> {
+ public:
+  // Empty FontInfo object.
+  FontInfo();
+  // chars_to_glyph_ids maps characters to GlyphIds for CMap construction
+  // resolved_glyph_ids defines GlyphIds which should be in the final font
+  // fonts is a map of font ids to fonts to reference any needed table
+  FontInfo(CharacterMap* chars_to_glyph_ids,
+           GlyphIdSet* resolved_glyph_ids,
+           FontIdMap* fonts);
+  virtual ~FontInfo();
+
+  // Gets the table with the specified tag from the font corresponding to
+  // font_id or NULL if there is no such font/table.
+  // font_id is the id of the font that contains the table
+  // tag identifies the table to be obtained
+  virtual sfntly::FontDataTable* GetTable(FontId font_id, int32_t tag);
+  // Gets the table map of the font whose id is font_id
+  virtual const sfntly::TableMap* GetTableMap(FontId);
+
+  CharacterMap* chars_to_glyph_ids() const { return chars_to_glyph_ids_; }
+  // Takes ownership of the chars_to_glyph_ids CharacterMap.
+  void set_chars_to_glyph_ids(CharacterMap* chars_to_glyph_ids);
+  GlyphIdSet* resolved_glyph_ids() const { return resolved_glyph_ids_; }
+  // Takes ownership of the glyph_ids GlyphIdSet.
+  void set_resolved_glyph_ids(GlyphIdSet* glyph_ids);
+  FontIdMap* fonts() const { return fonts_; }
+  // Takes ownership of the fonts FontIdMap.
+  void set_fonts(FontIdMap* fonts);
+
+ private:
+  CharacterMap* chars_to_glyph_ids_;
+  GlyphIdSet* resolved_glyph_ids_;
+  FontIdMap* fonts_;
+};
+
+// FontSourcedInfoBuilder is used to create a FontInfo object from a Font
+// optionally specifying a CharacterPredicate to filter out some of
+// the font's characters.
+// It does not take ownership or copy the values its constructor receives.
+class FontSourcedInfoBuilder :
+      public sfntly::RefCounted<FontSourcedInfoBuilder> {
+ public:
+  FontSourcedInfoBuilder(sfntly::Font* font, FontId font_id);
+  FontSourcedInfoBuilder(sfntly::Font* font,
+                         FontId font_id,
+                         CharacterPredicate* predicate);
+  virtual ~FontSourcedInfoBuilder() { }
+
+  virtual CALLER_ATTACH FontInfo* GetFontInfo();
+
+ protected:
+  bool GetCharacterMap(CharacterMap* chars_to_glyph_ids);
+  bool ResolveCompositeGlyphs(CharacterMap* chars_to_glyph_ids,
+                              GlyphIdSet* resolved_glyph_ids);
+  void Initialize();
+
+ private:
+  sfntly::Ptr<sfntly::Font> font_;
+  FontId font_id_;
+  CharacterPredicate* predicate_;
+
+  sfntly::Ptr<sfntly::CMapTable::CMap> cmap_;
+  sfntly::Ptr<sfntly::LocaTable> loca_table_;
+  sfntly::Ptr<sfntly::GlyphTable> glyph_table_;
+};
+}
+#endif  // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_INFO_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/merger.cc
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "subtly/merger.h"
+
+#include <stdio.h>
+
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "subtly/character_predicate.h"
+#include "subtly/font_assembler.h"
+#include "subtly/font_info.h"
+#include "subtly/utils.h"
+
+namespace subtly {
+using namespace sfntly;
+
+/******************************************************************************
+ * Merger class
+ ******************************************************************************/
+Merger::Merger(FontArray* fonts) {
+  if (!fonts) {
+    return;
+  }
+  int32_t num_fonts = fonts->size();
+  for (int32_t i = 0; i < num_fonts; ++i) {
+    fonts_.insert(std::make_pair(i, fonts->at(i)));
+  }
+}
+
+CALLER_ATTACH Font* Merger::Merge() {
+  Ptr<FontInfo> merged_info;
+  merged_info.Attach(MergeFontInfos());
+  if (!merged_info) {
+#if defined (SUBTLY_DEBUG)
+    fprintf(stderr, "Could not create merged font info\n");
+#endif
+    return NULL;
+  }
+  Ptr<FontAssembler> font_assembler = new FontAssembler(merged_info);
+  return font_assembler->Assemble();
+}
+
+CALLER_ATTACH FontInfo* Merger::MergeFontInfos() {
+  Ptr<FontInfo> font_info = new FontInfo;
+  font_info->set_fonts(&fonts_);
+  for (FontIdMap::iterator it = fonts_.begin(),
+           e = fonts_.end(); it != e; ++it) {
+    Ptr<FontSourcedInfoBuilder> info_builder =
+        new FontSourcedInfoBuilder(it->second, it->first, NULL);
+    Ptr<FontInfo> current_font_info;
+    current_font_info.Attach(info_builder->GetFontInfo());
+    if (!current_font_info) {
+#if defined (SUBTLY_DEBUG)
+      fprintf(stderr, "Couldn't create font info. "
+              "No subset will be generated.\n");
+#endif
+      return NULL;
+    }
+    font_info->chars_to_glyph_ids()->insert(
+        current_font_info->chars_to_glyph_ids()->begin(),
+        current_font_info->chars_to_glyph_ids()->end());
+    font_info->resolved_glyph_ids()->insert(
+        current_font_info->resolved_glyph_ids()->begin(),
+        current_font_info->resolved_glyph_ids()->end());
+#if defined (SUBTLY_DEBUG)
+    fprintf(stderr, "Counts: chars_to_glyph_ids: %d; resoved_glyph_ids: %d\n",
+            font_info->chars_to_glyph_ids()->size(),
+            font_info->resolved_glyph_ids()->size());
+#endif
+  }
+  return font_info.Detach();
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/merger.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_MERGER_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_MERGER_H_
+
+#include "subtly/character_predicate.h"
+#include "subtly/font_info.h"
+
+namespace sfntly {
+class Font;
+}
+
+namespace subtly {
+// Merges the subsets in the font array into a single font.
+class Merger : public sfntly::RefCounted<Merger> {
+ public:
+  explicit Merger(sfntly::FontArray* fonts);
+  virtual ~Merger() { }
+
+  // Performs merging returning the subsetted font.
+  virtual CALLER_ATTACH sfntly::Font* Merge();
+
+ protected:
+  virtual CALLER_ATTACH FontInfo* MergeFontInfos();
+
+ private:
+  FontIdMap fonts_;
+};
+}
+
+#endif  // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_MERGER_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/merger_main.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <map>
+#include <utility>
+
+#include "sfntly/font.h"
+#include "subtly/merger.h"
+#include "subtly/stats.h"
+#include "subtly/utils.h"
+
+using namespace subtly;
+
+void PrintUsage(const char* program_name) {
+  fprintf(stdout, "Usage: %s <input_font_file1> <input_font_file2> ..."
+          "<input_font_filen> <output_font_file>\n",
+          program_name);
+}
+
+void CheckLoading(const char* font_path, Font* font) {
+  if (!font || font->num_tables() == 0) {
+    fprintf(stderr, "Could not load font %s. Terminating.\n", font_path);
+    exit(1);
+  }
+}
+
+int main(int argc, const char** argv) {
+  if (argc < 3) {
+    PrintUsage(argv[0]);
+    exit(1);
+  }
+
+  FontArray fonts;
+  for (int32_t i = 1; i < argc - 1; ++i) {
+    Ptr<Font> font;
+    font.Attach(LoadFont(argv[i]));
+    CheckLoading(argv[i], font);
+    fonts.push_back(font);
+  }
+
+  Ptr<Merger> merger = new Merger(&fonts);
+  FontPtr new_font;
+  new_font.Attach(merger->Merge());
+
+  fprintf(stderr, "Serializing font to %s\n", argv[argc - 1]);
+  SerializeFont(argv[argc - 1], new_font);
+  if (!new_font) {
+    fprintf(stdout, "Cannot create merged font.\n");
+    return 1;
+  }
+
+  return 0;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/stats.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#include "sfntly/font.h"
+#include "sfntly/table/table.h"
+#include "sfntly/tag.h"
+#include "subtly/stats.h"
+
+namespace subtly {
+using namespace sfntly;
+
+int32_t TotalFontSize(Font* font) {
+  int32_t size = 0;
+  const TableMap* table_map = font->GetTableMap();
+  for (TableMap::const_iterator it = table_map->begin(),
+           e = table_map->end(); it != e; ++it) {
+    size += it->second->DataLength();
+  }
+  return size;
+}
+
+double TableSizePercent(Font* font, int32_t tag) {
+  TablePtr table = font->GetTable(tag);
+  return static_cast<double>(table->DataLength()) / TotalFontSize(font) * 100;
+}
+
+void PrintComparison(FILE* out, Font* font, Font* new_font) {
+  fprintf(out, "====== Table Comparison (original v. subset) ======\n");
+  const TableMap* tables = font->GetTableMap();
+  for (TableMap::const_iterator it = tables->begin(),
+           e = tables->end(); it != e; ++it) {
+    char *name = TagToString(it->first);
+    int32_t size = it->second->DataLength();
+    fprintf(out, "-- %s: %d (%lf%%) ", name, size,
+            TableSizePercent(font, it->first));
+    delete[] name;
+
+    Ptr<FontDataTable> new_table = new_font->GetTable(it->first);
+    int32_t new_size = 0;
+    double size_percent = 0;
+    if (new_table) {
+      new_size = new_table->DataLength();
+      size_percent = subtly::TableSizePercent(new_font, it->first);
+    }
+
+    if (new_size == size) {
+      fprintf(out, "| same size\n");
+    } else {
+      fprintf(out, "-> %d (%lf%%) | %lf%% of original\n", new_size,
+              size_percent, static_cast<double>(new_size) / size * 100);
+    }
+  }
+}
+
+void PrintStats(FILE* out, Font* font) {
+  fprintf(out, "====== Table Stats ======\n");
+  const TableMap* tables = font->GetTableMap();
+  for (TableMap::const_iterator it = tables->begin(),
+           e = tables->end(); it != e; ++it) {
+    char *name = TagToString(it->first);
+    int32_t size = it->second->DataLength();
+    fprintf(out, "-- %s: %d (%lf%%)\n", name, size,
+            TableSizePercent(font, it->first));
+    delete[] name;
+  }
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/stats.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_STATS_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_STATS_H_
+
+#include <stdio.h>
+
+#include "sfntly/port/type.h"
+
+namespace sfntly {
+class Font;
+}
+
+namespace subtly {
+using namespace sfntly;
+
+int32_t TotalFontSize(Font* font);
+
+double TableSizePercent(Font* font, int32_t tag);
+
+void PrintComparison(FILE* out, Font* font, Font* new_font);
+
+void PrintStats(FILE* out, Font* font);
+}
+
+#endif  // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_STATS_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/subsetter.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "subtly/subsetter.h"
+
+#include <stdio.h>
+
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/tag.h"
+#include "subtly/character_predicate.h"
+#include "subtly/font_assembler.h"
+#include "subtly/font_info.h"
+#include "subtly/utils.h"
+
+namespace subtly {
+using namespace sfntly;
+
+/******************************************************************************
+ * Subsetter class
+ ******************************************************************************/
+Subsetter::Subsetter(Font* font, CharacterPredicate* predicate)
+    : font_(font),
+      predicate_(predicate) {
+}
+
+Subsetter::Subsetter(const char* font_path, CharacterPredicate* predicate)
+    : predicate_(predicate) {
+  font_.Attach(LoadFont(font_path));
+}
+
+CALLER_ATTACH Font* Subsetter::Subset() {
+  Ptr<FontSourcedInfoBuilder> info_builder =
+      new FontSourcedInfoBuilder(font_, 0, predicate_);
+
+  Ptr<FontInfo> font_info;
+  font_info.Attach(info_builder->GetFontInfo());
+  if (!font_info) {
+#if defined (SUBTLY_DEBUG)
+    fprintf(stderr,
+            "Couldn't create font info. No subset will be generated.\n");
+#endif
+    return NULL;
+  }
+  IntegerSet* table_blacklist = new IntegerSet;
+  table_blacklist->insert(Tag::DSIG);
+  Ptr<FontAssembler> font_assembler = new FontAssembler(font_info,
+                                                        table_blacklist);
+  Ptr<Font> font_subset;
+  font_subset.Attach(font_assembler->Assemble());
+  delete table_blacklist;
+  return font_subset.Detach();
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/subsetter.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_SUBSETTER_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_SUBSETTER_H_
+
+#include "sfntly/font.h"
+// Cannot remove this header due to Ptr<T> instantiation issue
+#include "subtly/character_predicate.h"
+
+namespace subtly {
+// Subsets a given font using a character predicate.
+class Subsetter : public sfntly::RefCounted<Subsetter> {
+ public:
+  Subsetter(sfntly::Font* font, CharacterPredicate* predicate);
+  Subsetter(const char* font_path, CharacterPredicate* predicate);
+  virtual ~Subsetter() { }
+
+  // Performs subsetting returning the subsetted font.
+  virtual CALLER_ATTACH sfntly::Font* Subset();
+
+ private:
+  sfntly::Ptr<sfntly::Font> font_;
+  sfntly::Ptr<CharacterPredicate> predicate_;
+};
+}
+
+#endif  // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_SUBSETTER_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/subsetter_main.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <map>
+#include <utility>
+
+#include "sfntly/font.h"
+#include "subtly/character_predicate.h"
+#include "subtly/stats.h"
+#include "subtly/subsetter.h"
+#include "subtly/utils.h"
+
+using namespace subtly;
+
+void PrintUsage(const char* program_name) {
+  fprintf(stdout, "Usage: %s <input_font_file> <output_font_file>"
+          "<start_char> <end_char>\n", program_name);
+}
+
+int main(int argc, const char** argv) {
+  const char* program_name = argv[0];
+  if (argc < 5) {
+    PrintUsage(program_name);
+    exit(1);
+  }
+
+  const char* input_font_path = argv[1];
+  const char* output_font_path = argv[2];
+  FontPtr font;
+  font.Attach(subtly::LoadFont(input_font_path));
+  if (font->num_tables() == 0) {
+    fprintf(stderr, "Could not load font %s.\n", input_font_path);
+    exit(1);
+  }
+
+  const char* start_char = argv[3];
+  const char* end_char = argv[4];
+  if (start_char[1] != 0) {
+    fprintf(stderr, "Start character %c invalid.\n", start_char[0]);
+    exit(1);
+  }
+  if (end_char[1] != 0) {
+    fprintf(stderr, "Start character %c invalid.\n", end_char[0]);
+    exit(1);
+  }
+  int32_t original_size = TotalFontSize(font);
+
+
+  Ptr<CharacterPredicate> range_predicate =
+      new AcceptRange(start_char[0], end_char[0]);
+  Ptr<Subsetter> subsetter = new Subsetter(font, range_predicate);
+  Ptr<Font> new_font;
+  new_font.Attach(subsetter->Subset());
+  if (!new_font) {
+    fprintf(stdout, "Cannot create subset.\n");
+    return 0;
+  }
+
+  subtly::SerializeFont(output_font_path, new_font);
+  subtly::PrintComparison(stdout, font, new_font);
+  int32_t new_size = TotalFontSize(new_font);
+  fprintf(stdout, "Went from %d to %d: %lf%% of original\n",
+          original_size, new_size,
+          static_cast<double>(new_size) / original_size * 100);
+  return 0;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/utils.cc
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "subtly/utils.h"
+
+#include "sfntly/data/growable_memory_byte_array.h"
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/port/file_input_stream.h"
+#include "sfntly/port/memory_output_stream.h"
+
+namespace subtly {
+using namespace sfntly;
+
+CALLER_ATTACH Font* LoadFont(const char* font_path) {
+  Ptr<FontFactory> font_factory;
+  font_factory.Attach(FontFactory::GetInstance());
+  FontArray fonts;
+  LoadFonts(font_path, font_factory, &fonts);
+  return fonts[0].Detach();
+}
+
+CALLER_ATTACH Font::Builder* LoadFontBuilder(const char* font_path) {
+  FontFactoryPtr font_factory;
+  font_factory.Attach(FontFactory::GetInstance());
+  FontBuilderArray builders;
+  LoadFontBuilders(font_path, font_factory, &builders);
+  return builders[0].Detach();
+}
+
+void LoadFonts(const char* font_path, FontFactory* factory, FontArray* fonts) {
+  FileInputStream input_stream;
+  input_stream.Open(font_path);
+  factory->LoadFonts(&input_stream, fonts);
+  input_stream.Close();
+}
+
+void LoadFontBuilders(const char* font_path,
+                      FontFactory* factory,
+                      FontBuilderArray* builders) {
+  FileInputStream input_stream;
+  input_stream.Open(font_path);
+  factory->LoadFontsForBuilding(&input_stream, builders);
+  input_stream.Close();
+}
+
+bool SerializeFont(const char* font_path, Font* font) {
+  if (!font_path)
+    return false;
+  FontFactoryPtr font_factory;
+  font_factory.Attach(FontFactory::GetInstance());
+  return SerializeFont(font_path, font_factory, font);
+}
+
+bool SerializeFont(const char* font_path, FontFactory* factory, Font* font) {
+  if (!font_path || !factory || !font)
+    return false;
+  // Serializing the font to a stream.
+  MemoryOutputStream output_stream;
+  factory->SerializeFont(font, &output_stream);
+  // Serializing the stream to a file.
+  FILE* output_file = NULL;
+#if defined WIN32
+  fopen_s(&output_file, font_path, "wb");
+#else
+  output_file = fopen(font_path, "wb");
+#endif
+  if (output_file == reinterpret_cast<FILE*>(NULL))
+    return false;
+  for (size_t i = 0; i < output_stream.Size(); ++i) {
+    fwrite(&(output_stream.Get()[i]), 1, 1, output_file);
+  }
+  fflush(output_file);
+  fclose(output_file);
+  return true;
+}
+};
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sample/subtly/utils.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_UTILS_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_UTILS_H_
+
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+
+namespace subtly {
+CALLER_ATTACH sfntly::Font* LoadFont(const char* font_path);
+CALLER_ATTACH sfntly::Font::Builder* LoadFontBuilder(const char* font_path);
+
+void LoadFonts(const char* font_path, sfntly::FontFactory* factory,
+               sfntly::FontArray* fonts);
+void LoadFontBuilders(const char* font_path,
+                      sfntly::FontFactory* factory,
+                      sfntly::FontBuilderArray* builders);
+
+bool SerializeFont(const char* font_path, sfntly::Font* font);
+bool SerializeFont(const char* font_path, sfntly::FontFactory* factory,
+                   sfntly::Font* font);
+}
+
+#endif  // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_UTILS_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/byte_array.cc
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sfntly/data/byte_array.h"
+
+#include <algorithm>
+
+#include "sfntly/port/exception_type.h"
+
+namespace sfntly {
+
+const int32_t ByteArray::COPY_BUFFER_SIZE = 8192;
+
+ByteArray::~ByteArray() {}
+
+int32_t ByteArray::Length() { return filled_length_; }
+int32_t ByteArray::Size() { return storage_length_; }
+
+int32_t ByteArray::SetFilledLength(int32_t filled_length) {
+  filled_length_ = std::min<int32_t>(filled_length, storage_length_);
+  return filled_length_;
+}
+
+int32_t ByteArray::Get(int32_t index) {
+  if (index < 0 || index >= Length())
+    return -1;
+  return InternalGet(index) & 0xff;
+}
+
+int32_t ByteArray::Get(int32_t index, ByteVector* b) {
+  assert(b);
+  return Get(index, &((*b)[0]), 0, b->size());
+}
+
+int32_t ByteArray::Get(int32_t index,
+                       byte_t* b,
+                       int32_t offset,
+                       int32_t length) {
+  assert(b);
+  if (index < 0 || index >= filled_length_) {
+    return 0;
+  }
+  int32_t actual_length = std::min<int32_t>(length, filled_length_ - index);
+  return InternalGet(index, b, offset, actual_length);
+}
+
+void ByteArray::Put(int32_t index, byte_t b) {
+  if (index < 0 || index >= Size()) {
+#if defined (SFNTLY_NO_EXCEPTION)
+    return;
+#else
+    throw IndexOutOfBoundException(
+        "Attempt to write outside the bounds of the data");
+#endif
+  }
+  InternalPut(index, b);
+  filled_length_ = std::max<int32_t>(filled_length_, index + 1);
+}
+
+int32_t ByteArray::Put(int index, ByteVector* b) {
+  assert(b);
+  return Put(index, &((*b)[0]), 0, b->size());
+}
+
+int32_t ByteArray::Put(int32_t index,
+                       byte_t* b,
+                       int32_t offset,
+                       int32_t length) {
+  assert(b);
+  if (index < 0 || index >= Size()) {
+#if defined (SFNTLY_NO_EXCEPTION)
+    return 0;
+#else
+    throw IndexOutOfBoundException(
+        "Attempt to write outside the bounds of the data");
+#endif
+  }
+  int32_t actual_length = std::min<int32_t>(length, Size() - index);
+  int32_t bytes_written = InternalPut(index, b, offset, actual_length);
+  filled_length_ = std::max<int32_t>(filled_length_, index + bytes_written);
+  return bytes_written;
+}
+
+int32_t ByteArray::CopyTo(ByteArray* array) {
+  return CopyTo(array, 0, Length());
+}
+
+int32_t ByteArray::CopyTo(ByteArray* array, int32_t offset, int32_t length) {
+  return CopyTo(0, array, offset, length);
+}
+
+int32_t ByteArray::CopyTo(int32_t dst_offset, ByteArray* array,
+                          int32_t src_offset, int32_t length) {
+  assert(array);
+  if (array->Size() < dst_offset + length) {  // insufficient space
+    return -1;
+  }
+
+  ByteVector b(COPY_BUFFER_SIZE);
+  int32_t bytes_read = 0;
+  int32_t index = 0;
+  int32_t remaining_length = length;
+  int32_t buffer_length = std::min<int32_t>(COPY_BUFFER_SIZE, length);
+  while ((bytes_read =
+              Get(index + src_offset, &(b[0]), 0, buffer_length)) > 0) {
+    int bytes_written = array->Put(index + dst_offset, &(b[0]), 0, bytes_read);
+    UNREFERENCED_PARAMETER(bytes_written);
+    index += bytes_read;
+    remaining_length -= bytes_read;
+    buffer_length = std::min<int32_t>(b.size(), remaining_length);
+  }
+  return index;
+}
+
+int32_t ByteArray::CopyTo(OutputStream* os) {
+    return CopyTo(os, 0, Length());
+}
+
+int32_t ByteArray::CopyTo(OutputStream* os, int32_t offset, int32_t length) {
+  ByteVector b(COPY_BUFFER_SIZE);
+  int32_t bytes_read = 0;
+  int32_t index = 0;
+  int32_t buffer_length = std::min<int32_t>(COPY_BUFFER_SIZE, length);
+  while ((bytes_read = Get(index + offset, &(b[0]), 0, buffer_length)) > 0) {
+    os->Write(&b, 0, bytes_read);
+    index += bytes_read;
+    buffer_length = std::min<int32_t>(b.size(), length - index);
+  }
+  return index;
+}
+
+bool ByteArray::CopyFrom(InputStream* is, int32_t length) {
+  ByteVector b(COPY_BUFFER_SIZE);
+  int32_t bytes_read = 0;
+  int32_t index = 0;
+  int32_t buffer_length = std::min<int32_t>(COPY_BUFFER_SIZE, length);
+  while ((bytes_read = is->Read(&b, 0, buffer_length)) > 0) {
+    if (Put(index, &(b[0]), 0, bytes_read) != bytes_read) {
+#if defined (SFNTLY_NO_EXCEPTION)
+      return 0;
+#else
+      throw IOException("Error writing bytes.");
+#endif
+    }
+    index += bytes_read;
+    length -= bytes_read;
+    buffer_length = std::min<int32_t>(b.size(), length);
+  }
+  return true;
+}
+
+bool ByteArray::CopyFrom(InputStream* is) {
+  ByteVector b(COPY_BUFFER_SIZE);
+  int32_t bytes_read = 0;
+  int32_t index = 0;
+  int32_t buffer_length = COPY_BUFFER_SIZE;
+  while ((bytes_read = is->Read(&b, 0, buffer_length)) > 0) {
+    if (Put(index, &b[0], 0, bytes_read) != bytes_read) {
+#if defined (SFNTLY_NO_EXCEPTION)
+      return 0;
+#else
+      throw IOException("Error writing bytes.");
+#endif
+    }
+    index += bytes_read;
+  }
+  return true;
+}
+
+ByteArray::ByteArray(int32_t filled_length,
+                     int32_t storage_length,
+                     bool growable) {
+  Init(filled_length, storage_length, growable);
+}
+
+ByteArray::ByteArray(int32_t filled_length, int32_t storage_length) {
+  Init(filled_length, storage_length, false);
+}
+
+void ByteArray::Init(int32_t filled_length,
+                     int32_t storage_length,
+                     bool growable) {
+  storage_length_ = storage_length;
+  growable_ = growable;
+  SetFilledLength(filled_length);
+}
+
+}  // namespace sfntly
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/byte_array.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2011 The sfntly Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_BYTE_ARRAY_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_BYTE_ARRAY_H_
+
+#include "sfntly/port/refcount.h"
+#include "sfntly/port/type.h"
+#include "sfntly/port/input_stream.h"
+#include "sfntly/port/output_stream.h"
+
+namespace sfntly {
+
+// An abstraction to a contiguous array of bytes.
+// C++ port of this class assumes that the data are stored in a linear region
+// like std::vector.
+class ByteArray : virtual public RefCount {
+ public:
+  virtual ~ByteArray();
+
+  // Gets the current filled and readable length of the array.
+  int32_t Length();
+
+  // Gets the maximum size of the array. This is the maximum number of bytes that
+  // the array can hold and all of it may not be filled with data or even fully
+  // allocated yet.
+  int32_t Size();
+
+  // Determines whether or not this array is growable or of fixed size.
+  bool growable() { return growable_; }
+
+  int32_t SetFilledLength(int32_t filled_length);
+
+  // Gets the byte from the given index.
+  // @param index the index into the byte array
+  // @return the byte or -1 if reading beyond the bounds of the data
+  virtual int32_t Get(int32_t index);
+
+  // Gets the bytes from the given index and fill the buffer with them. As many
+  // bytes as will fit into the buffer are read unless that would go past the
+  // end of the array.
+  // @param index the index into the byte array
+  // @param b the buffer to put the bytes read into
+  // @return the number of bytes read from the buffer
+  virtual int32_t Get(int32_t index, ByteVector* b);
+
+  // Gets the bytes from the given index and fill the buffer with them starting
+  // at the offset given. As many bytes as the specified length are read unless
+  // that would go past the end of the array.
+  // @param index the index into the byte array
+  // @param b the buffer to put the bytes read into
+  // @param offset the location in the buffer to start putting the bytes
+  // @param length the number of bytes to put into the buffer
+  // @return the number of bytes read from the buffer
+  virtual int32_t Get(int32_t index,
+                      byte_t* b,
+                      int32_t offset,
+                      int32_t length);
+
+  // Puts the specified byte into the array at the given index unless that would
+  // be beyond the length of the array and it isn't growable.
+  virtual void Put(int32_t index, byte_t b);
+
+  // Puts the specified bytes into the array at the given index. The entire
+  // buffer is put into the array unless that would extend beyond the length and
+  // the array isn't growable.
+  virtual int32_t Put(int32_t index, ByteVector* b);
+
+  // Puts the specified bytes into the array at the given index. All of the bytes
+  // specified are put into the array unless that would extend beyond the length
+  // and the array isn't growable. The bytes to be put into the array are those
+  // in the buffer from the given offset and for the given length.
+  // @param index the index into the ByteArray
+  // @param b the bytes to put into the array
+  // @param offset the offset in the bytes to start copying from
+  // @param length the number of bytes to copy into the array
+  // @return the number of bytes actually written
+  virtual int32_t Put(int32_t index,
+                      byte_t* b,
+                      int32_t offset,
+                      int32_t length);
+
+  // Fully copies this ByteArray to another ByteArray to the extent that the
+  // destination array has storage for the data copied.
+  virtual int32_t CopyTo(ByteArray* array);
+
+  // Copies a segment of this ByteArray to another ByteArray.
+  // @param array the destination
+  // @param offset the offset in this ByteArray to start copying from
+  // @param length the maximum length in bytes to copy
+  // @return the number of bytes copied
+  virtual int32_t CopyTo(ByteArray* array, int32_t offset, int32_t length);
+
+  // Copies this ByteArray to another ByteArray.
+  // @param dstOffset the offset in the destination array to start copying to
+  // @param array the destination
+  // @param srcOffset the offset in this ByteArray to start copying from
+  // @param length the maximum length in bytes to copy
+  // @return the number of bytes copied
+  virtual int32_t CopyTo(int32_t dst_offset,
+                         ByteArray* array,
+                         int32_t src_offset,
+                         int32_t length);
+
+  // Copies this ByteArray to an OutputStream.
+  // @param os the destination
+  // @return the number of bytes copied
+  virtual int32_t CopyTo(OutputStream* os);
+
+  // Copies this ByteArray to an OutputStream.
+  // @param os the destination
+  // @param offset
+  // @param length
+  // @return the number of bytes copied
+  virtual int32_t CopyTo(OutputStream* os, int32_t offset, int32_t length);
+
+  // Copies from the InputStream into this ByteArray.
+  // @param is the source
+  // @param length the number of bytes to copy
+  virtual bool CopyFrom(InputStream* is, int32_t length);
+
+  // Copies everything from the InputStream into this ByteArray.
+  // @param is the source
+  virtual bool CopyFrom(InputStream* is);
+
+ protected:
+  // filledLength the length that is "filled" and readable counting from offset.
+  // storageLength the maximum storage size of the underlying data.
+  // growable is the storage growable - storageLength is the max growable size.
+  ByteArray(int32_t filled_length, int32_t storage_length, bool growable);
+  ByteArray(int32_t filled_length, int32_t storage_length);
+  void Init(int32_t filled_length, int32_t storage_length, bool growable);
+
+  // Internal subclass API
+
+  // Stores the byte at the index given.
+  // @param index the location to store at
+  // @param b the byte to store
+  virtual void InternalPut(int32_t index, byte_t b) = 0;
+
+  // Stores the array of bytes at the given index.
+  // @param index the location to store at
+  // @param b the bytes to store
+  // @param offset the offset to start from in the byte array
+  // @param length the length of the byte array to store from the offset
+  // @return the number of bytes actually stored
+  virtual int32_t InternalPut(int32_t index,
+                              byte_t* b,
+                              int32_t offset,
+                              int32_t length) = 0;
+
+  // Gets the byte at the index given.
+  // @param index the location to get from
+  // @return the byte stored at the index
+  virtual byte_t InternalGet(int32_t index) = 0;
+
+  // Gets the bytes at the index given of the given length.
+  // @param index the location to start getting from
+  // @param b the array to put the bytes into
+  // @param offset the offset in the array to put the bytes into
+  // @param length the length of bytes to read
+  // @return the number of bytes actually ready
+  virtual int32_t InternalGet(int32_t index,
+                              byte_t* b,
+                              int32_t offset,
+                              int32_t length) = 0;
+
+  // Close this instance of the ByteArray.
+  virtual void Close() = 0;
+
+  // C++ port only, raw pointer to the first element of storage.
+  virtual byte_t* Begin() = 0;
+
+  // Java toString() not ported.
+
+  static const int32_t COPY_BUFFER_SIZE;
+
+ private:
+  //bool bound_;  // unused, comment out
+  int32_t filled_length_;
+  int32_t storage_length_;
+  bool growable_;
+};
+typedef Ptr<ByteArray> ByteArrayPtr;
+
+}  // namespace sfntly
+
+#endif  // SFNTLY_CPP_SRC_SFNTLY_DATA_BYTE_ARRAY_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/font_data.cc
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sfntly/data/font_data.h"
+
+#include <algorithm>
+#include <functional>
+#include <limits>
+
+#include "sfntly/port/logging.h"
+
+namespace sfntly {
+
+int32_t FontData::Size() const {
+  return std::min<int32_t>(array_->Size() - bound_offset_, bound_length_);
+}
+
+void FontData::Bound(int32_t offset, int32_t length) {
+  // Inputs should not be negative.
+  CHECK(offset >= 0);
+  CHECK(length >= 0);
+
+  // Check to make sure |bound_offset_| will not overflow.
+  CHECK(bound_offset_ <= std::numeric_limits<int32_t>::max() - offset);
+  const int32_t new_offset = bound_offset_ + offset;
+
+  if (length == GROWABLE_SIZE) {
+    // When |length| has the special value of GROWABLE_SIZE, it means the size
+    // should not have any artificial limits, thus it is just the underlying
+    // |array_|'s size. Just make sure |new_offset| is still within bounds.
+    CHECK(new_offset <= array_->Size());
+  } else {
+    // When |length| has any other value, |new_offset| + |length| points to the
+    // end of the array. Make sure that is within bounds, but use subtraction to
+    // avoid an integer overflow.
+    CHECK(new_offset <= array_->Size() - length);
+  }
+
+  bound_offset_ = new_offset;
+  bound_length_ = length;
+}
+
+int32_t FontData::Length() const {
+  return std::min<int32_t>(array_->Length() - bound_offset_, bound_length_);
+}
+
+FontData::FontData(ByteArray* ba) {
+  Init(ba);
+}
+
+FontData::FontData(FontData* data, int32_t offset, int32_t length) {
+  Init(data->array_);
+  Bound(data->bound_offset_ + offset, length);
+}
+
+FontData::FontData(FontData* data, int32_t offset) {
+  Init(data->array_);
+  Bound(data->bound_offset_ + offset,
+        (data->bound_length_ == GROWABLE_SIZE)
+        ? GROWABLE_SIZE : data->bound_length_ - offset);
+}
+
+FontData::~FontData() {}
+
+void FontData::Init(ByteArray* ba) {
+  array_ = ba;
+  bound_offset_ = 0;
+  bound_length_ = GROWABLE_SIZE;
+}
+
+int32_t FontData::BoundOffset(int32_t offset) {
+  return offset + bound_offset_;
+}
+
+int32_t FontData::BoundLength(int32_t offset, int32_t length) {
+  return std::min<int32_t>(length, bound_length_ - offset);
+}
+
+}  // namespace sfntly
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/font_data.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_DATA_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_DATA_H_
+
+#include <limits.h>
+
+#include "sfntly/data/byte_array.h"
+#include "sfntly/port/refcount.h"
+#include "sfntly/port/type.h"
+
+namespace sfntly {
+
+struct DataSize {
+  enum {
+    kBYTE = 1,
+    kCHAR = 1,
+    kUSHORT = 2,
+    kSHORT = 2,
+    kUINT24 = 3,
+    kULONG = 4,
+    kLONG = 4,
+    kFixed = 4,
+    kFUNIT = 4,
+    kFWORD = 2,
+    kUFWORD = 2,
+    kF2DOT14 = 2,
+    kLONGDATETIME = 8,
+    kTag = 4,
+    kGlyphID = 2,
+    kOffset = 2
+  };
+};
+
+class FontData : virtual public RefCount {
+ public:
+  // Gets the maximum size of the FontData. This is the maximum number of bytes
+  // that the font data can hold and all of it may not be filled with data or
+  // even fully allocated yet.
+  // @return the maximum size of this font data
+  virtual int32_t Size() const;
+
+  // Sets limits on the size of the FontData. The FontData is then only
+  // visible within the bounds set.
+  // @param offset the start of the new bounds
+  // @param length the number of bytes in the bounded array
+  virtual void Bound(int32_t offset, int32_t length);
+
+  // Makes a slice of this FontData. The returned slice will share the data with
+  // the original <code>FontData</code>.
+  // @param offset the start of the slice
+  // @param length the number of bytes in the slice
+  // @return a slice of the original FontData
+  virtual CALLER_ATTACH FontData* Slice(int32_t offset, int32_t length) = 0;
+
+  // Makes a bottom bound only slice of this array. The returned slice will
+  // share the data with the original <code>FontData</code>.
+  // @param offset the start of the slice
+  // @return a slice of the original FontData
+  virtual CALLER_ATTACH FontData* Slice(int32_t offset) = 0;
+
+  // Gets the length of the data.
+  virtual int32_t Length() const;
+
+ protected:
+  // Constructor.
+  // @param ba the byte array to use for the backing data
+  explicit FontData(ByteArray* ba);
+
+  // Constructor.
+  // @param data the data to wrap
+  // @param offset the offset to start the wrap from
+  // @param length the length of the data wrapped
+  FontData(FontData* data, int32_t offset, int32_t length);
+
+  // Constructor.
+  // @param data the data to wrap
+  // @param offset the offset to start the wrap from
+  FontData(FontData* data, int32_t offset);
+  virtual ~FontData();
+
+  void Init(ByteArray* ba);
+
+  // Gets the offset in the underlying data taking into account any bounds on
+  // the data.
+  // @param offset the offset to get the bound compensated offset for
+  // @return the bound compensated offset
+  int32_t BoundOffset(int32_t offset);
+
+  // Gets the length in the underlying data taking into account any bounds on
+  // the data.
+  // @param offset the offset that the length is being used at
+  // @param length the length to get the bound compensated length for
+  // @return the bound compensated length
+  int32_t BoundLength(int32_t offset, int32_t length);
+
+  static const int32_t GROWABLE_SIZE = INT_MAX;
+
+  // TODO(arthurhsu): style guide violation: refactor this protected member
+  ByteArrayPtr array_;
+
+ private:
+  int32_t bound_offset_;
+  int32_t bound_length_;
+};
+typedef Ptr<FontData> FontDataPtr;
+
+}  // namespace sfntly
+
+#endif  // SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_DATA_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/font_input_stream.cc
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sfntly/data/font_input_stream.h"
+
+#include <algorithm>
+
+namespace sfntly {
+
+FontInputStream::FontInputStream(InputStream* is)
+    : stream_(is), position_(0), length_(0), bounded_(false) {
+}
+
+FontInputStream::FontInputStream(InputStream* is, size_t length)
+    : stream_(is), position_(0), length_(length), bounded_(true) {
+}
+
+FontInputStream::~FontInputStream() {
+  // Do not close here, underlying InputStream will close themselves.
+}
+
+int32_t FontInputStream::Available() {
+  if (stream_) {
+    return stream_->Available();
+  }
+  return 0;
+}
+
+void FontInputStream::Close() {
+  if (stream_) {
+    stream_->Close();
+  }
+}
+
+void FontInputStream::Mark(int32_t readlimit) {
+  if (stream_) {
+    stream_->Mark(readlimit);
+  }
+}
+
+bool FontInputStream::MarkSupported() {
+  if (stream_) {
+    return stream_->MarkSupported();
+  }
+  return false;
+}
+
+void FontInputStream::Reset() {
+  if (stream_) {
+    stream_->Reset();
+  }
+}
+
+int32_t FontInputStream::Read() {
+  if (!stream_ || (bounded_ && position_ >= length_)) {
+    return -1;
+  }
+  int32_t b = stream_->Read();
+  if (b >= 0) {
+    position_++;
+  }
+  return b;
+}
+
+int32_t FontInputStream::Read(ByteVector* b, int32_t offset, int32_t length) {
+  if (!stream_ || offset < 0 || length < 0 ||
+      (bounded_ && position_ >= length_)) {
+    return -1;
+  }
+  int32_t bytes_to_read =
+      bounded_ ? std::min<int32_t>(length, (int32_t)(length_ - position_)) :
+                 length;
+  int32_t bytes_read = stream_->Read(b, offset, bytes_to_read);
+  position_ += bytes_read;
+  return bytes_read;
+}
+
+int32_t FontInputStream::Read(ByteVector* b) {
+  return Read(b, 0, b->size());
+}
+
+int32_t FontInputStream::ReadChar() {
+  return Read();
+}
+
+int32_t FontInputStream::ReadUShort() {
+  return 0xffff & (Read() << 8 | Read());
+}
+
+int32_t FontInputStream::ReadShort() {
+  return ((Read() << 8 | Read()) << 16) >> 16;
+}
+
+int32_t FontInputStream::ReadUInt24() {
+  return 0xffffff & (Read() << 16 | Read() << 8 | Read());
+}
+
+int64_t FontInputStream::ReadULong() {
+  return 0xffffffffL & ReadLong();
+}
+
+int32_t FontInputStream::ReadULongAsInt() {
+  int64_t ulong = ReadULong();
+  return ((int32_t)ulong) & ~0x80000000;
+}
+
+int32_t FontInputStream::ReadLong() {
+  return Read() << 24 | Read() << 16 | Read() << 8 | Read();
+}
+
+int32_t FontInputStream::ReadFixed() {
+  return ReadLong();
+}
+
+int64_t FontInputStream::ReadDateTimeAsLong() {
+  return (int64_t)ReadULong() << 32 | ReadULong();
+}
+
+int64_t FontInputStream::Skip(int64_t n) {
+  if (stream_) {
+    int64_t skipped = stream_->Skip(n);
+    position_ += skipped;
+    return skipped;
+  }
+  return 0;
+}
+
+}  // namespace sfntly
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/font_input_stream.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_INPUT_STREAM_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_INPUT_STREAM_H_
+
+#include "sfntly/port/type.h"
+#include "sfntly/port/input_stream.h"
+
+namespace sfntly {
+
+// An input stream for reading font data.
+// The data types used are as listed:
+// BYTE       8-bit unsigned integer.
+// CHAR       8-bit signed integer.
+// USHORT     16-bit unsigned integer.
+// SHORT      16-bit signed integer.
+// UINT24     24-bit unsigned integer.
+// ULONG      32-bit unsigned integer.
+// LONG       32-bit signed integer.
+// Fixed      32-bit signed fixed-point number (16.16)
+// FUNIT      Smallest measurable distance in the em space.
+// FWORD      16-bit signed integer (SHORT) that describes a quantity in FUnits.
+// UFWORD     16-bit unsigned integer (USHORT) that describes a quantity in
+//            FUnits.
+// F2DOT14    16-bit signed fixed number with the low 14 bits of fraction (2.14)
+// LONGDATETIME  Date represented in number of seconds since 12:00 midnight,
+//               January 1, 1904. The value is represented as a signed 64-bit
+//               integer.
+
+// Note: Original class inherits from Java's FilterOutputStream, which wraps
+//       an InputStream within.  In C++, we directly do the wrapping without
+//       defining another layer of abstraction.  The wrapped output stream is
+//       *NOT* reference counted (because it's meaningless to ref-count an I/O
+//       stream).
+class FontInputStream : public InputStream {
+ public:
+  // Constructor.
+  // @param is input stream to wrap
+  explicit FontInputStream(InputStream* is);
+
+  // Constructor for a bounded font input stream.
+  // @param is input stream to wrap
+  // @param length the maximum length of bytes to read
+  FontInputStream(InputStream* is, size_t length);
+
+  virtual ~FontInputStream();
+
+
+  virtual int32_t Available();
+  virtual void Close();
+  virtual void Mark(int32_t readlimit);
+  virtual bool MarkSupported();
+  virtual void Reset();
+
+  virtual int32_t Read();
+  virtual int32_t Read(ByteVector* buffer);
+  virtual int32_t Read(ByteVector* buffer, int32_t offset, int32_t length);
+
+  // Get the current position in the stream in bytes.
+  // @return the current position in bytes
+  virtual int64_t position() { return position_; }
+
+  virtual int32_t ReadChar();
+  virtual int32_t ReadUShort();
+  virtual int32_t ReadShort();
+  virtual int32_t ReadUInt24();
+  virtual int64_t ReadULong();
+  virtual int32_t ReadULongAsInt();
+  virtual int32_t ReadLong();
+  virtual int32_t ReadFixed();
+  virtual int64_t ReadDateTimeAsLong();
+  virtual int64_t Skip(int64_t n);  // n can be negative.
+
+ private:
+  InputStream* stream_;
+  int64_t position_;
+  int64_t length_;  // Bound on length of data to read.
+  bool bounded_;
+};
+
+}  // namespace sfntly
+
+#endif  // SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_INPUT_STREAM_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/font_output_stream.cc
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sfntly/data/font_output_stream.h"
+
+#include <algorithm>
+
+namespace sfntly {
+
+FontOutputStream::FontOutputStream(OutputStream* os)
+    : stream_(os),
+      position_(0) {
+}
+
+FontOutputStream::~FontOutputStream() {
+  // Do not close, underlying stream shall clean up themselves.
+}
+
+void FontOutputStream::Write(byte_t b) {
+  if (stream_) {
+    stream_->Write(b);
+    position_++;
+  }
+}
+
+void FontOutputStream::Write(ByteVector* b) {
+  if (b) {
+    Write(b, 0, b->size());
+    position_ += b->size();
+  }
+}
+
+void FontOutputStream::Write(ByteVector* b, int32_t off, int32_t len) {
+  assert(b);
+  assert(stream_);
+  if (off < 0 || len < 0 || off + len < 0 ||
+      static_cast<size_t>(off + len) > b->size()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+    throw IndexOutOfBoundException();
+#else
+    return;
+#endif
+  }
+
+  stream_->Write(b, off, len);
+  position_ += len;
+}
+
+void FontOutputStream::Write(byte_t* b, int32_t off, int32_t len) {
+  assert(b);
+  assert(stream_);
+  if (off < 0 || len < 0 || off + len < 0) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+    throw IndexOutOfBoundException();
+#else
+    return;
+#endif
+  }
+
+  stream_->Write(b, off, len);
+  position_ += len;
+}
+
+void FontOutputStream::WriteChar(byte_t c) {
+  Write(c);
+}
+
+void FontOutputStream::WriteUShort(int32_t us) {
+  Write((byte_t)((us >> 8) & 0xff));
+  Write((byte_t)(us & 0xff));
+}
+
+void FontOutputStream::WriteShort(int32_t s) {
+  WriteUShort(s);
+}
+
+void FontOutputStream::WriteUInt24(int32_t ui) {
+  Write((byte_t)(ui >> 16) & 0xff);
+  Write((byte_t)(ui >> 8) & 0xff);
+  Write((byte_t)ui & 0xff);
+}
+
+void FontOutputStream::WriteULong(int64_t ul) {
+  Write((byte_t)((ul >> 24) & 0xff));
+  Write((byte_t)((ul >> 16) & 0xff));
+  Write((byte_t)((ul >> 8) & 0xff));
+  Write((byte_t)(ul & 0xff));
+}
+
+void FontOutputStream::WriteLong(int64_t l) {
+  WriteULong(l);
+}
+
+void FontOutputStream::WriteFixed(int32_t f) {
+  WriteULong(f);
+}
+
+void FontOutputStream::WriteDateTime(int64_t date) {
+  WriteULong((date >> 32) & 0xffffffff);
+  WriteULong(date & 0xffffffff);
+}
+
+void FontOutputStream::Flush() {
+  if (stream_) {
+    stream_->Flush();
+  }
+}
+
+void FontOutputStream::Close() {
+  if (stream_) {
+    stream_->Flush();
+    stream_->Close();
+    position_ = 0;
+  }
+}
+
+}  // namespace sfntly
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/font_output_stream.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_OUTPUT_STREAM_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_OUTPUT_STREAM_H_
+
+#include "sfntly/port/type.h"
+#include "sfntly/port/output_stream.h"
+
+namespace sfntly {
+
+// An output stream for writing font data.
+// The data types used are as listed:
+// BYTE       8-bit unsigned integer.
+// CHAR       8-bit signed integer.
+// USHORT     16-bit unsigned integer.
+// SHORT      16-bit signed integer.
+// UINT24     24-bit unsigned integer.
+// ULONG      32-bit unsigned integer.
+// LONG       32-bit signed integer.
+// Fixed      32-bit signed fixed-point number (16.16)
+// FUNIT      Smallest measurable distance in the em space.
+// FWORD      16-bit signed integer (SHORT) that describes a quantity in FUnits.
+// UFWORD     16-bit unsigned integer (USHORT) that describes a quantity in
+//            FUnits.
+// F2DOT14    16-bit signed fixed number with the low 14 bits of fraction (2.14)
+// LONGDATETIME  Date represented in number of seconds since 12:00 midnight,
+//               January 1, 1904. The value is represented as a signed 64-bit
+//               integer.
+
+// Note: The wrapped output stream is *NOT* reference counted (because it's
+//       meaningless to ref-count an I/O stream).
+class FontOutputStream : public OutputStream {
+ public:
+  explicit FontOutputStream(OutputStream* os);
+  virtual ~FontOutputStream();
+
+  virtual size_t position() { return position_; }
+
+  virtual void Write(byte_t b);
+  virtual void Write(ByteVector* b);
+  virtual void Write(ByteVector* b, int32_t off, int32_t len);
+  virtual void Write(byte_t* b, int32_t off, int32_t len);
+  virtual void WriteChar(byte_t c);
+  virtual void WriteUShort(int32_t us);
+  virtual void WriteShort(int32_t s);
+  virtual void WriteUInt24(int32_t ui);
+  virtual void WriteULong(int64_t ul);
+  virtual void WriteLong(int64_t l);
+  virtual void WriteFixed(int32_t l);
+  virtual void WriteDateTime(int64_t date);
+
+  // Note: C++ port only.
+  virtual void Flush();
+  virtual void Close();
+
+ private:
+  // Note: we do not use the variable name out as in Java because it has
+  //       special meaning in VC++ and will be very confusing.
+  OutputStream* stream_;
+  size_t position_;
+};
+
+}  // namespace sfntly
+
+#endif  // SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_OUTPUT_STREAM_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/growable_memory_byte_array.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sfntly/data/growable_memory_byte_array.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include <algorithm>
+
+namespace sfntly {
+
+GrowableMemoryByteArray::GrowableMemoryByteArray()
+    : ByteArray(0, INT_MAX, true) {
+  // Note: We did not set an initial size of array like Java because STL
+  //       implementation will determine the best strategy.
+}
+
+GrowableMemoryByteArray::~GrowableMemoryByteArray() {}
+
+int32_t GrowableMemoryByteArray::CopyTo(OutputStream* os,
+                                        int32_t offset,
+                                        int32_t length) {
+  assert(os);
+  os->Write(&b_, offset, length);
+  return length;
+}
+
+void GrowableMemoryByteArray::InternalPut(int32_t index, byte_t b) {
+  if ((size_t)index >= b_.size()) {
+    b_.resize((size_t)(index + 1));
+  }
+  b_[index] = b;
+}
+
+int32_t GrowableMemoryByteArray::InternalPut(int32_t index,
+                                             byte_t* b,
+                                             int32_t offset,
+                                             int32_t length) {
+  if ((size_t)index + length >= b_.size()) {
+    // Note: We grow one byte more than Java version. VC debuggers shows
+    //       data better this way.
+    b_.resize((size_t)(index + length + 1));
+  }
+  std::copy(b + offset, b + offset + length, b_.begin() + index);
+  return length;
+}
+
+byte_t GrowableMemoryByteArray::InternalGet(int32_t index) {
+  return b_[index];
+}
+
+int32_t GrowableMemoryByteArray::InternalGet(int32_t index,
+                                             byte_t* b,
+                                             int32_t offset,
+                                             int32_t length) {
+  memcpy(b + offset, &(b_[0]) + index, length);
+  return length;
+}
+
+void GrowableMemoryByteArray::Close() {
+  b_.clear();
+}
+
+byte_t* GrowableMemoryByteArray::Begin() {
+  return &(b_[0]);
+}
+
+}  // namespace sfntly
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/growable_memory_byte_array.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_GROWABLE_MEMORY_BYTE_ARRAY_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_GROWABLE_MEMORY_BYTE_ARRAY_H_
+
+#include "sfntly/data/byte_array.h"
+
+namespace sfntly {
+
+// Note: This is not really a port of Java version. Instead, this wraps a
+//       std::vector inside and let it grow by calling resize().
+class GrowableMemoryByteArray : public ByteArray,
+                                public RefCounted<GrowableMemoryByteArray> {
+ public:
+  GrowableMemoryByteArray();
+  virtual ~GrowableMemoryByteArray();
+  virtual int32_t CopyTo(OutputStream* os, int32_t offset, int32_t length);
+
+  // Make gcc -Woverloaded-virtual happy.
+  virtual int32_t CopyTo(ByteArray* array) { return ByteArray::CopyTo(array); }
+  virtual int32_t CopyTo(ByteArray* array, int32_t offset, int32_t length) {
+    return ByteArray::CopyTo(array, offset, length);
+  }
+  virtual int32_t CopyTo(int32_t dst_offset,
+                         ByteArray* array,
+                         int32_t src_offset,
+                         int32_t length) {
+    return ByteArray::CopyTo(dst_offset, array, src_offset, length);
+  }
+  virtual int32_t CopyTo(OutputStream* os) { return ByteArray::CopyTo(os); }
+
+ protected:
+  virtual void InternalPut(int32_t index, byte_t b);
+  virtual int32_t InternalPut(int32_t index,
+                              byte_t* b,
+                              int32_t offset,
+                              int32_t length);
+  virtual byte_t InternalGet(int32_t index);
+  virtual int32_t InternalGet(int32_t index,
+                              byte_t* b,
+                              int32_t offset,
+                              int32_t length);
+  virtual void Close();
+  virtual byte_t* Begin();
+
+ private:
+  ByteVector b_;
+};
+
+}  // namespace sfntly
+
+#endif  // SFNTLY_CPP_SRC_SFNTLY_DATA_GROWABLE_MEMORY_BYTE_ARRAY_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/memory_byte_array.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sfntly/data/memory_byte_array.h"
+
+#include <string.h>
+
+namespace sfntly {
+
+MemoryByteArray::MemoryByteArray(int32_t length)
+    : ByteArray(0, length), b_(NULL), allocated_(true) {
+}
+
+MemoryByteArray::MemoryByteArray(byte_t* b, int32_t filled_length)
+    : ByteArray(filled_length, filled_length), b_(b), allocated_(false) {
+  assert(b);
+}
+
+MemoryByteArray::~MemoryByteArray() {
+  Close();
+}
+
+int32_t MemoryByteArray::CopyTo(OutputStream* os,
+                                int32_t offset,
+                                int32_t length) {
+  assert(os);
+  os->Write(b_, offset, length);
+  return length;
+}
+
+void MemoryByteArray::Init() {
+  if (allocated_ && b_ == NULL) {
+    b_ = new byte_t[Size()];
+    memset(b_, 0, Size());
+  }
+}
+
+void MemoryByteArray::InternalPut(int32_t index, byte_t b) {
+  Init();
+  b_[index] = b;
+}
+
+int32_t MemoryByteArray::InternalPut(int32_t index,
+                                     byte_t* b,
+                                     int32_t offset,
+                                     int32_t length) {
+  assert(b);
+  Init();
+  memcpy(b_ + index, b + offset, length);
+  return length;
+}
+
+byte_t MemoryByteArray::InternalGet(int32_t index) {
+  Init();
+  return b_[index];
+}
+
+int32_t MemoryByteArray::InternalGet(int32_t index,
+                                     byte_t* b,
+                                     int32_t offset,
+                                     int32_t length) {
+  assert(b);
+  Init();
+  memcpy(b + offset, b_ + index, length);
+  return length;
+}
+
+void MemoryByteArray::Close() {
+  if (allocated_ && b_) {
+    delete[] b_;
+  }
+  b_ = NULL;
+}
+
+byte_t* MemoryByteArray::Begin() {
+  Init();
+  return b_;
+}
+
+}  // namespace sfntly
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/memory_byte_array.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_MEMORY_BYTE_ARRAY_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_MEMORY_BYTE_ARRAY_H_
+
+#include "sfntly/data/byte_array.h"
+
+namespace sfntly {
+
+class MemoryByteArray : public ByteArray, public RefCounted<MemoryByteArray> {
+ public:
+  // Construct a new MemoryByteArray with a new array of the size given. It is
+  // assumed that none of the array is filled and readable.
+  explicit MemoryByteArray(int32_t length);
+
+  // Note: not implemented due to dangerous operations in constructor.
+  //explicit MemoryByteArray(ByteVector* b);
+
+  // Construct a new MemoryByteArray using byte array.
+  // @param b the byte array that provides the actual storage
+  // @param filled_length the index of the last byte in the array has data
+  // Note: This is different from Java version, it does not take over the
+  //       ownership of b.  Caller is responsible for handling the lifetime
+  //       of b.  C++ port also assumes filled_length is buffer_length since
+  //       there is not a reliable way to identify the actual size of buffer.
+  MemoryByteArray(byte_t* b, int32_t filled_length);
+
+  virtual ~MemoryByteArray();
+  virtual int32_t CopyTo(OutputStream* os, int32_t offset, int32_t length);
+
+  // Make gcc -Woverloaded-virtual happy.
+  virtual int32_t CopyTo(ByteArray* array) { return ByteArray::CopyTo(array); }
+  virtual int32_t CopyTo(ByteArray* array, int32_t offset, int32_t length) {
+    return ByteArray::CopyTo(array, offset, length);
+  }
+  virtual int32_t CopyTo(int32_t dst_offset,
+                         ByteArray* array,
+                         int32_t src_offset,
+                         int32_t length) {
+    return ByteArray::CopyTo(dst_offset, array, src_offset, length);
+  }
+  virtual int32_t CopyTo(OutputStream* os) { return ByteArray::CopyTo(os); }
+
+ protected:
+  virtual void InternalPut(int32_t index, byte_t b);
+  virtual int32_t InternalPut(int32_t index,
+                              byte_t* b,
+                              int32_t offset,
+                              int32_t length);
+  virtual byte_t InternalGet(int32_t index);
+  virtual int32_t InternalGet(int32_t index,
+                              byte_t* b,
+                              int32_t offset,
+                              int32_t length);
+  virtual void Close();
+  virtual byte_t* Begin();
+
+ private:
+  void Init();  // C++ port only, used to allocate memory outside constructor.
+
+  byte_t* b_;
+  bool allocated_;
+};
+
+}  // namespace sfntly
+
+#endif  // SFNTLY_CPP_SRC_SFNTLY_DATA_MEMORY_BYTE_ARRAY_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/readable_font_data.cc
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sfntly/data/readable_font_data.h"
+
+#include <stdio.h>
+
+#include <limits>
+
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/data/writable_font_data.h"
+#include "sfntly/port/exception_type.h"
+
+namespace sfntly {
+
+ReadableFontData::ReadableFontData(ByteArray* array)
+    : FontData(array),
+      checksum_set_(false),
+      checksum_(0) {
+}
+
+ReadableFontData::~ReadableFontData() {}
+
+// TODO(arthurhsu): re-investigate the memory model of this function.  It's
+//                  not too useful without copying, but it's not performance
+//                  savvy to do copying.
+CALLER_ATTACH
+ReadableFontData* ReadableFontData::CreateReadableFontData(ByteVector* b) {
+  assert(b);
+  ByteArrayPtr ba = new MemoryByteArray(b->size());
+  ba->Put(0, b);
+  ReadableFontDataPtr wfd = new ReadableFontData(ba);
+  return wfd.Detach();
+}
+
+int64_t ReadableFontData::Checksum() {
+  AutoLock lock(checksum_lock_);
+  if (!checksum_set_) {
+    ComputeChecksum();
+  }
+  return checksum_;
+}
+
+void ReadableFontData::SetCheckSumRanges(const IntegerList& ranges) {
+  checksum_range_ = ranges;
+  checksum_set_ = false;  // UNIMPLEMENTED: atomicity
+}
+
+int32_t ReadableFontData::ReadUByte(int32_t index) {
+  int32_t b = array_->Get(BoundOffset(index));
+  if (b < 0) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+    throw IndexOutOfBoundException(
+        "Index attempted to be read from is out of bounds", index);
+#endif
+    return kInvalidUnsigned;
+  }
+  return b;
+}
+
+int32_t ReadableFontData::ReadByte(int32_t index) {
+  int32_t b = array_->Get(BoundOffset(index));
+  if (b < 0) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+    throw IndexOutOfBoundException(
+        "Index attempted to be read from is out of bounds", index);
+#endif
+    return kInvalidByte;
+  }
+  return (b << 24) >> 24;
+}
+
+int32_t ReadableFontData::ReadBytes(int32_t index,
+                                    byte_t* b,
+                                    int32_t offset,
+                                    int32_t length) {
+  return array_->Get(BoundOffset(index), b, offset, BoundLength(index, length));
+}
+
+int32_t ReadableFontData::ReadChar(int32_t index) {
+  return ReadUByte(index);
+}
+
+int32_t ReadableFontData::ReadUShort(int32_t index) {
+  int32_t b1 = ReadUByte(index);
+  if (b1 < 0)
+    return kInvalidUnsigned;
+  int32_t b2 = ReadUByte(index + 1);
+  if (b2 < 0)
+    return kInvalidUnsigned;
+  return 0xffff & (b1 << 8 | b2);
+}
+
+int32_t ReadableFontData::ReadShort(int32_t index) {
+  int32_t b1 = ReadByte(index);
+  if (b1 == kInvalidByte)
+    return kInvalidShort;
+  int32_t b2 = ReadUByte(index + 1);
+  if (b2 < 0)
+    return kInvalidShort;
+
+  uint32_t result = static_cast<uint32_t>(b1) << 8 | b2;
+  return static_cast<int32_t>(result << 16) >> 16;
+}
+
+int32_t ReadableFontData::ReadUInt24(int32_t index) {
+  int32_t b1 = ReadUByte(index);
+  if (b1 < 0)
+    return kInvalidUnsigned;
+  int32_t b2 = ReadUByte(index + 1);
+  if (b2 < 0)
+    return kInvalidUnsigned;
+  int32_t b3 = ReadUByte(index + 2);
+  if (b3 < 0)
+    return kInvalidUnsigned;
+  return 0xffffff & (b1 << 16 | b2 << 8 | b3);
+}
+
+int64_t ReadableFontData::ReadULong(int32_t index) {
+  int32_t b1 = ReadUByte(index);
+  if (b1 < 0)
+    return kInvalidUnsigned;
+  int32_t b2 = ReadUByte(index + 1);
+  if (b2 < 0)
+    return kInvalidUnsigned;
+  int32_t b3 = ReadUByte(index + 2);
+  if (b3 < 0)
+    return kInvalidUnsigned;
+  int32_t b4 = ReadUByte(index + 3);
+  if (b4 < 0)
+    return kInvalidUnsigned;
+  return 0xffffffffL & (b1 << 24 | b2 << 16 | b3 << 8 | b4);
+}
+
+int32_t ReadableFontData::ReadULongAsInt(int32_t index) {
+  int64_t ulong = ReadULong(index);
+#if !defined (SFNTLY_NO_EXCEPTION)
+  if ((ulong & 0x80000000) == 0x80000000) {
+    throw ArithmeticException("Long value too large to fit into an integer.");
+  }
+#endif
+  return static_cast<int32_t>(ulong);
+}
+
+int64_t ReadableFontData::ReadULongLE(int32_t index) {
+  int32_t b1 = ReadUByte(index);
+  if (b1 < 0)
+    return kInvalidUnsigned;
+  int32_t b2 = ReadUByte(index + 1);
+  if (b2 < 0)
+    return kInvalidUnsigned;
+  int32_t b3 = ReadUByte(index + 2);
+  if (b3 < 0)
+    return kInvalidUnsigned;
+  int32_t b4 = ReadUByte(index + 3);
+  if (b4 < 0)
+    return kInvalidUnsigned;
+  return 0xffffffffL & (b1 | b2 << 8 | b3 << 16 | b4 << 24);
+}
+
+int32_t ReadableFontData::ReadLong(int32_t index) {
+  int32_t b1 = ReadByte(index);
+  if (b1 == kInvalidByte)
+    return kInvalidLong;
+  int32_t b2 = ReadUByte(index + 1);
+  if (b2 < 0)
+    return kInvalidLong;
+  int32_t b3 = ReadUByte(index + 2);
+  if (b3 < 0)
+    return kInvalidLong;
+  int32_t b4 = ReadUByte(index + 3);
+  if (b4 < 0)
+    return kInvalidLong;
+  return static_cast<uint32_t>(b1) << 24 | b2 << 16 | b3 << 8 | b4;
+}
+
+int32_t ReadableFontData::ReadFixed(int32_t index) {
+  return ReadLong(index);
+}
+
+int64_t ReadableFontData::ReadDateTimeAsLong(int32_t index) {
+  int32_t high = ReadULong(index);
+  if (high == kInvalidUnsigned)
+    return kInvalidLongDateTime;
+  int32_t low = ReadULong(index + 4);
+  if (low == kInvalidUnsigned)
+    return kInvalidLongDateTime;
+  return (int64_t)high << 32 | low;
+}
+
+int32_t ReadableFontData::ReadFWord(int32_t index) {
+  return ReadShort(index);
+}
+
+int32_t ReadableFontData::ReadFUFWord(int32_t index) {
+  return ReadUShort(index);
+}
+
+int32_t ReadableFontData::CopyTo(OutputStream* os) {
+  return array_->CopyTo(os, BoundOffset(0), Length());
+}
+
+int32_t ReadableFontData::CopyTo(WritableFontData* wfd) {
+  return array_->CopyTo(wfd->BoundOffset(0),
+                        wfd->array_,
+                        BoundOffset(0),
+                        Length());
+}
+
+int32_t ReadableFontData::CopyTo(ByteArray* ba) {
+  return array_->CopyTo(ba, BoundOffset(0), Length());
+}
+
+int32_t ReadableFontData::SearchUShort(int32_t start_index,
+                                       int32_t start_offset,
+                                       int32_t end_index,
+                                       int32_t end_offset,
+                                       int32_t length,
+                                       int32_t key) {
+  int32_t location = 0;
+  int32_t bottom = 0;
+  int32_t top = length;
+  while (top != bottom) {
+    location = (top + bottom) / 2;
+    int32_t location_start = ReadUShort(start_index + location * start_offset);
+    if (key < location_start) {
+      // location is below current location
+      top = location;
+    } else {
+      // is key below the upper bound?
+      int32_t location_end = ReadUShort(end_index + location * end_offset);
+#if defined (SFNTLY_DEBUG_FONTDATA)
+      fprintf(stderr, "**start: %d; end: %d\n", location_start, location_end);
+#endif
+      if (key <= location_end)
+        return location;
+
+      // location is above the current location
+      bottom = location + 1;
+    }
+  }
+  return -1;
+}
+
+int32_t ReadableFontData::SearchUShort(int32_t start_index,
+                                       int32_t start_offset,
+                                       int32_t length,
+                                       int32_t key) {
+  int32_t location = 0;
+  int32_t bottom = 0;
+  int32_t top = length;
+  while (top != bottom) {
+    location = (top + bottom) / 2;
+    int32_t location_start = ReadUShort(start_index + location * start_offset);
+    if (key == location_start)
+      return location;
+
+    if (key < location_start) {
+      // location is below current location
+      top = location;
+    } else {
+      // location is above current location
+      bottom = location + 1;
+    }
+  }
+  return -1;
+}
+
+int32_t ReadableFontData::SearchULong(int32_t start_index,
+                                      int32_t start_offset,
+                                      int32_t end_index,
+                                      int32_t end_offset,
+                                      int32_t length,
+                                      int32_t key) {
+  int32_t location = 0;
+  int32_t bottom = 0;
+  int32_t top = length;
+  while (top != bottom) {
+    location = (top + bottom) / 2;
+    int32_t location_start = ReadULongAsInt(start_index
+                                            + location * start_offset);
+    if (key < location_start) {
+      // location is below current location
+      top = location;
+    } else {
+      // is key below the upper bound?
+      int32_t location_end = ReadULongAsInt(end_index + location * end_offset);
+#if defined (SFNTLY_DEBUG_FONTDATA)
+      fprintf(stderr, "**start: %d; end: %d\n", location_start, location_end);
+#endif
+      if (key <= location_end)
+        return location;
+
+      // location is above the current location
+      bottom = location + 1;
+    }
+  }
+  return -1;
+}
+
+CALLER_ATTACH FontData* ReadableFontData::Slice(int32_t offset,
+                                                int32_t length) {
+  if (offset < 0 || length < 0 ||
+      offset > std::numeric_limits<int32_t>::max() - length ||
+      offset + length > Size()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+    throw IndexOutOfBoundsException(
+        "Attempt to bind data outside of its limits");
+#endif
+    return NULL;
+  }
+  FontDataPtr slice = new ReadableFontData(this, offset, length);
+  return slice.Detach();
+}
+
+CALLER_ATTACH FontData* ReadableFontData::Slice(int32_t offset) {
+  if (offset < 0 || offset > Size()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+    throw IndexOutOfBoundsException(
+        "Attempt to bind data outside of its limits");
+#endif
+    return NULL;
+  }
+  FontDataPtr slice = new ReadableFontData(this, offset);
+  return slice.Detach();
+}
+
+ReadableFontData::ReadableFontData(ReadableFontData* data, int32_t offset)
+    : FontData(data, offset),
+      checksum_set_(false),
+      checksum_(0) {
+}
+
+ReadableFontData::ReadableFontData(ReadableFontData* data,
+                                   int32_t offset,
+                                   int32_t length)
+    : FontData(data, offset, length),
+      checksum_set_(false),
+      checksum_(0) {
+}
+
+void ReadableFontData::ComputeChecksum() {
+  // TODO(arthurhsu): IMPLEMENT: synchronization/atomicity
+  int64_t sum = 0;
+  if (checksum_range_.empty()) {
+    sum = ComputeCheckSum(0, Length());
+  } else {
+    for (uint32_t low_bound_index = 0; low_bound_index < checksum_range_.size();
+         low_bound_index += 2) {
+      int32_t low_bound = checksum_range_[low_bound_index];
+      int32_t high_bound = (low_bound_index == checksum_range_.size() - 1) ?
+                                Length() :
+                                checksum_range_[low_bound_index + 1];
+      sum += ComputeCheckSum(low_bound, high_bound);
+    }
+  }
+
+  checksum_ = sum & 0xffffffffL;
+  checksum_set_ = true;
+}
+
+int64_t ReadableFontData::ComputeCheckSum(int32_t low_bound,
+                                          int32_t high_bound) {
+  int64_t sum = 0;
+  // Checksum all whole 4-byte chunks.
+  for (int32_t i = low_bound; i <= high_bound - 4; i += 4) {
+    sum += ReadULong(i);
+  }
+
+  // Add last fragment if not 4-byte multiple
+  int32_t off = high_bound & -4;
+  if (off < high_bound) {
+    int32_t b3 = ReadUByte(off);
+    int32_t b2 = (off + 1 < high_bound) ? ReadUByte(off + 1) : 0;
+    int32_t b1 = (off + 2 < high_bound) ? ReadUByte(off + 2) : 0;
+    int32_t b0 = 0;
+    sum += (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
+  }
+  return sum;
+}
+
+}  // namespace sfntly
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/readable_font_data.h
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_READABLE_FONT_DATA_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_READABLE_FONT_DATA_H_
+
+#include "sfntly/data/font_data.h"
+#include "sfntly/port/lock.h"
+
+namespace sfntly {
+
+class OutputStream;
+class WritableFontData;
+
+// Writable font data wrapper. Supports reading of data primitives in the
+// TrueType / OpenType spec.
+// The data types used are as listed:
+// BYTE       8-bit unsigned integer.
+// CHAR       8-bit signed integer.
+// USHORT     16-bit unsigned integer.
+// SHORT      16-bit signed integer.
+// UINT24     24-bit unsigned integer.
+// ULONG      32-bit unsigned integer.
+// LONG       32-bit signed integer.
+// Fixed      32-bit signed fixed-point number (16.16)
+// FUNIT      Smallest measurable distance in the em space.
+// FWORD      16-bit signed integer (SHORT) that describes a quantity in FUnits.
+// UFWORD     16-bit unsigned integer (USHORT) that describes a quantity in
+//            FUnits.
+// F2DOT14    16-bit signed fixed number with the low 14 bits of fraction (2.14)
+// LONGDATETIME  Date represented in number of seconds since 12:00 midnight,
+//               January 1, 1904. The value is represented as a signed 64-bit
+//               integer.
+
+class ReadableFontData : public FontData,
+                         public RefCounted<ReadableFontData> {
+ public:
+  explicit ReadableFontData(ByteArray* array);
+  virtual ~ReadableFontData();
+
+  static const int32_t kInvalidByte = 128;
+  static const int32_t kInvalidShort = 32768;
+  static const int32_t kInvalidLong = 0xffffffff;
+  static const int32_t kInvalidUnsigned = -1;
+  static const int64_t kInvalidLongDateTime = -1;
+
+  static CALLER_ATTACH ReadableFontData* CreateReadableFontData(ByteVector* b);
+
+  // Gets a computed checksum for the data. This checksum uses the OpenType spec
+  // calculation. Every ULong value (32 bit unsigned) in the data is summed and
+  // the resulting value is truncated to 32 bits. If the data length in bytes is
+  // not an integral multiple of 4 then any remaining bytes are treated as the
+  // start of a 4 byte sequence whose remaining bytes are zero.
+  // @return the checksum
+  int64_t Checksum();
+
+  // Sets the bounds to use for computing the checksum. These bounds are in
+  // begin and end pairs. If an odd number is given then the final range is
+  // assumed to extend to the end of the data. The lengths of each range must be
+  // a multiple of 4.
+  // @param ranges the range bounds to use for the checksum
+  void SetCheckSumRanges(const IntegerList& ranges);
+
+  // Read the UBYTE at the given index.
+  // @param index index into the font data
+  // @return the UBYTE; -1 if outside the bounds of the font data
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t ReadUByte(int32_t index);
+
+  // Read the BYTE at the given index.
+  // @param index index into the font data
+  // @return the BYTE; |kInvalidByte| if outside the bounds of the font data
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t ReadByte(int32_t index);
+
+  // Read the bytes at the given index into the array.
+  // @param index index into the font data
+  // @param b the destination for the bytes read
+  // @param offset offset in the byte array to place the bytes
+  // @param length the length of bytes to read
+  // @return the number of bytes actually read; -1 if the index is outside the
+  //         bounds of the font data
+  virtual int32_t ReadBytes(int32_t index,
+                            byte_t* b,
+                            int32_t offset,
+                            int32_t length);
+
+  // Read the CHAR at the given index.
+  // @param index index into the font data
+  // @return the CHAR; -1 if outside the bounds of the font data
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t ReadChar(int32_t index);
+
+  // Read the USHORT at the given index.
+  // @param index index into the font data
+  // @return the USHORT; -1 if outside the bounds of the font data
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t ReadUShort(int32_t index);
+
+  // Read the SHORT at the given index.
+  // @param index index into the font data
+  // @return the SHORT; |kInvalidShort| if outside the bounds of the font data
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t ReadShort(int32_t index);
+
+  // Read the UINT24 at the given index.
+  // @param index index into the font data
+  // @return the UINT24; -1 if outside the bounds of the font data
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t ReadUInt24(int32_t index);
+
+  // Read the ULONG at the given index.
+  // @param index index into the font data
+  // @return the ULONG; kInvalidUnsigned if outside the bounds of the font data
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int64_t ReadULong(int32_t index);
+
+  // Read the ULONG at the given index as int32_t.
+  // @param index index into the font data
+  // @return the ULONG
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t ReadULongAsInt(int32_t index);
+
+  // Read the ULONG at the given index, little-endian variant
+  // @param index index into the font data
+  // @return the ULONG
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int64_t ReadULongLE(int32_t index);
+
+  // Read the LONG at the given index.
+  // @param index index into the font data
+  // @return the LONG; kInvalidLong if outside the bounds of the font data
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t ReadLong(int32_t index);
+
+  // Read the Fixed at the given index.
+  // @param index index into the font data
+  // @return the Fixed
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t ReadFixed(int32_t index);
+
+  // Read the LONGDATETIME at the given index.
+  // @param index index into the font data
+  // @return the LONGDATETIME; kInvalidLongDateTime if outside the bounds of the
+  // font data
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int64_t ReadDateTimeAsLong(int32_t index);
+
+  // Read the FWORD at the given index.
+  // @param index index into the font data
+  // @return the FWORD
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t ReadFWord(int32_t index);
+
+  // Read the UFWORD at the given index.
+  // @param index index into the font data
+  // @return the UFWORD
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t ReadFUFWord(int32_t index);
+
+  // Note: Not ported because they just throw UnsupportedOperationException()
+  //       in Java.
+  /*
+  virtual int32_t ReadFUnit(int32_t index);
+  virtual int64_t ReadF2Dot14(int32_t index);
+  */
+
+  // Copy the FontData to an OutputStream.
+  // @param os the destination
+  // @return number of bytes copied
+  // @throws IOException
+  virtual int32_t CopyTo(OutputStream* os);
+
+  // Copy the FontData to a WritableFontData.
+  // @param wfd the destination
+  // @return number of bytes copied
+  // @throws IOException
+  virtual int32_t CopyTo(WritableFontData* wfd);
+
+  // Make gcc -Woverloaded-virtual happy.
+  virtual int32_t CopyTo(ByteArray* ba);
+
+  // Search for the key value in the range tables provided.
+  // The search looks through the start-end pairs looking for the key value. It
+  // is assumed that the start-end pairs are both represented by UShort values,
+  // ranges do not overlap, and are monotonically increasing.
+  // @param startIndex the position to read the first start value from
+  // @param startOffset the offset between subsequent start values
+  // @param endIndex the position to read the first end value from
+  // @param endOffset the offset between subsequent end values
+  // @param length the number of start-end pairs
+  // @param key the value to search for
+  // @return the index of the start-end pairs in which the key was found; -1
+  //         otherwise
+  int32_t SearchUShort(int32_t start_index,
+                       int32_t start_offset,
+                       int32_t end_index,
+                       int32_t end_offset,
+                       int32_t length,
+                       int32_t key);
+
+  // Search for the key value in the table provided.
+  // The search looks through the values looking for the key value. It is
+  // assumed that the are represented by UShort values and are monotonically
+  // increasing.
+  // @param startIndex the position to read the first start value from
+  // @param startOffset the offset between subsequent start values
+  // @param length the number of start-end pairs
+  // @param key the value to search for
+  // @return the index of the start-end pairs in which the key was found; -1
+  //         otherwise
+  int32_t SearchUShort(int32_t start_index,
+                       int32_t start_offset,
+                       int32_t length,
+                       int32_t key);
+
+  // Search for the key value in the range tables provided.
+  // The search looks through the start-end pairs looking for the key value. It
+  // is assumed that the start-end pairs are both represented by ULong values
+  // that can be represented within 31 bits, ranges do not overlap, and are
+  // monotonically increasing.
+  // @param startIndex the position to read the first start value from
+  // @param startOffset the offset between subsequent start values
+  // @param endIndex the position to read the first end value from
+  // @param endOffset the offset between subsequent end values
+  // @param length the number of start-end pairs
+  // @param key the value to search for
+  // @return the index of the start-end pairs in which the key was found; -1
+  //         otherwise
+  int32_t SearchULong(int32_t start_index,
+                      int32_t start_offset,
+                      int32_t end_index,
+                      int32_t end_offset,
+                      int32_t length,
+                      int32_t key);
+
+
+  // TODO(arthurhsu): IMPLEMENT
+  /*
+  virtual int32_t ReadFUnit(int32_t index);
+  virtual int64_t ReadF2Dot14(int32_t index);
+  virtual int64_t ReadLongDateTime(int32_t index);
+  */
+
+  // Makes a slice of this FontData. The returned slice will share the data with
+  // the original FontData.
+  // @param offset the start of the slice
+  // @param length the number of bytes in the slice
+  // @return a slice of the original FontData
+  // Note: C++ polymorphism requires return type to be consistent
+  virtual CALLER_ATTACH FontData* Slice(int32_t offset, int32_t length);
+
+  // Makes a bottom bound only slice of this array. The returned slice will
+  // share the data with the original FontData.
+  // @param offset the start of the slice
+  // @return a slice of the original FontData
+  // Note: C++ polymorphism requires return type to be consistent
+  virtual CALLER_ATTACH FontData* Slice(int32_t offset);
+
+  // Not Ported: toString()
+
+ protected:
+  // Constructor. Creates a bounded wrapper of another ReadableFontData from the
+  // given offset until the end of the original ReadableFontData.
+  // @param data data to wrap
+  // @param offset the start of this data's view of the original data
+  ReadableFontData(ReadableFontData* data, int32_t offset);
+
+  // Constructor. Creates a bounded wrapper of another ReadableFontData from the
+  // given offset until the end of the original ReadableFontData.
+  // @param data data to wrap
+  // @param offset the start of this data's view of the original data
+  // @param length the length of the other FontData to use
+  ReadableFontData(ReadableFontData* data, int32_t offset, int32_t length);
+
+ private:
+  // Compute the checksum for the font data using any ranges set for the
+  // calculation.
+  void ComputeChecksum();
+
+  // Do the actual computation of the checksum for a range using the
+  // TrueType/OpenType checksum algorithm. The range used is from the low bound
+  // to the high bound in steps of four bytes. If any of the bytes within that 4
+  // byte segment are not readable then it will considered a zero for
+  // calculation.
+  // Only called from within a synchronized method so it does not need to be
+  // synchronized itself.
+  // @param lowBound first position to start a 4 byte segment on
+  // @param highBound last possible position to start a 4 byte segment on
+  // @return the checksum for the total range
+  int64_t ComputeCheckSum(int32_t low_bound, int32_t high_bound);
+
+  Lock checksum_lock_;
+  bool checksum_set_;
+  int64_t checksum_;
+  IntegerList checksum_range_;
+};
+typedef Ptr<ReadableFontData> ReadableFontDataPtr;
+
+}  // namespace sfntly
+
+#endif  // SFNTLY_CPP_SRC_SFNTLY_DATA_READABLE_FONT_DATA_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/writable_font_data.cc
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sfntly/data/writable_font_data.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/data/growable_memory_byte_array.h"
+
+namespace sfntly {
+
+WritableFontData::WritableFontData(ByteArray* ba) : ReadableFontData(ba) {
+}
+
+WritableFontData::~WritableFontData() {}
+
+// static
+CALLER_ATTACH
+WritableFontData* WritableFontData::CreateWritableFontData(int32_t length) {
+  ByteArrayPtr ba;
+  if (length > 0) {
+    ba = new MemoryByteArray(length);
+    ba->SetFilledLength(length);
+  } else {
+    ba = new GrowableMemoryByteArray();
+  }
+  WritableFontDataPtr wfd = new WritableFontData(ba);
+  return wfd.Detach();
+}
+
+// TODO(arthurhsu): re-investigate the memory model of this function.  It's
+//                  not too useful without copying, but it's not performance
+//                  savvy to do copying.
+CALLER_ATTACH
+WritableFontData* WritableFontData::CreateWritableFontData(ByteVector* b) {
+  ByteArrayPtr ba = new GrowableMemoryByteArray();
+  ba->Put(0, b);
+  WritableFontDataPtr wfd = new WritableFontData(ba);
+  return wfd.Detach();
+}
+
+int32_t WritableFontData::WriteByte(int32_t index, byte_t b) {
+  array_->Put(BoundOffset(index), b);
+  return 1;
+}
+
+int32_t WritableFontData::WriteBytes(int32_t index,
+                                     byte_t* b,
+                                     int32_t offset,
+                                     int32_t length) {
+  return array_->Put(BoundOffset(index),
+                     b,
+                     offset,
+                     BoundLength(index, length));
+}
+
+int32_t WritableFontData::WriteBytes(int32_t index, ByteVector* b) {
+  assert(b);
+  return WriteBytes(index, &((*b)[0]), 0, b->size());
+}
+
+int32_t WritableFontData::WriteBytesPad(int32_t index,
+                                        ByteVector* b,
+                                        int32_t offset,
+                                        int32_t length,
+                                        byte_t pad) {
+  int32_t written =
+      array_->Put(BoundOffset(index),
+                  &((*b)[0]),
+                  offset,
+                  BoundLength(index,
+                              std::min<int32_t>(length, b->size() - offset)));
+  written += WritePadding(written + index, length - written, pad);
+  return written;
+}
+
+int32_t WritableFontData::WritePadding(int32_t index, int32_t count) {
+  return WritePadding(index, count, (byte_t)0);
+}
+
+int32_t WritableFontData::WritePadding(int32_t index, int32_t count,
+                                       byte_t pad) {
+  for (int32_t i = 0; i < count; ++i) {
+    array_->Put(index + i, pad);
+  }
+  return count;
+}
+
+int32_t WritableFontData::WriteChar(int32_t index, byte_t c) {
+  return WriteByte(index, c);
+}
+
+int32_t WritableFontData::WriteUShort(int32_t index, int32_t us) {
+  WriteByte(index, (byte_t)((us >> 8) & 0xff));
+  WriteByte(index + 1, (byte_t)(us & 0xff));
+  return 2;
+}
+
+int32_t WritableFontData::WriteUShortLE(int32_t index, int32_t us) {
+  WriteByte(index, (byte_t)(us & 0xff));
+  WriteByte(index + 1, (byte_t)((us >> 8) & 0xff));
+  return 2;
+}
+
+int32_t WritableFontData::WriteShort(int32_t index, int32_t s) {
+  return WriteUShort(index, s);
+}
+
+int32_t WritableFontData::WriteUInt24(int32_t index, int32_t ui) {
+  WriteByte(index, (byte_t)((ui >> 16) & 0xff));
+  WriteByte(index + 1, (byte_t)((ui >> 8) & 0xff));
+  WriteByte(index + 2, (byte_t)(ui & 0xff));
+  return 3;
+}
+
+int32_t WritableFontData::WriteULong(int32_t index, int64_t ul) {
+  WriteByte(index, (byte_t)((ul >> 24) & 0xff));
+  WriteByte(index + 1, (byte_t)((ul >> 16) & 0xff));
+  WriteByte(index + 2, (byte_t)((ul >> 8) & 0xff));
+  WriteByte(index + 3, (byte_t)(ul & 0xff));
+  return 4;
+}
+
+int32_t WritableFontData::WriteULongLE(int32_t index, int64_t ul) {
+  WriteByte(index, (byte_t)(ul & 0xff));
+  WriteByte(index + 1, (byte_t)((ul >> 8) & 0xff));
+  WriteByte(index + 2, (byte_t)((ul >> 16) & 0xff));
+  WriteByte(index + 3, (byte_t)((ul >> 24) & 0xff));
+  return 4;
+}
+
+int32_t WritableFontData::WriteLong(int32_t index, int64_t l) {
+  return WriteULong(index, l);
+}
+
+int32_t WritableFontData::WriteFixed(int32_t index, int32_t f) {
+  return WriteLong(index, f);
+}
+
+int32_t WritableFontData::WriteDateTime(int32_t index, int64_t date) {
+  WriteULong(index, (date >> 32) & 0xffffffff);
+  WriteULong(index + 4, date & 0xffffffff);
+  return 8;
+}
+
+void WritableFontData::CopyFrom(InputStream* is, int32_t length) {
+  array_->CopyFrom(is, length);
+}
+
+void WritableFontData::CopyFrom(InputStream* is) {
+  array_->CopyFrom(is);
+}
+
+CALLER_ATTACH FontData* WritableFontData::Slice(int32_t offset,
+                                                int32_t length) {
+  if (offset < 0 || length < 0 ||
+      offset > std::numeric_limits<int32_t>::max() - length ||
+      offset + length > Size()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+    throw IndexOutOfBoundsException(
+        "Attempt to bind data outside of its limits");
+#endif
+    return NULL;
+  }
+  FontDataPtr slice = new WritableFontData(this, offset, length);
+  return slice.Detach();
+}
+
+CALLER_ATTACH FontData* WritableFontData::Slice(int32_t offset) {
+  if (offset < 0 || offset > Size()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+    throw IndexOutOfBoundsException(
+        "Attempt to bind data outside of its limits");
+#endif
+    return NULL;
+  }
+  FontDataPtr slice = new WritableFontData(this, offset);
+  return slice.Detach();
+}
+
+WritableFontData::WritableFontData(WritableFontData* data, int32_t offset)
+    : ReadableFontData(data, offset) {
+}
+
+WritableFontData::WritableFontData(WritableFontData* data,
+                                   int32_t offset,
+                                   int32_t length)
+    : ReadableFontData(data, offset, length) {
+}
+
+}  // namespace sfntly
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/data/writable_font_data.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_WRITABLE_FONT_DATA_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_WRITABLE_FONT_DATA_H_
+
+#include "sfntly/data/readable_font_data.h"
+
+namespace sfntly {
+
+// Writable font data wrapper. Supports writing of data primitives in the
+// TrueType / OpenType spec.
+class WritableFontData : public ReadableFontData {
+ public:
+  explicit WritableFontData(ByteArray* ba);
+  virtual ~WritableFontData();
+
+  // Constructs a writable font data object. If the length is specified as
+  // positive then a fixed size font data object will be created. If the length
+  // is zero or less then a growable font data object will be created and the
+  // size will be used as an estimate to help in allocating the original space.
+  // @param length if length > 0 create a fixed length font data; otherwise
+  //        create a growable font data
+  // @return a new writable font data
+  static CALLER_ATTACH WritableFontData* CreateWritableFontData(int32_t length);
+
+  // Constructs a writable font data object. The new font data object will wrap
+  // the bytes passed in to the factory and it will take make a copy of those
+  // bytes.
+  // @param b the byte vector to wrap
+  // @return a new writable font data
+  static CALLER_ATTACH WritableFontData* CreateWritableFontData(ByteVector* b);
+
+  // Write a byte at the given index.
+  // @param index index into the font data
+  // @param b the byte to write
+  // @return the number of bytes written
+  virtual int32_t WriteByte(int32_t index, byte_t b);
+
+  // Write the bytes from the array.
+  // @param index index into the font data
+  // @param b the source for the bytes to be written
+  // @param offset offset in the byte array
+  // @param length the length of the bytes to be written
+  // @return the number of bytes actually written; -1 if the index is outside
+  //         the FontData's range
+  virtual int32_t WriteBytes(int32_t index,
+                             byte_t* b,
+                             int32_t offset,
+                             int32_t length);
+
+  // Write the bytes from the array.
+  // @param index index into the font data
+  // @param b the source for the bytes to be written
+  // @return the number of bytes actually written; -1 if the index is outside
+  //         the FontData's range
+  virtual int32_t WriteBytes(int32_t index, ByteVector* b);
+
+  // Write the bytes from the array and pad if necessary.
+  // Write to the length given using the byte array provided and if there are
+  // not enough bytes in the array then pad to the requested length using the
+  // pad byte specified.
+  // @param index index into the font data
+  // @param b the source for the bytes to be written
+  // @param offset offset in the byte array
+  // @param length the length of the bytes to be written
+  // @param pad the padding byte to be used if necessary
+  // @return the number of bytes actually written
+  virtual int32_t WriteBytesPad(int32_t index,
+                                ByteVector* b,
+                                int32_t offset,
+                                int32_t length,
+                                byte_t pad);
+
+  // Writes padding to the FontData. The padding byte written is 0x00.
+  // @param index index into the font data
+  // @param count the number of pad bytes to write
+  // @return the number of pad bytes written
+  virtual int32_t WritePadding(int32_t index, int32_t count);
+
+  // Writes padding to the FontData.
+  // @param index index into the font data
+  // @param count the number of pad bytes to write
+  // @param pad the byte value to use as padding
+  // @return the number of pad bytes written
+  virtual int32_t WritePadding(int32_t index, int32_t count, byte_t pad);
+
+  // Write the CHAR at the given index.
+  // @param index index into the font data
+  // @param c the CHAR
+  // @return the number of bytes actually written
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t WriteChar(int32_t index, byte_t c);
+
+  // Write the USHORT at the given index.
+  // @param index index into the font data
+  // @param us the USHORT
+  // @return the number of bytes actually written
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t WriteUShort(int32_t index, int32_t us);
+
+  // Write the USHORT at the given index in little endian format.
+  // @param index index into the font data
+  // @param us the USHORT
+  // @return the number of bytes actually written
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t WriteUShortLE(int32_t index, int32_t us);
+
+  // Write the SHORT at the given index.
+  // @param index index into the font data
+  // @param s the SHORT
+  // @return the number of bytes actually written
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t WriteShort(int32_t index, int32_t s);
+
+  // Write the UINT24 at the given index.
+  // @param index index into the font data
+  // @param ui the UINT24
+  // @return the number of bytes actually written
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t WriteUInt24(int32_t index, int32_t ui);
+
+  // Write the ULONG at the given index.
+  // @param index index into the font data
+  // @param ul the ULONG
+  // @return the number of bytes actually written
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t WriteULong(int32_t index, int64_t ul);
+
+  // Write the ULONG at the given index in little endian format.
+  // @param index index into the font data
+  // @param ul the ULONG
+  // @return the number of bytes actually written
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t WriteULongLE(int32_t index, int64_t ul);
+
+  // Write the LONG at the given index.
+  // @param index index into the font data
+  // @param l the LONG
+  // @return the number of bytes actually written
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t WriteLong(int32_t index, int64_t l);
+
+  // Write the Fixed at the given index.
+  // @param index index into the font data
+  // @param f the Fixed
+  // @return the number of bytes actually written
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t WriteFixed(int32_t index, int32_t f);
+
+  // Write the LONGDATETIME at the given index.
+  // @param index index into the font data
+  // @param date the LONGDATETIME
+  // @return the number of bytes actually written
+  // @throws IndexOutOfBoundsException if index is outside the FontData's range
+  virtual int32_t WriteDateTime(int32_t index, int64_t date);
+
+  // Copy from the InputStream into this FontData.
+  // @param is the source
+  // @param length the number of bytes to copy
+  // @throws IOException
+  virtual void CopyFrom(InputStream* is, int32_t length);
+
+  // Copy everything from the InputStream into this FontData.
+  // @param is the source
+  // @throws IOException
+  virtual void CopyFrom(InputStream* is);
+
+  // Makes a slice of this FontData. The returned slice will share the data with
+  // the original FontData.
+  // @param offset the start of the slice
+  // @param length the number of bytes in the slice
+  // @return a slice of the original FontData
+  virtual CALLER_ATTACH FontData* Slice(int32_t offset, int32_t length);
+
+  // Makes a bottom bound only slice of this array. The returned slice will
+  // share the data with the original FontData.
+  // @param offset the start of the slice
+  // @return a slice of the original FontData
+  virtual CALLER_ATTACH FontData* Slice(int32_t offset);
+
+ private:
+  // Constructor with a lower bound.
+  // @param data other WritableFontData object to share data with
+  // @param offset offset from the other WritableFontData's data
+  WritableFontData(WritableFontData* data, int32_t offset);
+
+  // Constructor with lower bound and a length bound.
+  // @param data other WritableFontData object to share data with
+  // @param offset offset from the other WritableFontData's data
+  // @param length length of other WritableFontData's data to use
+  WritableFontData(WritableFontData* data, int32_t offset, int32_t length);
+};
+typedef Ptr<WritableFontData> WritableFontDataPtr;
+
+}  // namespace sfntly
+
+#endif  // SFNTLY_CPP_SRC_SFNTLY_DATA_WRITABLE_FONT_DATA_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/font.cc
@@ -0,0 +1,572 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sfntly/font.h"
+
+#include <stdio.h>
+
+#include <functional>
+#include <algorithm>
+#include <map>
+#include <string>
+#include <typeinfo>
+#include <iterator>
+
+#include "sfntly/data/font_input_stream.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/math/fixed1616.h"
+#include "sfntly/math/font_math.h"
+#include "sfntly/port/exception_type.h"
+#include "sfntly/table/core/font_header_table.h"
+#include "sfntly/table/core/horizontal_device_metrics_table.h"
+#include "sfntly/table/core/horizontal_header_table.h"
+#include "sfntly/table/core/horizontal_metrics_table.h"
+#include "sfntly/table/core/maximum_profile_table.h"
+#include "sfntly/table/truetype/loca_table.h"
+#include "sfntly/tag.h"
+
+namespace sfntly {
+
+namespace {
+
+const int32_t kSFNTVersionMajor = 1;
+const int32_t kSFNTVersionMinor = 0;
+
+const int32_t kMaxTableSize = 200 * 1024 * 1024;
+
+}  // namespace
+
+/******************************************************************************
+ * Font class
+ ******************************************************************************/
+Font::~Font() {}
+
+bool Font::HasTable(int32_t tag) const {
+  return tables_.find(tag) != tables_.end();
+}
+
+Table* Font::GetTable(int32_t tag) {
+  if (!HasTable(tag))
+    return NULL;
+  return tables_[tag];
+}
+
+const TableMap* Font::GetTableMap() {
+  return &tables_;
+}
+
+void Font::Serialize(OutputStream* os, IntegerList* table_ordering) {
+  assert(table_ordering);
+  IntegerList final_table_ordering;
+  GenerateTableOrdering(table_ordering, &final_table_ordering);
+  TableHeaderList table_records;
+  BuildTableHeadersForSerialization(&final_table_ordering, &table_records);
+
+  FontOutputStream fos(os);
+  SerializeHeader(&fos, &table_records);
+  SerializeTables(&fos, &table_records);
+}
+
+Font::Font(int32_t sfnt_version, ByteVector* digest)
+    : sfnt_version_(sfnt_version) {
+  // non-trivial assignments that makes debugging hard if placed in
+  // initialization list
+  digest_ = *digest;
+}
+
+void Font::BuildTableHeadersForSerialization(IntegerList* table_ordering,
+                                             TableHeaderList* table_headers) {
+  assert(table_headers);
+  assert(table_ordering);
+
+  IntegerList final_table_ordering;
+  GenerateTableOrdering(table_ordering, &final_table_ordering);
+  int32_t table_offset = Offset::kTableRecordBegin + num_tables() *
+                         Offset::kTableRecordSize;
+  for (IntegerList::iterator tag = final_table_ordering.begin(),
+                             tag_end = final_table_ordering.end();
+                             tag != tag_end; ++tag) {
+    if (tables_.find(*tag) == tables_.end()) {
+      continue;
+    }
+    TablePtr table = tables_[*tag];
+    if (table != NULL) {
+      HeaderPtr header =
+          new Header(*tag, table->CalculatedChecksum(), table_offset,
+                     table->header()->length());
+      table_headers->push_back(header);
+      table_offset += (table->DataLength() + 3) & ~3;
+    }
+  }
+}
+
+void Font::SerializeHeader(FontOutputStream* fos,
+                           TableHeaderList* table_headers) {
+  fos->WriteFixed(sfnt_version_);
+  fos->WriteUShort(table_headers->size());
+  int32_t log2_of_max_power_of_2 = FontMath::Log2(table_headers->size());
+  int32_t search_range = 2 << (log2_of_max_power_of_2 - 1 + 4);
+  fos->WriteUShort(search_range);
+  fos->WriteUShort(log2_of_max_power_of_2);
+  fos->WriteUShort((table_headers->size() * 16) - search_range);
+
+  HeaderTagSortedSet sorted_headers;
+  std::copy(table_headers->begin(),
+            table_headers->end(),
+            std::inserter(sorted_headers, sorted_headers.end()));
+
+  for (HeaderTagSortedSet::iterator record = sorted_headers.begin(),
+                                    record_end = sorted_headers.end();
+                                    record != record_end; ++record) {
+    fos->WriteULong((*record)->tag());
+    fos->WriteULong((int32_t)((*record)->checksum()));
+    fos->WriteULong((*record)->offset());
+    fos->WriteULong((*record)->length());
+  }
+}
+
+void Font::SerializeTables(FontOutputStream* fos,
+                           TableHeaderList* table_headers) {
+  assert(fos);
+  assert(table_headers);
+  for (TableHeaderList::iterator record = table_headers->begin(),
+                                 end_of_headers = table_headers->end();
+                                 record != end_of_headers; ++record) {
+    TablePtr target_table = GetTable((*record)->tag());
+    if (target_table == NULL) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+      throw IOException("Table out of sync with font header.");
+#endif
+      return;
+    }
+    int32_t table_size = target_table->Serialize(fos);
+    if (table_size != (*record)->length()) {
+      assert(false);
+    }
+    int32_t filler_size = ((table_size + 3) & ~3) - table_size;
+    for (int32_t i = 0; i < filler_size; ++i) {
+      fos->Write(static_cast<byte_t>(0));
+    }
+  }
+}
+
+void Font::GenerateTableOrdering(IntegerList* default_table_ordering,
+                                 IntegerList* table_ordering) {
+  assert(default_table_ordering);
+  assert(table_ordering);
+  table_ordering->clear();
+  if (default_table_ordering->empty()) {
+    DefaultTableOrdering(default_table_ordering);
+  }
+
+  typedef std::map<int32_t, bool> Int2Bool;
+  typedef std::pair<int32_t, bool> Int2BoolEntry;
+  Int2Bool tables_in_font;
+  for (TableMap::iterator table = tables_.begin(), table_end = tables_.end();
+                          table != table_end; ++table) {
+    tables_in_font.insert(Int2BoolEntry(table->first, false));
+  }
+  for (IntegerList::iterator tag = default_table_ordering->begin(),
+                             tag_end = default_table_ordering->end();
+                             tag != tag_end; ++tag) {
+    if (HasTable(*tag)) {
+      table_ordering->push_back(*tag);
+      tables_in_font[*tag] = true;
+    }
+  }
+  for (Int2Bool::iterator table = tables_in_font.begin(),
+                          table_end = tables_in_font.end();
+                          table != table_end; ++table) {
+    if (table->second == false)
+      table_ordering->push_back(table->first);
+  }
+}
+
+void Font::DefaultTableOrdering(IntegerList* default_table_ordering) {
+  assert(default_table_ordering);
+  default_table_ordering->clear();
+  if (HasTable(Tag::CFF)) {
+    default_table_ordering->resize(CFF_TABLE_ORDERING_SIZE);
+    std::copy(CFF_TABLE_ORDERING, CFF_TABLE_ORDERING + CFF_TABLE_ORDERING_SIZE,
+              default_table_ordering->begin());
+    return;
+  }
+  default_table_ordering->resize(TRUE_TYPE_TABLE_ORDERING_SIZE);
+  std::copy(TRUE_TYPE_TABLE_ORDERING,
+            TRUE_TYPE_TABLE_ORDERING + TRUE_TYPE_TABLE_ORDERING_SIZE,
+            default_table_ordering->begin());
+}
+
+/******************************************************************************
+ * Font::Builder class
+ ******************************************************************************/
+Font::Builder::~Builder() {}
+
+CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder(FontFactory* factory,
+                                                          InputStream* is) {
+  FontBuilderPtr builder = new Builder(factory);
+  builder->LoadFont(is);
+  return builder.Detach();
+}
+
+CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder(
+    FontFactory* factory,
+    WritableFontData* wfd,
+    int32_t offset_to_offset_table) {
+  FontBuilderPtr builder = new Builder(factory);
+  builder->LoadFont(wfd, offset_to_offset_table);
+  return builder.Detach();
+}
+
+CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder(
+    FontFactory* factory) {
+  FontBuilderPtr builder = new Builder(factory);
+  return builder.Detach();
+}
+
+bool Font::Builder::ReadyToBuild() {
+  // just read in data with no manipulation
+  if (table_builders_.empty() && !data_blocks_.empty()) {
+    return true;
+  }
+
+  // TODO(stuartg): font level checks - required tables etc?
+  for (TableBuilderMap::iterator table_builder = table_builders_.begin(),
+                                 table_builder_end = table_builders_.end();
+                                 table_builder != table_builder_end;
+                                 ++table_builder) {
+    if (!table_builder->second->ReadyToBuild())
+      return false;
+  }
+  return true;
+}
+
+CALLER_ATTACH Font* Font::Builder::Build() {
+  FontPtr font = new Font(sfnt_version_, &digest_);
+
+  if (!table_builders_.empty()) {
+    // Note: Different from Java. Directly use font->tables_ here to avoid
+    //       STL container copying.
+    BuildTablesFromBuilders(font, &table_builders_, &font->tables_);
+  }
+
+  table_builders_.clear();
+  data_blocks_.clear();
+  return font.Detach();
+}
+
+void Font::Builder::SetDigest(ByteVector* digest) {
+  digest_.clear();
+  digest_ = *digest;
+}
+
+void Font::Builder::ClearTableBuilders() {
+  table_builders_.clear();
+}
+
+bool Font::Builder::HasTableBuilder(int32_t tag) {
+  return (table_builders_.find(tag) != table_builders_.end());
+}
+
+Table::Builder* Font::Builder::GetTableBuilder(int32_t tag) {
+  if (HasTableBuilder(tag))
+    return table_builders_[tag];
+  return NULL;
+}
+
+Table::Builder* Font::Builder::NewTableBuilder(int32_t tag) {
+  HeaderPtr header = new Header(tag);
+  TableBuilderPtr builder;
+  builder.Attach(Table::Builder::GetBuilder(header, NULL));
+  table_builders_.insert(TableBuilderEntry(header->tag(), builder));
+  return builder;
+}
+
+Table::Builder* Font::Builder::NewTableBuilder(int32_t tag,
+                                               ReadableFontData* src_data) {
+  assert(src_data);
+  WritableFontDataPtr data;
+  data.Attach(WritableFontData::CreateWritableFontData(src_data->Length()));
+  // TODO(stuarg): take over original data instead?
+  src_data->CopyTo(data);
+
+  HeaderPtr header = new Header(tag, data->Length());
+  TableBuilderPtr builder;
+  builder.Attach(Table::Builder::GetBuilder(header, data));
+  table_builders_.insert(TableBuilderEntry(tag, builder));
+  return builder;
+}
+
+void Font::Builder::RemoveTableBuilder(int32_t tag) {
+  table_builders_.erase(tag);
+}
+
+Font::Builder::Builder(FontFactory* factory)
+    : factory_(factory),
+      sfnt_version_(Fixed1616::Fixed(kSFNTVersionMajor, kSFNTVersionMinor)) {
+}
+
+void Font::Builder::LoadFont(InputStream* is) {
+  // Note: we do not throw exception here for is.  This is more of an assertion.
+  assert(is);
+  FontInputStream font_is(is);
+  HeaderOffsetSortedSet records;
+  ReadHeader(&font_is, &records);
+  LoadTableData(&records, &font_is, &data_blocks_);
+  BuildAllTableBuilders(&data_blocks_, &table_builders_);
+  font_is.Close();
+}
+
+void Font::Builder::LoadFont(WritableFontData* wfd,
+                             int32_t offset_to_offset_table) {
+  // Note: we do not throw exception here for is.  This is more of an assertion.
+  assert(wfd);
+  HeaderOffsetSortedSet records;
+  ReadHeader(wfd, offset_to_offset_table, &records);
+  LoadTableData(&records, wfd, &data_blocks_);
+  BuildAllTableBuilders(&data_blocks_, &table_builders_);
+}
+
+int32_t Font::Builder::SfntWrapperSize() {
+  return Offset::kSfntHeaderSize +
+         (Offset::kTableRecordSize * table_builders_.size());
+}
+
+void Font::Builder::BuildAllTableBuilders(DataBlockMap* table_data,
+                                          TableBuilderMap* builder_map) {
+  for (DataBlockMap::iterator record = table_data->begin(),
+                              record_end = table_data->end();
+                              record != record_end; ++record) {
+    TableBuilderPtr builder;
+    builder.Attach(GetTableBuilder(record->first.p_, record->second.p_));
+    builder_map->insert(TableBuilderEntry(record->first->tag(), builder));
+  }
+  InterRelateBuilders(&table_builders_);
+}
+
+CALLER_ATTACH
+Table::Builder* Font::Builder::GetTableBuilder(Header* header,
+                                               WritableFontData* data) {
+  return Table::Builder::GetBuilder(header, data);
+}
+
+void Font::Builder::BuildTablesFromBuilders(Font* font,
+                                            TableBuilderMap* builder_map,
+                                            TableMap* table_map) {
+  UNREFERENCED_PARAMETER(font);
+  InterRelateBuilders(builder_map);
+
+  // Now build all the tables.
+  for (TableBuilderMap::iterator builder = builder_map->begin(),
+                                 builder_end = builder_map->end();
+                                 builder != builder_end; ++builder) {
+    TablePtr table;
+    if (builder->second && builder->second->ReadyToBuild()) {
+      table.Attach(down_cast<Table*>(builder->second->Build()));
+    }
+    if (table == NULL) {
+      table_map->clear();
+#if !defined (SFNTLY_NO_EXCEPTION)
+      std::string builder_string = "Unable to build table - ";
+      char* table_name = TagToString(builder->first);
+      builder_string += table_name;
+      delete[] table_name;
+      throw RuntimeException(builder_string.c_str());
+#endif
+      return;
+    }
+    table_map->insert(TableMapEntry(table->header()->tag(), table));
+  }
+}
+
+static Table::Builder* GetBuilder(TableBuilderMap* builder_map, int32_t tag) {
+  if (!builder_map)
+    return NULL;
+
+  TableBuilderMap::iterator target = builder_map->find(tag);
+  if (target == builder_map->end())
+    return NULL;
+
+  return target->second.p_;
+}
+
+// Like GetBuilder(), but the returned Builder must be able to support reads.
+static Table::Builder* GetReadBuilder(TableBuilderMap* builder_map, int32_t tag) {
+  Table::Builder* builder = GetBuilder(builder_map, tag);
+  if (!builder || !builder->InternalReadData())
+    return NULL;
+
+  return builder;
+}
+
+void Font::Builder::InterRelateBuilders(TableBuilderMap* builder_map) {
+  Table::Builder* raw_head_builder = GetReadBuilder(builder_map, Tag::head);
+  FontHeaderTableBuilderPtr header_table_builder;
+  if (raw_head_builder != NULL) {
+    header_table_builder =
+        down_cast<FontHeaderTable::Builder*>(raw_head_builder);
+  }
+
+  Table::Builder* raw_hhea_builder = GetReadBuilder(builder_map, Tag::hhea);
+  HorizontalHeaderTableBuilderPtr horizontal_header_builder;
+  if (raw_head_builder != NULL) {
+    horizontal_header_builder =
+        down_cast<HorizontalHeaderTable::Builder*>(raw_hhea_builder);
+  }
+
+  Table::Builder* raw_maxp_builder = GetReadBuilder(builder_map, Tag::maxp);
+  MaximumProfileTableBuilderPtr max_profile_builder;
+  if (raw_maxp_builder != NULL) {
+    max_profile_builder =
+        down_cast<MaximumProfileTable::Builder*>(raw_maxp_builder);
+  }
+
+  Table::Builder* raw_loca_builder = GetBuilder(builder_map, Tag::loca);
+  LocaTableBuilderPtr loca_table_builder;
+  if (raw_loca_builder != NULL) {
+    loca_table_builder = down_cast<LocaTable::Builder*>(raw_loca_builder);
+  }
+
+  Table::Builder* raw_hmtx_builder = GetBuilder(builder_map, Tag::hmtx);
+  HorizontalMetricsTableBuilderPtr horizontal_metrics_builder;
+  if (raw_hmtx_builder != NULL) {
+    horizontal_metrics_builder =
+        down_cast<HorizontalMetricsTable::Builder*>(raw_hmtx_builder);
+  }
+
+#if defined (SFNTLY_EXPERIMENTAL)
+  Table::Builder* raw_hdmx_builder = GetBuilder(builder_map, Tag::hdmx);
+  HorizontalDeviceMetricsTableBuilderPtr hdmx_table_builder;
+  if (raw_hdmx_builder != NULL) {
+    hdmx_table_builder =
+        down_cast<HorizontalDeviceMetricsTable::Builder*>(raw_hdmx_builder);
+  }
+#endif
+
+  // set the inter table data required to build certain tables
+  if (horizontal_metrics_builder != NULL) {
+    if (max_profile_builder != NULL) {
+      horizontal_metrics_builder->SetNumGlyphs(
+          max_profile_builder->NumGlyphs());
+    }
+    if (horizontal_header_builder != NULL) {
+      horizontal_metrics_builder->SetNumberOfHMetrics(
+          horizontal_header_builder->NumberOfHMetrics());
+    }
+  }
+
+  if (loca_table_builder != NULL) {
+    if (max_profile_builder != NULL) {
+      loca_table_builder->SetNumGlyphs(max_profile_builder->NumGlyphs());
+    }
+    if (header_table_builder != NULL) {
+      loca_table_builder->set_format_version(
+          header_table_builder->IndexToLocFormat());
+    }
+  }
+
+#if defined (SFNTLY_EXPERIMENTAL)
+  // Note: In C++, hdmx_table_builder can be NULL in a subsetter.
+  if (max_profile_builder != NULL && hdmx_table_builder != NULL) {
+    hdmx_table_builder->SetNumGlyphs(max_profile_builder->NumGlyphs());
+  }
+#endif
+}
+
+void Font::Builder::ReadHeader(FontInputStream* is,
+                               HeaderOffsetSortedSet* records) {
+  assert(records);
+  sfnt_version_ = is->ReadFixed();
+  num_tables_ = is->ReadUShort();
+  search_range_ = is->ReadUShort();
+  entry_selector_ = is->ReadUShort();
+  range_shift_ = is->ReadUShort();
+
+  for (int32_t table_number = 0; table_number < num_tables_; ++table_number) {
+    // Need to use temporary vars here.  C++ evaluates function parameters from
+    // right to left and thus breaks the order of input stream.
+    int32_t tag = is->ReadULongAsInt();
+    int64_t checksum = is->ReadULong();
+    int32_t offset = is->ReadULongAsInt();
+    int32_t length = is->ReadULongAsInt();
+    HeaderPtr table = new Header(tag, checksum, offset, length);
+    records->insert(table);
+  }
+}
+
+void Font::Builder::ReadHeader(ReadableFontData* fd,
+                               int32_t offset,
+                               HeaderOffsetSortedSet* records) {
+  assert(records);
+  sfnt_version_ = fd->ReadFixed(offset + Offset::kSfntVersion);
+  num_tables_ = fd->ReadUShort(offset + Offset::kNumTables);
+  search_range_ = fd->ReadUShort(offset + Offset::kSearchRange);
+  entry_selector_ = fd->ReadUShort(offset + Offset::kEntrySelector);
+  range_shift_ = fd->ReadUShort(offset + Offset::kRangeShift);
+
+  int32_t table_offset = offset + Offset::kTableRecordBegin;
+  for (int32_t table_number = 0;
+       table_number < num_tables_;
+       table_number++, table_offset += Offset::kTableRecordSize) {
+    int32_t tag = fd->ReadULongAsInt(table_offset + Offset::kTableTag);
+    int64_t checksum = fd->ReadULong(table_offset + Offset::kTableCheckSum);
+    int32_t offset = fd->ReadULongAsInt(table_offset + Offset::kTableOffset);
+    int32_t length = fd->ReadULongAsInt(table_offset + Offset::kTableLength);
+    HeaderPtr table = new Header(tag, checksum, offset, length);
+    records->insert(table);
+  }
+}
+
+void Font::Builder::LoadTableData(HeaderOffsetSortedSet* headers,
+                                  FontInputStream* is,
+                                  DataBlockMap* table_data) {
+  assert(table_data);
+  for (HeaderOffsetSortedSet::iterator it = headers->begin(),
+                                       table_end = headers->end();
+                                       it != table_end;
+                                       ++it) {
+    const Ptr<Header> header = *it;
+    is->Skip(header->offset() - is->position());
+    if (header->length() > kMaxTableSize)
+      continue;
+
+    FontInputStream table_is(is, header->length());
+    WritableFontDataPtr data;
+    data.Attach(WritableFontData::CreateWritableFontData(header->length()));
+    data->CopyFrom(&table_is, header->length());
+    table_data->insert(DataBlockEntry(header, data));
+  }
+}
+
+void Font::Builder::LoadTableData(HeaderOffsetSortedSet* headers,
+                                  WritableFontData* fd,
+                                  DataBlockMap* table_data) {
+  for (HeaderOffsetSortedSet::iterator it = headers->begin(),
+                                       table_end = headers->end();
+                                       it != table_end;
+                                       ++it) {
+    const Ptr<Header> header = *it;
+    if (header->length() > kMaxTableSize)
+      continue;
+
+    FontDataPtr sliced_data;
+    sliced_data.Attach(fd->Slice(header->offset(), header->length()));
+    WritableFontDataPtr data = down_cast<WritableFontData*>(sliced_data.p_);
+    table_data->insert(DataBlockEntry(header, data));
+  }
+}
+
+}  // namespace sfntly
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/font.h
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_FONT_H_
+#define SFNTLY_CPP_SRC_SFNTLY_FONT_H_
+
+#include <vector>
+
+#include "sfntly/port/refcount.h"
+#include "sfntly/port/type.h"
+#include "sfntly/port/endian.h"
+#include "sfntly/data/font_input_stream.h"
+#include "sfntly/data/font_output_stream.h"
+#include "sfntly/data/writable_font_data.h"
+#include "sfntly/table/table.h"
+
+namespace sfntly {
+
+// Note: following constants are embedded in Font class in Java.  They are
+//       extracted out for easier reference from other classes.  Offset is the
+//       one that is kept within class.
+// Platform ids. These are used in a number of places within the font whenever
+// the platform needs to be specified.
+struct PlatformId {
+  enum {
+    kUnknown = -1,
+    kUnicode = 0,
+    kMacintosh = 1,
+    kISO = 2,
+    kWindows = 3,
+    kCustom = 4
+  };
+};
+
+// Unicode encoding ids. These are used in a number of places within the font
+// whenever character encodings need to be specified.
+struct UnicodeEncodingId {
+  enum {
+    kUnknown = -1,
+    kUnicode1_0 = 0,
+    kUnicode1_1 = 1,
+    kISO10646 = 2,
+    kUnicode2_0_BMP = 3,
+    kUnicode2_0 = 4,
+    kUnicodeVariationSequences = 5
+  };
+};
+
+// Windows encoding ids. These are used in a number of places within the font
+// whenever character encodings need to be specified.
+struct WindowsEncodingId {
+  enum {
+    kUnknown = 0xffffffff,
+    kSymbol = 0,
+    kUnicodeUCS2 = 1,
+    kShiftJIS = 2,
+    kPRC = 3,
+    kBig5 = 4,
+    kWansung = 5,
+    kJohab = 6,
+    kUnicodeUCS4 = 10
+  };
+};
+
+// Macintosh encoding ids. These are used in a number of places within the
+// font whenever character encodings need to be specified.
+struct MacintoshEncodingId {
+  // Macintosh Platform Encodings
+  enum {
+    kUnknown = -1,
+    kRoman = 0,
+    kJapanese = 1,
+    kChineseTraditional = 2,
+    kKorean = 3,
+    kArabic = 4,
+    kHebrew = 5,
+    kGreek = 6,
+    kRussian = 7,
+    kRSymbol = 8,
+    kDevanagari = 9,
+    kGurmukhi = 10,
+    kGujarati = 11,
+    kOriya = 12,
+    kBengali = 13,
+    kTamil = 14,
+    kTelugu = 15,
+    kKannada = 16,
+    kMalayalam = 17,
+    kSinhalese = 18,
+    kBurmese = 19,
+    kKhmer = 20,
+    kThai = 21,
+    kLaotian = 22,
+    kGeorgian = 23,
+    kArmenian = 24,
+    kChineseSimplified = 25,
+    kTibetan = 26,
+    kMongolian = 27,
+    kGeez = 28,
+    kSlavic = 29,
+    kVietnamese = 30,
+    kSindhi = 31,
+    kUninterpreted = 32
+  };
+};
+
+class FontFactory;
+
+// An sfnt container font object. This object is immutable and thread safe. To
+// construct one use an instance of Font::Builder.
+class Font : public RefCounted<Font> {
+ public:
+  // A builder for a font object. The builder allows the for the creation of
+  // immutable Font objects. The builder is a one use non-thread safe object and
+  // once the Font object has been created it is no longer usable. To create a
+  // further Font object new builder will be required.
+  class Builder : public RefCounted<Builder> {
+   public:
+    virtual ~Builder();
+
+    static CALLER_ATTACH Builder*
+        GetOTFBuilder(FontFactory* factory, InputStream* is);
+    static CALLER_ATTACH Builder*
+        GetOTFBuilder(FontFactory* factory,
+                      WritableFontData* ba,
+                      int32_t offset_to_offset_table);
+    static CALLER_ATTACH Builder* GetOTFBuilder(FontFactory* factory);
+
+    // Get the font factory that created this font builder.
+    FontFactory* GetFontFactory() { return factory_; }
+
+    // Is the font ready to build?
+    bool ReadyToBuild();
+
+    // Build the Font. After this call this builder will no longer be usable.
+    CALLER_ATTACH Font* Build();
+
+    // Set a unique fingerprint for the font object.
+    void SetDigest(ByteVector* digest);
+
+    // Clear all table builders.
+    void ClearTableBuilders();
+
+    // Does this font builder have the specified table builder.
+    bool HasTableBuilder(int32_t tag);
+
+    // Get the table builder for the given tag. If there is no builder for that
+    // tag then return a null.
+    Table::Builder* GetTableBuilder(int32_t tag);
+
+    // Creates a new table builder for the table type given by the table id tag.
+    // This new table has been added to the font and will replace any existing
+    // builder for that table.
+    // @return new empty table of the type specified by tag; if tag is not known
+    //         then a generic OpenTypeTable is returned
+    virtual Table::Builder* NewTableBuilder(int32_t tag);
+
+    // Creates a new table builder for the table type given by the table id tag.
+    // It makes a copy of the data provided and uses that copy for the table.
+    // This new table has been added to the font and will replace any existing
+    // builder for that table.
+    virtual Table::Builder* NewTableBuilder(int32_t tag,
+                                            ReadableFontData* src_data);
+
+    // Get a map of the table builders in this font builder accessed by table
+    // tag.
+    virtual TableBuilderMap* table_builders() { return &table_builders_; }
+
+    // Remove the specified table builder from the font builder.
+    // Note: different from Java: we don't return object in removeTableBuilder
+    virtual void RemoveTableBuilder(int32_t tag);
+
+    // Get the number of table builders in the font builder.
+    virtual int32_t number_of_table_builders() {
+      return (int32_t)table_builders_.size();
+    }
+
+   private:
+    explicit Builder(FontFactory* factory);
+    virtual void LoadFont(InputStream* is);
+    virtual void LoadFont(WritableFontData* wfd,
+                          int32_t offset_to_offset_table);
+    int32_t SfntWrapperSize();
+    void BuildAllTableBuilders(DataBlockMap* table_data,
+                               TableBuilderMap* builder_map);
+    CALLER_ATTACH Table::Builder*
+        GetTableBuilder(Header* header, WritableFontData* data);
+    void BuildTablesFromBuilders(Font* font,
+                                 TableBuilderMap* builder_map,
+                                 TableMap* tables);
+    static void InterRelateBuilders(TableBuilderMap* builder_map);
+
+    void ReadHeader(FontInputStream* is,
+                    HeaderOffsetSortedSet* records);
+
+    void ReadHeader(ReadableFontData* fd,
+                    int32_t offset,
+                    HeaderOffsetSortedSet* records);
+
+    void LoadTableData(HeaderOffsetSortedSet* headers,
+                       FontInputStream* is,
+                       DataBlockMap* table_data);
+
+    void LoadTableData(HeaderOffsetSortedSet* headers,
+                       WritableFontData* fd,
+                       DataBlockMap* table_data);
+
+    TableBuilderMap table_builders_;
+    FontFactory* factory_;  // dumb pointer, avoid circular refcounting
+    int32_t sfnt_version_;
+    int32_t num_tables_;
+    int32_t search_range_;
+    int32_t entry_selector_;
+    int32_t range_shift_;
+    DataBlockMap data_blocks_;
+    ByteVector digest_;
+  };
+
+  virtual ~Font();
+
+  // Gets the sfnt version set in the sfnt wrapper of the font.
+  int32_t sfnt_version() { return sfnt_version_; }
+
+  // Gets a copy of the fonts digest that was created when the font was read. If
+  // no digest was set at creation time then the return result will be null.
+  ByteVector* digest() { return &digest_; }
+
+  // Get the checksum for this font.
+  int64_t checksum() { return checksum_; }
+
+  // Get the number of tables in this font.
+  int32_t num_tables() { return (int32_t)tables_.size(); }
+
+  // Whether the font has a particular table.
+  bool HasTable(int32_t tag) const;
+
+  // UNIMPLEMENTED: public Iterator<? extends Table> iterator
+
+  // Get the table in this font with the specified id.
+  // @param tag the identifier of the table
+  // @return the table specified if it exists; null otherwise
+  // C++ port: rename table() to GetTable()
+  Table* GetTable(int32_t tag);
+
+  // Get a map of the tables in this font accessed by table tag.
+  // @return an unmodifiable view of the tables in this font
+  // Note: renamed tableMap() to GetTableMap()
+  const TableMap* GetTableMap();
+
+  // UNIMPLEMENTED: toString()
+
+  // Serialize the font to the output stream.
+  // @param os the destination for the font serialization
+  // @param tableOrdering the table ordering to apply
+  void Serialize(OutputStream* os, IntegerList* table_ordering);
+
+ private:
+  // Offsets to specific elements in the underlying data. These offsets are
+  // relative to the start of the table or the start of sub-blocks within the
+  // table.
+  struct Offset {
+    enum {
+      // Offsets within the main directory
+      kSfntVersion = 0,
+      kNumTables = 4,
+      kSearchRange = 6,
+      kEntrySelector = 8,
+      kRangeShift = 10,
+      kTableRecordBegin = 12,
+      kSfntHeaderSize = 12,
+
+      // Offsets within a specific table record
+      kTableTag = 0,
+      kTableCheckSum = 4,
+      kTableOffset = 8,
+      kTableLength = 12,
+      kTableRecordSize = 16
+    };
+  };
+
+  // Note: the two constants are moved to tag.h to avoid VC++ bug.
+//  static const int32_t CFF_TABLE_ORDERING[];
+//  static const int32_t TRUE_TYPE_TABLE_ORDERING[];
+
+  // Constructor.
+  // @param sfntVersion the sfnt version
+  // @param digest the computed digest for the font; null if digest was not
+  //        computed
+  // Note: Current C++ port does not support SHA digest validation.
+  Font(int32_t sfnt_version, ByteVector* digest);
+
+  // Build the table headers to be used for serialization. These headers will be
+  // filled out with the data required for serialization. The headers will be
+  // sorted in the order specified and only those specified will have headers
+  // generated.
+  // @param tableOrdering the tables to generate headers for and the order to
+  //        sort them
+  // @return a list of table headers ready for serialization
+  void BuildTableHeadersForSerialization(IntegerList* table_ordering,
+                                         TableHeaderList* table_headers);
+
+  // Searialize the headers.
+  // @param fos the destination stream for the headers
+  // @param tableHeaders the headers to serialize
+  // @throws IOException
+  void SerializeHeader(FontOutputStream* fos, TableHeaderList* table_headers);
+
+  // Serialize the tables.
+  // @param fos the destination stream for the headers
+  // @param tableHeaders the headers for the tables to serialize
+  // @throws IOException
+  void SerializeTables(FontOutputStream* fos, TableHeaderList* table_headers);
+
+  // Generate the full table ordering to used for serialization. The full
+  // ordering uses the partial ordering as a seed and then adds all remaining
+  // tables in the font in an undefined order.
+  // @param defaultTableOrdering the partial ordering to be used as a seed for
+  //        the full ordering
+  // @param (out) table_ordering the full ordering for serialization
+  void GenerateTableOrdering(IntegerList* default_table_ordering,
+                             IntegerList* table_ordering);
+
+  // Get the default table ordering based on the type of the font.
+  // @param (out) default_table_ordering the default table ordering
+  void DefaultTableOrdering(IntegerList* default_table_ordering);
+
+  int32_t sfnt_version_;
+  ByteVector digest_;
+  int64_t checksum_;
+  TableMap tables_;
+};
+typedef Ptr<Font> FontPtr;
+typedef std::vector<FontPtr> FontArray;
+typedef Ptr<Font::Builder> FontBuilderPtr;
+typedef std::vector<FontBuilderPtr> FontBuilderArray;
+
+}  // namespace sfntly
+
+#endif  // SFNTLY_CPP_SRC_SFNTLY_FONT_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/font_factory.cc
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sfntly/font_factory.h"
+
+#include <string.h>
+
+#include "sfntly/tag.h"
+
+namespace sfntly {
+
+FontFactory::~FontFactory() {
+}
+
+CALLER_ATTACH FontFactory* FontFactory::GetInstance() {
+  FontFactoryPtr instance = new FontFactory();
+  return instance.Detach();
+}
+
+void FontFactory::FingerprintFont(bool fingerprint) {
+  fingerprint_ = fingerprint;
+}
+
+bool FontFactory::FingerprintFont() {
+  return fingerprint_;
+}
+
+void FontFactory::LoadFonts(InputStream* is, FontArray* output) {
+  assert(output);
+  PushbackInputStream* pbis = down_cast<PushbackInputStream*>(is);
+  if (IsCollection(pbis)) {
+    LoadCollection(pbis, output);
+    return;
+  }
+  FontPtr font;
+  font.Attach(LoadSingleOTF(pbis));
+  if (font) {
+    output->push_back(font);
+  }
+}
+
+void FontFactory::LoadFonts(ByteVector* b, FontArray* output) {
+  WritableFontDataPtr wfd;
+  wfd.Attach(WritableFontData::CreateWritableFontData(b));
+  if (IsCollection(wfd)) {
+    LoadCollection(wfd, output);
+    return;
+  }
+  FontPtr font;
+  font.Attach(LoadSingleOTF(wfd));
+  if (font) {
+    output->push_back(font);
+  }
+}
+
+void FontFactory::LoadFontsForBuilding(InputStream* is,
+                                       FontBuilderArray* output) {
+  PushbackInputStream* pbis = down_cast<PushbackInputStream*>(is);
+  if (IsCollection(pbis)) {
+    LoadCollectionForBuilding(pbis, output);
+    return;
+  }
+  FontBuilderPtr builder;
+  builder.Attach(LoadSingleOTFForBuilding(pbis));
+  if (builder) {
+    output->push_back(builder);
+  }
+}
+
+void FontFactory::LoadFontsForBuilding(ByteVector* b,
+                                       FontBuilderArray* output) {
+  WritableFontDataPtr wfd;
+  wfd.Attach(WritableFontData::CreateWritableFontData(b));
+  if (IsCollection(wfd)) {
+    LoadCollectionForBuilding(wfd, output);
+    return;
+  }
+  FontBuilderPtr builder;
+  builder.Attach(LoadSingleOTFForBuilding(wfd, 0));
+  if (builder) {
+    output->push_back(builder);
+  }
+}
+
+void FontFactory::SerializeFont(Font* font, OutputStream* os) {
+  font->Serialize(os, &table_ordering_);
+}
+
+void FontFactory::SetSerializationTableOrdering(
+    const IntegerList& table_ordering) {
+  table_ordering_ = table_ordering;
+}
+
+CALLER_ATTACH Font::Builder* FontFactory::NewFontBuilder() {
+  return Font::Builder::GetOTFBuilder(this);
+}
+
+CALLER_ATTACH Font* FontFactory::LoadSingleOTF(InputStream* is) {
+  FontBuilderPtr builder;
+  builder.Attach(LoadSingleOTFForBuilding(is));
+  return builder->Build();
+}
+
+CALLER_ATTACH Font* FontFactory::LoadSingleOTF(WritableFontData* wfd) {
+  FontBuilderPtr builder;
+  builder.Attach(LoadSingleOTFForBuilding(wfd, 0));
+  return builder->Build();
+}
+
+void FontFactory::LoadCollection(InputStream* is, FontArray* output) {
+  FontBuilderArray ba;
+  LoadCollectionForBuilding(is, &ba);
+  output->reserve(ba.size());
+  for (FontBuilderArray::iterator builder = ba.begin(), builders_end = ba.end();
+                                  builder != builders_end; ++builder) {
+      FontPtr font;
+      font.Attach((*builder)->Build());
+      output->push_back(font);
+  }
+}
+
+void FontFactory::LoadCollection(WritableFontData* wfd, FontArray* output) {
+  FontBuilderArray builders;
+  LoadCollectionForBuilding(wfd, &builders);
+  output->reserve(builders.size());
+  for (FontBuilderArray::iterator builder = builders.begin(),
+                                  builders_end = builders.end();
+                                  builder != builders_end; ++builder) {
+    FontPtr font;
+    font.Attach((*builder)->Build());
+    output->push_back(font);
+  }
+}
+
+CALLER_ATTACH
+Font::Builder* FontFactory::LoadSingleOTFForBuilding(InputStream* is) {
+  // UNIMPLEMENTED: SHA-1 hash checking via Java DigestStream
+  Font::Builder* builder = Font::Builder::GetOTFBuilder(this, is);
+  // UNIMPLEMENTED: setDigest
+  return builder;
+}
+
+CALLER_ATTACH Font::Builder*
+    FontFactory::LoadSingleOTFForBuilding(WritableFontData* wfd,
+                                          int32_t offset_to_offset_table) {
+  // UNIMPLEMENTED: SHA-1 hash checking via Java DigestStream
+  Font::Builder* builder =
+      Font::Builder::GetOTFBuilder(this, wfd, offset_to_offset_table);
+  // UNIMPLEMENTED: setDigest
+  return builder;
+}
+
+void FontFactory::LoadCollectionForBuilding(InputStream* is,
+                                            FontBuilderArray* builders) {
+  assert(is);
+  assert(builders);
+  WritableFontDataPtr wfd;
+  wfd.Attach(WritableFontData::CreateWritableFontData(is->Available()));
+  wfd->CopyFrom(is);
+  LoadCollectionForBuilding(wfd, builders);
+}
+
+void FontFactory::LoadCollectionForBuilding(WritableFontData* wfd,
+                                            FontBuilderArray* builders) {
+  int32_t ttc_tag = wfd->ReadULongAsInt(Offset::kTTCTag);
+  UNREFERENCED_PARAMETER(ttc_tag);
+  int32_t version = wfd->ReadFixed(Offset::kVersion);
+  UNREFERENCED_PARAMETER(version);
+  int32_t num_fonts = wfd->ReadULongAsInt(Offset::kNumFonts);
+
+  builders->reserve(num_fonts);
+  int32_t offset_table_offset = Offset::kOffsetTable;
+  for (int32_t font_number = 0;
+               font_number < num_fonts;
+               font_number++, offset_table_offset += DataSize::kULONG) {
+    int32_t offset = wfd->ReadULongAsInt(offset_table_offset);
+    FontBuilderPtr builder;
+    builder.Attach(LoadSingleOTFForBuilding(wfd, offset));
+    builders->push_back(builder);
+  }
+}
+
+bool FontFactory::IsCollection(PushbackInputStream* pbis) {
+  ByteVector tag(4);
+  pbis->Read(&tag);
+  pbis->Unread(&tag);
+  return Tag::ttcf == GenerateTag(tag[0], tag[1], tag[2], tag[3]);
+}
+
+bool FontFactory::IsCollection(ReadableFontData* rfd) {
+  ByteVector tag(4);
+  rfd->ReadBytes(0, &(tag[0]), 0, tag.size());
+  return Tag::ttcf ==
+         GenerateTag(tag[0], tag[1], tag[2], tag[3]);
+}
+
+FontFactory::FontFactory()
+    : fingerprint_(false) {
+}
+
+}  // namespace sfntly
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/font_factory.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_FONT_FACTORY_H_
+#define SFNTLY_CPP_SRC_SFNTLY_FONT_FACTORY_H_
+
+#include <vector>
+
+#include "sfntly/port/refcount.h"
+#include "sfntly/port/type.h"
+#include "sfntly/font.h"
+
+namespace sfntly {
+
+class FontFactory : public RefCounted<FontFactory> {
+ public:
+  virtual ~FontFactory();
+
+  // Factory method for the construction of a font factory.
+  static CALLER_ATTACH FontFactory* GetInstance();
+
+  // Toggle whether fonts that are loaded are fingerprinted with a SHA-1 hash.
+  // If a font is fingerprinted then a SHA-1 hash is generated at load time and
+  // stored in the font. This is useful for uniquely identifying fonts. By
+  // default this is turned on.
+  // @param fingerprint whether fingerprinting should be turned on or off
+  // TODO(arthurhsu): IMPLEMENT: C++ port currently don't do any SHA-1
+  void FingerprintFont(bool fingerprint);
+  bool FingerprintFont();
+
+  // Load the font(s) from the input stream. The current settings on the factory
+  // are used during the loading process. One or more fonts are returned if the
+  // stream contains valid font data. Some font container formats may have more
+  // than one font and in this case multiple font objects will be returned. If
+  // the data in the stream cannot be parsed or is invalid an array of size zero
+  // will be returned.
+  void LoadFonts(InputStream* is, FontArray* output);
+
+  // ByteArray font loading
+  // Load the font(s) from the byte array. The current settings on the factory
+  // are used during the loading process. One or more fonts are returned if the
+  // stream contains valid font data. Some font container formats may have more
+  // than one font and in this case multiple font objects will be returned. If
+  // the data in the stream cannot be parsed or is invalid an array of size zero
+  // will be returned.
+  void LoadFonts(ByteVector* b, FontArray* output);
+
+  // Load the font(s) from the input stream into font builders. The current
+  // settings on the factory are used during the loading process. One or more
+  // font builders are returned if the stream contains valid font data. Some
+  // font container formats may have more than one font and in this case
+  // multiple font builder objects will be returned. If the data in the stream
+  // cannot be parsed or is invalid an array of size zero will be returned.
+  void LoadFontsForBuilding(InputStream* is, FontBuilderArray* output);
+
+  // Load the font(s) from the byte array into font builders. The current
+  // settings on the factory are used during the loading process. One or more
+  // font builders are returned if the stream contains valid font data. Some
+  // font container formats may have more than one font and in this case
+  // multiple font builder objects will be returned. If the data in the stream
+  // cannot be parsed or is invalid an array of size zero will be returned.
+  void LoadFontsForBuilding(ByteVector* b, FontBuilderArray* output);
+
+  // Font serialization
+  // Serialize the font to the output stream.
+  // NOTE: in this port we attempted not to implement I/O stream because dealing
+  //       with cross-platform I/O stream itself is big enough as a project.
+  //       Byte buffer it is.
+  void SerializeFont(Font* font, OutputStream* os);
+
+  // Set the table ordering to be used in serializing a font. The table ordering
+  // is an ordered list of table ids and tables will be serialized in the order
+  // given. Any tables whose id is not listed in the ordering will be placed in
+  // an unspecified order following those listed.
+  void SetSerializationTableOrdering(const IntegerList& table_ordering);
+
+  // Get an empty font builder for creating a new font from scratch.
+  CALLER_ATTACH Font::Builder* NewFontBuilder();
+
+ private:
+  // Offsets to specific elements in the underlying data. These offsets are
+  // relative to the start of the table or the start of sub-blocks within the
+  // table.
+  struct Offset {
+    enum {
+      // Offsets within the main directory.
+      kTTCTag = 0,
+      kVersion = 4,
+      kNumFonts = 8,
+      kOffsetTable = 12,
+
+      // TTC Version 2.0 extensions.
+      // Offsets from end of OffsetTable.
+      kulDsigTag = 0,
+      kulDsigLength = 4,
+      kulDsigOffset = 8
+    };
+  };
+
+  FontFactory();
+
+  CALLER_ATTACH Font* LoadSingleOTF(InputStream* is);
+  CALLER_ATTACH Font* LoadSingleOTF(WritableFontData* wfd);
+
+  void LoadCollection(InputStream* is, FontArray* output);
+  void LoadCollection(WritableFontData* wfd, FontArray* output);
+
+  CALLER_ATTACH Font::Builder* LoadSingleOTFForBuilding(InputStream* is);
+  CALLER_ATTACH Font::Builder*
+      LoadSingleOTFForBuilding(WritableFontData* wfd,
+                               int32_t offset_to_offset_table);
+
+  void LoadCollectionForBuilding(InputStream* is, FontBuilderArray* builders);
+  void LoadCollectionForBuilding(WritableFontData* ba,
+                                 FontBuilderArray* builders);
+
+  static bool IsCollection(PushbackInputStream* pbis);
+  static bool IsCollection(ReadableFontData* wfd);
+
+  bool fingerprint_;
+  IntegerList table_ordering_;
+};
+typedef Ptr<FontFactory> FontFactoryPtr;
+
+}  // namespace sfntly
+
+#endif  // SFNTLY_CPP_SRC_SFNTLY_FONT_FACTORY_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/math/fixed1616.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_MATH_FIXED1616_H_
+#define SFNTLY_CPP_SRC_SFNTLY_MATH_FIXED1616_H_
+
+#include "sfntly/port/type.h"
+
+namespace sfntly {
+
+class Fixed1616 {
+ public:
+  static inline int32_t Integral(int32_t fixed) {
+    return (fixed >> 16);
+  }
+
+  static inline int32_t Fractional(int32_t fixed) {
+    return (fixed & 0xffff);
+  }
+
+  static inline int32_t Fixed(int32_t integral, int32_t fractional) {
+    return ((integral & 0xffff) << 16) | (fractional & 0xffff);
+  }
+};
+
+}  // namespace sfntly
+
+#endif  // SFNTLY_CPP_SRC_SFNTLY_MATH_FIXED1616_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/math/font_math.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_MATH_FONT_MATH_H_
+#define SFNTLY_CPP_SRC_SFNTLY_MATH_FONT_MATH_H_
+
+#include "sfntly/port/type.h"
+
+namespace sfntly {
+
+class FontMath {
+ public:
+  static int32_t Log2(int32_t a) {
+    int r = 0;  // r will be lg(a)
+    while (a != 0) {
+      a >>= 1;
+      r++;
+    }
+    return r - 1;
+  }
+
+  // Calculates the amount of padding needed. The values provided need to be in
+  // the same units. So, if the size is given as the number of bytes then the
+  // alignment size must also be specified as byte size to align to.
+  // @param size the size of the data that may need padding
+  // @param alignmentSize the number of units to align to
+  // @return the number of units needing to be added for alignment
+  static int32_t PaddingRequired(int32_t size, int32_t alignment_size) {
+    int32_t padding = alignment_size - (size % alignment_size);
+    return padding == alignment_size ? 0 : padding;
+  }
+};
+
+}  // namespace sfntly
+
+#endif  // SFNTLY_CPP_SRC_SFNTLY_MATH_FONT_MATH_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/port/atomic.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_ATOMIC_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_ATOMIC_H_
+
+#if defined (WIN32)
+
+#include <windows.h>
+
+static inline size_t AtomicIncrement(size_t* address) {
+#if defined (_WIN64)
+  return InterlockedIncrement64(reinterpret_cast<LONGLONG*>(address));
+#else
+  return InterlockedIncrement(reinterpret_cast<LONG*>(address));
+#endif
+}
+
+static inline size_t AtomicDecrement(size_t* address) {
+#if defined (_WIN64)
+  return InterlockedDecrement64(reinterpret_cast<LONGLONG*>(address));
+#else
+  return InterlockedDecrement(reinterpret_cast<LONG*>(address));
+#endif
+}
+
+#elif defined (__APPLE__)
+
+#include <libkern/OSAtomic.h>
+
+static inline size_t AtomicIncrement(size_t* address) {
+  return OSAtomicIncrement32Barrier(reinterpret_cast<int32_t*>(address));
+}
+
+static inline size_t AtomicDecrement(size_t* address) {
+  return OSAtomicDecrement32Barrier(reinterpret_cast<int32_t*>(address));
+}
+
+// Originally we check __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4, however, there are
+// issues that clang not carring over this definition.  Therefore we boldly
+// assume it's gcc or gcc-compatible here.  Compilation shall still fail since
+// the intrinsics used are GCC-specific.
+
+#else
+
+#include <stddef.h>
+
+static inline size_t AtomicIncrement(size_t* address) {
+  return __sync_add_and_fetch(address, 1);
+}
+
+static inline size_t AtomicDecrement(size_t* address) {
+  return __sync_sub_and_fetch(address, 1);
+}
+
+#endif  // WIN32
+
+#endif  // SFNTLY_CPP_SRC_SFNTLY_PORT_ATOMIC_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/port/config.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_CONFIG_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_CONFIG_H_
+
+#if !defined(SFNTLY_BIG_ENDIAN) && !defined(SFNTLY_LITTLE_ENDIAN)
+  #if defined (__ppc__) || defined (__ppc64__)
+    #define SFNTLY_BIG_ENDIAN
+  #else
+    #define SFNTLY_LITTLE_ENDIAN
+  #endif
+#endif
+
+#endif  // SFNTLY_CPP_SRC_SFNTLY_PORT_CONFIG_H_
new file mode 100644
--- /dev/null
+++ b/gfx/sfntly/cpp/src/sfntly/port/endian.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_ENDIAN_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_ENDIAN_H_
+
+#include "sfntly/port/config.h"
+#include "sfntly/port/type.h"
+
+namespace sfntly {
+
+static inline uint16_t EndianSwap16(uint16_t value) {
+  return (uint16_t)((value >> 8) | (value << 8));
+}
+
+static inline int32_t EndianSwap32(int32_t value) {
+  return (((value & 0x000000ff) << 24) |
+          ((value & 0x0000ff00) <<  8) |
+          ((value & 0x00ff0000) >>  8) |
+          ((value & 0xff000000) >> 24));
+}
+
+static inline uint64_t EndianSwap64(uint64_t value) {
+  return (((value & 0x00000000000000ffLL) << 56) |
+          ((value & 0x000000000000ff00LL) << 40) |
+          ((value & 0x0000000000ff0000LL) << 24) |
+          ((value & 0x00000000ff000000LL) << 8)  |
+          ((value & 0x000000ff00000000LL) >> 8)  |
+          ((value & 0x0000ff0000000000LL) >> 24) |
+          ((value & 0x00ff000000000000LL) >> 40) |
+          ((value & 0xff00000000000000LL) >> 56));
+}
+
+#ifdef SFNTLY_LITTLE_ENDIAN
+  #define ToBE16(n) EndianSwap16(n)
+  #define ToBE32(n) EndianSwap32(n)
+  #define ToBE64(n) EndianSwap64(n)
+  #define ToLE16(n) (n)
+  #define ToLE32(n) (n)
+  #define ToLE64(n) (n)
+  #define FromBE16(n) EndianSwap16(n)
+  #define FromBE32(n) EndianSwap32(n)
+  #define FromBE64(n) EndianSwap64(n)
+  #define FromLE16(n) (n)
+  #define FromLE32(n) (n)
+  #define FromLE64(n) (n)
+#else  // SFNTLY_BIG_ENDIAN
+  #define ToBE16(n) (n)
+  #define ToBE32(n) (n)
+  #define ToBE64(n) (n)
+  #define ToLE16(n) EndianSwap16(n)
+  #define ToLE32(n) EndianSwap32(n)
+  #define ToLE64(n) EndianSwap64(n)
+  #define FromBE16(n) (n)