Skip to content

Commit c2666fb

Browse files
authored
update: 2.1.18
进一步优化 #20
1 parent 23e178e commit c2666fb

3 files changed

Lines changed: 128 additions & 29 deletions

File tree

index.html

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<meta name="robots" content="index, follow">
1414
<link rel="canonical" href="https://subs.js.org/ass-subset/">
1515
<link rel="icon" type="image/jpeg" href="icons/icon-256.png">
16-
<meta name="version" content="2.1.17">
16+
<meta name="version" content="2.1.18">
1717
<meta property="og:type" content="website">
1818
<meta property="og:title" content="ASS 在线子集化工具 · MontageSubs">
1919
<meta property="og:description" content="在浏览器中一键优化 ASS/SSA 字幕文件:绘图指令子集化与字体子集嵌入,纯浏览器本地处理。">
@@ -1924,7 +1924,7 @@ <h3 id="preview-title" data-i18n="preview.title">字体预览</h3>
19241924
}
19251925
if (p.drawings > 0 || p.hasExistingDrawSubset) hasDrawings = true;
19261926
if (extKeys.length > 0) hasFonts = true;
1927-
if (p.hasExistingDrawSubset) hasResubset = true;
1927+
if (p.subsetNeedsUpdate) hasResubset = true;
19281928
}
19291929

19301930
area.innerHTML = '<div class="stats-bar">' +
@@ -1963,8 +1963,9 @@ <h3 id="preview-title" data-i18n="preview.title">字体预览</h3>
19631963
const rowFont = document.getElementById('opt-row-font');
19641964

19651965
cbDraw.disabled = !hasDrawings;
1966-
cbDraw.checked = hasDrawings;
1967-
rowDraw.classList.toggle('selected', hasDrawings);
1966+
const shouldDraw = (totalUnique > 0 || hasResubset);
1967+
cbDraw.checked = shouldDraw;
1968+
rowDraw.classList.toggle('selected', shouldDraw);
19681969
rowDraw.classList.toggle('disabled', !hasDrawings);
19691970
rowDraw.classList.toggle('hidden', !hasDrawings);
19701971

