您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add a download button to download the font for free.
// ==UserScript== // @name Adobe Fonts Downloader // @description Add a download button to download the font for free. // @icon https://fonts.adobe.com/favicon.ico // @version 1.0 // @author afkarxyz // @namespace https://github.com/afkarxyz/userscripts/ // @supportURL https://github.com/afkarxyz/userscripts/issues // @license MIT // @match https://fonts.adobe.com/* // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/opentype.js/1.3.4/opentype.min.js // @grant GM_setValue // @grant GM_getValue // @run-at document-end // ==/UserScript== (function() { 'use strict'; var TypeRip = { handleCurrentPage: function (button) { const url = window.location.href; if (url.indexOf("fonts.adobe.com/collections") != -1) { this.getFontCollection(url, (status, data) => this.handleResult(status, data, button)); } else { this.getFontFamily(url, (status, data) => this.handleResult(status, data, button)); } }, handleResult: function (status, data, button) { if (status === "success") { TypeRip.downloadFonts(data.fonts, data.name, button); } else { console.error("Error: " + data); alert("Error downloading fonts: " + data); if (button) { const labelSpan = button.querySelector('.spectrum-Button-label, .add-family-label'); if (labelSpan) { labelSpan.textContent = 'Download'; } button.disabled = false; } } }, getFontCollection: function (url_, callback_) { const pageContent = document.documentElement.outerHTML; let fontCollection = { name: "", designers: [], fonts: [], }; let json_start = pageContent.search('{"fontpack":{"all_valid_slugs":'); if (json_start == -1) { callback_("error", "Font collection data not found"); return; } let data = pageContent.substring(json_start); let json_end = data.search("</script>"); if (json_end == -1) { callback_("error", "Could not parse collection data"); return; } let json; try { json = JSON.parse(data.substring(0, json_end)); } catch (e) { callback_("error", "Failed to parse collection data"); return; } fontCollection.name = json.fontpack.name; fontCollection.designers.push({ name: json.fontpack.contributor_credit, }); for (let i = 0; i < json.fontpack.font_variations.length; i++) { fontCollection.fonts.push({ url: "https://use.typekit.net/pf/tk/" + json.fontpack.font_variations[i].opaque_id + "/" + json.fontpack.font_variations[i].fvd + "/a?unicode=AAAAAQAAAAEAAAAB&features=ALL&v=3&ec_token=3bb2a6e53c9684ffdc9a9bf71d5b2a620e68abb153386c46ebe547292f11a96176a59ec4f0c7aacfef2663c08018dc100eedf850c284fb72392ba910777487b32ba21c08cc8c33d00bda49e7e2cc90baff01835518dde43e2e8d5ebf7b76545fc2687ab10bc2b0911a141f3cf7f04f3cac438a135f", name: json.fontpack.font_variations[i].full_display_name, style: json.fontpack.font_variations[i].variation_name, familyName: json.fontpack.font_variations[i].family.name, }); } callback_("success", fontCollection); }, getFontFamily: function (url_, callback_) { const pageContent = document.documentElement.outerHTML; let fontFamily = { name: "", designers: [], fonts: [], }; let json_start = pageContent.search('{"family":{"slug":"'); if (json_start == -1) { callback_("error", "Font data not found on this page"); return; } let data = pageContent.substring(json_start); let json_end = data.search("</script>"); if (json_end == -1) { callback_("error", "Could not parse font data"); return; } let json; try { json = JSON.parse(data.substring(0, json_end)); } catch (e) { callback_("error", "Failed to parse font data"); return; } fontFamily.name = json.family.name; fontFamily.slug = json.family.slug; for (let i = 0; i < json.family.designers.length; i++) { fontFamily.designers.push({ name: json.family.designers[i].name, }); } for (let i = 0; i < json.family.fonts.length; i++) { fontFamily.fonts.push({ url: "https://use.typekit.net/pf/tk/" + json.family.fonts[i].family.web_id + "/" + json.family.fonts[i].font.web.fvd + "/a?unicode=AAAAAQAAAAEAAAAB&features=ALL&v=3&ec_token=3bb2a6e53c9684ffdc9a9bf71d5b2a620e68abb153386c46ebe547292f11a96176a59ec4f0c7aacfef2663c08018dc100eedf850c284fb72392ba910777487b32ba21c08cc8c33d00bda49e7e2cc90baff01835518dde43e2e8d5ebf7b76545fc2687ab10bc2b0911a141f3cf7f04f3cac438a135f", name: json.family.fonts[i].name, style: json.family.fonts[i].variation_name, familyName: fontFamily.name, }); } callback_("success", fontFamily); }, downloadFonts: function (fonts_, zipFileName_, button_) { if (!fonts_ || fonts_.length === 0) { alert("No fonts found to download"); return; } const zip = new JSZip(); let fontProcessCounter = 0; console.log(`Starting download of ${fonts_.length} fonts as OTF...`); for (let i = 0; i < fonts_.length; i++) { this.downloadFont(fonts_[i], (fontBuffer, fontMeta) => { if (fontBuffer) { zip.file(fontMeta.name + ".otf", fontBuffer); fontProcessCounter++; console.log(`Downloaded ${fontProcessCounter}/${fonts_.length}: ${fontMeta.name}`); if (button_) { const labelSpan = button_.querySelector('.spectrum-Button-label, .add-family-label'); if (labelSpan) { labelSpan.textContent = `Processing (${fontProcessCounter}/${fonts_.length})`; } } if (fontProcessCounter === fonts_.length) { zip.generateAsync({ type: "blob" }).then(function (content) { saveAs(content, zipFileName_ + ".zip"); console.log("All fonts downloaded successfully as OTF!"); if (button_) { const labelSpan = button_.querySelector('.spectrum-Button-label, .add-family-label'); if (labelSpan) { labelSpan.textContent = 'Download'; } button_.disabled = false; } }); } } else { console.error("Failed to download font:", fontMeta.name); fontProcessCounter++; if (fontProcessCounter === fonts_.length) { if (button_) { const labelSpan = button_.querySelector('.spectrum-Button-label, .add-family-label'); if (labelSpan) { labelSpan.textContent = 'Download'; } button_.disabled = false; } } } }); } }, downloadFont: function (font_, callback_) { opentype.load(font_.url, (error, fontData) => { if (error) { console.error("Error loading font:", font_.name, error); callback_(null, font_); return; } try { let rebuiltGlyphs = []; for (let i = 0; i < fontData.glyphs.length; i++) { let glyphData = {}; let glyphFields = [ "name", "unicode", "unicodes", "path", "index", "advanceWidth", "leftSideBearing", ]; glyphFields.forEach((field) => { if (fontData.glyphs.glyphs[i][field] != null) { glyphData[field] = fontData.glyphs.glyphs[i][field]; } }); if (glyphData.advanceWidth == null || isNaN(glyphData.advanceWidth)) { let newAdvanceWidth = Math.floor(fontData.glyphs.glyphs[i].getBoundingBox().x2); if (newAdvanceWidth == 0) { newAdvanceWidth = fontData.glyphs.glyphs[0].getBoundingBox().x2; } glyphData.advanceWidth = newAdvanceWidth; } let rebuiltGlyph = new opentype.Glyph(glyphData); glyphFields.forEach((field) => { if (glyphData[field] != null && glyphData[field] == 0) { rebuiltGlyph[field] = 0; } }); rebuiltGlyphs.push(rebuiltGlyph); } let newFontData = { familyName: font_.familyName, styleName: font_.style, glyphs: rebuiltGlyphs, }; let optionalFields = [ "defaultWidthX", "nominalWidthX", "unitsPerEm", "ascender", "descender", ]; optionalFields.forEach((field) => { if (fontData[field] != null) { newFontData[field] = fontData[field]; } }); let newFont = new opentype.Font(newFontData); if (newFont.outlinesFormat !== 'cff') { newFont.outlinesFormat = 'cff'; } callback_(newFont.toArrayBuffer(), font_); } catch (repairError) { console.error("Error repairing font:", font_.name, repairError); fetch(font_.url) .then((response) => response.arrayBuffer()) .then((buffer) => callback_(buffer, font_)) .catch((fetchError) => { console.error("Fallback download failed:", fetchError); callback_(null, font_); }); } }); }, }; function getFullFontUrl(href) { if (!href) return ''; if (href.startsWith('http')) return href; return `https://fonts.adobe.com${href}`; } function createDownloadButton(originalButton) { const newButton = originalButton.cloneNode(true); originalButton.parentNode.replaceChild(newButton, originalButton); const labelSpan = newButton.querySelector('.spectrum-Button-label, .add-family-label'); if (labelSpan) { labelSpan.textContent = 'Download'; } newButton.removeAttribute('ng-click'); newButton.removeAttribute('ng-show'); newButton.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); newButton.disabled = true; const labelSpan = newButton.querySelector('.spectrum-Button-label, .add-family-label'); if (labelSpan) { labelSpan.textContent = 'Processing...'; } let fontUrl = ''; const cardLink = newButton.closest('.adobe-fonts-family-card')?.querySelector('.adobe-fonts-family-card--link'); if (cardLink) { fontUrl = getFullFontUrl(cardLink.getAttribute('href')); } else { fontUrl = window.location.href; } console.log('Starting direct download for:', fontUrl); const currentUrl = window.location.href; if (fontUrl !== currentUrl) { window.location.href = fontUrl; return; } TypeRip.handleCurrentPage(newButton); }); return newButton; } function modifyButtons() { const buttons = document.querySelectorAll([ '.adobe-fonts-family__top-actions-add-family button', 'button[ng-click*="useModelSyncFontpack"]', '.collection-show__font-pack-actions-bottom button.add-family-button' ].join(',')); buttons.forEach(button => { if (button.hasAttribute('data-modified')) return; const newButton = createDownloadButton(button); newButton.setAttribute('data-modified', 'true'); }); } function changeTextToDownload(node) { if (node.nodeType === Node.ELEMENT_NODE) { if (node.shadowRoot) { changeTextToDownload(node.shadowRoot); } node.childNodes.forEach(child => changeTextToDownload(child)); } } function init() { modifyButtons(); changeTextToDownload(document.body); } init(); const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { const currentUrl = location.href; if (window.lastUrl !== currentUrl) { window.lastUrl = currentUrl; init(); continue; } const addedNodes = Array.from(mutation.addedNodes); const hasNewButton = addedNodes.some(node => node.querySelector && ( node.querySelector('.adobe-fonts-family__top-actions-add-family') || node.querySelector('button[ng-click*="useModelSyncFontpack"]') || node.querySelector('.collection-show__font-pack-actions-bottom') ) ); if (hasNewButton) { init(); break; } addedNodes.forEach(changeTextToDownload); } }); window.lastUrl = location.href; observer.observe(document.body, { childList: true, subtree: true }); })();