diff --git a/automation/include/karaskel-auto4.lua b/automation/include/karaskel-auto4.lua index 39cd538f5..df019c149 100644 --- a/automation/include/karaskel-auto4.lua +++ b/automation/include/karaskel-auto4.lua @@ -116,21 +116,118 @@ function karaskel.preproc_line_text(meta, styles, line) line.kara = { n = 0 } line.furi = { n = 0 } + if styles[line.style] then + line.styleref = styles[line.style] + else + aegisub.debug.out(2, "WARNING: Style not found: " .. line.style .. "\n") + line.styleref = styles[1] + end + line.text_stripped = "" line.duration = line.end_time - line.start_time - - local worksyl = { } + + local worksyl = { highlights = {n=0} } local cur_inline_fx = "" for i = 0, #kara do local syl = kara[i] -- Detect any inline-fx tags - local inline_fx = syl.text:match("%{.*\\%-(.-)[}\\]") + local inline_fx = syl.text:match("%{.*\\%-([^}\\]-)") if inline_fx then cur_inline_fx = inline_fx end + -- Strip spaces (only basic ones, no fullwidth etc.) + local prespace, syltext, postspace = syl.text_stripped:match("^([ \t]*)(.-)([ \t]*)$") + + -- See if we've broken a (possible) multi-hl stretch + -- If we did it's time for a new worksyl (though never for the zero'th syllable) + local prefix = syltext:sub(1,unicode.charwidth(syltext,1)) + if prefix ~= "#" and prefix ~= "#" and i > 0 then + line.kara[line.kara.n] = worksyl + line.kara.n = line.kara.n + 1 + worksyl = { highlights = {n=0} } + end + + -- Detect furigana (both regular and fullwidth pipes work) + -- Furigana is stored independantly from syllables + if syltext:find("|") or syltext:find("|") then + -- Replace fullwidth pipes, they aren't regex friendly + syltext = syltext:gsub("|", "|") + -- Get before/after pipe text + local maintext, furitext = syltext:match("^(.-)|(.-)$") + syltext = maintext + + local furi = { } + furi.syl = worksyl + + -- Magic happens here + -- isbreak = Don't join this furi visually with previous furi, even if their main texts are adjacent + -- spillback = Allow this furi text to spill over the left edge of the main text + -- (Furi is always allowed to spill over the right edge of main text.) + local prefix = furitext:sub(1,unicode.charwidth(furitext,1)) + if prefix == "!" or prefix == "!" then + furi.isbreak = true -- Don't join with furi in previous syllable + furi.spillback = false -- Allow to "spill" furi over the left edge of main text + elseif prefix == "<" or prefix == "<" then + furi.isbreak = true + furi.spillback = true + else + furi.isbreak = false + furi.spillback = false + end + -- Remove the prefix character from furitext, if there was one + if furi.isbreak then + furitext = furitext:sub(unicode.charwidth(furitext,1)+1) + end + + furi.start_time = syl.start_time + furi.end_time = syl.end_time + furi.duration = syl.duration + furi.text = furitext + + line.furi.n = line.furi.n + 1 + line.furi[line.furi.n] = furi + end + + -- Always add highlight data + local hl = { + start_time = syl.start_time, + end_time = syl.end_time, + duration = syl.duration + } + worksyl.highlights.n = worksyl.highlights.n + 1 + worksyl.highlights[worksyl.highlights.n] = hl + + -- Syllables that aren't part of a multi-highlight generate a new output-syllable + if prefix ~= "#" and prefix ~= "#" then + -- Update stripped line-text + line.text_stripped = line.text_stripped .. prespace .. syltext .. postspace + + -- Copy data from syl to worksyl + worksyl.text = syl.text + worksyl.duration = syl.duration + worksyl.kdur = syl.duration / 10 + worksyl.start_time = syl.start_time + worksyl.end_time = syl.end_time + worksyl.tag = syl.tag + worksyl.line = line + worksyl.style = line.styleref + + -- And add new data to worksyl + worksyl.i = line.kara.n + worksyl.text_stripped = syltext + worksyl.inline_fx = cur_inline_fx + else + -- This is just an extra highlight + worksyl.duration = worksyl.duration + syl.duration + worksyl.end_time = syl.end_time + end end + + -- Add the last syllable + line.kara[line.kara.n] = worksyl + -- But don't increment n here, n should be the highest syllable index! (The zero'th syllable doesn't count.) end diff --git a/automation/tests/test-auto3-parsing.auto3 b/automation/tests/test-auto3-parsing.auto3 new file mode 100644 index 000000000..b80881bd9 --- /dev/null +++ b/automation/tests/test-auto3-parsing.auto3 @@ -0,0 +1,10 @@ +name = "Test auto3 parsing" +description = "Make sure it works" +version, kind = 3, "basic_ass" + +include "karaskel-base.auto3" + +function process_lines(meta, styles, lines, config) + karaskel.parse_syllable_data(meta, styles, lines) + return lines +end diff --git a/automation/tests/test-furi.lua b/automation/tests/test-furi.lua new file mode 100644 index 000000000..9d01164e3 --- /dev/null +++ b/automation/tests/test-furi.lua @@ -0,0 +1,42 @@ +script_name = "Test furigana parsing" +script_description = "Tests the Auto4/Lua karaskel furigana and multi-highlight parsing code by running it and dumping the result" +script_author = "jfs" + +include "karaskel.lua" + +function test_furi(subs) + aegisub.progress.task("Collecting header data") + local meta, styles = karaskel.collect_head(subs, true) -- make sure to create furigana styles + + aegisub.progress.task("Preprocessing lines") + for i = 1, #subs do + local l = subs[i] + if l.class == "dialogue" then + aegisub.progress.task(l.text) + karaskel.preproc_line_text(meta, styles, l) + + -- Dump the thing + aegisub.debug.out(4, "Line: %s\nStripped: %s\nDuration: %d\n", l.text, l.text_stripped, l.duration) + aegisub.debug.out(4, "Karaoke syllables: (%d)\n", l.kara.n) + for s = 0, l.kara.n do + local syl = l.kara[s] + aegisub.debug.out(4, "\tSyllable: text='%s' stripped='%s' duration=%d, start/end_time=%d/%d, inline_fx='%s', highlights=%d\n", syl.text, syl.text_stripped, syl.duration, syl.start_time, syl.end_time, syl.inline_fx, syl.highlights.n) + aegisub.debug.out(4, "\t\tHighlights:") + for h = 1, syl.highlights.n do + local hl = syl.highlights[h] + aegisub.debug.out(4, " %d-%d=%d", hl.start_time, hl.end_time, hl.duration) + end + aegisub.debug.out(4, "\n") + end + aegisub.debug.out(4, "Furigana parts: (%d)\n", l.furi.n) + for f = 1, l.furi.n do + local furi = l.furi[f] + aegisub.debug.out(4, "\tFurigana: text='%s', duration=%d, start/end_time=%d/%d, flags=%s%s, syl='%s'\n", furi.text, furi.duration, furi.start_time, furi.end_time, furi.isbreak and "b" or "", furi.spillback and "s" or "", furi.syl.text_stripped) + end + aegisub.debug.out(4, " - - - - - -\n") + end + end + aegisub.debug.out(4, "Done dumping!") +end + +aegisub.register_macro(script_name, script_description, test_furi)