mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-12 18:50:08 +00:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
3ad97e202a
@ -8,6 +8,7 @@ module.exports = {
|
|||||||
"rules": {
|
"rules": {
|
||||||
"mozilla/import-globals": "warn",
|
"mozilla/import-globals": "warn",
|
||||||
"mozilla/no-import-into-var-and-global": "error",
|
"mozilla/no-import-into-var-and-global": "error",
|
||||||
|
"mozilla/no-useless-parameters": "error",
|
||||||
|
|
||||||
// No (!foo in bar) or (!object instanceof Class)
|
// No (!foo in bar) or (!object instanceof Class)
|
||||||
"no-unsafe-negation": "error",
|
"no-unsafe-negation": "error",
|
||||||
|
@ -56,6 +56,6 @@ function destroy() {
|
|||||||
|
|
||||||
// stop listening to preference changes
|
// stop listening to preference changes
|
||||||
let branch = prefTargetNS(this).branch;
|
let branch = prefTargetNS(this).branch;
|
||||||
branch.removeObserver('', prefTargetNS(this).observer, false);
|
branch.removeObserver('', prefTargetNS(this).observer);
|
||||||
prefTargetNS(this).observer = null;
|
prefTargetNS(this).observer = null;
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ exports.asyncWindowLeakTest = function*(assert, asyncTestFunc) {
|
|||||||
|
|
||||||
// Stop tracking new windows and attempt to GC any resources allocated
|
// Stop tracking new windows and attempt to GC any resources allocated
|
||||||
// by the test body.
|
// by the test body.
|
||||||
Services.obs.removeObserver(windowObserver, "domwindowopened", false);
|
Services.obs.removeObserver(windowObserver, "domwindowopened");
|
||||||
yield gc();
|
yield gc();
|
||||||
|
|
||||||
// Check to see if any of the windows we saw survived the GC. We consider
|
// Check to see if any of the windows we saw survived the GC. We consider
|
||||||
|
@ -45,7 +45,7 @@ this.SafeMode = {
|
|||||||
return new Promise((aResolve, aReject) => {
|
return new Promise((aResolve, aReject) => {
|
||||||
let observer = function(aSubject, aTopic, aData) {
|
let observer = function(aSubject, aTopic, aData) {
|
||||||
if (Services.prefs.getCharPref(kSafeModePref)) {
|
if (Services.prefs.getCharPref(kSafeModePref)) {
|
||||||
Services.prefs.removeObserver(kSafeModePref, observer, false);
|
Services.prefs.removeObserver(kSafeModePref, observer);
|
||||||
aResolve();
|
aResolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,8 +124,8 @@ var DevEdition = {
|
|||||||
|
|
||||||
uninit() {
|
uninit() {
|
||||||
Services.prefs.removeObserver(this._devtoolsThemePrefName, this);
|
Services.prefs.removeObserver(this._devtoolsThemePrefName, this);
|
||||||
Services.obs.removeObserver(this, "lightweight-theme-styling-update", false);
|
Services.obs.removeObserver(this, "lightweight-theme-styling-update");
|
||||||
Services.obs.removeObserver(this, "lightweight-theme-window-updated", false);
|
Services.obs.removeObserver(this, "lightweight-theme-window-updated");
|
||||||
if (this.styleSheet) {
|
if (this.styleSheet) {
|
||||||
this.styleSheet.removeEventListener("load", this);
|
this.styleSheet.removeEventListener("load", this);
|
||||||
}
|
}
|
||||||
|
@ -1412,7 +1412,7 @@ var BookmarkingUI = {
|
|||||||
if (event.target == event.currentTarget) {
|
if (event.target == event.currentTarget) {
|
||||||
updatePlacesContextMenu(true);
|
updatePlacesContextMenu(true);
|
||||||
|
|
||||||
Services.prefs.removeObserver(this.RECENTLY_BOOKMARKED_PREF, prefObserver, false);
|
Services.prefs.removeObserver(this.RECENTLY_BOOKMARKED_PREF, prefObserver);
|
||||||
PlacesUtils.bookmarks.removeObserver(this._recentlyBookmarkedObserver);
|
PlacesUtils.bookmarks.removeObserver(this._recentlyBookmarkedObserver);
|
||||||
this._recentlyBookmarkedObserver = null;
|
this._recentlyBookmarkedObserver = null;
|
||||||
if (placesContextMenu) {
|
if (placesContextMenu) {
|
||||||
|
@ -7776,6 +7776,9 @@ var TabContextMenu = {
|
|||||||
for (let menuItem of menuItems)
|
for (let menuItem of menuItems)
|
||||||
menuItem.disabled = disabled;
|
menuItem.disabled = disabled;
|
||||||
|
|
||||||
|
if (this.contextTab.hasAttribute("customizemode"))
|
||||||
|
document.getElementById("context_openTabInWindow").disabled = true;
|
||||||
|
|
||||||
if (AppConstants.E10S_TESTING_ONLY) {
|
if (AppConstants.E10S_TESTING_ONLY) {
|
||||||
menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-remote");
|
menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-remote");
|
||||||
for (let menuItem of menuItems) {
|
for (let menuItem of menuItems) {
|
||||||
|
@ -5025,9 +5025,9 @@
|
|||||||
|
|
||||||
<destructor>
|
<destructor>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
Services.obs.removeObserver(this, "live-resize-start", false);
|
Services.obs.removeObserver(this, "live-resize-start");
|
||||||
Services.obs.removeObserver(this, "live-resize-end", false);
|
Services.obs.removeObserver(this, "live-resize-end");
|
||||||
Services.obs.removeObserver(this, "contextual-identity-updated", false);
|
Services.obs.removeObserver(this, "contextual-identity-updated");
|
||||||
|
|
||||||
for (let tab of this.tabs) {
|
for (let tab of this.tabs) {
|
||||||
let browser = tab.linkedBrowser;
|
let browser = tab.linkedBrowser;
|
||||||
|
@ -52,7 +52,7 @@ function checkPage(data) {
|
|||||||
Services.obs.addObserver(function observer(aSubject, aTopic) {
|
Services.obs.addObserver(function observer(aSubject, aTopic) {
|
||||||
ok(!Services.io.offline, "After clicking the Try Again button, we're back " +
|
ok(!Services.io.offline, "After clicking the Try Again button, we're back " +
|
||||||
"online.");
|
"online.");
|
||||||
Services.obs.removeObserver(observer, "network:offline-status-changed", false);
|
Services.obs.removeObserver(observer, "network:offline-status-changed");
|
||||||
finish();
|
finish();
|
||||||
}, "network:offline-status-changed", false);
|
}, "network:offline-status-changed", false);
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ var Downloads = (Components.utils.import("resource://gre/modules/Downloads.jsm",
|
|||||||
function promiseFormHistoryRemoved() {
|
function promiseFormHistoryRemoved() {
|
||||||
let deferred = Promise.defer();
|
let deferred = Promise.defer();
|
||||||
Services.obs.addObserver(function onfh() {
|
Services.obs.addObserver(function onfh() {
|
||||||
Services.obs.removeObserver(onfh, "satchel-storage-changed", false);
|
Services.obs.removeObserver(onfh, "satchel-storage-changed");
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
}, "satchel-storage-changed", false);
|
}, "satchel-storage-changed", false);
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
|
@ -40,8 +40,8 @@ window.addEventListener("message", function(event) {
|
|||||||
getService(SpecialPowers.Ci.nsIPermissionManager);
|
getService(SpecialPowers.Ci.nsIPermissionManager);
|
||||||
var ioService = Cc["@mozilla.org/network/io-service;1"]
|
var ioService = Cc["@mozilla.org/network/io-service;1"]
|
||||||
.getService(SpecialPowers.Ci.nsIIOService);
|
.getService(SpecialPowers.Ci.nsIIOService);
|
||||||
var uri1 = ioService.newURI(frames.testFrame.location, null, null);
|
var uri1 = ioService.newURI(frames.testFrame.location);
|
||||||
var uri2 = ioService.newURI(frames.testFrame3.location, null, null);
|
var uri2 = ioService.newURI(frames.testFrame3.location);
|
||||||
|
|
||||||
var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||||
.getService(SpecialPowers.Ci.nsIScriptSecurityManager);
|
.getService(SpecialPowers.Ci.nsIScriptSecurityManager);
|
||||||
|
@ -26,15 +26,11 @@
|
|||||||
<label class="identity-popup-headline hostless" crop="end"/>
|
<label class="identity-popup-headline hostless" crop="end"/>
|
||||||
</label>
|
</label>
|
||||||
<description class="identity-popup-connection-not-secure"
|
<description class="identity-popup-connection-not-secure"
|
||||||
value="&identity.connectionNotSecure;"
|
when-connection="not-secure secure-cert-user-overridden">&identity.connectionNotSecure;</description>
|
||||||
when-connection="not-secure secure-cert-user-overridden"/>
|
|
||||||
<description class="identity-popup-connection-secure"
|
<description class="identity-popup-connection-secure"
|
||||||
value="&identity.connectionSecure;"
|
when-connection="secure secure-ev">&identity.connectionSecure;</description>
|
||||||
when-connection="secure secure-ev"/>
|
<description when-connection="chrome">&identity.connectionInternal;</description>
|
||||||
<description value="&identity.connectionInternal;"
|
<description when-connection="file">&identity.connectionFile;</description>
|
||||||
when-connection="chrome"/>
|
|
||||||
<description value="&identity.connectionFile;"
|
|
||||||
when-connection="file"/>
|
|
||||||
|
|
||||||
<vbox id="identity-popup-security-descriptions">
|
<vbox id="identity-popup-security-descriptions">
|
||||||
<description class="identity-popup-warning-gray"
|
<description class="identity-popup-warning-gray"
|
||||||
@ -105,11 +101,9 @@
|
|||||||
<label class="identity-popup-headline hostless" crop="end"/>
|
<label class="identity-popup-headline hostless" crop="end"/>
|
||||||
</label>
|
</label>
|
||||||
<description class="identity-popup-connection-not-secure"
|
<description class="identity-popup-connection-not-secure"
|
||||||
value="&identity.connectionNotSecure;"
|
when-connection="not-secure secure-cert-user-overridden">&identity.connectionNotSecure;</description>
|
||||||
when-connection="not-secure secure-cert-user-overridden"/>
|
|
||||||
<description class="identity-popup-connection-secure"
|
<description class="identity-popup-connection-secure"
|
||||||
value="&identity.connectionSecure;"
|
when-connection="secure secure-ev">&identity.connectionSecure;</description>
|
||||||
when-connection="secure secure-ev"/>
|
|
||||||
</vbox>
|
</vbox>
|
||||||
|
|
||||||
<vbox id="identity-popup-securityView-body" flex="1">
|
<vbox id="identity-popup-securityView-body" flex="1">
|
||||||
|
@ -449,7 +449,7 @@ CustomizeMode.prototype = {
|
|||||||
yield this._doTransition(false);
|
yield this._doTransition(false);
|
||||||
this.removeLWTStyling();
|
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 (this.browser.selectedTab == gTab) {
|
||||||
if (gTab.linkedBrowser.currentURI.spec == "about:blank") {
|
if (gTab.linkedBrowser.currentURI.spec == "about:blank") {
|
||||||
|
@ -60,12 +60,12 @@ add_task(function*() {
|
|||||||
function promiseObserverNotification(aObserver) {
|
function promiseObserverNotification(aObserver) {
|
||||||
let deferred = Promise.defer();
|
let deferred = Promise.defer();
|
||||||
function notificationCallback(e) {
|
function notificationCallback(e) {
|
||||||
Services.obs.removeObserver(notificationCallback, aObserver, false);
|
Services.obs.removeObserver(notificationCallback, aObserver);
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
}
|
}
|
||||||
let timeoutId = setTimeout(() => {
|
let timeoutId = setTimeout(() => {
|
||||||
Services.obs.removeObserver(notificationCallback, aObserver, false);
|
Services.obs.removeObserver(notificationCallback, aObserver);
|
||||||
deferred.reject("Notification '" + aObserver + "' did not happen within 20 seconds.");
|
deferred.reject("Notification '" + aObserver + "' did not happen within 20 seconds.");
|
||||||
}, kTimeoutInMS);
|
}, kTimeoutInMS);
|
||||||
Services.obs.addObserver(notificationCallback, aObserver, false);
|
Services.obs.addObserver(notificationCallback, aObserver, false);
|
||||||
|
@ -11,7 +11,7 @@ var {
|
|||||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
|
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
|
||||||
"resource:///modules/sessionstore/SessionStore.jsm");
|
"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) {
|
function getRecentlyClosed(maxResults, extension) {
|
||||||
let recentlyClosed = [];
|
let recentlyClosed = [];
|
||||||
@ -62,6 +62,7 @@ extensions.registerSchemaAPI("sessions", "addon_parent", context => {
|
|||||||
let maxResults = filter.maxResults == undefined ? this.MAX_SESSION_RESULTS : filter.maxResults;
|
let maxResults = filter.maxResults == undefined ? this.MAX_SESSION_RESULTS : filter.maxResults;
|
||||||
return Promise.resolve(getRecentlyClosed(maxResults, extension));
|
return Promise.resolve(getRecentlyClosed(maxResults, extension));
|
||||||
},
|
},
|
||||||
|
|
||||||
restore: function(sessionId) {
|
restore: function(sessionId) {
|
||||||
let session, closedId;
|
let session, closedId;
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
@ -90,31 +91,15 @@ extensions.registerSchemaAPI("sessions", "addon_parent", context => {
|
|||||||
}
|
}
|
||||||
return createSession(session, extension, closedId);
|
return createSession(session, extension, closedId);
|
||||||
},
|
},
|
||||||
|
|
||||||
onChanged: new SingletonEventManager(context, "sessions.onChanged", fire => {
|
onChanged: new SingletonEventManager(context, "sessions.onChanged", fire => {
|
||||||
let listenerCount = 0;
|
let observer = () => {
|
||||||
|
|
||||||
let observer = {
|
|
||||||
observe: function() {
|
|
||||||
this.emit("changed");
|
|
||||||
},
|
|
||||||
};
|
|
||||||
EventEmitter.decorate(observer);
|
|
||||||
|
|
||||||
let listener = (event) => {
|
|
||||||
context.runSafe(fire);
|
context.runSafe(fire);
|
||||||
};
|
};
|
||||||
|
|
||||||
observer.on("changed", listener);
|
Services.obs.addObserver(observer, SS_ON_CLOSED_OBJECTS_CHANGED, false);
|
||||||
listenerCount++;
|
|
||||||
if (listenerCount == 1) {
|
|
||||||
Services.obs.addObserver(observer, ssOnChangedTopic, false);
|
|
||||||
}
|
|
||||||
return () => {
|
return () => {
|
||||||
observer.off("changed", listener);
|
Services.obs.removeObserver(observer, SS_ON_CLOSED_OBJECTS_CHANGED);
|
||||||
listenerCount -= 1;
|
|
||||||
if (!listenerCount) {
|
|
||||||
Services.obs.removeObserver(observer, ssOnChangedTopic);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}).api(),
|
}).api(),
|
||||||
},
|
},
|
||||||
|
@ -98,7 +98,7 @@ PrefsProvider.prototype = {
|
|||||||
|
|
||||||
uninit() {
|
uninit() {
|
||||||
for (let pref of gPrefsMap.keys()) {
|
for (let pref of gPrefsMap.keys()) {
|
||||||
Services.prefs.removeObserver(pref, this, false);
|
Services.prefs.removeObserver(pref, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -56,7 +56,7 @@ SearchProvider.prototype = {
|
|||||||
|
|
||||||
uninit() {
|
uninit() {
|
||||||
try {
|
try {
|
||||||
Services.obs.removeObserver(this, CURRENT_ENGINE, true);
|
Services.obs.removeObserver(this, CURRENT_ENGINE);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Cu.reportError(e);
|
Cu.reportError(e);
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ function clearAllPlacesFavicons() {
|
|||||||
observe(aSubject, aTopic, aData) {
|
observe(aSubject, aTopic, aData) {
|
||||||
if (aTopic === "places-favicons-expired") {
|
if (aTopic === "places-favicons-expired") {
|
||||||
resolve();
|
resolve();
|
||||||
Services.obs.removeObserver(observer, "places-favicons-expired", false);
|
Services.obs.removeObserver(observer, "places-favicons-expired");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -102,7 +102,7 @@ function observeFavicon(aFirstPartyDomain, aExpectedCookie, aPageURI) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (faviconReqXUL && faviconReqPlaces) {
|
if (faviconReqXUL && faviconReqPlaces) {
|
||||||
Services.obs.removeObserver(observer, "http-on-modify-request", false);
|
Services.obs.removeObserver(observer, "http-on-modify-request");
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,8 +132,8 @@ function waitOnFaviconResponse(aFaviconURL) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
resolve(result);
|
resolve(result);
|
||||||
Services.obs.removeObserver(observer, "http-on-examine-response", false);
|
Services.obs.removeObserver(observer, "http-on-examine-response");
|
||||||
Services.obs.removeObserver(observer, "http-on-examine-cached-response", false);
|
Services.obs.removeObserver(observer, "http-on-examine-cached-response");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -42,7 +42,7 @@ function clearAllPlacesFavicons() {
|
|||||||
observe(aSubject, aTopic, aData) {
|
observe(aSubject, aTopic, aData) {
|
||||||
if (aTopic === "places-favicons-expired") {
|
if (aTopic === "places-favicons-expired") {
|
||||||
resolve();
|
resolve();
|
||||||
Services.obs.removeObserver(observer, "places-favicons-expired", false);
|
Services.obs.removeObserver(observer, "places-favicons-expired");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -196,7 +196,7 @@ function* doTest(aTestPage, aFaviconHost, aFaviconURL) {
|
|||||||
// Waiting for favicon requests are all made.
|
// Waiting for favicon requests are all made.
|
||||||
yield observer.promise;
|
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);
|
yield BrowserTestUtils.removeTab(tabInfo.tab);
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ var gSearchPane = {
|
|||||||
|
|
||||||
Services.obs.addObserver(this, "browser-search-engine-modified", false);
|
Services.obs.addObserver(this, "browser-search-engine-modified", false);
|
||||||
window.addEventListener("unload", () => {
|
window.addEventListener("unload", () => {
|
||||||
Services.obs.removeObserver(this, "browser-search-engine-modified", false);
|
Services.obs.removeObserver(this, "browser-search-engine-modified");
|
||||||
});
|
});
|
||||||
|
|
||||||
this._initAutocomplete();
|
this._initAutocomplete();
|
||||||
|
@ -35,7 +35,7 @@ function clearAllPlacesFavicons() {
|
|||||||
observe(aSubject, aTopic, aData) {
|
observe(aSubject, aTopic, aData) {
|
||||||
if (aTopic === "places-favicons-expired") {
|
if (aTopic === "places-favicons-expired") {
|
||||||
resolve();
|
resolve();
|
||||||
Services.obs.removeObserver(observer, "places-favicons-expired", false);
|
Services.obs.removeObserver(observer, "places-favicons-expired");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -105,7 +105,7 @@ function observeFavicon(aIsPrivate, aExpectedCookie, aPageURI) {
|
|||||||
|
|
||||||
if (faviconReqXUL && faviconReqPlaces) {
|
if (faviconReqXUL && faviconReqPlaces) {
|
||||||
resolve();
|
resolve();
|
||||||
Services.obs.removeObserver(observer, "http-on-modify-request", false);
|
Services.obs.removeObserver(observer, "http-on-modify-request");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -134,8 +134,8 @@ function waitOnFaviconResponse(aFaviconURL) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
resolve(result);
|
resolve(result);
|
||||||
Services.obs.removeObserver(observer, "http-on-examine-response", false);
|
Services.obs.removeObserver(observer, "http-on-examine-response");
|
||||||
Services.obs.removeObserver(observer, "http-on-examine-cached-response", false);
|
Services.obs.removeObserver(observer, "http-on-examine-cached-response");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -32,7 +32,7 @@ var tests = [
|
|||||||
let windowDestroyedDeferred = Promise.defer();
|
let windowDestroyedDeferred = Promise.defer();
|
||||||
let onDOMWindowDestroyed = (aWindow) => {
|
let onDOMWindowDestroyed = (aWindow) => {
|
||||||
if (gContentWindow && aWindow == gContentWindow) {
|
if (gContentWindow && aWindow == gContentWindow) {
|
||||||
Services.obs.removeObserver(onDOMWindowDestroyed, "dom-window-destroyed", false);
|
Services.obs.removeObserver(onDOMWindowDestroyed, "dom-window-destroyed");
|
||||||
windowDestroyedDeferred.resolve();
|
windowDestroyedDeferred.resolve();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -197,11 +197,11 @@ var PdfJs = {
|
|||||||
|
|
||||||
uninit: function uninit() {
|
uninit: function uninit() {
|
||||||
if (this._initialized) {
|
if (this._initialized) {
|
||||||
Services.prefs.removeObserver(PREF_DISABLED, this, false);
|
Services.prefs.removeObserver(PREF_DISABLED, this);
|
||||||
Services.prefs.removeObserver(PREF_DISABLED_PLUGIN_TYPES, this, false);
|
Services.prefs.removeObserver(PREF_DISABLED_PLUGIN_TYPES, this);
|
||||||
Services.obs.removeObserver(this, TOPIC_PDFJS_HANDLER_CHANGED, false);
|
Services.obs.removeObserver(this, TOPIC_PDFJS_HANDLER_CHANGED);
|
||||||
Services.obs.removeObserver(this, TOPIC_PLUGINS_LIST_UPDATED, false);
|
Services.obs.removeObserver(this, TOPIC_PLUGINS_LIST_UPDATED);
|
||||||
Services.obs.removeObserver(this, TOPIC_PLUGIN_INFO_UPDATED, false);
|
Services.obs.removeObserver(this, TOPIC_PLUGIN_INFO_UPDATED);
|
||||||
this._initialized = false;
|
this._initialized = false;
|
||||||
}
|
}
|
||||||
this._ensureUnregistered();
|
this._ensureUnregistered();
|
||||||
|
@ -110,7 +110,7 @@ var PdfjsChromeUtils = {
|
|||||||
this._mmg.removeMessageListener('PDFJS:Parent:removeEventListener', this);
|
this._mmg.removeMessageListener('PDFJS:Parent:removeEventListener', this);
|
||||||
this._mmg.removeMessageListener('PDFJS:Parent:updateControlState', 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._mmg = null;
|
||||||
this._ppmm = null;
|
this._ppmm = null;
|
||||||
|
@ -212,7 +212,7 @@ let urlbarListener = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
uninit() {
|
uninit() {
|
||||||
Services.obs.removeObserver(this, AUTOCOMPLETE_ENTER_TEXT_TOPIC, true);
|
Services.obs.removeObserver(this, AUTOCOMPLETE_ENTER_TEXT_TOPIC);
|
||||||
},
|
},
|
||||||
|
|
||||||
observe(subject, topic, data) {
|
observe(subject, topic, data) {
|
||||||
@ -300,9 +300,9 @@ let BrowserUsageTelemetry = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
uninit() {
|
uninit() {
|
||||||
Services.obs.removeObserver(this, DOMWINDOW_OPENED_TOPIC, false);
|
Services.obs.removeObserver(this, DOMWINDOW_OPENED_TOPIC);
|
||||||
Services.obs.removeObserver(this, TELEMETRY_SUBSESSIONSPLIT_TOPIC, false);
|
Services.obs.removeObserver(this, TELEMETRY_SUBSESSIONSPLIT_TOPIC);
|
||||||
Services.obs.removeObserver(this, WINDOWS_RESTORED_TOPIC, false);
|
Services.obs.removeObserver(this, WINDOWS_RESTORED_TOPIC);
|
||||||
urlbarListener.uninit();
|
urlbarListener.uninit();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -909,9 +909,9 @@ this.PluginCrashReporter = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
uninit() {
|
uninit() {
|
||||||
Services.obs.removeObserver(this, "plugin-crashed", false);
|
Services.obs.removeObserver(this, "plugin-crashed");
|
||||||
Services.obs.removeObserver(this, "gmp-plugin-crash", false);
|
Services.obs.removeObserver(this, "gmp-plugin-crash");
|
||||||
Services.obs.removeObserver(this, "profile-after-change", false);
|
Services.obs.removeObserver(this, "profile-after-change");
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -60,12 +60,12 @@ add_task(function* asyncCleanup() {
|
|||||||
function promiseObserverNotification(aObserver) {
|
function promiseObserverNotification(aObserver) {
|
||||||
let deferred = Promise.defer();
|
let deferred = Promise.defer();
|
||||||
function notificationCallback(e) {
|
function notificationCallback(e) {
|
||||||
Services.obs.removeObserver(notificationCallback, aObserver, false);
|
Services.obs.removeObserver(notificationCallback, aObserver);
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
}
|
}
|
||||||
let timeoutId = setTimeout(() => {
|
let timeoutId = setTimeout(() => {
|
||||||
Services.obs.removeObserver(notificationCallback, aObserver, false);
|
Services.obs.removeObserver(notificationCallback, aObserver);
|
||||||
deferred.reject("Notification '" + aObserver + "' did not happen within 20 seconds.");
|
deferred.reject("Notification '" + aObserver + "' did not happen within 20 seconds.");
|
||||||
}, kTimeoutInMS);
|
}, kTimeoutInMS);
|
||||||
Services.obs.addObserver(notificationCallback, aObserver, false);
|
Services.obs.addObserver(notificationCallback, aObserver, false);
|
||||||
|
@ -117,16 +117,16 @@ body.compact .newtab-cell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* SITES */
|
/* SITES */
|
||||||
body:not(.compact) .newtab-site {
|
.newtab-site {
|
||||||
border-radius: var(--cell-corner-radius);
|
border-radius: var(--cell-corner-radius);
|
||||||
box-shadow: 0 1px 3px #c1c1c1;
|
box-shadow: 0 1px 3px #c1c1c1;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition-property: top, left, opacity, box-shadow, background-color;
|
transition-property: top, left, opacity, box-shadow, background-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
body:not(.compact) .newtab-cell:not([ignorehover]) .newtab-control:hover ~ .newtab-link,
|
.newtab-cell:not([ignorehover]) .newtab-control:hover ~ .newtab-link,
|
||||||
body:not(.compact) .newtab-cell:not([ignorehover]) .newtab-link:hover,
|
.newtab-cell:not([ignorehover]) .newtab-link:hover,
|
||||||
body:not(.compact) .newtab-site[dragged] {
|
.newtab-site[dragged] {
|
||||||
border: 2px solid white;
|
border: 2px solid white;
|
||||||
box-shadow: 0 0 6px 1px #add6ff;
|
box-shadow: 0 0 6px 1px #add6ff;
|
||||||
margin: -2px;
|
margin: -2px;
|
||||||
|
@ -91,9 +91,6 @@ if test "$MOZ_BUILD_APP" != js -o -n "$JS_STANDALONE"; then
|
|||||||
# Force disable DSS support in jemalloc.
|
# Force disable DSS support in jemalloc.
|
||||||
ac_configure_args="$ac_configure_args ac_cv_func_sbrk=false"
|
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.
|
# Make Linux builds munmap freed chunks instead of recycling them.
|
||||||
ac_configure_args="$ac_configure_args --enable-munmap"
|
ac_configure_args="$ac_configure_args --enable-munmap"
|
||||||
|
|
||||||
|
@ -184,6 +184,7 @@ inline bool isIgnoredPathForImplicitCtor(const Decl *Declaration) {
|
|||||||
End = llvm::sys::path::rend(FileName);
|
End = llvm::sys::path::rend(FileName);
|
||||||
for (; Begin != End; ++Begin) {
|
for (; Begin != End; ++Begin) {
|
||||||
if (Begin->compare_lower(StringRef("skia")) == 0 ||
|
if (Begin->compare_lower(StringRef("skia")) == 0 ||
|
||||||
|
Begin->compare_lower(StringRef("sfntly")) == 0 ||
|
||||||
Begin->compare_lower(StringRef("angle")) == 0 ||
|
Begin->compare_lower(StringRef("angle")) == 0 ||
|
||||||
Begin->compare_lower(StringRef("harfbuzz")) == 0 ||
|
Begin->compare_lower(StringRef("harfbuzz")) == 0 ||
|
||||||
Begin->compare_lower(StringRef("hunspell")) == 0 ||
|
Begin->compare_lower(StringRef("hunspell")) == 0 ||
|
||||||
@ -241,6 +242,7 @@ inline bool isIgnoredPathForSprintfLiteral(const CallExpr *Call, const SourceMan
|
|||||||
Begin->compare_lower(StringRef("mtransport")) == 0 ||
|
Begin->compare_lower(StringRef("mtransport")) == 0 ||
|
||||||
Begin->compare_lower(StringRef("protobuf")) == 0 ||
|
Begin->compare_lower(StringRef("protobuf")) == 0 ||
|
||||||
Begin->compare_lower(StringRef("skia")) == 0 ||
|
Begin->compare_lower(StringRef("skia")) == 0 ||
|
||||||
|
Begin->compare_lower(StringRef("sfntly")) == 0 ||
|
||||||
// Gtest uses snprintf as GTEST_SNPRINTF_ with sizeof
|
// Gtest uses snprintf as GTEST_SNPRINTF_ with sizeof
|
||||||
Begin->compare_lower(StringRef("testing")) == 0) {
|
Begin->compare_lower(StringRef("testing")) == 0) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -14,4 +14,6 @@ fi
|
|||||||
MOZ_AUTOMATION_INSTALLER=${MOZ_AUTOMATION_INSTALLER-1}
|
MOZ_AUTOMATION_INSTALLER=${MOZ_AUTOMATION_INSTALLER-1}
|
||||||
|
|
||||||
export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=c:/builds/crash-stats-api.token
|
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
|
||||||
|
@ -122,7 +122,7 @@ add_task(function* reloadButtonReloadsAddon() {
|
|||||||
|
|
||||||
const onBootstrapInstallCalled = new Promise(done => {
|
const onBootstrapInstallCalled = new Promise(done => {
|
||||||
Services.obs.addObserver(function listener() {
|
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);
|
info("Add-on was re-installed: " + ADDON_NAME);
|
||||||
done();
|
done();
|
||||||
}, ADDON_NAME, false);
|
}, ADDON_NAME, false);
|
||||||
|
@ -1089,11 +1089,11 @@ CssRuleView.prototype = {
|
|||||||
this.element.appendChild(div);
|
this.element.appendChild(div);
|
||||||
}
|
}
|
||||||
|
|
||||||
let inheritedSource = rule.inheritedSource;
|
let inheritedSource = rule.inherited;
|
||||||
if (inheritedSource && inheritedSource !== lastInheritedSource) {
|
if (inheritedSource && inheritedSource !== lastInheritedSource) {
|
||||||
let div = this.styleDocument.createElementNS(HTML_NS, "div");
|
let div = this.styleDocument.createElementNS(HTML_NS, "div");
|
||||||
div.className = this._getRuleViewHeaderClassName();
|
div.className = this._getRuleViewHeaderClassName();
|
||||||
div.textContent = inheritedSource;
|
div.textContent = rule.inheritedSource;
|
||||||
lastInheritedSource = inheritedSource;
|
lastInheritedSource = inheritedSource;
|
||||||
this.element.appendChild(div);
|
this.element.appendChild(div);
|
||||||
}
|
}
|
||||||
|
@ -150,6 +150,7 @@ skip-if = (os == "win" && debug) # bug 963492: win.
|
|||||||
[browser_rules_inherited-properties_01.js]
|
[browser_rules_inherited-properties_01.js]
|
||||||
[browser_rules_inherited-properties_02.js]
|
[browser_rules_inherited-properties_02.js]
|
||||||
[browser_rules_inherited-properties_03.js]
|
[browser_rules_inherited-properties_03.js]
|
||||||
|
[browser_rules_inherited-properties_04.js]
|
||||||
[browser_rules_inline-source-map.js]
|
[browser_rules_inline-source-map.js]
|
||||||
[browser_rules_invalid.js]
|
[browser_rules_invalid.js]
|
||||||
[browser_rules_invalid-source-map.js]
|
[browser_rules_invalid-source-map.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");
|
||||||
|
}
|
@ -16,7 +16,7 @@
|
|||||||
.getService(SpecialPowers.Ci.nsIIOService);
|
.getService(SpecialPowers.Ci.nsIIOService);
|
||||||
|
|
||||||
var style = "data:text/css,a { background-color: seagreen; }";
|
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)
|
var windowUtils = SpecialPowers.wrap(window)
|
||||||
.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
|
.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
|
||||||
.getInterface(SpecialPowers.Ci.nsIDOMWindowUtils);
|
.getInterface(SpecialPowers.Ci.nsIDOMWindowUtils);
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
.getService(SpecialPowers.Ci.nsIIOService);
|
.getService(SpecialPowers.Ci.nsIIOService);
|
||||||
|
|
||||||
var style = "data:text/css,div { background-color: seagreen; }";
|
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)
|
var windowUtils = SpecialPowers.wrap(window)
|
||||||
.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
|
.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
|
||||||
.getInterface(SpecialPowers.Ci.nsIDOMWindowUtils);
|
.getInterface(SpecialPowers.Ci.nsIDOMWindowUtils);
|
||||||
|
@ -2466,8 +2466,7 @@ var CloseObserver = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._uninited = true;
|
this._uninited = true;
|
||||||
Services.obs.removeObserver(this, "browser-lastwindow-close-requested",
|
Services.obs.removeObserver(this, "browser-lastwindow-close-requested");
|
||||||
false);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -74,6 +74,6 @@ var CloseObserver = {
|
|||||||
|
|
||||||
uninit: function ()
|
uninit: function ()
|
||||||
{
|
{
|
||||||
Services.obs.removeObserver(this, "browser-lastwindow-close-requested", false);
|
Services.obs.removeObserver(this, "browser-lastwindow-close-requested");
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -34,7 +34,7 @@ window.addEventListener("unload", function onUnload() {
|
|||||||
for (let i of inputs) {
|
for (let i of inputs) {
|
||||||
let pref = i.dataset.pref;
|
let pref = i.dataset.pref;
|
||||||
i.removeEventListener("change", SaveForm, false);
|
i.removeEventListener("change", SaveForm, false);
|
||||||
Services.prefs.removeObserver(pref, FillForm, false);
|
Services.prefs.removeObserver(pref, FillForm);
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ StorageActors.defaults = function (typeName, observationTopics) {
|
|||||||
destroy() {
|
destroy() {
|
||||||
if (observationTopics) {
|
if (observationTopics) {
|
||||||
observationTopics.forEach((observationTopic) => {
|
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-ready", this.onWindowReady);
|
||||||
@ -894,7 +894,7 @@ var cookieHelpers = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
removeCookieObservers() {
|
removeCookieObservers() {
|
||||||
Services.obs.removeObserver(cookieHelpers, "cookie-changed", false);
|
Services.obs.removeObserver(cookieHelpers, "cookie-changed");
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2461,8 +2461,8 @@ let StorageActor = protocol.ActorClassWithSpec(specs.storageSpec, {
|
|||||||
clearTimeout(this.batchTimer);
|
clearTimeout(this.batchTimer);
|
||||||
this.batchTimer = null;
|
this.batchTimer = null;
|
||||||
// Remove observers
|
// Remove observers
|
||||||
Services.obs.removeObserver(this, "content-document-global-created", false);
|
Services.obs.removeObserver(this, "content-document-global-created");
|
||||||
Services.obs.removeObserver(this, "inner-window-destroyed", false);
|
Services.obs.removeObserver(this, "inner-window-destroyed");
|
||||||
this.destroyed = true;
|
this.destroyed = true;
|
||||||
if (this.parentActor.browser) {
|
if (this.parentActor.browser) {
|
||||||
this.parentActor.browser.removeEventListener("pageshow", this.onPageChange, true);
|
this.parentActor.browser.removeEventListener("pageshow", this.onPageChange, true);
|
||||||
|
@ -1498,7 +1498,7 @@ DebuggerProgressListener.prototype = {
|
|||||||
]),
|
]),
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
Services.obs.removeObserver(this, "inner-window-destroyed", false);
|
Services.obs.removeObserver(this, "inner-window-destroyed");
|
||||||
this._knownWindowIDs.clear();
|
this._knownWindowIDs.clear();
|
||||||
this._knownWindowIDs = null;
|
this._knownWindowIDs = null;
|
||||||
},
|
},
|
||||||
|
@ -166,7 +166,7 @@ CustomizedReload.prototype = {
|
|||||||
this.docShell.removeProgressListener(this);
|
this.docShell.removeProgressListener(this);
|
||||||
|
|
||||||
if (this.injectedScript) {
|
if (this.injectedScript) {
|
||||||
Services.obs.removeObserver(this, "document-element-inserted", false);
|
Services.obs.removeObserver(this, "document-element-inserted");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the customized user agent.
|
// Reset the customized user agent.
|
||||||
|
@ -302,7 +302,7 @@ protocol.ActorClassWithSpec(serviceWorkerRegistrationSpec, {
|
|||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
protocol.Actor.prototype.destroy.call(this);
|
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.removeListener(this);
|
||||||
this._registration = null;
|
this._registration = null;
|
||||||
if (this._pushSubscriptionActor) {
|
if (this._pushSubscriptionActor) {
|
||||||
|
@ -92,7 +92,7 @@ function test_lazy_api() {
|
|||||||
do_check_true(isActorLoaded);
|
do_check_true(isActorLoaded);
|
||||||
do_check_true(isActorInstanciated);
|
do_check_true(isActorInstanciated);
|
||||||
|
|
||||||
Services.obs.removeObserver(onActorEvent, "actor", false);
|
Services.obs.removeObserver(onActorEvent, "actor");
|
||||||
client.close().then(() => run_next_test());
|
client.close().then(() => run_next_test());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,9 +38,9 @@ ContentObserver.prototype = {
|
|||||||
*/
|
*/
|
||||||
stopListening: function () {
|
stopListening: function () {
|
||||||
Services.obs.removeObserver(
|
Services.obs.removeObserver(
|
||||||
this._onContentGlobalCreated, "content-document-global-created", false);
|
this._onContentGlobalCreated, "content-document-global-created");
|
||||||
Services.obs.removeObserver(
|
Services.obs.removeObserver(
|
||||||
this._onInnerWindowDestroyed, "inner-window-destroyed", false);
|
this._onInnerWindowDestroyed, "inner-window-destroyed");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1489,7 +1489,7 @@ NetworkMonitor.prototype = {
|
|||||||
Services.obs.removeObserver(this._httpResponseExaminer,
|
Services.obs.removeObserver(this._httpResponseExaminer,
|
||||||
"http-on-examine-cached-response");
|
"http-on-examine-cached-response");
|
||||||
Services.obs.removeObserver(this._httpModifyExaminer,
|
Services.obs.removeObserver(this._httpModifyExaminer,
|
||||||
"http-on-modify-request", false);
|
"http-on-modify-request");
|
||||||
}
|
}
|
||||||
|
|
||||||
Services.obs.removeObserver(this._serviceWorkerRequest,
|
Services.obs.removeObserver(this._serviceWorkerRequest,
|
||||||
|
@ -122,7 +122,7 @@ var ServerLoggerMonitor = {
|
|||||||
if (!size) {
|
if (!size) {
|
||||||
trace.log("ServerLoggerMonitor.onDetachChild; Remove HTTP Observer");
|
trace.log("ServerLoggerMonitor.onDetachChild; Remove HTTP Observer");
|
||||||
Services.obs.removeObserver(this.onExamineResponse,
|
Services.obs.removeObserver(this.onExamineResponse,
|
||||||
"http-on-examine-response", false);
|
"http-on-examine-response");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ var ServerLoggingListener = Class({
|
|||||||
this.detachParentProcess();
|
this.detachParentProcess();
|
||||||
} else {
|
} else {
|
||||||
Services.obs.removeObserver(this.onExamineResponse,
|
Services.obs.removeObserver(this.onExamineResponse,
|
||||||
"http-on-examine-response", false);
|
"http-on-examine-response");
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -347,6 +347,34 @@ KeyframeEffectReadOnly::CompositeValue(
|
|||||||
return StyleAnimationValue();
|
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
|
StyleAnimationValue
|
||||||
KeyframeEffectReadOnly::CompositeValue(
|
KeyframeEffectReadOnly::CompositeValue(
|
||||||
nsCSSPropertyID aProperty,
|
nsCSSPropertyID aProperty,
|
||||||
@ -374,23 +402,7 @@ KeyframeEffectReadOnly::CompositeValue(
|
|||||||
aCompositeOperation == CompositeOperation::Add,
|
aCompositeOperation == CompositeOperation::Add,
|
||||||
"InputValue should be null only if additive composite");
|
"InputValue should be null only if additive composite");
|
||||||
|
|
||||||
if (aAnimationRule->HasValue(aProperty)) {
|
result = GetUnderlyingStyle(aProperty, aAnimationRule);
|
||||||
// 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 CompositeValue(aProperty,
|
return CompositeValue(aProperty,
|
||||||
aValueToComposite,
|
aValueToComposite,
|
||||||
@ -533,14 +545,17 @@ KeyframeEffectReadOnly::ComposeStyle(
|
|||||||
prop.mSegments.LastElement();
|
prop.mSegments.LastElement();
|
||||||
// FIXME: Bug 1293492: Add a utility function to calculate both of
|
// FIXME: Bug 1293492: Add a utility function to calculate both of
|
||||||
// below StyleAnimationValues.
|
// below StyleAnimationValues.
|
||||||
|
StyleAnimationValue lastValue = lastSegment.mToValue.IsNull()
|
||||||
|
? GetUnderlyingStyle(prop.mProperty, aStyleRule)
|
||||||
|
: lastSegment.mToValue;
|
||||||
fromValue =
|
fromValue =
|
||||||
StyleAnimationValue::Accumulate(prop.mProperty,
|
StyleAnimationValue::Accumulate(prop.mProperty,
|
||||||
lastSegment.mToValue,
|
lastValue,
|
||||||
Move(fromValue),
|
Move(fromValue),
|
||||||
computedTiming.mCurrentIteration);
|
computedTiming.mCurrentIteration);
|
||||||
toValue =
|
toValue =
|
||||||
StyleAnimationValue::Accumulate(prop.mProperty,
|
StyleAnimationValue::Accumulate(prop.mProperty,
|
||||||
lastSegment.mToValue,
|
lastValue,
|
||||||
Move(toValue),
|
Move(toValue),
|
||||||
computedTiming.mCurrentIteration);
|
computedTiming.mCurrentIteration);
|
||||||
}
|
}
|
||||||
|
@ -426,6 +426,11 @@ protected:
|
|||||||
const StyleAnimationValue& aValueToComposite,
|
const StyleAnimationValue& aValueToComposite,
|
||||||
CompositeOperation aCompositeOperation);
|
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
|
// Set a bit in mNeedsBaseStyleSet if |aProperty| can be run on the
|
||||||
// compositor.
|
// compositor.
|
||||||
void SetNeedsBaseStyle(nsCSSPropertyID aProperty);
|
void SetNeedsBaseStyle(nsCSSPropertyID aProperty);
|
||||||
|
18
dom/animation/test/crashtests/1325193-1.html
Normal file
18
dom/animation/test/crashtests/1325193-1.html
Normal file
@ -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>
|
@ -16,3 +16,4 @@ 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
|
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-1.html # bug 1324690
|
||||||
asserts-if(stylo,0-5) pref(dom.animations-api.core.enabled,true) load 1323114-2.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
|
||||||
|
@ -490,6 +490,26 @@ promise_test(t => {
|
|||||||
}, 'Transform value for animation with no keyframe at offset 0 and with ' +
|
}, 'Transform value for animation with no keyframe at offset 0 and with ' +
|
||||||
'positive delay');
|
'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();
|
done();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -85,10 +85,24 @@ test(t => {
|
|||||||
// (200px + 200px) * 0.5
|
// (200px + 200px) * 0.5
|
||||||
assert_equals(getComputedStyle(div).marginLeft, '200px',
|
assert_equals(getComputedStyle(div).marginLeft, '200px',
|
||||||
'The margin-left value at 50% should be additive value of ' +
|
'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 ' +
|
}, 'margin-left value at 50% for an animation with no keyframe at offset 1 ' +
|
||||||
'is that of transition');
|
'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();
|
done();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -2001,8 +2001,17 @@ Element::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
DeclarationBlock*
|
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Get the inline style declaration, if any, for this element.
|
* 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
|
* Set the inline style declaration for this element. This will send
|
||||||
|
@ -6748,8 +6748,6 @@ nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
|
|||||||
jsapi.Init();
|
jsapi.Init();
|
||||||
JSContext* cx = jsapi.cx();
|
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
|
// We can use the junk scope here, because we're just using it for
|
||||||
// regexp evaluation, not actual script execution.
|
// regexp evaluation, not actual script execution.
|
||||||
JSAutoCompartment ac(cx, xpc::UnprivilegedJunkScope());
|
JSAutoCompartment ac(cx, xpc::UnprivilegedJunkScope());
|
||||||
|
@ -4026,7 +4026,10 @@ nsDOMWindowUtils::ForceUseCounterFlush(nsIDOMNode *aNode)
|
|||||||
mozilla::css::ImageLoader* loader = doc->StyleImageLoader();
|
mozilla::css::ImageLoader* loader = doc->StyleImageLoader();
|
||||||
loader->FlushUseCounters();
|
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;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12304,8 +12304,17 @@ MightBeAboutOrChromeScheme(nsIURI* aURI)
|
|||||||
return isAbout || isChrome;
|
return isAbout || isChrome;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
ReportExternalResourceUseCounters(nsIDocument* aDocument, void* aData)
|
||||||
|
{
|
||||||
|
const auto reportKind
|
||||||
|
= nsDocument::UseCounterReportKind::eIncludeExternalResources;
|
||||||
|
static_cast<nsDocument*>(aDocument)->ReportUseCounters(reportKind);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsDocument::ReportUseCounters()
|
nsDocument::ReportUseCounters(UseCounterReportKind aKind)
|
||||||
{
|
{
|
||||||
static const bool sDebugUseCounters = false;
|
static const bool sDebugUseCounters = false;
|
||||||
if (mReportedUseCounters) {
|
if (mReportedUseCounters) {
|
||||||
@ -12314,6 +12323,10 @@ nsDocument::ReportUseCounters()
|
|||||||
|
|
||||||
mReportedUseCounters = true;
|
mReportedUseCounters = true;
|
||||||
|
|
||||||
|
if (aKind == UseCounterReportKind::eIncludeExternalResources) {
|
||||||
|
EnumerateExternalResources(ReportExternalResourceUseCounters, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
if (Telemetry::HistogramUseCounterCount > 0 &&
|
if (Telemetry::HistogramUseCounterCount > 0 &&
|
||||||
(IsContentDocument() || IsResourceDoc())) {
|
(IsContentDocument() || IsResourceDoc())) {
|
||||||
nsCOMPtr<nsIURI> uri;
|
nsCOMPtr<nsIURI> uri;
|
||||||
|
@ -774,7 +774,19 @@ public:
|
|||||||
|
|
||||||
virtual nsViewportInfo GetViewportInfo(const mozilla::ScreenIntSize& aDisplaySize) override;
|
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(
|
virtual void AddIntersectionObserver(
|
||||||
mozilla::dom::DOMIntersectionObserver* aObserver) override;
|
mozilla::dom::DOMIntersectionObserver* aObserver) override;
|
||||||
|
@ -93,21 +93,6 @@ nsStyledElement::SetInlineStyleDeclaration(DeclarationBlock* aDeclaration,
|
|||||||
aNotify, kDontCallAfterSetAttr);
|
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
|
// Others and helpers
|
||||||
|
|
||||||
|
@ -44,7 +44,6 @@ public:
|
|||||||
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
|
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
|
||||||
|
|
||||||
// Element interface methods
|
// Element interface methods
|
||||||
virtual mozilla::DeclarationBlock* GetInlineStyleDeclaration() override;
|
|
||||||
virtual nsresult SetInlineStyleDeclaration(mozilla::DeclarationBlock* aDeclaration,
|
virtual nsresult SetInlineStyleDeclaration(mozilla::DeclarationBlock* aDeclaration,
|
||||||
const nsAString* aSerialized,
|
const nsAString* aSerialized,
|
||||||
bool aNotify) override;
|
bool aNotify) override;
|
||||||
|
@ -734,6 +734,8 @@ skip-if = toolkit == 'android'
|
|||||||
[test_root_iframe.html]
|
[test_root_iframe.html]
|
||||||
[test_screen_orientation.html]
|
[test_screen_orientation.html]
|
||||||
[test_script_loader_crossorigin_data_url.html]
|
[test_script_loader_crossorigin_data_url.html]
|
||||||
|
[test_selection_with_anon_trees.html]
|
||||||
|
skip-if = toolkit == 'android'
|
||||||
[test_setInterval_uncatchable_exception.html]
|
[test_setInterval_uncatchable_exception.html]
|
||||||
skip-if = debug == false
|
skip-if = debug == false
|
||||||
[test_settimeout_extra_arguments.html]
|
[test_settimeout_extra_arguments.html]
|
||||||
|
445
dom/base/test/test_selection_with_anon_trees.html
Normal file
445
dom/base/test/test_selection_with_anon_trees.html
Normal file
@ -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>
|
@ -1079,6 +1079,7 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
|
|||||||
MakeContextCurrent();
|
MakeContextCurrent();
|
||||||
|
|
||||||
gl->fViewport(0, 0, mWidth, mHeight);
|
gl->fViewport(0, 0, mWidth, mHeight);
|
||||||
|
mViewportX = mViewportY = 0;
|
||||||
mViewportWidth = mWidth;
|
mViewportWidth = mWidth;
|
||||||
mViewportHeight = mHeight;
|
mViewportHeight = mHeight;
|
||||||
|
|
||||||
|
@ -1447,6 +1447,8 @@ protected:
|
|||||||
uint32_t mImplMaxColorAttachments;
|
uint32_t mImplMaxColorAttachments;
|
||||||
uint32_t mImplMaxDrawBuffers;
|
uint32_t mImplMaxDrawBuffers;
|
||||||
|
|
||||||
|
uint32_t mImplMaxViewportDims[2];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GLenum LastColorAttachmentEnum() const {
|
GLenum LastColorAttachmentEnum() const {
|
||||||
return LOCAL_GL_COLOR_ATTACHMENT0 + mImplMaxColorAttachments - 1;
|
return LOCAL_GL_COLOR_ATTACHMENT0 + mImplMaxColorAttachments - 1;
|
||||||
|
@ -2140,6 +2140,9 @@ WebGLContext::Viewport(GLint x, GLint y, GLsizei width, GLsizei height)
|
|||||||
if (width < 0 || height < 0)
|
if (width < 0 || height < 0)
|
||||||
return ErrorInvalidValue("viewport: negative size");
|
return ErrorInvalidValue("viewport: negative size");
|
||||||
|
|
||||||
|
width = std::min(width, (GLsizei)mImplMaxViewportDims[0]);
|
||||||
|
height = std::min(height, (GLsizei)mImplMaxViewportDims[1]);
|
||||||
|
|
||||||
MakeContextCurrent();
|
MakeContextCurrent();
|
||||||
gl->fViewport(x, y, width, height);
|
gl->fViewport(x, y, width, height);
|
||||||
|
|
||||||
|
@ -569,6 +569,8 @@ WebGLContext::InitAndValidateGL(FailureReason* const out_failReason)
|
|||||||
mBound2DArrayTextures.SetLength(mGLMaxTextureUnits);
|
mBound2DArrayTextures.SetLength(mGLMaxTextureUnits);
|
||||||
mBoundSamplers.SetLength(mGLMaxTextureUnits);
|
mBoundSamplers.SetLength(mGLMaxTextureUnits);
|
||||||
|
|
||||||
|
gl->fGetIntegerv(LOCAL_GL_MAX_VIEWPORT_DIMS, (GLint*)mImplMaxViewportDims);
|
||||||
|
|
||||||
////////////////
|
////////////////
|
||||||
|
|
||||||
if (MinCapabilityMode()) {
|
if (MinCapabilityMode()) {
|
||||||
|
@ -317,7 +317,6 @@ HangMonitorChild::InterruptCallback()
|
|||||||
RefPtr<TabChild> tabChild = TabChild::FindTabChild(forcePaintTab);
|
RefPtr<TabChild> tabChild = TabChild::FindTabChild(forcePaintTab);
|
||||||
if (tabChild) {
|
if (tabChild) {
|
||||||
JS::AutoAssertNoGC nogc(mContext);
|
JS::AutoAssertNoGC nogc(mContext);
|
||||||
JS::AutoAssertOnBarrier nobarrier(mContext);
|
|
||||||
tabChild->ForcePaint(forcePaintEpoch);
|
tabChild->ForcePaint(forcePaintEpoch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -401,7 +400,6 @@ HangMonitorChild::RecvForcePaint(const TabId& aTabId, const uint64_t& aLayerObse
|
|||||||
}
|
}
|
||||||
|
|
||||||
JS_RequestInterruptCallback(mContext);
|
JS_RequestInterruptCallback(mContext);
|
||||||
JS::RequestGCInterruptCallback(mContext);
|
|
||||||
|
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
@ -1198,7 +1196,6 @@ mozilla::CreateHangMonitorChild(Endpoint<PProcessHangMonitorChild>&& aEndpoint)
|
|||||||
|
|
||||||
JSContext* cx = danger::GetJSContext();
|
JSContext* cx = danger::GetJSContext();
|
||||||
JS_AddInterruptCallback(cx, InterruptCallback);
|
JS_AddInterruptCallback(cx, InterruptCallback);
|
||||||
JS::AddGCInterruptCallback(cx, InterruptCallback);
|
|
||||||
|
|
||||||
ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
|
ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
|
||||||
auto* child = new HangMonitorChild(monitor);
|
auto* child = new HangMonitorChild(monitor);
|
||||||
|
@ -219,12 +219,14 @@ VideoDecoderManagerChild::Readback(const SurfaceDescriptorGPUVideo& aSD)
|
|||||||
|
|
||||||
RefPtr<VideoDecoderManagerChild> ref = this;
|
RefPtr<VideoDecoderManagerChild> ref = this;
|
||||||
SurfaceDescriptor sd;
|
SurfaceDescriptor sd;
|
||||||
sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([&]() {
|
if (NS_FAILED(sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([&]() {
|
||||||
AutoCompleteTask complete(&task);
|
AutoCompleteTask complete(&task);
|
||||||
if (ref->CanSend()) {
|
if (ref->CanSend()) {
|
||||||
ref->SendReadback(aSD, &sd);
|
ref->SendReadback(aSD, &sd);
|
||||||
}
|
}
|
||||||
}), NS_DISPATCH_NORMAL);
|
}), NS_DISPATCH_NORMAL))) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
task.Wait();
|
task.Wait();
|
||||||
|
|
||||||
|
@ -9,6 +9,6 @@ load 570884.html
|
|||||||
# Plugin arch is going to change anyway with OOP content so skipping
|
# Plugin arch is going to change anyway with OOP content so skipping
|
||||||
# this test for now is OK.
|
# this test for now is OK.
|
||||||
skip-if(!haveTestPlugin||http.platform!="X11") load 598862.html
|
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
|
load 752340.html
|
||||||
asserts-if(stylo,1) load 843086.xhtml # bug 1324647
|
asserts-if(stylo,1) load 843086.xhtml # bug 1324647
|
||||||
|
@ -32,7 +32,7 @@ var isParent = Cc['@mozilla.org/xre/runtime;1']
|
|||||||
|
|
||||||
// Stop and clean up after the PushService.
|
// Stop and clean up after the PushService.
|
||||||
Services.obs.addObserver(function observe(subject, topic, data) {
|
Services.obs.addObserver(function observe(subject, topic, data) {
|
||||||
Services.obs.removeObserver(observe, topic, false);
|
Services.obs.removeObserver(observe, topic);
|
||||||
serviceExports.PushService.uninit();
|
serviceExports.PushService.uninit();
|
||||||
// Occasionally, `profile-change-teardown` and `xpcom-shutdown` will fire
|
// Occasionally, `profile-change-teardown` and `xpcom-shutdown` will fire
|
||||||
// before the PushService and AlarmService finish writing to IndexedDB. This
|
// before the PushService and AlarmService finish writing to IndexedDB. This
|
||||||
@ -92,7 +92,7 @@ function promiseObserverNotification(topic, matchFunc) {
|
|||||||
if (!matches) {
|
if (!matches) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Services.obs.removeObserver(observe, topic, false);
|
Services.obs.removeObserver(observe, topic);
|
||||||
resolve({subject, data});
|
resolve({subject, data});
|
||||||
}, topic, false);
|
}, topic, false);
|
||||||
});
|
});
|
||||||
|
@ -75,7 +75,7 @@ load 898915-1.svg
|
|||||||
load 1035248-1.svg
|
load 1035248-1.svg
|
||||||
load 1035248-2.svg
|
load 1035248-2.svg
|
||||||
load 1244898-1.xhtml
|
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 1267272-1.svg
|
||||||
load 1282985-1.svg
|
load 1282985-1.svg
|
||||||
# Disabled for now due to it taking a very long time to run - bug 1259356
|
# Disabled for now due to it taking a very long time to run - bug 1259356
|
||||||
|
@ -15,7 +15,7 @@ const ConsoleObserver = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
uninit() {
|
uninit() {
|
||||||
Services.obs.removeObserver(this, "console-api-log-event", false);
|
Services.obs.removeObserver(this, "console-api-log-event");
|
||||||
},
|
},
|
||||||
|
|
||||||
observe(aSubject, aTopic, aData) {
|
observe(aSubject, aTopic, aData) {
|
||||||
|
@ -2,7 +2,7 @@ load 336081-1.xhtml
|
|||||||
asserts-if(stylo,2-3) load 336104.html # bug 1324669
|
asserts-if(stylo,2-3) load 336104.html # bug 1324669
|
||||||
load 382527-1.html
|
load 382527-1.html
|
||||||
load 382778-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 403965-1.xhtml
|
||||||
load 407074-1.html
|
load 407074-1.html
|
||||||
load 407079-1.html
|
load 407079-1.html
|
||||||
@ -34,7 +34,7 @@ load 612565-1.html
|
|||||||
load 615015-1.html
|
load 615015-1.html
|
||||||
load 615450-1.html
|
load 615450-1.html
|
||||||
load 633709.xhtml
|
load 633709.xhtml
|
||||||
asserts-if(stylo,1) load 636074-1.html # bug 1324663
|
load 636074-1.html
|
||||||
load 639736-1.xhtml
|
load 639736-1.xhtml
|
||||||
load 643786-1.html
|
load 643786-1.html
|
||||||
load 650572-1.html
|
load 650572-1.html
|
||||||
@ -62,12 +62,12 @@ load 772282.html
|
|||||||
load 776323.html
|
load 776323.html
|
||||||
needs-focus load 793866.html
|
needs-focus load 793866.html
|
||||||
load 1057677.html
|
load 1057677.html
|
||||||
asserts-if(stylo,1) needs-focus load 1128787.html # bug 1324663
|
needs-focus load 1128787.html
|
||||||
load 1134545.html
|
load 1134545.html
|
||||||
load 1158452.html
|
load 1158452.html
|
||||||
load 1158651.html
|
load 1158651.html
|
||||||
load 1244894.xhtml
|
load 1244894.xhtml
|
||||||
load 1264921.html
|
load 1264921.html
|
||||||
load 1272490.html
|
load 1272490.html
|
||||||
asserts-if(stylo,1-24) load 1317704.html # bug 1324663
|
load 1317704.html
|
||||||
load 1317718.html
|
load 1317718.html
|
||||||
|
@ -120,7 +120,7 @@ function flushApzRepaints(aCallback, aWindow = window) {
|
|||||||
throw "A callback must be provided!";
|
throw "A callback must be provided!";
|
||||||
}
|
}
|
||||||
var repaintDone = function() {
|
var repaintDone = function() {
|
||||||
SpecialPowers.Services.obs.removeObserver(repaintDone, "apz-repaints-flushed", false);
|
SpecialPowers.Services.obs.removeObserver(repaintDone, "apz-repaints-flushed");
|
||||||
setTimeout(aCallback, 0);
|
setTimeout(aCallback, 0);
|
||||||
};
|
};
|
||||||
SpecialPowers.Services.obs.addObserver(repaintDone, "apz-repaints-flushed", false);
|
SpecialPowers.Services.obs.addObserver(repaintDone, "apz-repaints-flushed", false);
|
||||||
|
@ -613,12 +613,16 @@ SampleValue(float aPortion, const Animation& aAnimation,
|
|||||||
// below StyleAnimationValues.
|
// below StyleAnimationValues.
|
||||||
startValue =
|
startValue =
|
||||||
StyleAnimationValue::Accumulate(aAnimation.property(),
|
StyleAnimationValue::Accumulate(aAnimation.property(),
|
||||||
aLastValue,
|
aLastValue.IsNull()
|
||||||
|
? aUnderlyingValue
|
||||||
|
: aLastValue,
|
||||||
Move(startValue),
|
Move(startValue),
|
||||||
aCurrentIteration);
|
aCurrentIteration);
|
||||||
endValue =
|
endValue =
|
||||||
StyleAnimationValue::Accumulate(aAnimation.property(),
|
StyleAnimationValue::Accumulate(aAnimation.property(),
|
||||||
aLastValue,
|
aLastValue.IsNull()
|
||||||
|
? aUnderlyingValue
|
||||||
|
: aLastValue,
|
||||||
Move(endValue),
|
Move(endValue),
|
||||||
aCurrentIteration);
|
aCurrentIteration);
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,9 @@ DIRS += [
|
|||||||
if CONFIG['MOZ_ENABLE_SKIA']:
|
if CONFIG['MOZ_ENABLE_SKIA']:
|
||||||
DIRS += ['skia']
|
DIRS += ['skia']
|
||||||
|
|
||||||
|
if CONFIG['MOZ_ENABLE_SKIA_PDF_SFNTLY'] and CONFIG['ENABLE_INTL_API']:
|
||||||
|
DIRS += ['sfntly/cpp/src']
|
||||||
|
|
||||||
if CONFIG['ENABLE_TESTS']:
|
if CONFIG['ENABLE_TESTS']:
|
||||||
DIRS += ['tests/gtest']
|
DIRS += ['tests/gtest']
|
||||||
|
|
||||||
|
5
gfx/sfntly/README.mozilla
Normal file
5
gfx/sfntly/README.mozilla
Normal file
@ -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).
|
73
gfx/sfntly/cpp/src/moz.build
Normal file
73
gfx/sfntly/cpp/src/moz.build
Normal file
@ -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
|
131
gfx/sfntly/cpp/src/sample/chromium/chrome_subsetter.cc
Normal file
131
gfx/sfntly/cpp/src/sample/chromium/chrome_subsetter.cc
Normal file
@ -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;
|
||||||
|
}
|
60
gfx/sfntly/cpp/src/sample/chromium/font_subsetter.cc
Normal file
60
gfx/sfntly/cpp/src/sample/chromium/font_subsetter.cc
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
|
75
gfx/sfntly/cpp/src/sample/chromium/font_subsetter.h
Normal file
75
gfx/sfntly/cpp/src/sample/chromium/font_subsetter.h
Normal file
@ -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_
|
803
gfx/sfntly/cpp/src/sample/chromium/subsetter_impl.cc
Normal file
803
gfx/sfntly/cpp/src/sample/chromium/subsetter_impl.cc
Normal file
@ -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
|
77
gfx/sfntly/cpp/src/sample/chromium/subsetter_impl.h
Normal file
77
gfx/sfntly/cpp/src/sample/chromium/subsetter_impl.h
Normal file
@ -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_
|
44
gfx/sfntly/cpp/src/sample/subsetter/main.cc
Normal file
44
gfx/sfntly/cpp/src/sample/subsetter/main.cc
Normal file
@ -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;
|
||||||
|
}
|
98
gfx/sfntly/cpp/src/sample/subsetter/subset_util.cc
Normal file
98
gfx/sfntly/cpp/src/sample/subsetter/subset_util.cc
Normal file
@ -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
|
32
gfx/sfntly/cpp/src/sample/subsetter/subset_util.h
Normal file
32
gfx/sfntly/cpp/src/sample/subsetter/subset_util.h
Normal file
@ -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_
|
53
gfx/sfntly/cpp/src/sample/subtly/character_predicate.cc
Normal file
53
gfx/sfntly/cpp/src/sample/subtly/character_predicate.cc
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
68
gfx/sfntly/cpp/src/sample/subtly/character_predicate.h
Normal file
68
gfx/sfntly/cpp/src/sample/subtly/character_predicate.h
Normal file
@ -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_
|
64
gfx/sfntly/cpp/src/sample/subtly/debug_main.cc
Normal file
64
gfx/sfntly/cpp/src/sample/subtly/debug_main.cc
Normal file
@ -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;
|
||||||
|
}
|
229
gfx/sfntly/cpp/src/sample/subtly/font_assembler.cc
Normal file
229
gfx/sfntly/cpp/src/sample/subtly/font_assembler.cc
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
67
gfx/sfntly/cpp/src/sample/subtly/font_assembler.h
Normal file
67
gfx/sfntly/cpp/src/sample/subtly/font_assembler.h
Normal file
@ -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_
|
256
gfx/sfntly/cpp/src/sample/subtly/font_info.cc
Normal file
256
gfx/sfntly/cpp/src/sample/subtly/font_info.cc
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
128
gfx/sfntly/cpp/src/sample/subtly/font_info.h
Normal file
128
gfx/sfntly/cpp/src/sample/subtly/font_info.h
Normal file
@ -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_
|
87
gfx/sfntly/cpp/src/sample/subtly/merger.cc
Normal file
87
gfx/sfntly/cpp/src/sample/subtly/merger.cc
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
45
gfx/sfntly/cpp/src/sample/subtly/merger.h
Normal file
45
gfx/sfntly/cpp/src/sample/subtly/merger.h
Normal file
@ -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_
|
69
gfx/sfntly/cpp/src/sample/subtly/merger_main.cc
Normal file
69
gfx/sfntly/cpp/src/sample/subtly/merger_main.cc
Normal file
@ -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;
|
||||||
|
}
|
82
gfx/sfntly/cpp/src/sample/subtly/stats.cc
Normal file
82
gfx/sfntly/cpp/src/sample/subtly/stats.cc
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user