mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Fix TTML ruby decoding to resolve styles by ID
The current code only works if the tts:ruby attributes are defined directly on the in-line elements. This changes that so we also consider tts:ruby attributes on `style` nodes referenced by ID. PiperOrigin-RevId: 319515177
This commit is contained in:
parent
08478d1163
commit
f39b1d0f90
@ -379,7 +379,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
regionOutput.setText(text);
|
||||
}
|
||||
if (resolvedStyle != null) {
|
||||
TtmlRenderUtil.applyStylesToSpan(text, start, end, resolvedStyle, parent);
|
||||
TtmlRenderUtil.applyStylesToSpan(text, start, end, resolvedStyle, parent, globalStyles);
|
||||
regionOutput
|
||||
.setTextAlignment(resolvedStyle.getTextAlign())
|
||||
.setVerticalType(resolvedStyle.getVerticalType());
|
||||
|
@ -78,7 +78,12 @@ import java.util.Map;
|
||||
}
|
||||
|
||||
public static void applyStylesToSpan(
|
||||
Spannable builder, int start, int end, TtmlStyle style, @Nullable TtmlNode parent) {
|
||||
Spannable builder,
|
||||
int start,
|
||||
int end,
|
||||
TtmlStyle style,
|
||||
@Nullable TtmlNode parent,
|
||||
Map<String, TtmlStyle> globalStyles) {
|
||||
|
||||
if (style.getStyle() != TtmlStyle.UNSPECIFIED) {
|
||||
builder.setSpan(new StyleSpan(style.getStyle()), start, end,
|
||||
@ -117,12 +122,12 @@ import java.util.Map;
|
||||
switch (style.getRubyType()) {
|
||||
case TtmlStyle.RUBY_TYPE_BASE:
|
||||
// look for the sibling RUBY_TEXT and add it as span between start & end.
|
||||
@Nullable TtmlNode containerNode = findRubyContainerNode(parent);
|
||||
@Nullable TtmlNode containerNode = findRubyContainerNode(parent, globalStyles);
|
||||
if (containerNode == null) {
|
||||
// No matching container node
|
||||
break;
|
||||
}
|
||||
@Nullable TtmlNode textNode = findRubyTextNode(containerNode);
|
||||
@Nullable TtmlNode textNode = findRubyTextNode(containerNode, globalStyles);
|
||||
if (textNode == null) {
|
||||
// no matching text node
|
||||
break;
|
||||
@ -200,12 +205,15 @@ import java.util.Map;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static TtmlNode findRubyTextNode(TtmlNode rubyContainerNode) {
|
||||
private static TtmlNode findRubyTextNode(
|
||||
TtmlNode rubyContainerNode, Map<String, TtmlStyle> globalStyles) {
|
||||
Deque<TtmlNode> childNodesStack = new ArrayDeque<>();
|
||||
childNodesStack.push(rubyContainerNode);
|
||||
while (!childNodesStack.isEmpty()) {
|
||||
TtmlNode childNode = childNodesStack.pop();
|
||||
if (childNode.style != null && childNode.style.getRubyType() == TtmlStyle.RUBY_TYPE_TEXT) {
|
||||
@Nullable
|
||||
TtmlStyle style = resolveStyle(childNode.style, childNode.getStyleIds(), globalStyles);
|
||||
if (style != null && style.getRubyType() == TtmlStyle.RUBY_TYPE_TEXT) {
|
||||
return childNode;
|
||||
}
|
||||
for (int i = childNode.getChildCount() - 1; i >= 0; i--) {
|
||||
@ -217,9 +225,10 @@ import java.util.Map;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static TtmlNode findRubyContainerNode(@Nullable TtmlNode node) {
|
||||
private static TtmlNode findRubyContainerNode(
|
||||
@Nullable TtmlNode node, Map<String, TtmlStyle> globalStyles) {
|
||||
while (node != null) {
|
||||
@Nullable TtmlStyle style = node.style;
|
||||
@Nullable TtmlStyle style = resolveStyle(node.style, node.getStyleIds(), globalStyles);
|
||||
if (style != null && style.getRubyType() == TtmlStyle.RUBY_TYPE_CONTAINER) {
|
||||
return node;
|
||||
}
|
||||
|
@ -656,15 +656,19 @@ public final class TtmlDecoderTest {
|
||||
|
||||
Spanned thirdCue = getOnlyCueTextAtTimeUs(subtitle, 30_000_000);
|
||||
assertThat(thirdCue.toString()).isEqualTo("Cue with annotated text.");
|
||||
assertThat(thirdCue).hasNoRubySpanBetween(0, thirdCue.length());
|
||||
assertThat(thirdCue).hasRubySpanBetween("Cue with ".length(), "Cue with annotated".length());
|
||||
|
||||
Spanned fourthCue = getOnlyCueTextAtTimeUs(subtitle, 40_000_000);
|
||||
assertThat(fourthCue.toString()).isEqualTo("Cue with text.");
|
||||
assertThat(fourthCue.toString()).isEqualTo("Cue with annotated text.");
|
||||
assertThat(fourthCue).hasNoRubySpanBetween(0, fourthCue.length());
|
||||
|
||||
Spanned fifthCue = getOnlyCueTextAtTimeUs(subtitle, 50_000_000);
|
||||
assertThat(fifthCue.toString()).isEqualTo("Cue with annotated text.");
|
||||
assertThat(fifthCue.toString()).isEqualTo("Cue with text.");
|
||||
assertThat(fifthCue).hasNoRubySpanBetween(0, fifthCue.length());
|
||||
|
||||
Spanned sixthCue = getOnlyCueTextAtTimeUs(subtitle, 60_000_000);
|
||||
assertThat(sixthCue.toString()).isEqualTo("Cue with annotated text.");
|
||||
assertThat(sixthCue).hasNoRubySpanBetween(0, sixthCue.length());
|
||||
}
|
||||
|
||||
private static Spanned getOnlyCueTextAtTimeUs(Subtitle subtitle, long timeUs) {
|
||||
|
30
testdata/src/test/assets/ttml/rubies.xml
vendored
30
testdata/src/test/assets/ttml/rubies.xml
vendored
@ -16,9 +16,15 @@
|
||||
-->
|
||||
<tt xmlns:ttm="http://www.w3.org/2006/10/ttaf1#metadata"
|
||||
xmlns:ttp="http://www.w3.org/2006/10/ttaf1#parameter"
|
||||
xmlns:tts="http://www.w3.org/2006/10/ttaf1#style"
|
||||
xmlns="http://www.w3.org/ns/ttml"
|
||||
xmlns:tts="http://www.w3.org/2006/10/ttaf1#style" xmlns="http://www.w3.org/ns/ttml"
|
||||
xmlns="http://www.w3.org/2006/10/ttaf1">
|
||||
<head>
|
||||
<styling>
|
||||
<style id="cont" tts:ruby="container" />
|
||||
<style id="base" tts:ruby="base" />
|
||||
<style id="text" tts:ruby="text" />
|
||||
</styling>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<!-- Base before and after text, one with explicit position -->
|
||||
@ -47,17 +53,29 @@
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<!-- No text section -> no span -->
|
||||
<!-- ruby info in style block -->
|
||||
<p begin="30s" end="38s">
|
||||
Cue with
|
||||
<span style="cont">
|
||||
<span style="base">annotated</span>
|
||||
<span style="text">rubies</span>
|
||||
text.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<!-- No text section -> no span -->
|
||||
<p begin="40s" end="48s">
|
||||
Cue with
|
||||
<span tts:ruby="container" tts:rubyPosition="before">
|
||||
<span tts:ruby="base">annotated</span>
|
||||
</span>
|
||||
text.</p>
|
||||
text.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<!-- No base section -> text still stripped-->
|
||||
<p begin="40s" end="48s">
|
||||
<p begin="50s" end="58s">
|
||||
Cue with
|
||||
<span tts:ruby="container" tts:rubyPosition="before">
|
||||
<span tts:ruby="text">rubies</span>
|
||||
@ -67,7 +85,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<!-- No container section -> text still stripped-->
|
||||
<p begin="50s" end="58s">
|
||||
<p begin="60s" end="68s">
|
||||
Cue with
|
||||
<span tts:ruby="text">rubies</span>
|
||||
<span tts:ruby="base">annotated</span>
|
||||
|
Loading…
x
Reference in New Issue
Block a user