Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 19 additions & 21 deletions src/libse/Common/ActorConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,12 @@ public string FixActorsFromActor(Paragraph p, int? changeCasing, SKColor? color)
public string FixActorsFromBeforeColon(Paragraph p, char ch, int? changeCasing, SKColor? color)
{
var sb = new StringBuilder();
var lineIdx = 0;
foreach (var line in p.Text.SplitToLines())
{
// index into the trimmed line - leading whitespace used to shift every cut by its width
var s = line.Trim();
var startIdx = line.IndexOf(ch);
var startIdx = s.IndexOf(ch);
if (startIdx > 0)
{
var actor = s.Substring(0, startIdx).Trim(' ', '-', '"');
Expand All @@ -97,34 +99,29 @@ public string FixActorsFromBeforeColon(Paragraph p, char ch, int? changeCasing,
{
actor = actor + ":";
}
else if (ToActor)
{
}

if (color.HasValue && !ToActor)
{
SetColor(_subtitleFormat, color.Value, actor);
actor = SetColor(_subtitleFormat, color.Value, actor);
}

if (ToSquare)
{
s = actor + " " + s.Substring(startIdx + 1).TrimStart(' ');
}
else if (ToParentheses)
if (ToActor)
{
s = actor + " " + s.Substring(startIdx + 1).TrimStart(' ');
if (lineIdx == 0)
{
p.Actor = actor;
}

s = s.Substring(startIdx + 1).TrimStart(' ');
}
else if (ToColon)
else
{
s = actor + " " + s.Substring(startIdx + 1).TrimStart(' ');
}
else if (ToActor)
{
s = s.Substring(startIdx + 1);
}
}

sb.AppendLine(s);
lineIdx++;
}

return sb.ToString().Trim();
Expand Down Expand Up @@ -269,6 +266,8 @@ private static string SetColor(SubtitleFormat format, SKColor color, string acto
return actor;
}

private static readonly string[] CommonTitles = { "Mr.", "Mrs.", "Dr." };

private bool IsActor(string s)
{
if (string.IsNullOrWhiteSpace(s))
Expand All @@ -294,15 +293,14 @@ private bool IsActor(string s)
return false;
}

if (word.Any(c => char.IsDigit(c) || (!char.IsLetter(c) && c != '-' && c != '\'')))
if (CommonTitles.Contains(word))
{
return false;
continue;
}

var commonTitles = new[] { "Mr.", "Mrs.", "Dr.", };
if (commonTitles.Contains(word))
if (word.Any(c => char.IsDigit(c) || (!char.IsLetter(c) && c != '-' && c != '\'')))
{
continue;
return false;
}

if (!_nameListInclMulti.Contains(word, StringComparer.OrdinalIgnoreCase))
Expand Down
6 changes: 3 additions & 3 deletions src/libse/Common/FixCasing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ private static string FixEnglishAloneILowerToUpper(string input)

if (text.StartsWith("I-if ", StringComparison.Ordinal))
{
text = text.Remove(0, 4).Insert(0, "I-If ");
text = text.Remove(0, 4).Insert(0, "I-If");
}

for (var indexOfI = text.IndexOf('i'); indexOfI >= 0; indexOfI = text.IndexOf('i', indexOfI + 1))
Expand Down Expand Up @@ -220,7 +220,7 @@ public static string FixStutter(string text)
}
else if (!tagOn && char.IsLetter(ch))
{
if (firstLetter && index < text.Length - 6 && char.IsUpper(text[index]) &&
if (firstLetter && index < text.Length - 5 && char.IsUpper(text[index]) &&
text[index + 1] == '-' && char.IsLower(text[index + 2]) && text[index] == char.ToUpperInvariant(text[index + 2]) &&
text[index + 3] == '-' && char.IsLower(text[index + 4]) && text[index] == char.ToUpperInvariant(text[index + 4]) &&
text[index + 5] != '-')
Expand All @@ -231,7 +231,7 @@ public static string FixStutter(string text)
sb.Append('-');
index += 4;
}
else if (firstLetter && index < text.Length - 4 && char.IsUpper(text[index]) &&
else if (firstLetter && index < text.Length - 3 && char.IsUpper(text[index]) &&
text[index + 1] == '-' && char.IsLower(text[index + 2]) && text[index] == char.ToUpperInvariant(text[index + 2]) &&
text[index + 3] != '-')
{
Expand Down
5 changes: 3 additions & 2 deletions src/libse/Common/TextEffect/KaraokeWordTransform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ public override string[] Transform(string text)
var karaoke = text.Substring(0, i) + fontClose + text.Substring(i);
lastFontCloseIdx = i;
result.Add(karaoke);
// skip only whitespace
while (i < len && char.IsWhiteSpace(text[i]))
// skip only whitespace - land on the last whitespace char so the loop's own
// i++ moves onto the next word (or tag) instead of past it
while (i + 1 < len && char.IsWhiteSpace(text[i + 1]))
{
i++;
}
Expand Down
7 changes: 5 additions & 2 deletions src/libse/Common/Utilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3424,8 +3424,11 @@ public static Subtitle LoadMatroskaSSA(MatroskaTrackInfo matroskaSubtitleInfo, s
{
text = text.Remove(0, idx); // remove ReadOrder
idx = text.IndexOf(',');
text = text.Insert(idx, "," + start + "," + end);
lines.Add("Dialogue: " + text);
if (idx >= 0)
{
text = text.Insert(idx, "," + start + "," + end);
lines.Add("Dialogue: " + text);
}
}
}
for (int commentIndex = 0; commentIndex < comments.Paragraphs.Count; commentIndex++)
Expand Down
5 changes: 3 additions & 2 deletions src/libse/SubtitleFormats/UnknownSubtitle33.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ public override string ToText(Subtitle subtitle, string title)

private static string EncodeTimeCode(TimeCode timeCode)
{
int seconds = (int)Math.Round(timeCode.Seconds + timeCode.Milliseconds / 1000.0);
return $"{timeCode.Hours:00}:{timeCode.Minutes:00}:{seconds:00}";
// round to whole seconds on the total so 59.6s carries into the minute instead of writing ":60"
var rounded = new TimeCode(Math.Round(timeCode.TotalMilliseconds / 1000.0) * 1000.0);
return $"{rounded.Hours:00}:{rounded.Minutes:00}:{rounded.Seconds:00}";
}

public override void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName)
Expand Down
5 changes: 3 additions & 2 deletions src/libse/SubtitleFormats/UnknownSubtitle34.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ public override string ToText(Subtitle subtitle, string title)

private static string EncodeTimeCode(TimeCode timeCode)
{
int seconds = (int)Math.Round(timeCode.Seconds + timeCode.Milliseconds / 1000.0);
return $"{timeCode.Hours:00}:{timeCode.Minutes:00}:{seconds:00}";
// round to whole seconds on the total so 59.6s carries into the minute instead of writing ":60"
var rounded = new TimeCode(Math.Round(timeCode.TotalMilliseconds / 1000.0) * 1000.0);
return $"{rounded.Hours:00}:{rounded.Minutes:00}:{rounded.Seconds:00}";
}

public override void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName)
Expand Down
5 changes: 3 additions & 2 deletions src/libse/SubtitleFormats/UnknownSubtitle59.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ public override string ToText(Subtitle subtitle, string title)

private static string EncodeTimeCode(TimeCode timeCode)
{
int seconds = (int)Math.Round(timeCode.Seconds + timeCode.Milliseconds / 1000.0);
return $"{timeCode.Hours:00}:{timeCode.Minutes:00}:{seconds:00}";
// round to whole seconds on the total so 59.6s carries into the minute instead of writing ":60"
var rounded = new TimeCode(Math.Round(timeCode.TotalMilliseconds / 1000.0) * 1000.0);
return $"{rounded.Hours:00}:{rounded.Minutes:00}:{rounded.Seconds:00}";
}

public override void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName)
Expand Down
2 changes: 1 addition & 1 deletion src/libuilogic/Ocr/FixEngine/OcrFixReplaceList2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,7 @@ public IEnumerable<string> CreateGuessesFromLetters(string word, string threeLet

foreach (var previousGuess in previousGuesses)
{
for (var i = 0; i < previousGuess.Length - letter.Length; i++)
for (var i = 0; i <= previousGuess.Length - letter.Length; i++)
{
if (previousGuess.AsSpan(i).StartsWith(letter, StringComparison.Ordinal))
{
Expand Down
4 changes: 4 additions & 0 deletions src/ui/Features/Tools/ConvertActors/ConvertActorsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ private void UpdatePreview()
{
var updatedVm = new SubtitleLineViewModel(vm);
updatedVm.Text = newText;
if (converter.ToActor)
{
updatedVm.Actor = p.Actor;
}
items.Add(new ConvertActorsDisplayItem(vm) { NewText = newText, IsChecked = true, UpdatedViewModel = updatedVm });
count++;
}
Expand Down
102 changes: 102 additions & 0 deletions tests/libse/Core/BugHunt20260823Test.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using Nikse.SubtitleEdit.Core.Common;
using Nikse.SubtitleEdit.Core.Common.TextEffect;
using Nikse.SubtitleEdit.Core.SubtitleFormats;
using SkiaSharp;

namespace LibSETests.Core;

public class BugHunt20260823Test
{
[Fact]
public void ActorConverter_ColonSource_AppliesColor()
{
var c = new ActorConverter(new SubRip(), "en") { ToSquare = true };
var p = new Paragraph { Text = "Joe: How are you?" };
var result = c.FixActorsFromBeforeColon(p, ':', null, SKColors.Red);
Assert.StartsWith("<font color=\"#ff0000", result);
Assert.EndsWith("\">[Joe]</font> How are you?", result);
}

[Fact]
public void ActorConverter_ColonSource_LeadingWhitespace()
{
var c = new ActorConverter(new SubRip(), "en") { ToSquare = true };
var p = new Paragraph { Text = " Joe: How are you?" };
var result = c.FixActorsFromBeforeColon(p, ':', null, null);
Assert.Equal("[Joe] How are you?", result);
}

[Fact]
public void ActorConverter_ColonSource_ToActor_SetsActor()
{
var c = new ActorConverter(new SubRip(), "en") { ToActor = true };
var p = new Paragraph { Text = "Joe: How are you?" };
var result = c.FixActorsFromBeforeColon(p, ':', null, null);
Assert.Equal("How are you?", result);
Assert.Equal("Joe", p.Actor);
}

[Fact]
public void ActorConverter_TitleIsAllowedInActorName()
{
var dataDir = Path.Combine(Path.GetTempPath(), "SeBugHunt20260823_" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(Path.Combine(dataDir, "Dictionaries"));
File.WriteAllText(Path.Combine(dataDir, "Dictionaries", "names.xml"), "<names><name>John</name><blacklist></blacklist></names>");
var oldDataDir = Configuration.DataDirectory;
Configuration.DataDirectory = dataDir;
try
{
var c = new ActorConverter(new SubRip(), "en") { ToSquare = true };
Assert.True(c.FixActors(new Paragraph { Text = "[John] How are you?" }, '[', ']', null, null).Selected);
Assert.True(c.FixActors(new Paragraph { Text = "[Mr. John] How are you?" }, '[', ']', null, null).Selected);
Assert.False(c.FixActors(new Paragraph { Text = "[Mr. 3] How are you?" }, '[', ']', null, null).Selected);
}
finally
{
Configuration.DataDirectory = oldDataDir;
Directory.Delete(dataDir, true);
}
}

[Fact]
public void FixCasing_IIf_NoDoubleSpace()
{
var fc = new FixCasing("en") { FixNormal = true, Format = new SubRip() };
var s = new Subtitle(new List<Paragraph> { new Paragraph("I-if you say so.", 0, 2000) });
fc.Fix(s);
Assert.Equal("I-If you say so.", s.Paragraphs[0].Text);
}

[Fact]
public void FixStutter_AtEndOfText()
{
Assert.Equal("N-N-No", FixCasing.FixStutter("N-n-no"));
Assert.Equal("N-No", FixCasing.FixStutter("N-no"));
}

[Fact]
public void UnknownSubtitle33_SecondsRoundingCarries()
{
var sub = new Subtitle();
sub.Paragraphs.Add(new Paragraph("Hello", new TimeCode(0, 0, 59, 600).TotalMilliseconds, new TimeCode(0, 1, 2, 0).TotalMilliseconds));
Assert.StartsWith("00:01:00", new UnknownSubtitle33().ToText(sub, "t"));
Assert.StartsWith("00:01:00", new UnknownSubtitle34().ToText(sub, "t"));
Assert.StartsWith("00:01:00", new UnknownSubtitle59().ToText(sub, "t"));
}

[Fact]
public void KaraokeWordTransform_TagAfterSpaceIsKeptIntact()
{
var res = new KaraokeWordTransform().Transform("<font color=\"#ffffff\">Hello <font face=\"Arial\">world</font>");
Assert.Equal(2, res.Length);
Assert.Equal("<font color=\"#ffffff\">Hello</font> <font face=\"Arial\">world</font>", res[0]);
Assert.Equal("<font color=\"#ffffff\">Hello <font face=\"Arial\">world</font></font>", res[1]);
}

[Fact]
public void KaraokeWordTransform_MultipleSpaces()
{
var res = new KaraokeWordTransform().Transform("a b c");
Assert.Equal(new[] { "a</font> b c", "a b</font> c", "a b c</font>" }, res);
}
}
Loading