Skip to content
Open
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
34 changes: 32 additions & 2 deletions src/BloomExe/Book/Book.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2628,7 +2628,13 @@ var chunk in text.InnerXml.Split(

/// <summary>
/// Convert old &lt;b&gt; and &lt;i&gt; to &lt;strong&gt; and &lt;em&gt; respectively.
/// Also remove instances like &lt;/b&gt;&lt;b&gt; altogether since such markup is redundant.
/// Remove instances like &lt;/b&gt;&lt;b&gt; altogether since such markup is redundant.
/// Cleanup instances like &lt;strong&gt;&lt;strong&gt;...&lt;/strong&gt;&lt;/strong&gt; to just &lt;strong&gt;...&lt;/strong&gt;.
/// Remove empty character markup like &lt;strong&gt;&lt;/strong&gt;, and seemingly empty markup that contains
/// only zero-width characters.
/// Doubled (nested) markup is handled only to one level, but that should be enough since Bloom can't
/// introduce such markup on its own. Handling general nested markup would require more than regular
/// expressions to handle properly.
/// </summary>
public static void UpdateCharacterStyleMarkup(HtmlDom bookDOM)
{
Expand Down Expand Up @@ -2667,19 +2673,43 @@ public static void UpdateCharacterStyleMarkup(HtmlDom bookDOM)
"<strong>$1</strong>",
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase
);
if (inner.IndexOf("<b>", StringComparison.OrdinalIgnoreCase) >= 0) // handle one level of nesting
inner = Regex.Replace(
inner,
@"<b>(.*?)</b>",
"<strong>$1</strong>",
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase
);
inner = Regex.Replace(
inner,
@"<i>(.*?)</i>",
"<em>$1</em>",
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase
);
if (inner.IndexOf("<i>", StringComparison.OrdinalIgnoreCase) >= 0) // handle one level of nesting
inner = Regex.Replace(
inner,
@"<i>(.*?)</i>",
"<em>$1</em>",
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase
);
// Replace doubled (nested) markup with single markup. This shouldn't happen, but it
// has been seen in the wild, possibly as a result of pasting text. (BL-16387)
// Only one level of nesting is handled, but that should (almost always) be enough.
inner = Regex.Replace(
inner,
@"<(strong|em|u)>([^<>]*)<\1>([^<>]*)</\1>([^<>]*)</\1>",
"<$1>$2$3$4</$1>",
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase
);
Comment thread
greptile-apps[bot] marked this conversation as resolved.
// Remove empty (or essentially empty) character markup tags. (BL-16387)
// strong em sup u, empty or zero-width characters in between are all considered empty.
// \u200B = zero-width space, \u200C = zero-width non-joiner, \u200D = zero-width joiner
inner = Regex.Replace(
inner,
@"<(strong|em|sup|u)>(\u200B|\u200C|\u200D)*</\1>",
""
"",
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase
);
if (inner != para.InnerXml)
para.InnerXml = inner;
Expand Down
76 changes: 76 additions & 0 deletions src/BloomTests/Book/BookTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3179,6 +3179,82 @@ public void UpdateCharacterStyleMarkup_LeavesPlainTextUnchanged()
);
}

[Test]
public void UpdateCharacterStyleMarkup_ReducesNestingOfSameTags()
{
var dom = new HtmlDom(
"<html><body><div class='bloom-editable'><p><b>some <b style='color:red'>text</b> here</b></p></div></body></html>"
);
Bloom.Book.Book.UpdateCharacterStyleMarkup(dom);
Assert.That(
GetFirstEditableParagraph(dom).InnerXml,
Is.EqualTo("<strong>some text here</strong>")
);
var dom1 = new HtmlDom(
"<html><body><div class='bloom-editable'><p><i><i style='color:red'>text</i></i></p></div></body></html>"
);
Bloom.Book.Book.UpdateCharacterStyleMarkup(dom1);
Assert.That(GetFirstEditableParagraph(dom1).InnerXml, Is.EqualTo("<em>text</em>"));
var dom2 = new HtmlDom(
"<html><body><div class='bloom-editable'><p><sup><sup style='color:red'>text</sup></sup></p></div></body></html>"
);
Bloom.Book.Book.UpdateCharacterStyleMarkup(dom2);
// <sup> does have a nesting effect, so we don't collapse them. We just remove the attributes from the inner tag.
Assert.That(
GetFirstEditableParagraph(dom2).InnerXml,
Is.EqualTo("<sup><sup>text</sup></sup>")
);
var dom3 = new HtmlDom(
"<html><body><div class='bloom-editable'><p>This is <u><u style='color:red'>some text.</u></u></p></div></body></html>"
);
Bloom.Book.Book.UpdateCharacterStyleMarkup(dom3);
Assert.That(
GetFirstEditableParagraph(dom3).InnerXml,
Is.EqualTo("This is <u>some text.</u>")
);
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.

[Test]
public void UpdateCharacterStyleMarkup_HandlesMultipleTags()
{
var dom = new HtmlDom(
"<html><body><div class='bloom-editable'><p><b>text</b>, <b><i>more text</i></b> and <i><b>yet more.</b></i></p></div></body></html>"
);
Bloom.Book.Book.UpdateCharacterStyleMarkup(dom);
Assert.That(
GetFirstEditableParagraph(dom).InnerXml,
Is.EqualTo(
"<strong>text</strong>, <strong><em>more text</em></strong> and <em><strong>yet more.</strong></em>"
)
);
var dom1 = new HtmlDom(
"<html><body><div class='bloom-editable'><p><i><i>text</i></i> with <i><i>more text</i></i></p></div></body></html>"
);
Bloom.Book.Book.UpdateCharacterStyleMarkup(dom1);
Assert.That(
GetFirstEditableParagraph(dom1).InnerXml,
Is.EqualTo("<em>text</em> with <em>more text</em>")
);
var dom2 = new HtmlDom(
"<html><body><div class='bloom-editable'><p><sup>number</sup> of <sup style='color:red'>text</sup> with <sup>xyz</sup></p></div></body></html>"
);
Bloom.Book.Book.UpdateCharacterStyleMarkup(dom2);
Assert.That(
GetFirstEditableParagraph(dom2).InnerXml,
Is.EqualTo("<sup>number</sup> of <sup>text</sup> with <sup>xyz</sup>")
);
var dom3 = new HtmlDom(
"<html><body><div class='bloom-editable'><p>This is <b><b>some text.</b></b> x <b>More stuff</b> of <b>sorts</b></p></div></body></html>"
);
Bloom.Book.Book.UpdateCharacterStyleMarkup(dom3);
Assert.That(
GetFirstEditableParagraph(dom3).InnerXml,
Is.EqualTo(
"This is <strong>some text.</strong> x <strong>More stuff</strong> of <strong>sorts</strong>"
)
);
}

[Test]
public void BringBookUpToDate_FixesDuplicateAudioIds_SentenceRecording()
{
Expand Down