зеркало из https://github.com/mozilla/gecko-dev.git
Bug 886041 - Make the font inspector remotable; r=bgrins
This commit is contained in:
Родитель
5030d409ca
Коммит
4ad91dab46
|
@ -15,3 +15,11 @@
|
||||||
.font.is-local .font-is-local {
|
.font.is-local .font-is-local {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.font-format::before {
|
||||||
|
content: "(";
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-format::after {
|
||||||
|
content: ")";
|
||||||
|
}
|
||||||
|
|
|
@ -9,9 +9,16 @@
|
||||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||||
const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||||
|
"resource://gre/modules/Task.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "console",
|
||||||
|
"resource://gre/modules/devtools/Console.jsm");
|
||||||
|
|
||||||
function FontInspector(inspector, window)
|
function FontInspector(inspector, window)
|
||||||
{
|
{
|
||||||
this.inspector = inspector;
|
this.inspector = inspector;
|
||||||
|
this.pageStyle = this.inspector.pageStyle;
|
||||||
this.chromeDoc = window.document;
|
this.chromeDoc = window.document;
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
@ -51,7 +58,6 @@ FontInspector.prototype = {
|
||||||
*/
|
*/
|
||||||
onNewNode: function FI_onNewNode() {
|
onNewNode: function FI_onNewNode() {
|
||||||
if (this.isActive() &&
|
if (this.isActive() &&
|
||||||
this.inspector.selection.isLocal() &&
|
|
||||||
this.inspector.selection.isConnected() &&
|
this.inspector.selection.isConnected() &&
|
||||||
this.inspector.selection.isElementNode()) {
|
this.inspector.selection.isElementNode()) {
|
||||||
this.undim();
|
this.undim();
|
||||||
|
@ -76,119 +82,92 @@ FontInspector.prototype = {
|
||||||
this.chromeDoc.body.classList.remove("dim");
|
this.chromeDoc.body.classList.remove("dim");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve all the font related info we have for the selected
|
* Retrieve all the font info for the selected node and display it.
|
||||||
* node and display them.
|
*/
|
||||||
*/
|
update: Task.async(function*() {
|
||||||
update: function FI_update() {
|
let node = this.inspector.selection.nodeFront;
|
||||||
if (!this.isActive() ||
|
|
||||||
|
if (!node ||
|
||||||
|
!this.isActive() ||
|
||||||
!this.inspector.selection.isConnected() ||
|
!this.inspector.selection.isConnected() ||
|
||||||
!this.inspector.selection.isElementNode() ||
|
!this.inspector.selection.isElementNode() ||
|
||||||
this.chromeDoc.body.classList.contains("dim")) {
|
this.chromeDoc.body.classList.contains("dim")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let node = this.inspector.selection.node;
|
|
||||||
let contentDocument = node.ownerDocument;
|
|
||||||
|
|
||||||
// We don't get fonts for a node, but for a range
|
|
||||||
let rng = contentDocument.createRange();
|
|
||||||
rng.selectNodeContents(node);
|
|
||||||
let fonts = DOMUtils.getUsedFontFaces(rng);
|
|
||||||
let fontsArray = [];
|
|
||||||
for (let i = 0; i < fonts.length; i++) {
|
|
||||||
fontsArray.push(fonts.item(i));
|
|
||||||
}
|
|
||||||
fontsArray = fontsArray.sort(function(a, b) {
|
|
||||||
return a.srcIndex < b.srcIndex;
|
|
||||||
});
|
|
||||||
this.chromeDoc.querySelector("#all-fonts").innerHTML = "";
|
this.chromeDoc.querySelector("#all-fonts").innerHTML = "";
|
||||||
for (let f of fontsArray) {
|
|
||||||
this.render(f, contentDocument);
|
let fillStyle = (Services.prefs.getCharPref("devtools.theme") == "light") ?
|
||||||
|
"black" : "white";
|
||||||
|
let options = {
|
||||||
|
includePreviews: true,
|
||||||
|
previewFillStyle: fillStyle
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
let fonts = yield this.pageStyle.getUsedFontFaces(node, options)
|
||||||
|
.then(null, console.error);
|
||||||
|
if (!fonts) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let font of fonts) {
|
||||||
|
font.previewUrl = yield font.preview.data.string();
|
||||||
|
}
|
||||||
|
|
||||||
|
// in case we've been destroyed in the meantime
|
||||||
|
if (!this.chromeDoc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear again in case an update got in right before us
|
||||||
|
this.chromeDoc.querySelector("#all-fonts").innerHTML = "";
|
||||||
|
|
||||||
|
for (let font of fonts) {
|
||||||
|
this.render(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inspector.emit("fontinspector-updated");
|
||||||
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the information of one font.
|
* Display the information of one font.
|
||||||
*/
|
*/
|
||||||
render: function FI_render(font, document) {
|
render: function FI_render(font) {
|
||||||
let s = this.chromeDoc.querySelector("#template > section");
|
let s = this.chromeDoc.querySelector("#template > section");
|
||||||
s = s.cloneNode(true);
|
s = s.cloneNode(true);
|
||||||
|
|
||||||
s.querySelector(".font-name").textContent = font.name;
|
s.querySelector(".font-name").textContent = font.name;
|
||||||
s.querySelector(".font-css-name").textContent = font.CSSFamilyName;
|
s.querySelector(".font-css-name").textContent = font.CSSFamilyName;
|
||||||
s.querySelector(".font-format").textContent = font.format;
|
|
||||||
|
|
||||||
if (font.srcIndex == -1) {
|
if (font.URI) {
|
||||||
s.classList.add("is-local");
|
|
||||||
} else {
|
|
||||||
s.classList.add("is-remote");
|
s.classList.add("is-remote");
|
||||||
|
} else {
|
||||||
|
s.classList.add("is-local");
|
||||||
|
}
|
||||||
|
|
||||||
|
let formatElem = s.querySelector(".font-format");
|
||||||
|
if (font.format) {
|
||||||
|
formatElem.textContent = font.format;
|
||||||
|
} else {
|
||||||
|
formatElem.hidden = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
s.querySelector(".font-url").value = font.URI;
|
s.querySelector(".font-url").value = font.URI;
|
||||||
|
|
||||||
let iframe = s.querySelector(".font-preview");
|
|
||||||
if (font.rule) {
|
if (font.rule) {
|
||||||
// This is the @font-face{…} code.
|
// This is the @font-face{…} code.
|
||||||
let cssText = font.rule.style.parentRule.cssText;
|
let cssText = font.ruleText;
|
||||||
|
|
||||||
s.classList.add("has-code");
|
s.classList.add("has-code");
|
||||||
s.querySelector(".font-css-code").textContent = cssText;
|
s.querySelector(".font-css-code").textContent = cssText;
|
||||||
|
|
||||||
// We guess the base URL of the stylesheet to make
|
|
||||||
// sure the font will be accessible in the preview.
|
|
||||||
// If the font-face is in an inline <style>, we get
|
|
||||||
// the location of the page.
|
|
||||||
let origin = font.rule.style.parentRule.parentStyleSheet.href;
|
|
||||||
if (!origin) { // Inline stylesheet
|
|
||||||
origin = document.location.href;
|
|
||||||
}
|
|
||||||
// We remove the last part of the URL to get a correct base.
|
|
||||||
let base = origin.replace(/\/[^\/]*$/,"/")
|
|
||||||
|
|
||||||
// From all this information, we build a preview.
|
|
||||||
this.buildPreview(iframe, font.CSSFamilyName, cssText, base);
|
|
||||||
} else {
|
|
||||||
this.buildPreview(iframe, font.CSSFamilyName, "", "");
|
|
||||||
}
|
}
|
||||||
|
let preview = s.querySelector(".font-preview");
|
||||||
|
preview.src = font.previewUrl;
|
||||||
|
|
||||||
this.chromeDoc.querySelector("#all-fonts").appendChild(s);
|
this.chromeDoc.querySelector("#all-fonts").appendChild(s);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Show a preview of the font in an iframe.
|
|
||||||
*/
|
|
||||||
buildPreview: function FI_buildPreview(iframe, name, cssCode, base) {
|
|
||||||
/* The HTML code of the preview is:
|
|
||||||
* <!DOCTYPE HTML>
|
|
||||||
* <head>
|
|
||||||
* <base href="{base}"></base>
|
|
||||||
* </head>
|
|
||||||
* <style>
|
|
||||||
* p {font-family: {name};}
|
|
||||||
* * {font-size: 40px;line-height:60px;padding:0 10px;margin:0};
|
|
||||||
* </style>
|
|
||||||
* <p contenteditable spellcheck='false'>Abc</p>
|
|
||||||
*/
|
|
||||||
let extraCSS = "* {padding:0;margin:0}";
|
|
||||||
extraCSS += ".theme-dark {color: white}";
|
|
||||||
extraCSS += "p {font-size: 40px;line-height:60px;padding:0 10px;margin:0;}";
|
|
||||||
cssCode += extraCSS;
|
|
||||||
let src = "data:text/html;charset=utf-8,<!DOCTYPE HTML><head><base></base></head><style></style><p contenteditable spellcheck='false'>Abc</p>";
|
|
||||||
iframe.addEventListener("load", function onload() {
|
|
||||||
iframe.removeEventListener("load", onload, true);
|
|
||||||
let doc = iframe.contentWindow.document;
|
|
||||||
// We could have done that earlier, but we want to avoid any URL-encoding
|
|
||||||
// nightmare.
|
|
||||||
doc.querySelector("base").href = base;
|
|
||||||
doc.querySelector("style").textContent = cssCode;
|
|
||||||
doc.querySelector("p").style.fontFamily = name;
|
|
||||||
// Forward theme
|
|
||||||
doc.documentElement.className = document.documentElement.className;
|
|
||||||
}, true);
|
|
||||||
iframe.src = src;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select the <body> to show all the fonts included in the document.
|
* Select the <body> to show all the fonts included in the document.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -24,14 +24,14 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="template">
|
<div id="template">
|
||||||
<section class="font">
|
<section class="font">
|
||||||
<iframe sandbox="" class="font-preview"></iframe>
|
<img class="font-preview"></img>
|
||||||
<div class="font-info">
|
<div class="font-info">
|
||||||
<h1 class="font-name"></h1>
|
<h1 class="font-name"></h1>
|
||||||
<span class="font-is-local">&system;</span>
|
<span class="font-is-local">&system;</span>
|
||||||
<span class="font-is-remote">&remote;</span>
|
<span class="font-is-remote">&remote;</span>
|
||||||
<p class="font-format-url">
|
<p class="font-format-url">
|
||||||
<input readonly="readonly" class="font-url"></input>
|
<input readonly="readonly" class="font-url"></input>
|
||||||
(<span class="font-format"></span>)
|
<span class="font-format"></span>
|
||||||
</p>
|
</p>
|
||||||
<p class="font-css">&usedAs; "<span class="font-css-name"></span>"</p>
|
<p class="font-css">&usedAs; "<span class="font-css-name"></span>"</p>
|
||||||
<pre class="font-css-code"></pre>
|
<pre class="font-css-code"></pre>
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
@ -1,10 +1,9 @@
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
|
|
||||||
subsuite = devtools
|
subsuite = devtools
|
||||||
support-files =
|
support-files =
|
||||||
browser_font.woff
|
|
||||||
browser_fontinspector.html
|
browser_fontinspector.html
|
||||||
|
ostrich-black.woff
|
||||||
|
ostrich-regular.woff
|
||||||
|
head.js
|
||||||
|
|
||||||
[browser_fontinspector.js]
|
[browser_fontinspector.js]
|
||||||
skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
|
|
||||||
|
|
||||||
|
|
Двоичные данные
browser/devtools/fontinspector/test/browser_font.woff
Двоичные данные
browser/devtools/fontinspector/test/browser_font.woff
Двоичный файл не отображается.
|
@ -3,8 +3,14 @@
|
||||||
<style>
|
<style>
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: bar;
|
font-family: bar;
|
||||||
src: url(bad/font/name.ttf), url(browser_font.woff) format("woff");
|
src: url(bad/font/name.ttf), url(ostrich-regular.woff) format("woff");
|
||||||
}
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: bar;
|
||||||
|
font-weight: 800;
|
||||||
|
src: url(ostrich-black.woff);
|
||||||
|
}
|
||||||
|
|
||||||
body{
|
body{
|
||||||
font-family:Arial;
|
font-family:Arial;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +18,10 @@
|
||||||
font-family:Arial;
|
font-family:Arial;
|
||||||
font-family:bar;
|
font-family:bar;
|
||||||
}
|
}
|
||||||
|
.bold-text {
|
||||||
|
font-family: bar;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -6,125 +6,86 @@ let {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
|
||||||
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||||
let TargetFactory = devtools.TargetFactory;
|
let TargetFactory = devtools.TargetFactory;
|
||||||
|
|
||||||
let DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
let TEST_URI = "http://mochi.test:8888/browser/browser/devtools/fontinspector/test/browser_fontinspector.html";
|
||||||
|
|
||||||
function test() {
|
let view, viewDoc;
|
||||||
waitForExplicitFinish();
|
|
||||||
|
|
||||||
let doc;
|
let test = asyncTest(function*() {
|
||||||
let view;
|
yield loadTab(TEST_URI);
|
||||||
let viewDoc;
|
let {toolbox, inspector} = yield openInspector();
|
||||||
let inspector;
|
|
||||||
|
|
||||||
gDevTools.testing = true;
|
info("Selecting the test node");
|
||||||
SimpleTest.registerCleanupFunction(() => {
|
yield selectNode("body", inspector);
|
||||||
gDevTools.testing = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
gBrowser.selectedTab = gBrowser.addTab();
|
let updated = inspector.once("fontinspector-updated");
|
||||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
inspector.sidebar.select("fontinspector");
|
||||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
yield updated;
|
||||||
doc = content.document;
|
|
||||||
waitForFocus(setupTest, content);
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
content.location = "http://mochi.test:8888/browser/browser/devtools/fontinspector/test/browser_fontinspector.html";
|
info("Font Inspector ready");
|
||||||
|
|
||||||
function setupTest() {
|
view = inspector.sidebar.getWindowForTab("fontinspector");
|
||||||
let rng = doc.createRange();
|
viewDoc = view.document;
|
||||||
rng.selectNode(doc.body);
|
|
||||||
let fonts = DOMUtils.getUsedFontFaces(rng);
|
|
||||||
if (fonts.length != 2) {
|
|
||||||
// Fonts are not loaded yet.
|
|
||||||
// Let try again in a couple of milliseconds (hacky, but
|
|
||||||
// there's not better way to do it. See bug 835247).
|
|
||||||
setTimeout(setupTest, 500);
|
|
||||||
} else {
|
|
||||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
|
||||||
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
|
|
||||||
openFontInspector(toolbox.getCurrentPanel());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function openFontInspector(aInspector) {
|
ok(!!view.fontInspector, "Font inspector document is alive.");
|
||||||
info("Inspector open");
|
|
||||||
inspector = aInspector;
|
|
||||||
|
|
||||||
inspector.selection.setNode(doc.body);
|
yield testBodyFonts(inspector);
|
||||||
inspector.sidebar.select("fontinspector");
|
|
||||||
inspector.sidebar.once("fontinspector-ready", testBodyFonts);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testBodyFonts() {
|
yield testDivFonts(inspector);
|
||||||
info("Font Inspector ready");
|
|
||||||
|
|
||||||
view = inspector.sidebar.getWindowForTab("fontinspector");
|
yield testShowAllFonts(inspector);
|
||||||
viewDoc = view.document;
|
|
||||||
|
|
||||||
ok(!!view.fontInspector, "Font inspector document is alive.");
|
view = viewDoc = null;
|
||||||
|
});
|
||||||
|
|
||||||
let s = viewDoc.querySelectorAll("#all-fonts > section");
|
function* testBodyFonts(inspector) {
|
||||||
is(s.length, 2, "Found 2 fonts");
|
let s = viewDoc.querySelectorAll("#all-fonts > section");
|
||||||
|
is(s.length, 2, "Found 2 fonts");
|
||||||
|
|
||||||
is(s[0].querySelector(".font-name").textContent,
|
// test first web font
|
||||||
"DeLarge Bold", "font 0: Right font name");
|
is(s[0].querySelector(".font-name").textContent,
|
||||||
ok(s[0].classList.contains("is-remote"),
|
"Ostrich Sans Medium", "font 0: Right font name");
|
||||||
"font 0: is remote");
|
ok(s[0].classList.contains("is-remote"),
|
||||||
is(s[0].querySelector(".font-url").value,
|
"font 0: is remote");
|
||||||
"http://mochi.test:8888/browser/browser/devtools/fontinspector/test/browser_font.woff",
|
is(s[0].querySelector(".font-url").value,
|
||||||
"font 0: right url");
|
"http://mochi.test:8888/browser/browser/devtools/fontinspector/test/ostrich-regular.woff",
|
||||||
is(s[0].querySelector(".font-format").textContent,
|
"font 0: right url");
|
||||||
"woff", "font 0: right font format");
|
is(s[0].querySelector(".font-format").textContent,
|
||||||
is(s[0].querySelector(".font-css-name").textContent,
|
"woff", "font 0: right font format");
|
||||||
"bar", "font 0: right css name");
|
is(s[0].querySelector(".font-css-name").textContent,
|
||||||
|
"bar", "font 0: right css name");
|
||||||
|
|
||||||
let font1Name = s[1].querySelector(".font-name").textContent;
|
// test system font
|
||||||
let font1CssName = s[1].querySelector(".font-css-name").textContent;
|
let font2Name = s[1].querySelector(".font-name").textContent;
|
||||||
|
let font2CssName = s[1].querySelector(".font-css-name").textContent;
|
||||||
|
|
||||||
// On Linux test machines, the Arial font doesn't exist.
|
// On Linux test machines, the Arial font doesn't exist.
|
||||||
// The fallback is "Liberation Sans"
|
// The fallback is "Liberation Sans"
|
||||||
|
ok((font2Name == "Arial") || (font2Name == "Liberation Sans"),
|
||||||
ok((font1Name == "Arial") || (font1Name == "Liberation Sans"),
|
"font 1: Right font name");
|
||||||
"font 1: Right font name");
|
ok(s[1].classList.contains("is-local"), "font 2: is local");
|
||||||
ok(s[1].classList.contains("is-local"), "font 1: is local");
|
ok((font2CssName == "Arial") || (font2CssName == "Liberation Sans"),
|
||||||
ok((font1CssName == "Arial") || (font1CssName == "Liberation Sans"),
|
"Arial", "font 2: right css name");
|
||||||
"Arial", "font 1: right css name");
|
}
|
||||||
|
|
||||||
testDivFonts();
|
function* testDivFonts(inspector) {
|
||||||
}
|
let updated = inspector.once("fontinspector-updated");
|
||||||
|
yield selectNode("div", inspector);
|
||||||
function testDivFonts() {
|
yield updated;
|
||||||
inspector.selection.setNode(doc.querySelector("div"));
|
|
||||||
inspector.once("inspector-updated", () => {
|
let sections1 = viewDoc.querySelectorAll("#all-fonts > section");
|
||||||
let s = viewDoc.querySelectorAll("#all-fonts > section");
|
is(sections1.length, 1, "Found 1 font on DIV");
|
||||||
is(s.length, 1, "Found 1 font on DIV");
|
is(sections1[0].querySelector(".font-name").textContent, "Ostrich Sans Medium",
|
||||||
is(s[0].querySelector(".font-name").textContent, "DeLarge Bold",
|
"The DIV font has the right name");
|
||||||
"The DIV font has the right name");
|
}
|
||||||
|
|
||||||
testShowAllFonts();
|
function* testShowAllFonts(inspector) {
|
||||||
});
|
info("testing showing all fonts");
|
||||||
}
|
|
||||||
|
let updated = inspector.once("fontinspector-updated");
|
||||||
function testShowAllFonts() {
|
viewDoc.querySelector("#showall").click();
|
||||||
viewDoc.querySelector("#showall").click();
|
yield updated;
|
||||||
inspector.once("inspector-updated", () => {
|
|
||||||
is(inspector.selection.node, doc.body, "Show all fonts selected the body node");
|
is(inspector.selection.nodeFront.nodeName, "BODY", "Show all fonts selected the body node");
|
||||||
let s = viewDoc.querySelectorAll("#all-fonts > section");
|
let sections = viewDoc.querySelectorAll("#all-fonts > section");
|
||||||
is(s.length, 2, "And font-inspector still shows 2 fonts for body");
|
is(sections.length, 2, "And font-inspector still shows 2 fonts for body");
|
||||||
|
|
||||||
finishUp();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function finishUp() {
|
|
||||||
executeSoon(function() {
|
|
||||||
gDevTools.once("toolbox-destroyed", () => {
|
|
||||||
doc = view = viewDoc = inspector = null;
|
|
||||||
gBrowser.removeCurrentTab();
|
|
||||||
finish();
|
|
||||||
});
|
|
||||||
inspector._toolbox.destroy();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* 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/. */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const Cu = Components.utils;
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
const Cc = Components.classes;
|
||||||
|
|
||||||
|
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||||
|
|
||||||
|
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||||
|
let TargetFactory = devtools.TargetFactory;
|
||||||
|
|
||||||
|
// All test are asynchronous
|
||||||
|
waitForExplicitFinish();
|
||||||
|
|
||||||
|
gDevTools.testing = true;
|
||||||
|
SimpleTest.registerCleanupFunction(() => {
|
||||||
|
gDevTools.testing = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
registerCleanupFunction(function*() {
|
||||||
|
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||||
|
yield gDevTools.closeToolbox(target);
|
||||||
|
|
||||||
|
while (gBrowser.tabs.length > 1) {
|
||||||
|
gBrowser.removeCurrentTab();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define an async test based on a generator function
|
||||||
|
*/
|
||||||
|
function asyncTest(generator) {
|
||||||
|
return () => Task.spawn(generator).then(null, ok.bind(null, false)).then(finish);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new test tab in the browser and load the given url.
|
||||||
|
* @param {String} url The url to be loaded in the new tab
|
||||||
|
* @return a promise that resolves to the tab object when the url is loaded
|
||||||
|
*/
|
||||||
|
function loadTab(url) {
|
||||||
|
let deferred = promise.defer();
|
||||||
|
|
||||||
|
let tab = gBrowser.selectedTab = gBrowser.addTab(url);
|
||||||
|
let browser = gBrowser.getBrowserForTab(tab);
|
||||||
|
|
||||||
|
browser.addEventListener("load", function onLoad() {
|
||||||
|
browser.removeEventListener("load", onLoad, true);
|
||||||
|
deferred.resolve({tab: tab, browser: browser});
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the toolbox, with the inspector tool visible.
|
||||||
|
* @param {Function} cb Optional callback, if you don't want to use the returned
|
||||||
|
* promise
|
||||||
|
* @return a promise that resolves when the inspector is ready
|
||||||
|
*/
|
||||||
|
let openInspector = Task.async(function*(cb) {
|
||||||
|
info("Opening the inspector");
|
||||||
|
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||||
|
|
||||||
|
let inspector, toolbox;
|
||||||
|
|
||||||
|
// Checking if the toolbox and the inspector are already loaded
|
||||||
|
// The inspector-updated event should only be waited for if the inspector
|
||||||
|
// isn't loaded yet
|
||||||
|
toolbox = gDevTools.getToolbox(target);
|
||||||
|
if (toolbox) {
|
||||||
|
inspector = toolbox.getPanel("inspector");
|
||||||
|
if (inspector) {
|
||||||
|
info("Toolbox and inspector already open");
|
||||||
|
if (cb) {
|
||||||
|
return cb(inspector, toolbox);
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
toolbox: toolbox,
|
||||||
|
inspector: inspector
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info("Opening the toolbox");
|
||||||
|
toolbox = yield gDevTools.showToolbox(target, "inspector");
|
||||||
|
yield waitForToolboxFrameFocus(toolbox);
|
||||||
|
inspector = toolbox.getPanel("inspector");
|
||||||
|
|
||||||
|
info("Waiting for the inspector to update");
|
||||||
|
yield inspector.once("inspector-updated");
|
||||||
|
|
||||||
|
if (cb) {
|
||||||
|
return cb(inspector, toolbox);
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
toolbox: toolbox,
|
||||||
|
inspector: inspector
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select a node in the inspector given its selector.
|
||||||
|
*/
|
||||||
|
let selectNode = Task.async(function*(selector, inspector, reason="test") {
|
||||||
|
info("Selecting the node for '" + selector + "'");
|
||||||
|
let nodeFront = yield getNodeFront(selector, inspector);
|
||||||
|
let updated = inspector.once("inspector-updated");
|
||||||
|
inspector.selection.setNodeFront(nodeFront, reason);
|
||||||
|
yield updated;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the NodeFront for a given css selector, via the protocol
|
||||||
|
* @param {String|NodeFront} selector
|
||||||
|
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
|
||||||
|
* loaded in the toolbox
|
||||||
|
* @return {Promise} Resolves to the NodeFront instance
|
||||||
|
*/
|
||||||
|
function getNodeFront(selector, {walker}) {
|
||||||
|
if (selector._form) {
|
||||||
|
return selector;
|
||||||
|
}
|
||||||
|
return walker.querySelector(walker.rootNode, selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for the toolbox frame to receive focus after it loads
|
||||||
|
* @param {Toolbox} toolbox
|
||||||
|
* @return a promise that resolves when focus has been received
|
||||||
|
*/
|
||||||
|
function waitForToolboxFrameFocus(toolbox) {
|
||||||
|
info("Making sure that the toolbox's frame is focused");
|
||||||
|
let def = promise.defer();
|
||||||
|
let win = toolbox.frame.contentWindow;
|
||||||
|
waitForFocus(def.resolve, win);
|
||||||
|
return def.promise;
|
||||||
|
}
|
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -108,6 +108,10 @@ InspectorPanel.prototype = {
|
||||||
return this._target.client.traits.getUniqueSelector;
|
return this._target.client.traits.getUniqueSelector;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get canGetUsedFontFaces() {
|
||||||
|
return this._target.client.traits.getUsedFontFaces;
|
||||||
|
},
|
||||||
|
|
||||||
get canPasteInnerOrAdjacentHTML() {
|
get canPasteInnerOrAdjacentHTML() {
|
||||||
return this._target.client.traits.pasteHTML;
|
return this._target.client.traits.pasteHTML;
|
||||||
},
|
},
|
||||||
|
@ -321,7 +325,7 @@ InspectorPanel.prototype = {
|
||||||
"chrome://browser/content/devtools/computedview.xhtml",
|
"chrome://browser/content/devtools/computedview.xhtml",
|
||||||
"computedview" == defaultTab);
|
"computedview" == defaultTab);
|
||||||
|
|
||||||
if (Services.prefs.getBoolPref("devtools.fontinspector.enabled") && !this.target.isRemote) {
|
if (Services.prefs.getBoolPref("devtools.fontinspector.enabled") && this.canGetUsedFontFaces) {
|
||||||
this.sidebar.addTab("fontinspector",
|
this.sidebar.addTab("fontinspector",
|
||||||
"chrome://browser/content/devtools/fontinspector/font-inspector.xhtml",
|
"chrome://browser/content/devtools/fontinspector/font-inspector.xhtml",
|
||||||
"fontinspector" == defaultTab);
|
"fontinspector" == defaultTab);
|
||||||
|
|
|
@ -43,9 +43,8 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.font-preview {
|
.font-preview {
|
||||||
|
margin-left: -4px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
width: 100%;
|
|
||||||
border: 0;
|
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ const object = require("sdk/util/object");
|
||||||
const events = require("sdk/event/core");
|
const events = require("sdk/event/core");
|
||||||
const {Unknown} = require("sdk/platform/xpcom");
|
const {Unknown} = require("sdk/platform/xpcom");
|
||||||
const {Class} = require("sdk/core/heritage");
|
const {Class} = require("sdk/core/heritage");
|
||||||
const {PageStyleActor} = require("devtools/server/actors/styles");
|
const {PageStyleActor, getFontPreviewData} = require("devtools/server/actors/styles");
|
||||||
const {
|
const {
|
||||||
HighlighterActor,
|
HighlighterActor,
|
||||||
CustomHighlighterActor,
|
CustomHighlighterActor,
|
||||||
|
@ -663,28 +663,14 @@ var NodeActor = exports.NodeActor = protocol.ActorClass({
|
||||||
*/
|
*/
|
||||||
getFontFamilyDataURL: method(function(font, fillStyle="black") {
|
getFontFamilyDataURL: method(function(font, fillStyle="black") {
|
||||||
let doc = this.rawNode.ownerDocument;
|
let doc = this.rawNode.ownerDocument;
|
||||||
let canvas = doc.createElementNS(XHTML_NS, "canvas");
|
let options = {
|
||||||
let ctx = canvas.getContext("2d");
|
previewText: FONT_FAMILY_PREVIEW_TEXT,
|
||||||
let fontValue = FONT_FAMILY_PREVIEW_TEXT_SIZE + "px " + font + ", serif";
|
previewFontSize: FONT_FAMILY_PREVIEW_TEXT_SIZE,
|
||||||
|
fillStyle: fillStyle
|
||||||
|
}
|
||||||
|
let { dataURL, size } = getFontPreviewData(font, doc, options);
|
||||||
|
|
||||||
// Get the correct preview text measurements and set the canvas dimensions
|
return { data: LongStringActor(this.conn, dataURL), size: size };
|
||||||
ctx.font = fontValue;
|
|
||||||
let textWidth = ctx.measureText(FONT_FAMILY_PREVIEW_TEXT).width;
|
|
||||||
canvas.width = textWidth * 2;
|
|
||||||
canvas.height = FONT_FAMILY_PREVIEW_TEXT_SIZE * 3;
|
|
||||||
|
|
||||||
ctx.font = fontValue;
|
|
||||||
ctx.fillStyle = fillStyle;
|
|
||||||
|
|
||||||
// Align the text to be vertically center in the tooltip and
|
|
||||||
// oversample the canvas for better text quality
|
|
||||||
ctx.textBaseline = "top";
|
|
||||||
ctx.scale(2, 2);
|
|
||||||
ctx.fillText(FONT_FAMILY_PREVIEW_TEXT, 0, Math.round(FONT_FAMILY_PREVIEW_TEXT_SIZE / 3));
|
|
||||||
|
|
||||||
let dataURL = canvas.toDataURL("image/png");
|
|
||||||
|
|
||||||
return { data: LongStringActor(this.conn, dataURL), size: textWidth };
|
|
||||||
}, {
|
}, {
|
||||||
request: {font: Arg(0, "string"), fillStyle: Arg(1, "nullable:string")},
|
request: {font: Arg(0, "string"), fillStyle: Arg(1, "nullable:string")},
|
||||||
response: RetVal("imageData")
|
response: RetVal("imageData")
|
||||||
|
|
|
@ -161,7 +161,10 @@ RootActor.prototype = {
|
||||||
// Whether the debugger server supports
|
// Whether the debugger server supports
|
||||||
// blackboxing/pretty-printing (not supported in Fever Dream yet)
|
// blackboxing/pretty-printing (not supported in Fever Dream yet)
|
||||||
noBlackBoxing: false,
|
noBlackBoxing: false,
|
||||||
noPrettyPrinting: false
|
noPrettyPrinting: false,
|
||||||
|
// Whether the page style actor implements the getUsedFontFaces method
|
||||||
|
// that returns the font faces used on a node
|
||||||
|
getUsedFontFaces: true
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,7 +11,9 @@ const protocol = require("devtools/server/protocol");
|
||||||
const {Arg, Option, method, RetVal, types} = protocol;
|
const {Arg, Option, method, RetVal, types} = protocol;
|
||||||
const events = require("sdk/event/core");
|
const events = require("sdk/event/core");
|
||||||
const object = require("sdk/util/object");
|
const object = require("sdk/util/object");
|
||||||
const { Class } = require("sdk/core/heritage");
|
const {Class} = require("sdk/core/heritage");
|
||||||
|
const {LongStringActor} = require("devtools/server/actors/string");
|
||||||
|
|
||||||
|
|
||||||
// This will add the "stylesheet" actor type for protocol.js to recognize
|
// This will add the "stylesheet" actor type for protocol.js to recognize
|
||||||
require("devtools/server/actors/stylesheets");
|
require("devtools/server/actors/stylesheets");
|
||||||
|
@ -34,6 +36,11 @@ const PSEUDO_ELEMENTS_TO_READ = PSEUDO_ELEMENTS.filter(pseudo => {
|
||||||
return pseudo !== ":before" && pseudo !== ":after";
|
return pseudo !== ":before" && pseudo !== ":after";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||||
|
const FONT_PREVIEW_TEXT = "Abc";
|
||||||
|
const FONT_PREVIEW_FONT_SIZE = 40;
|
||||||
|
const FONT_PREVIEW_FILLSTYLE = "black";
|
||||||
|
|
||||||
// Predeclare the domnode actor type for use in requests.
|
// Predeclare the domnode actor type for use in requests.
|
||||||
types.addActorType("domnode");
|
types.addActorType("domnode");
|
||||||
|
|
||||||
|
@ -70,6 +77,23 @@ types.addDictType("appliedStylesReturn", {
|
||||||
sheets: "array:stylesheet"
|
sheets: "array:stylesheet"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
types.addDictType("fontpreview", {
|
||||||
|
data: "nullable:longstring",
|
||||||
|
size: "json"
|
||||||
|
});
|
||||||
|
|
||||||
|
types.addDictType("fontface", {
|
||||||
|
name: "string",
|
||||||
|
CSSFamilyName: "string",
|
||||||
|
rule: "nullable:domstylerule",
|
||||||
|
srcIndex: "number",
|
||||||
|
URI: "string",
|
||||||
|
format: "string",
|
||||||
|
preview: "nullable:fontpreview",
|
||||||
|
localName: "string",
|
||||||
|
metadata: "string"
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The PageStyle actor lets the client look at the styles on a page, as
|
* The PageStyle actor lets the client look at the styles on a page, as
|
||||||
* they are applied to a given node.
|
* they are applied to a given node.
|
||||||
|
@ -195,6 +219,90 @@ var PageStyleActor = protocol.ActorClass({
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the font faces used in an element.
|
||||||
|
*
|
||||||
|
* @param NodeActor node
|
||||||
|
* The node to get fonts from.
|
||||||
|
* @param object options
|
||||||
|
* `includePreviews`: Whether to also return image previews of the fonts.
|
||||||
|
* `previewText`: The text to display in the previews.
|
||||||
|
* `previewFontSize`: The font size of the text in the previews.
|
||||||
|
*
|
||||||
|
* @returns object
|
||||||
|
* object with 'fontFaces', a list of fonts that apply to this node.
|
||||||
|
*/
|
||||||
|
getUsedFontFaces: method(function(node, options) {
|
||||||
|
let contentDocument = node.rawNode.ownerDocument;
|
||||||
|
|
||||||
|
// We don't get fonts for a node, but for a range
|
||||||
|
let rng = contentDocument.createRange();
|
||||||
|
rng.selectNodeContents(node.rawNode);
|
||||||
|
let fonts = DOMUtils.getUsedFontFaces(rng);
|
||||||
|
let fontsArray = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < fonts.length; i++) {
|
||||||
|
let font = fonts.item(i);
|
||||||
|
let fontFace = {
|
||||||
|
name: font.name,
|
||||||
|
CSSFamilyName: font.CSSFamilyName,
|
||||||
|
srcIndex: font.srcIndex,
|
||||||
|
URI: font.URI,
|
||||||
|
format: font.format,
|
||||||
|
localName: font.localName,
|
||||||
|
metadata: font.metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this font comes from a @font-face rule
|
||||||
|
if (font.rule) {
|
||||||
|
fontFace.rule = StyleRuleActor(this, font.rule);
|
||||||
|
fontFace.ruleText = font.rule.cssText;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.includePreviews) {
|
||||||
|
let opts = {
|
||||||
|
previewText: options.previewText,
|
||||||
|
previewFontSize: options.previewFontSize,
|
||||||
|
fillStyle: options.previewFillStyle
|
||||||
|
}
|
||||||
|
let { dataURL, size } = getFontPreviewData(font.CSSFamilyName,
|
||||||
|
contentDocument, opts);
|
||||||
|
fontFace.preview = {
|
||||||
|
data: LongStringActor(this.conn, dataURL),
|
||||||
|
size: size
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fontsArray.push(fontFace);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @font-face fonts at the top, then alphabetically, then by weight
|
||||||
|
fontsArray.sort(function(a, b) {
|
||||||
|
if (a.CSSFamilyName == b.CSSFamilyName) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return a.CSSFamilyName > b.CSSFamilyName ? 1 : -1;
|
||||||
|
});
|
||||||
|
fontsArray.sort(function(a, b) {
|
||||||
|
if ((a.rule && b.rule) || (!a.rule && !b.rule)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return !a.rule && b.rule ? 1 : -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
return fontsArray;
|
||||||
|
}, {
|
||||||
|
request: {
|
||||||
|
node: Arg(0, "domnode"),
|
||||||
|
includePreviews: Option(1, "boolean"),
|
||||||
|
previewText: Option(1, "string"),
|
||||||
|
previewFontSize: Option(1, "string"),
|
||||||
|
previewFillStyle: Option(1, "string")
|
||||||
|
},
|
||||||
|
response: {
|
||||||
|
fontFaces: RetVal("array:fontface")
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of selectors that match a given property for a node.
|
* Get a list of selectors that match a given property for a node.
|
||||||
*
|
*
|
||||||
|
@ -1104,3 +1212,53 @@ var RuleModificationList = Class({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for getting an image preview of the given font.
|
||||||
|
*
|
||||||
|
* @param font {string}
|
||||||
|
* Name of font to preview
|
||||||
|
* @param doc {Document}
|
||||||
|
* Document to use to render font
|
||||||
|
* @param options {object}
|
||||||
|
* Object with options 'previewText' and 'previewFontSize'
|
||||||
|
*
|
||||||
|
* @return dataUrl
|
||||||
|
* The data URI of the font preview image
|
||||||
|
*/
|
||||||
|
function getFontPreviewData(font, doc, options) {
|
||||||
|
options = options || {};
|
||||||
|
let previewText = options.previewText || FONT_PREVIEW_TEXT;
|
||||||
|
let previewFontSize = options.previewFontSize || FONT_PREVIEW_FONT_SIZE;
|
||||||
|
let fillStyle = options.fillStyle || FONT_PREVIEW_FILLSTYLE;
|
||||||
|
let fontStyle = options.fontStyle || "";
|
||||||
|
|
||||||
|
let canvas = doc.createElementNS(XHTML_NS, "canvas");
|
||||||
|
let ctx = canvas.getContext("2d");
|
||||||
|
let fontValue = fontStyle + " " + previewFontSize + "px " + font + ", serif";
|
||||||
|
|
||||||
|
// Get the correct preview text measurements and set the canvas dimensions
|
||||||
|
ctx.font = fontValue;
|
||||||
|
ctx.fillStyle = fillStyle;
|
||||||
|
let textWidth = ctx.measureText(previewText).width;
|
||||||
|
let offset = 4; // offset to avoid cutting off text edge of italics
|
||||||
|
canvas.width = textWidth * 2 + offset * 2;
|
||||||
|
canvas.height = previewFontSize * 3;
|
||||||
|
|
||||||
|
// we have to reset these after changing the canvas size
|
||||||
|
ctx.font = fontValue;
|
||||||
|
ctx.fillStyle = fillStyle;
|
||||||
|
|
||||||
|
// Oversample the canvas for better text quality
|
||||||
|
ctx.textBaseline = "top";
|
||||||
|
ctx.scale(2, 2);
|
||||||
|
ctx.fillText(previewText, offset, Math.round(previewFontSize / 3));
|
||||||
|
|
||||||
|
let dataURL = canvas.toDataURL("image/png");
|
||||||
|
|
||||||
|
return {
|
||||||
|
dataURL: dataURL,
|
||||||
|
size: textWidth + offset * 2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getFontPreviewData = getFontPreviewData;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче