Skip to content

Commit 6842c6e

Browse files
committed
Merge PR CorentinTh#1651: ASCII text drawer robust font loading (keep HTTPS CDN with fallback)
2 parents 3f2a805 + 6ff4a5e commit 6842c6e

1 file changed

Lines changed: 80 additions & 19 deletions

File tree

src/tools/ascii-text-drawer/ascii-text-drawer.vue

Lines changed: 80 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
<script setup lang="ts">
22
import figlet from 'figlet';
3+
// Local fallback for the Standard font to avoid network flakiness
4+
// figlet >=1.7.0 provides importable fonts as JS modules
5+
// This guarantees Standard works even if CDN fetch fails
6+
// eslint-disable-next-line ts/ban-ts-comment
7+
// @ts-ignore
8+
import StandardFont from 'figlet/importable-fonts/Standard.js';
39
import TextareaCopyable from '@/components/TextareaCopyable.vue';
410
511
const input = ref('Ascii ART');
@@ -9,32 +15,87 @@ const output = ref('');
915
const errored = ref(false);
1016
const processing = ref(false);
1117
12-
figlet.defaults({ fontPath: '//unpkg.com/figlet@1.6.0/fonts' });
18+
// Prefer HTTPS CDN to avoid protocol-relative issues; add a fallback CDN
19+
const FONT_CDNS = [
20+
'https://unpkg.com/figlet@1.6.0/fonts/',
21+
'https://cdn.jsdelivr.net/npm/figlet@1.6.0/fonts/',
22+
];
23+
const cdnIndex = useStorage('ascii-text-drawer:cdnIndex', 0);
24+
figlet.defaults({ fontPath: FONT_CDNS[Math.min(cdnIndex.value, FONT_CDNS.length - 1)] });
1325
14-
watchEffect(async () => {
26+
// Register the local Standard font immediately
27+
try {
28+
figlet.parseFont('Standard', StandardFont);
29+
} catch {}
30+
31+
function loadFigletFont(targetFont: string) {
32+
return new Promise<boolean>((resolve) => {
33+
figlet.loadFont(targetFont as figlet.Fonts, (err) => {
34+
resolve(!err);
35+
});
36+
});
37+
}
38+
39+
async function renderAscii() {
1540
processing.value = true;
16-
try {
17-
const options: figlet.Options = {
18-
font: font.value as figlet.Fonts,
19-
width: width.value,
20-
whitespaceBreak: true,
21-
};
22-
output.value = await (new Promise<string>((resolve, reject) =>
23-
figlet.text(input.value, options,
24-
(err, text) => {
25-
if (err) {
26-
reject(err);
27-
return;
28-
}
29-
30-
resolve(text ?? '');
31-
})));
41+
const baseOptions = {
42+
width: Math.max(1, Number(width.value || 80)),
43+
whitespaceBreak: true,
44+
} satisfies Partial<figlet.Options>;
45+
46+
let success = false;
47+
let rendered = '';
48+
49+
// Try each CDN, and for each try the selected font then Standard as a fallback
50+
for (let attempt = 0; attempt < FONT_CDNS.length && !success; attempt++) {
51+
const idx = (cdnIndex.value + attempt) % FONT_CDNS.length;
52+
figlet.defaults({ fontPath: FONT_CDNS[idx] });
53+
54+
const candidates = [String(font.value), 'Standard'];
55+
for (const candidate of candidates) {
56+
// If candidate is Standard, we already have it locally
57+
if (candidate !== 'Standard') {
58+
const loaded = await loadFigletFont(candidate);
59+
if (!loaded)
60+
continue;
61+
}
62+
63+
try {
64+
rendered = await new Promise<string>((resolve, reject) =>
65+
figlet.text(String(input.value ?? ''), { ...baseOptions, font: candidate as figlet.Fonts }, (err, text) => {
66+
if (err)
67+
reject(err);
68+
else
69+
resolve(text ?? '');
70+
}),
71+
);
72+
// Rendering succeeded; remember the working CDN for next time
73+
cdnIndex.value = idx;
74+
success = true;
75+
break;
76+
}
77+
catch {
78+
// try next candidate
79+
}
80+
}
81+
}
82+
83+
if (success) {
3284
errored.value = false;
85+
output.value = rendered;
3386
}
34-
catch (e: any) {
87+
else {
3588
errored.value = true;
89+
output.value = '';
3690
}
91+
3792
processing.value = false;
93+
}
94+
95+
watchEffect(() => {
96+
// include cdnIndex so switching CDN also triggers recompute
97+
void cdnIndex.value;
98+
void renderAscii();
3899
});
39100
40101
const fonts = ['1Row', '3-D', '3D Diagonal', '3D-ASCII', '3x5', '4Max', '5 Line Oblique', 'AMC 3 Line', 'AMC 3 Liv1', 'AMC AAA01', 'AMC Neko', 'AMC Razor', 'AMC Razor2', 'AMC Slash', 'AMC Slider', 'AMC Thin', 'AMC Tubes', 'AMC Untitled', 'ANSI Shadow', 'ASCII New Roman', 'Acrobatic', 'Alligator', 'Alligator2', 'Alpha', 'Alphabet', 'Arrows', 'Avatar', 'B1FF', 'B1FF', 'Banner', 'Banner3-D', 'Banner3', 'Banner4', 'Barbwire', 'Basic', 'Bear', 'Bell', 'Benjamin', 'Big Chief', 'Big Money-ne', 'Big Money-nw', 'Big Money-se', 'Big Money-sw', 'Big', 'Bigfig', 'Binary', 'Block', 'Blocks', 'Bloody', 'Bolger', 'Braced', 'Bright', 'Broadway KB', 'Broadway', 'Bubble', 'Bulbhead', 'Caligraphy', 'Caligraphy2', 'Calvin S', 'Cards', 'Catwalk', 'Chiseled', 'Chunky', 'Coinstak', 'Cola', 'Colossal', 'Computer', 'Contessa', 'Contrast', 'Cosmike', 'Crawford', 'Crawford2', 'Crazy', 'Cricket', 'Cursive', 'Cyberlarge', 'Cybermedium', 'Cybersmall', 'Cygnet', 'DANC4', 'DOS Rebel', 'DWhistled', 'Dancing Font', 'Decimal', 'Def Leppard', 'Delta Corps Priest 1', 'Diamond', 'Diet Cola', 'Digital', 'Doh', 'Doom', 'Dot Matrix', 'Double Shorts', 'Double', 'Dr Pepper', 'Efti Chess', 'Efti Font', 'Efti Italic', 'Efti Piti', 'Efti Robot', 'Efti Wall', 'Efti Water', 'Electronic', 'Elite', 'Epic', 'Fender', 'Filter', 'Fire Font-k', 'Fire Font-s', 'Flipped', 'Flower Power', 'Four Tops', 'Fraktur', 'Fun Face', 'Fun Faces', 'Fuzzy', 'Georgi16', 'Georgia11', 'Ghost', 'Ghoulish', 'Glenyn', 'Goofy', 'Gothic', 'Graceful', 'Gradient', 'Graffiti', 'Greek', 'Heart Left', 'Heart Right', 'Henry 3D', 'Hex', 'Hieroglyphs', 'Hollywood', 'Horizontal Left', 'Horizontal Right', 'ICL-1900', 'Impossible', 'Invita', 'Isometric1', 'Isometric2', 'Isometric3', 'Isometric4', 'Italic', 'Ivrit', 'JS Block Letters', 'JS Bracket Letters', 'JS Capital Curves', 'JS Cursive', 'JS Stick Letters', 'Jacky', 'Jazmine', 'Jerusalem', 'Katakana', 'Kban', 'Keyboard', 'Knob', 'Konto Slant', 'Konto', 'LCD', 'Larry 3D 2', 'Larry 3D', 'Lean', 'Letters', 'Lil Devil', 'Line Blocks', 'Linux', 'Lockergnome', 'Madrid', 'Marquee', 'Maxfour', 'Merlin1', 'Merlin2', 'Mike', 'Mini', 'Mirror', 'Mnemonic', 'Modular', 'Morse', 'Morse2', 'Moscow', 'Mshebrew210', 'Muzzle', 'NScript', 'NT Greek', 'NV Script', 'Nancyj-Fancy', 'Nancyj-Improved', 'Nancyj-Underlined', 'Nancyj', 'Nipples', 'O8', 'OS2', 'Octal', 'Ogre', 'Old Banner', 'Patorjk\'s Cheese', 'Patorjk-HeX', 'Pawp', 'Peaks Slant', 'Peaks', 'Pebbles', 'Pepper', 'Poison', 'Puffy', 'Puzzle', 'Pyramid', 'Rammstein', 'Rectangles', 'Red Phoenix', 'Relief', 'Relief2', 'Reverse', 'Roman', 'Rot13', 'Rot13', 'Rotated', 'Rounded', 'Rowan Cap', 'Rozzo', 'Runic', 'Runyc', 'S Blood', 'SL Script', 'Santa Clara', 'Script', 'Serifcap', 'Shadow', 'Shimrod', 'Short', 'Slant Relief', 'Slant', 'Slide', 'Small Caps', 'Small Isometric1', 'Small Keyboard', 'Small Poison', 'Small Script', 'Small Shadow', 'Small Slant', 'Small Tengwar', 'Small', 'Soft', 'Speed', 'Spliff', 'Stacey', 'Stampate', 'Stampatello', 'Standard', 'Star Strips', 'Star Wars', 'Stellar', 'Stforek', 'Stick Letters', 'Stop', 'Straight', 'Stronger Than All', 'Sub-Zero', 'Swamp Land', 'Swan', 'Sweet', 'THIS', 'Tanja', 'Tengwar', 'Term', 'Test1', 'The Edge', 'Thick', 'Thin', 'Thorned', 'Three Point', 'Ticks Slant', 'Ticks', 'Tiles', 'Tinker-Toy', 'Tombstone', 'Train', 'Trek', 'Tsalagi', 'Tubular', 'Twisted', 'Two Point', 'USA Flag', 'Univers', 'Varsity', 'Wavy', 'Weird', 'Wet Letter', 'Whimsy', 'Wow'];

0 commit comments

Comments
 (0)