sw.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const CACHE_NAME = 'ass-subset-v2.1.17';
3+
const CACHE_NAME = 'ass-subset-v2.1.18';
44
const PRECACHE = [
55
'/ass-subset/',
66
'/ass-subset/index.html',

worker.js

Lines changed: 122 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,28 @@ function parseASSText(text, id) {
236236
} catch (_) { }
237237
}
238238
}
239+
let subsetNeedsUpdate = false;
240+
if (hasExistingDrawSubset) {
241+
if (uniqueDrawings.size > 0) {
242+
subsetNeedsUpdate = true;
243+
} else if (existingSubsetFontBuffer) {
244+
try {
245+
const existingFont = opentype.parse(existingSubsetFontBuffer);
246+
const existingChars = new Set();
247+
for (let i = 1; i < existingFont.glyphs.length; i++) {
248+
const g = existingFont.glyphs.get(i);
249+
if (g && g.unicode && g.unicode > 0) existingChars.add(String.fromCodePoint(g.unicode));
250+
}
251+
const refSet = subsetReferencedChars;
252+
subsetNeedsUpdate = existingChars.size !== refSet.size ||
253+
![...refSet].every(ch => existingChars.has(ch));
254+
} catch (_) {
255+
subsetNeedsUpdate = true;
256+
}
257+
} else {
258+
subsetNeedsUpdate = true;
259+
}
260+
}
239261
return {
240262
styles,
241263
externalFonts,
@@ -245,6 +267,7 @@ function parseASSText(text, id) {
245267
playResX, playResY,
246268
lineCount: totalLines,
247269
hasExistingDrawSubset,
270+
subsetNeedsUpdate,
248271
existingSubsetFontBuffer,
249272
subsetReferencedChars: Array.from(subsetReferencedChars),
250273
embeddedFontNames: Object.keys(embeddedFonts),
@@ -457,18 +480,14 @@ function buildDrawingFont(uniqueDrawingsArray, existingFontBuffer, referencedCha
457480
if (sameChars) {
458481
return {
459482
ttf: new Uint8Array(existingFontBuffer),
460-
dataToCharArr: []
483+
dataToCharArr: [],
484+
charRemap: new Map()
461485
};
462486
}
463487
} catch (_) { }
464488
}
465489

466-
const notdef = new opentype.Glyph({
467-
name: '.notdef', unicode: 0, advanceWidth: EM, path: new opentype.Path()
468-
});
469-
const glyphs = [notdef];
470-
const existingCharToGlyph = new Map();
471-
490+
const existingGlyphsToKeep = [];
472491
if (existingFontBuffer && existingFontBuffer.byteLength > 0) {
473492
try {
474493
const existingFont = opentype.parse(existingFontBuffer);
@@ -477,20 +496,18 @@ function buildDrawingFont(uniqueDrawingsArray, existingFontBuffer, referencedCha
477496
if (!g || !g.unicode || g.unicode === 0) continue;
478497
const ch = String.fromCodePoint(g.unicode);
479498
if (!referencedCharsSet.has(ch)) continue;
480-
existingCharToGlyph.set(ch, g);
481-
glyphs.push(new opentype.Glyph({
482-
name: g.name || `draw_${g.unicode}`,
483-
unicode: g.unicode,
484-
advanceWidth: g.advanceWidth || EM,
485-
path: g.path
486-
}));
499+
existingGlyphsToKeep.push({ oldChar: ch, g });
487500
}
501+
existingGlyphsToKeep.sort((a, b) => a.g.unicode - b.g.unicode);
488502
} catch (_) { }
489503
}
490504

491-
const drawingDataToChar = {};
492-
493-
const usedCodepoints = new Set(glyphs.filter(g => g.unicode > 0).map(g => g.unicode));
505+
const notdef = new opentype.Glyph({
506+
name: '.notdef', unicode: 0, advanceWidth: EM, path: new opentype.Path()
507+
});
508+
const glyphs = [notdef];
509+
const charRemap = new Map();
510+
const usedCodepoints = new Set([0]);
494511
let safeIdx = 0;
495512
const getNextSafeChar = () => {
496513
while (true) {
@@ -499,6 +516,20 @@ function buildDrawingFont(uniqueDrawingsArray, existingFontBuffer, referencedCha
499516
}
500517
};
501518

519+
for (const { oldChar, g } of existingGlyphsToKeep) {
520+
const newChar = getNextSafeChar();
521+
const newCp = newChar.codePointAt(0);
522+
usedCodepoints.add(newCp);
523+
if (oldChar !== newChar) charRemap.set(oldChar, newChar);
524+
glyphs.push(new opentype.Glyph({
525+
name: g.name || `draw_${newCp}`,
526+
unicode: newCp,
527+
advanceWidth: g.advanceWidth || EM,
528+
path: g.path
529+
}));
530+
}
531+
532+
const drawingDataToChar = {};
502533
const sortedSubsets = Array.from(uniqueDrawingsArray).sort((a, b) => a.data.localeCompare(b.data));
503534
for (const item of sortedSubsets) {
504535
const data = item.data;
@@ -519,7 +550,8 @@ function buildDrawingFont(uniqueDrawingsArray, existingFontBuffer, referencedCha
519550
});
520551
return {
521552
ttf: new Uint8Array(font.toArrayBuffer()),
522-
dataToCharArr: Object.entries(drawingDataToChar).map(([d, c]) => ({ data: d, char: c }))
553+
dataToCharArr: Object.entries(drawingDataToChar).map(([d, c]) => ({ data: d, char: c })),
554+
charRemap
523555
};
524556
}
525557

@@ -701,12 +733,15 @@ function subsetFont(fontBuffer, charArray, fontName, isTTC, targetWeight, ttcInd
701733
};
702734
}
703735
function rewriteASS(rawContent, opts, id) {
704-
const { drawingDataToChar, drawFontFamily, drawTTF, embeddedFonts } = opts;
736+
const { drawingDataToChar, drawFontFamily, drawTTF, embeddedFonts, drawCharRemap } = opts;
705737
const blockRegex = /\r?\n(?=\[(?:Script Info|v4\+\s+Styles|v4\s+Styles|Styles|Events|Fonts|Graphics|Aegisub\s+(?:Extradata|Project\s+Garbage))\])/i;
706738
const blocks = rawContent.split(blockRegex);
707739
const totalBlocks = blocks.length;
708740
const processedBlocks = [];
709741
let fontInsertIndex = -1;
742+
const subsetStyles = new Set();
743+
let styleFmt = null;
744+
let eventFmt = null;
710745

711746
for (let i = 0; i < totalBlocks; i++) {
712747
if (i % 5 === 0) emitProgress(id, 'rewrite', i, totalBlocks);
@@ -720,11 +755,41 @@ function rewriteASS(rawContent, opts, id) {
720755
continue;
721756
}
722757

723-
if (header === 'events') {
758+
if (header.includes('styles')) {
759+
const lines = block.split(/\r?\n/);
760+
for (const l of lines) {
761+
if (/^format\s*:/i.test(l)) {
762+
const flds = l.replace(/^format\s*:/i, '').split(',').map(f => f.trim().toLowerCase());
763+
styleFmt = { nameIdx: flds.indexOf('name'), fontIdx: flds.indexOf('fontname') };
764+
} else if (/^style\s*:/i.test(l) && styleFmt) {
765+
const parts = l.replace(/^style\s*:/i, '').split(',');
766+
const sName = parts[styleFmt.nameIdx]?.trim();
767+
const sFont = normFont(parts[styleFmt.fontIdx]?.trim() || '');
768+
if (sName && sFont.toLowerCase() === drawFontFamily.toLowerCase()) {
769+
subsetStyles.add(sName);
770+
}
771+
}
772+
}
773+
processedBlocks.push(block);
774+
} else if (header === 'events') {
724775
const lines = block.split(/\r?\n/);
725776
const newLines = lines.map(l => {
726-
if (/^dialogue\s*:/i.test(l.trim())) {
727-
return replaceDrawingsInLine(l, drawingDataToChar, drawFontFamily);
777+
if (/^format\s*:/i.test(l)) {
778+
const flds = l.replace(/^format\s*:/i, '').split(',').map(f => f.trim().toLowerCase());
779+
eventFmt = { styleIdx: flds.indexOf('style'), textIdx: flds.indexOf('text') };
780+
return l;
781+
}
782+
if (/^dialogue\s*:/i.test(l.trim()) && eventFmt) {
783+
const rest = l.replace(/^dialogue\s*:/i, '');
784+
const parts = rest.split(',');
785+
const sName = parts[eventFmt.styleIdx]?.trim();
786+
const initialIsSubset = subsetStyles.has(sName);
787+
788+
let processed = replaceDrawingsInLine(l, drawingDataToChar, drawFontFamily);
789+
if (opts.drawCharRemap && opts.drawCharRemap.size > 0) {
790+
processed = renameSubsetCharsInLine(processed, opts.drawCharRemap, drawFontFamily, initialIsSubset);
791+
}
792+
return processed;
728793
}
729794
return l;
730795
});
@@ -756,6 +821,37 @@ function rewriteASS(rawContent, opts, id) {
756821
return processedBlocks.join('\n');
757822
}
758823

824+
function renameSubsetCharsInLine(line, charRemap, fontFamily, initialIsSubset) {
825+
const m = line.match(/^([^:]*?:\s*)(.*)$/s);
826+
if (!m) return line;
827+
const prefix = m[1];
828+
const content = m[2];
829+
const segs = content.split(/(\{[^}]*\})/);
830+
let isSubsetFont = !!initialIsSubset;
831+
let result = prefix;
832+
for (const seg of segs) {
833+
if (seg.startsWith('{')) {
834+
const inner = seg.slice(1, -1);
835+
const fm = inner.match(/\\fn([^\\}]*)/);
836+
if (fm) {
837+
const fn = normFont(fm[1].trim());
838+
isSubsetFont = fn.toLowerCase() === fontFamily.toLowerCase();
839+
}
840+
const rm = inner.match(/\\r([^\\}]*)/);
841+
if (rm) isSubsetFont = false;
842+
result += seg;
843+
} else {
844+
if (isSubsetFont) {
845+
let remapped = '';
846+
for (const ch of seg) remapped += charRemap.has(ch) ? charRemap.get(ch) : ch;
847+
result += remapped;
848+
} else {
849+
result += seg;
850+
}
851+
}
852+
}
853+
return result;
854+
}
759855
function replaceDrawingsInLine(line, dataToCharArr, fontFamily) {
760856
if (!dataToCharArr || dataToCharArr.length === 0) return line;
761857
const m = line.match(/^([^:]*?:\s*)(.*)$/);
@@ -831,7 +927,7 @@ function doConvert(data, id) {
831927
const { text, fonts, options } = data;
832928
emitLog(id, 'log.convert.start', 'info', {});
833929
const parsed = parseASSText(text, id);
834-
let drawTTF = null, drawingDataToChar = null;
930+
let drawTTF = null, drawingDataToChar = null, drawCharRemap = null;
835931
const drawFontFamily = DRAW_FONT_NAME;
836932
const embeddedFonts = [];
837933
if (options.wantDraw) {
@@ -850,6 +946,7 @@ function doConvert(data, id) {
850946
);
851947
drawTTF = result.ttf;
852948
drawingDataToChar = result.dataToCharArr;
949+
drawCharRemap = result.charRemap;
853950
emitLog(id, 'log.draw.done', 'ok', {
854951
size: (drawTTF.length / 1024).toFixed(1)
855952
});
@@ -917,7 +1014,8 @@ function doConvert(data, id) {
9171014
drawingDataToChar: drawingDataToChar,
9181015
drawFontFamily,
9191016
drawTTF,
920-
embeddedFonts: embeddedFonts
1017+
embeddedFonts: embeddedFonts,
1018+
drawCharRemap: drawCharRemap
9211019
}, id);
9221020
const origSize = new Blob([text]).size;
9231021
const newSize = new Blob(['\uFEFF' + finalText]).size;

0 commit comments

Comments
 (0)