|
21 | 21 | <link rel="alternate" hreflang="ar" href="https://subs.js.org/ass-subset/?lang=ar"> |
22 | 22 | <link rel="alternate" hreflang="x-default" href="https://subs.js.org/ass-subset/"> |
23 | 23 | <link rel="icon" type="image/jpeg" href="icons/icon-256.png"> |
24 | | - <meta name="version" content="2.6.22"> |
| 24 | + <meta name="version" content="2.6.23"> |
25 | 25 | <meta property="og:type" content="website"> |
26 | 26 | <meta property="og:title" content="ASS Subsetter · Font Embedding for Subtitles"> |
27 | 27 | <meta property="og:description" content="Font embedding tool for ASS/SSA subtitles. Subset fonts to compress file size, or embed them fully. Perfect for complex subtitle typesetting with multiple font files."> |
@@ -4956,6 +4956,47 @@ <h3 id="preview-title" data-i18n="preview.title">Font Preview / Download</h3> |
4956 | 4956 | } |
4957 | 4957 | } |
4958 | 4958 |
|
| 4959 | + const WEIGHT_KW = ['heavy', 'black', 'extrabold', 'ultrabold', 'semibold', 'demibold', 'bold', 'medium', 'normal', 'regular', 'light', 'extralight', 'ultralight', 'thin', 'hairline']; |
| 4960 | + const ITALIC_KW = ['italic', 'oblique', 'slanted']; |
| 4961 | + |
| 4962 | + function parseVersion(s) { |
| 4963 | + const m = (s || '').match(/(\d+)\.(\d+)/); |
| 4964 | + return m ? [parseInt(m[1], 10), parseInt(m[2], 10)] : [0, 0]; |
| 4965 | + } |
| 4966 | + function cmpVersion(a, b) { |
| 4967 | + if (a[0] !== b[0]) return a[0] - b[0]; |
| 4968 | + return a[1] - b[1]; |
| 4969 | + } |
| 4970 | + |
| 4971 | + function isBetterCandidate(newCand, oldCand, fontNameStr) { |
| 4972 | + const fLow = fontNameStr.toLowerCase(); |
| 4973 | + const needsW = WEIGHT_KW.some(kw => fLow.includes(kw)); |
| 4974 | + const needsI = ITALIC_KW.some(kw => fLow.includes(kw)); |
| 4975 | + const getScore = (c) => { |
| 4976 | + let score = 0; |
| 4977 | + if (needsW || needsI) { |
| 4978 | + const sLow = (c.subfamilyName || '').toLowerCase(); |
| 4979 | + const wOk = !needsW || WEIGHT_KW.some(kw => fLow.includes(kw) && sLow.includes(kw)); |
| 4980 | + const iOk = !needsI || ITALIC_KW.some(kw => fLow.includes(kw) && sLow.includes(kw)); |
| 4981 | + if (wOk && iOk) score -= 10000; |
| 4982 | + } |
| 4983 | + score += Math.abs((c.weight || 400) - 400); |
| 4984 | + if (c.isItalic && !needsI) score += 5000; |
| 4985 | + return score; |
| 4986 | + }; |
| 4987 | + const sNew = getScore(newCand); |
| 4988 | + const sOld = getScore(oldCand); |
| 4989 | + if (sNew !== sOld) return sNew < sOld; |
| 4990 | + const vNew = parseVersion(newCand.version || ''); |
| 4991 | + const vOld = parseVersion(oldCand.version || ''); |
| 4992 | + const cmp = cmpVersion(vNew, vOld); |
| 4993 | + if (cmp !== 0) return cmp > 0; |
| 4994 | + const dNew = newCand.file?.lastModified || newCand.lastModified || 0; |
| 4995 | + const dOld = oldCand.file?.lastModified || oldCand.lastModified || 0; |
| 4996 | + if (dNew !== dOld) return dNew > dOld; |
| 4997 | + return (newCand.size || 0) > (oldCand.size || 0); |
| 4998 | + } |
| 4999 | + |
4959 | 5000 | async function handleFonts(files) { |
4960 | 5001 | const dz = document.getElementById('dz-font'); |
4961 | 5002 | const notice = document.getElementById('font-loading-notice'); |
@@ -5067,12 +5108,14 @@ <h3 id="preview-title" data-i18n="preview.title">Font Preview / Download</h3> |
5067 | 5108 | const vk = nameKey + '::' + slotKey; |
5068 | 5109 | const isPendingReplace = S.pendingReplaceKeys && S.pendingReplaceKeys.has(vk); |
5069 | 5110 | if (isPendingReplace) { S.pendingReplaceKeys.delete(vk); S.uploadedFonts.set(vk, fontRecord); continue; } |
5070 | | - if (!S.uploadedFonts.has(vk)) S.uploadedFonts.set(vk, fontRecord); |
| 5111 | + const existing = S.uploadedFonts.get(vk); |
| 5112 | + if (!existing || isBetterCandidate(fontRecord, existing, match.matchedFor)) S.uploadedFonts.set(vk, fontRecord); |
5071 | 5113 | } else { |
5072 | 5114 | const vk = nameKey + '::default'; |
5073 | 5115 | const isPendingReplace = S.pendingReplaceKeys && S.pendingReplaceKeys.has(vk); |
5074 | 5116 | if (isPendingReplace) { S.pendingReplaceKeys.delete(vk); S.uploadedFonts.set(vk, fontRecord); continue; } |
5075 | | - if (!S.uploadedFonts.has(vk)) S.uploadedFonts.set(vk, fontRecord); |
| 5117 | + const existing = S.uploadedFonts.get(vk); |
| 5118 | + if (!existing || isBetterCandidate(fontRecord, existing, match.matchedFor)) S.uploadedFonts.set(vk, fontRecord); |
5076 | 5119 | } |
5077 | 5120 | } |
5078 | 5121 | itemLines.push(t('font.upload.item.ok', { name: entry.file.name, matched: matchNames })); |
@@ -5171,12 +5214,24 @@ <h3 id="preview-title" data-i18n="preview.title">Font Preview / Download</h3> |
5171 | 5214 | for (const matchName of matched) { |
5172 | 5215 | const vk = wantMultiWeight ? matchName + '::' + slotKey : matchName + '::default'; |
5173 | 5216 | const isPendingReplace = S.pendingReplaceKeys && S.pendingReplaceKeys.has(vk); |
5174 | | - if (isPendingReplace) { |
5175 | | - S.pendingReplaceKeys.delete(vk); |
5176 | | - } else if (S.uploadedFonts.has(vk)) { |
| 5217 | + const existing = S.uploadedFonts.get(vk); |
| 5218 | + if (!isPendingReplace && existing) { |
| 5219 | + if (!buffer) { const blob = await font.blob(); buffer = await blob.arrayBuffer(); } |
| 5220 | + const dataView = new DataView(buffer); |
| 5221 | + const isTTC = buffer.byteLength > 12 && dataView.getUint32(0, false) === 0x74746366; |
| 5222 | + const candidate = { |
| 5223 | + file: { name: font.fullName + ' (system)' }, |
| 5224 | + buffer, matchedFor: matchName, |
| 5225 | + isTTC, ttcIndex: isTTC ? 0 : -1, |
| 5226 | + weight, isItalic, |
| 5227 | + subfamilyName: font.style || '', |
| 5228 | + isFamilyMatch: true, isSystem: true |
| 5229 | + }; |
| 5230 | + if (isBetterCandidate(candidate, existing, matchName)) S.uploadedFonts.set(vk, candidate); |
5177 | 5231 | matchedCount++; |
5178 | 5232 | continue; |
5179 | 5233 | } |
| 5234 | + if (isPendingReplace) S.pendingReplaceKeys.delete(vk); |
5180 | 5235 | if (!buffer) { const blob = await font.blob(); buffer = await blob.arrayBuffer(); } |
5181 | 5236 | const dataView = new DataView(buffer); |
5182 | 5237 | const isTTC = buffer.byteLength > 12 && dataView.getUint32(0, false) === 0x74746366; |
|
0 commit comments