Last active
March 9, 2025 07:09
-
-
Save nexpid/fc93225e03903907fbcaffcc8877ea9b to your computer and use it in GitHub Desktop.
Bypass Font Awesome Pro.user.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Bypass Font Awesome Pro | |
// @namespace https://gist.github.com/nexpid/fc93225e03903907fbcaffcc8877ea9b/edit | |
// @updateURL https://gist.github.com/nexpid/fc93225e03903907fbcaffcc8877ea9b/raw/bypass.fontawesome.user.js | |
// @description Bypasses Font Awesome's Pro paywall, because it sucks. | |
// @author nexpid | |
// @icon https://fontawesome.com/favicon.ico | |
// @match *://fontawesome.com/* | |
// @run-at document-start | |
// @version 1.2.4 | |
// ==/UserScript== | |
(() => { | |
const patched = Symbol("is patched"); | |
/** | |
* @param {(element: "div" | "span" | "font-awesome-icon", props: { | |
* staticClass?: string; | |
* staticStyle?: object; | |
* attrs?: object; | |
* on?: { click: () => void } | |
* }, children: any[])} jsx | |
* @param {{ solid: Record<string, any> }} icons | |
* @param {(text: string) => any} text | |
* @param {{ name: string, label: string, unicode: string, family: "classic" | "duotone" | "sharp" | "sharp-doutone", style: "solid" | "regular" | "light" | "thin" }} icon | |
*/ | |
window.fabypassrender = (jsx, icons, text, icon) => { | |
const label = "Snap HD Png"; | |
window.HawkTuah = icons; | |
window.HonkTuah = icon; | |
return jsx("div", { staticStyle: { marginLeft: "0.2rem" } }, [ | |
jsx( | |
"button", | |
{ | |
staticClass: | |
"icon-action-download-svg flat compact display-inline-block padding-x-2xs", | |
staticStyle: { "--button-color": "var(--fa-navy)" }, | |
attrs: { | |
type: "button", | |
"aria-label": label, | |
"data-balloon-pos": "up", | |
}, | |
on: { | |
click: () => { | |
const font = { | |
classic: "Pro", | |
duotone: "Duotone", | |
sharp: "Sharp", | |
"sharp-duotone": "Sharp Duotone", | |
}[icon.family]; | |
const weight = { | |
solid: 900, | |
regular: 400, | |
light: 300, | |
thin: 100, | |
}[icon.style]; | |
const symbol = String.fromCharCode(parseInt(icon.unicode, 16)); | |
const layers = icon.family.includes("duotone") | |
? [symbol, symbol + symbol] | |
: [symbol]; | |
const canvas = document.createElement("canvas"); | |
canvas.width = 512; | |
canvas.height = 512; | |
canvas.style.position = "fixed"; | |
canvas.style.left = 0; | |
canvas.style.top = 0; | |
canvas.style.backgroundColor = "#000"; | |
const ctx = canvas.getContext("2d"); | |
const color = { | |
"1": "#4e5058" | |
}[prompt("What color would you like to use?\n\n0 = White\n1 = Discord gray")] ?? "#ffffff"; | |
ctx.textRendering = "geometricPrecision"; | |
ctx.textAlign = "center"; | |
ctx.textBaseline = "middle"; | |
ctx.fillStyle = color; | |
const maxSize = canvas.width * 0.8; | |
let size = 100; | |
const fontify = () => | |
(ctx.font = `${weight} ${size}px "Font Awesome 6 ${font}", serif`); | |
while (size < 1000) { | |
size++; | |
fontify(); | |
const { width, actualBoundingBoxAscent, actualBoundingBoxDescent } = ctx.measureText(layers[0]); | |
const height = actualBoundingBoxAscent + actualBoundingBoxDescent; | |
if (width > maxSize || height > maxSize) { | |
console.debug("Width:", width, "Height:", height, "Size:", size, "Max size:", maxSize); | |
size--; | |
fontify(); | |
break; | |
} | |
} | |
ctx.fillText(layers[0], canvas.width / 2, canvas.height / 2); | |
if (layers[1]) { | |
ctx.fillStyle = color + "66"; | |
ctx.fillText(layers[1], canvas.width / 2, canvas.height / 2); | |
} | |
const url = canvas.toDataURL("image/png"); | |
const a = document.createElement("a"); | |
a.href = url; | |
a.download = `fa${icon.label.replace(/ +/g, "")}.png`; | |
a.click(); | |
}, | |
}, | |
}, | |
[ | |
jsx("font-awesome-icon", { | |
attrs: { | |
icon: icons.solid.faCameraRetro, | |
"fixed-width": "", | |
size: "lg", | |
}, | |
}), | |
text(" "), | |
jsx("span", { staticClass: "sr-only" }, [text(label)]), | |
], | |
1 | |
), | |
]); | |
}; | |
/** @type {{ search: string, patches: { match: RegExp, replace: string }[] }[]} */ | |
const patches = [ | |
{ | |
search: 'name:"IconCodeSnippet"', | |
patches: [ | |
{ | |
match: /this\.canDownloadIcon/, | |
replace: "true", | |
}, | |
], | |
}, | |
{ | |
search: 'name:"IconSvgDownload"', | |
patches: [ | |
{ | |
match: /this\.canDownloadIcon/, | |
replace: "true", | |
}, | |
{ | |
match: | |
/\[((\w)\("div",{stat.*?(\w\.\$.*?)\..*?(\w\._v).*?,1\)\]\))\]/, | |
replace: "[$1,window.fabypassrender($2,$3,$4,t.icon)]", | |
}, | |
], | |
}, | |
{ | |
search: 'name:"IconNameAndLink"', | |
patches: [ | |
{ | |
match: /\w\.isProUser/, | |
replace: "true", | |
}, | |
], | |
}, | |
{ | |
search: '"icon-customize"', | |
patches: [ | |
{ | |
match: /\w\.isProUser/g, | |
replace: "true", | |
}, | |
], | |
}, | |
]; | |
/** @param {Record<string, Function} modules */ | |
function patchModules(modules) { | |
for (const id in modules) { | |
if (modules[id][patched]) continue; | |
/** @type {string} */ | |
const funcStr = Function.prototype.toString.apply(modules[id]); | |
const patc = patches.find((x) => funcStr.includes(x.search)); | |
const footer = `\n//# sourceURL=WebpackModule${id}`; | |
if (patc && patc.patches && patc.patches[0]) { | |
try { | |
modules[id] = (0, eval)( | |
`// Webpack Module ${id} - Patched by Font Awesome Pro Bypass\n// Debug - patch group ${JSON.stringify( | |
patc.search | |
)}\n${patc.patches.reduce( | |
(str, patch) => str.replace(patch.match, patch.replace), | |
funcStr | |
)}${footer}` | |
); | |
console.log( | |
`[PATCHER] Patched ${id} with ${JSON.stringify(patc.search)}!`, | |
modules[id] | |
); | |
} catch (e) { | |
if (!confirm("A patch errored! Continue?")) location.reload(); | |
} | |
} | |
modules[id][patched] = true; | |
} | |
} | |
let base = []; | |
Object.defineProperty(window, "webpackChunkfontawesome", { | |
set(value) { | |
base = value; | |
if (!value.push[patched]) { | |
/** @type {Function} */ | |
const realPush = value.push; | |
value.push = (...args) => { | |
const [chunk] = args; | |
if (!chunk[patched]) { | |
chunk[patched] = true; | |
patchModules(chunk[1]); | |
} | |
return realPush.apply(value, args); | |
}; | |
value.push[patched] = true; | |
} | |
}, | |
get() { | |
return base; | |
}, | |
configurable: true, | |
}); | |
console.log("[Patcher] Attached!"); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment