@@ -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}
703735function rewriteASS ( rawContent , opts , id ) {
704- const { drawingDataToChar, drawFontFamily, drawTTF, embeddedFonts } = opts ;
736+ const { drawingDataToChar, drawFontFamily, drawTTF, embeddedFonts, drawCharRemap } = opts ;
705737 const blockRegex = / \r ? \n (? = \[ (?: S c r i p t I n f o | v 4 \+ \s + S t y l e s | v 4 \s + S t y l e s | S t y l e s | E v e n t s | F o n t s | G r a p h i c s | A e g i s u b \s + (?: E x t r a d a t a | P r o j e c t \s + G a r b a g e ) ) \] ) / 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 ( / ^ f o r m a t \s * : / i. test ( l ) ) {
762+ const flds = l . replace ( / ^ f o r m a t \s * : / i, '' ) . split ( ',' ) . map ( f => f . trim ( ) . toLowerCase ( ) ) ;
763+ styleFmt = { nameIdx : flds . indexOf ( 'name' ) , fontIdx : flds . indexOf ( 'fontname' ) } ;
764+ } else if ( / ^ s t y l e \s * : / i. test ( l ) && styleFmt ) {
765+ const parts = l . replace ( / ^ s t y l e \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 ( / ^ d i a l o g u e \s * : / i. test ( l . trim ( ) ) ) {
727- return replaceDrawingsInLine ( l , drawingDataToChar , drawFontFamily ) ;
777+ if ( / ^ f o r m a t \s * : / i. test ( l ) ) {
778+ const flds = l . replace ( / ^ f o r m a t \s * : / i, '' ) . split ( ',' ) . map ( f => f . trim ( ) . toLowerCase ( ) ) ;
779+ eventFmt = { styleIdx : flds . indexOf ( 'style' ) , textIdx : flds . indexOf ( 'text' ) } ;
780+ return l ;
781+ }
782+ if ( / ^ d i a l o g u e \s * : / i. test ( l . trim ( ) ) && eventFmt ) {
783+ const rest = l . replace ( / ^ d i a l o g u e \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 ( / \\ f n ( [ ^ \\ } ] * ) / ) ;
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+ }
759855function 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