diff --git a/library/src/androidTest/assets/ttml/chain_multiple_styles.xml b/library/src/androidTest/assets/ttml/chain_multiple_styles.xml
index d4de3c8ef9..7bcce6527d 100644
--- a/library/src/androidTest/assets/ttml/chain_multiple_styles.xml
+++ b/library/src/androidTest/assets/ttml/chain_multiple_styles.xml
@@ -18,6 +18,9 @@
+
diff --git a/library/src/androidTest/assets/ttml/inherit_multiple_styles.xml b/library/src/androidTest/assets/ttml/inherit_multiple_styles.xml
index 3320db70b2..3ee089c3ff 100644
--- a/library/src/androidTest/assets/ttml/inherit_multiple_styles.xml
+++ b/library/src/androidTest/assets/ttml/inherit_multiple_styles.xml
@@ -26,20 +26,20 @@
text 1
diff --git a/library/src/androidTest/assets/ttml/instance_creation.xml b/library/src/androidTest/assets/ttml/instance_creation.xml
deleted file mode 100644
index 0916002e52..0000000000
--- a/library/src/androidTest/assets/ttml/instance_creation.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/library/src/androidTest/assets/ttml/non_inheritable_properties.xml b/library/src/androidTest/assets/ttml/non_inheritable_properties.xml
deleted file mode 100644
index b16e63e674..0000000000
--- a/library/src/androidTest/assets/ttml/non_inheritable_properties.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
diff --git a/library/src/androidTest/java/com/google/android/exoplayer/text/ttml/TtmlParserTest.java b/library/src/androidTest/java/com/google/android/exoplayer/text/ttml/TtmlParserTest.java
index 804dca8b15..9d5d953977 100644
--- a/library/src/androidTest/java/com/google/android/exoplayer/text/ttml/TtmlParserTest.java
+++ b/library/src/androidTest/java/com/google/android/exoplayer/text/ttml/TtmlParserTest.java
@@ -15,12 +15,24 @@
*/
package com.google.android.exoplayer.text.ttml;
+import com.google.android.exoplayer.text.Cue;
+
import android.graphics.Color;
import android.test.InstrumentationTestCase;
import android.text.Layout;
+import android.text.SpannableStringBuilder;
+import android.text.style.AlignmentSpan;
+import android.text.style.BackgroundColorSpan;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.TypefaceSpan;
+import android.text.style.UnderlineSpan;
import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
/**
* Unit test for {@link TtmlParser}.
@@ -35,16 +47,12 @@ public final class TtmlParserTest extends InstrumentationTestCase {
"ttml/inherit_and_override_style.xml";
private static final String INHERIT_GLOBAL_AND_PARENT_TTML_FILE =
"ttml/inherit_global_and_parent.xml";
- private static final String NON_INHERTABLE_PROPERTIES_TTML_FILE =
- "ttml/non_inheritable_properties.xml";
private static final String INHERIT_MULTIPLE_STYLES_TTML_FILE =
"ttml/inherit_multiple_styles.xml";
private static final String CHAIN_MULTIPLE_STYLES_TTML_FILE =
"ttml/chain_multiple_styles.xml";
private static final String NO_UNDERLINE_LINETHROUGH_TTML_FILE =
"ttml/no_underline_linethrough.xml";
- private static final String INSTANCE_CREATION_TTML_FILE =
- "ttml/instance_creation.xml";
private static final String NAMESPACE_CONFUSION_TTML_FILE =
"ttml/namespace_confusion.xml";
private static final String NAMESPACE_NOT_DECLARED_TTML_FILE =
@@ -67,143 +75,53 @@ public final class TtmlParserTest extends InstrumentationTestCase {
}
public void testInheritInlineAttributes() throws IOException {
-
TtmlSubtitle subtitle = getSubtitle(INLINE_ATTRIBUTES_TTML_FILE);
assertEquals(4, subtitle.getEventTimeCount());
-
- TtmlNode root = subtitle.getRoot();
- // inherite inline attributes
- TtmlNode body = queryChildrenForTag(root, TtmlNode.TAG_BODY, 0);
- TtmlNode secondDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 1);
- TtmlStyle secondPStyle = queryChildrenForTag(secondDiv, TtmlNode.TAG_P, 0).style;
- assertEquals(Color.parseColor("lime"), secondPStyle.getColor());
- assertFalse(secondPStyle.hasBackgroundColorSpecified());
- assertEquals(0, secondPStyle.getBackgroundColor());
- assertEquals("sansSerif", secondPStyle.getFontFamily());
- assertEquals(TtmlStyle.STYLE_ITALIC, secondPStyle.getStyle());
- assertTrue(secondPStyle.isLinethrough());
+ assertSpans(subtitle, 20, "text 2", "sansSerif", TtmlStyle.STYLE_ITALIC,
+ Color.CYAN, Color.parseColor("lime"), false, true, null);
}
public void testInheritGlobalStyle() throws IOException {
TtmlSubtitle subtitle = getSubtitle(INHERIT_STYLE_TTML_FILE);
assertEquals(2, subtitle.getEventTimeCount());
-
- TtmlNode root = subtitle.getRoot();
-
- TtmlNode body = queryChildrenForTag(root, TtmlNode.TAG_BODY, 0);
- TtmlNode firstDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 0);
- TtmlStyle firstPStyle = queryChildrenForTag(firstDiv, TtmlNode.TAG_P, 0).style;
- assertEquals(Color.parseColor("yellow"), firstPStyle.getColor());
- assertEquals(Color.parseColor("blue"), firstPStyle.getBackgroundColor());
- assertEquals("serif", firstPStyle.getFontFamily());
- assertEquals(TtmlStyle.STYLE_BOLD_ITALIC, firstPStyle.getStyle());
- assertTrue(firstPStyle.isUnderline());
+ assertSpans(subtitle, 10, "text 1", "serif", TtmlStyle.STYLE_BOLD_ITALIC,
+ Color.BLUE, Color.YELLOW, true, false, null);
}
public void testInheritGlobalStyleOverriddenByInlineAttributes() throws IOException {
TtmlSubtitle subtitle = getSubtitle(INHERIT_STYLE_OVERRIDE_TTML_FILE);
assertEquals(4, subtitle.getEventTimeCount());
- TtmlNode root = subtitle.getRoot();
-
- // first pNode inherits global style
- TtmlNode body = queryChildrenForTag(root, TtmlNode.TAG_BODY, 0);
- TtmlNode firstDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 0);
- TtmlStyle firstPStyle = queryChildrenForTag(firstDiv, TtmlNode.TAG_P, 0).style;
- assertEquals(Color.parseColor("yellow"), firstPStyle.getColor());
- assertEquals(Color.parseColor("blue"), firstPStyle.getBackgroundColor());
- assertEquals("serif", firstPStyle.getFontFamily());
- assertEquals(TtmlStyle.STYLE_BOLD_ITALIC, firstPStyle.getStyle());
- assertTrue(firstPStyle.isUnderline());
-
- // second pNode inherits global style and overrides with attribute
- TtmlNode secondDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 1);
- TtmlStyle secondPStyle = queryChildrenForTag(secondDiv, TtmlNode.TAG_P, 0).style;
- assertEquals(Color.parseColor("yellow"), secondPStyle.getColor());
- assertEquals(Color.parseColor("red"), secondPStyle.getBackgroundColor());
- assertEquals("sansSerif", secondPStyle.getFontFamily());
- assertEquals(TtmlStyle.STYLE_ITALIC, secondPStyle.getStyle());
- assertTrue(secondPStyle.isUnderline());
+ assertSpans(subtitle, 10, "text 1", "serif", TtmlStyle.STYLE_BOLD_ITALIC, Color.BLUE,
+ Color.YELLOW, true, false, null);
+ assertSpans(subtitle, 20, "text 2", "sansSerif", TtmlStyle.STYLE_ITALIC, Color.RED,
+ Color.YELLOW, true, false, null);
}
public void testInheritGlobalAndParent() throws IOException {
TtmlSubtitle subtitle = getSubtitle(INHERIT_GLOBAL_AND_PARENT_TTML_FILE);
assertEquals(4, subtitle.getEventTimeCount());
- TtmlNode root = subtitle.getRoot();
-
- // first pNode inherits parent style
- TtmlNode body = queryChildrenForTag(root, TtmlNode.TAG_BODY, 0);
- TtmlNode firstDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 0);
- TtmlStyle firstPStyle = queryChildrenForTag(firstDiv, TtmlNode.TAG_P, 0).style;
-
- assertFalse(firstPStyle.hasBackgroundColorSpecified());
- assertEquals(0, firstPStyle.getBackgroundColor());
-
- assertEquals(Color.parseColor("lime"), firstPStyle.getColor());
- assertEquals("sansSerif", firstPStyle.getFontFamily());
- assertEquals(TtmlStyle.STYLE_NORMAL, firstPStyle.getStyle());
- assertTrue(firstPStyle.isLinethrough());
-
- // second pNode inherits parent style and overrides with global style
- TtmlNode secondDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 1);
- TtmlStyle secondPStyle = queryChildrenForTag(secondDiv, TtmlNode.TAG_P, 0).style;
- // attributes overridden by global style
- assertEquals(Color.parseColor("blue"), secondPStyle.getBackgroundColor());
- assertEquals(Color.parseColor("yellow"), secondPStyle.getColor());
- assertEquals(TtmlStyle.STYLE_BOLD_ITALIC, secondPStyle.getStyle());
- assertEquals("serif", secondPStyle.getFontFamily());
- assertTrue(secondPStyle.isUnderline());
- assertEquals(Layout.Alignment.ALIGN_CENTER, secondPStyle.getTextAlign());
- }
-
- public void testNonInheritablePropertiesAreNotInherited() throws IOException {
- TtmlSubtitle subtitle = getSubtitle(NON_INHERTABLE_PROPERTIES_TTML_FILE);
- assertEquals(2, subtitle.getEventTimeCount());
-
- TtmlNode root = subtitle.getRoot();
-
- TtmlNode body = queryChildrenForTag(root, TtmlNode.TAG_BODY, 0);
- TtmlNode firstDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 0);
- TtmlNode firstPStyle = queryChildrenForTag(firstDiv, TtmlNode.TAG_P, 0);
- TtmlStyle spanStyle = queryChildrenForTag(firstPStyle, TtmlNode.TAG_SPAN, 0).style;
-
- assertFalse("background color must not be inherited from a context node",
- spanStyle.hasBackgroundColorSpecified());
+ assertSpans(subtitle, 10, "text 1", "sansSerif", TtmlStyle.STYLE_NORMAL,
+ Color.RED, Color.parseColor("lime"), false, true, Layout.Alignment.ALIGN_CENTER);
+ assertSpans(subtitle, 20, "text 2", "serif", TtmlStyle.STYLE_BOLD_ITALIC,
+ Color.BLUE, Color.YELLOW, true, true, Layout.Alignment.ALIGN_CENTER);
}
public void testInheritMultipleStyles() throws IOException {
TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
assertEquals(12, subtitle.getEventTimeCount());
- TtmlNode root = subtitle.getRoot();
-
- TtmlNode body = queryChildrenForTag(root, TtmlNode.TAG_BODY, 0);
- TtmlNode firstDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 0);
- TtmlStyle firstPStyle = queryChildrenForTag(firstDiv, TtmlNode.TAG_P, 0).style;
-
- assertEquals(Color.parseColor("blue"), firstPStyle.getBackgroundColor());
- assertEquals(Color.parseColor("yellow"), firstPStyle.getColor());
- assertEquals("sansSerif", firstPStyle.getFontFamily());
- assertEquals(TtmlStyle.STYLE_BOLD_ITALIC, firstPStyle.getStyle());
- assertTrue(firstPStyle.isLinethrough());
+ assertSpans(subtitle, 10, "text 1", "sansSerif", TtmlStyle.STYLE_BOLD_ITALIC,
+ Color.BLUE, Color.YELLOW, false, true, null);
}
public void testInheritMultipleStylesWithoutLocalAttributes() throws IOException {
TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
assertEquals(12, subtitle.getEventTimeCount());
- TtmlNode root = subtitle.getRoot();
-
- TtmlNode body = queryChildrenForTag(root, TtmlNode.TAG_BODY, 0);
- TtmlNode secondDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 1);
- TtmlStyle firstPStyle = queryChildrenForTag(secondDiv, TtmlNode.TAG_P, 0).style;
-
- assertEquals(Color.parseColor("blue"), firstPStyle.getBackgroundColor());
- assertEquals(Color.parseColor("black"), firstPStyle.getColor());
- assertEquals("sansSerif", firstPStyle.getFontFamily());
- assertEquals(TtmlStyle.STYLE_BOLD_ITALIC, firstPStyle.getStyle());
- assertTrue(firstPStyle.isLinethrough());
+ assertSpans(subtitle, 20, "text 2", "sansSerif", TtmlStyle.STYLE_BOLD_ITALIC,
+ Color.BLUE, Color.BLACK, false, true, null);
}
@@ -211,21 +129,8 @@ public final class TtmlParserTest extends InstrumentationTestCase {
TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
assertEquals(12, subtitle.getEventTimeCount());
- TtmlNode root = subtitle.getRoot();
-
- TtmlNode body = queryChildrenForTag(root, TtmlNode.TAG_BODY, 0);
- TtmlNode thirdDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 2);
- TtmlStyle firstPStyle = queryChildrenForTag(thirdDiv, TtmlNode.TAG_P, 0).style;
-
- // inherit from first global style
- assertEquals(Color.parseColor("red"), firstPStyle.getBackgroundColor());
- // inherit from second global style
- assertTrue(firstPStyle.isLinethrough());
- // inherited from parent node
- assertEquals("sansSerifInline", firstPStyle.getFontFamily());
- assertEquals(TtmlStyle.STYLE_ITALIC, firstPStyle.getStyle());
- assertTrue(firstPStyle.isUnderline());
- assertEquals(Color.parseColor("yellow"), firstPStyle.getColor());
+ assertSpans(subtitle, 30, "text 2.5", "sansSerifInline", TtmlStyle.STYLE_ITALIC,
+ Color.RED, Color.YELLOW, true, true, null);
}
public void testEmptyStyleAttribute() throws IOException {
@@ -236,8 +141,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
TtmlNode body = queryChildrenForTag(root, TtmlNode.TAG_BODY, 0);
TtmlNode fourthDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 3);
- // no styles specified
- assertNull(queryChildrenForTag(fourthDiv, TtmlNode.TAG_P, 0).style);
+ assertNull(queryChildrenForTag(fourthDiv, TtmlNode.TAG_P, 0).getStyleIds());
}
public void testNonexistingStyleId() throws IOException {
@@ -248,11 +152,10 @@ public final class TtmlParserTest extends InstrumentationTestCase {
TtmlNode body = queryChildrenForTag(root, TtmlNode.TAG_BODY, 0);
TtmlNode fifthDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 4);
- // no styles specified
- assertNull(queryChildrenForTag(fifthDiv, TtmlNode.TAG_P, 0).style);
+ assertEquals(1, queryChildrenForTag(fifthDiv, TtmlNode.TAG_P, 0).getStyleIds().length);
}
- public void testNonExistingAndExistingStyleId() throws IOException {
+ public void testNonExistingAndExistingStyleIdWithRedundantSpaces() throws IOException {
TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
assertEquals(12, subtitle.getEventTimeCount());
@@ -260,28 +163,30 @@ public final class TtmlParserTest extends InstrumentationTestCase {
TtmlNode body = queryChildrenForTag(root, TtmlNode.TAG_BODY, 0);
TtmlNode sixthDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 5);
- // no styles specified
- TtmlStyle style = queryChildrenForTag(sixthDiv, TtmlNode.TAG_P, 0).style;
- assertNotNull(style);
- assertEquals(Color.RED, style.getBackgroundColor());
+ String[] styleIds = queryChildrenForTag(sixthDiv, TtmlNode.TAG_P, 0).getStyleIds();
+ assertEquals(2, styleIds.length);
}
public void testMultipleChaining() throws IOException {
TtmlSubtitle subtitle = getSubtitle(CHAIN_MULTIPLE_STYLES_TTML_FILE);
assertEquals(2, subtitle.getEventTimeCount());
- TtmlNode root = subtitle.getRoot();
- TtmlNode body = queryChildrenForTag(root, TtmlNode.TAG_BODY, 0);
- TtmlNode div = queryChildrenForTag(body, TtmlNode.TAG_DIV, 0);
+ Map globalStyles = subtitle.getGlobalStyles();
- // no styles specified
- TtmlStyle style = queryChildrenForTag(div, TtmlNode.TAG_P, 0).style;
+ TtmlStyle style = globalStyles.get("s2");
assertEquals("serif", style.getFontFamily());
assertEquals(Color.RED, style.getBackgroundColor());
assertEquals(Color.BLACK, style.getColor());
assertEquals(TtmlStyle.STYLE_BOLD_ITALIC, style.getStyle());
assertTrue(style.isLinethrough());
+ style = globalStyles.get("s3");
+ // only difference: color must be RED
+ assertEquals(Color.RED, style.getColor());
+ assertEquals("serif", style.getFontFamily());
+ assertEquals(Color.RED, style.getBackgroundColor());
+ assertEquals(TtmlStyle.STYLE_BOLD_ITALIC, style.getStyle());
+ assertTrue(style.isLinethrough());
}
public void testNoUnderline() throws IOException {
@@ -309,32 +214,6 @@ public final class TtmlParserTest extends InstrumentationTestCase {
style.isLinethrough());
}
-
- public void testOnlySingleInstance() throws IOException {
- TtmlSubtitle subtitle = getSubtitle(INSTANCE_CREATION_TTML_FILE);
- assertEquals(4, subtitle.getEventTimeCount());
-
- TtmlNode root = subtitle.getRoot();
- TtmlNode body = queryChildrenForTag(root, TtmlNode.TAG_BODY, 0);
- TtmlNode firstDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 0);
- TtmlNode secondDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 1);
- TtmlNode thirdDiv = queryChildrenForTag(body, TtmlNode.TAG_DIV, 2);
-
- TtmlNode firstP = queryChildrenForTag(firstDiv, TtmlNode.TAG_P, 0);
- TtmlNode secondP = queryChildrenForTag(secondDiv, TtmlNode.TAG_P, 0);
- TtmlNode secondSpan = queryChildrenForTag(secondP, TtmlNode.TAG_SPAN, 0);
- TtmlNode thirdP = queryChildrenForTag(thirdDiv, TtmlNode.TAG_P, 0);
- TtmlNode thirdSpan = queryChildrenForTag(secondP, TtmlNode.TAG_SPAN, 0);
-
- // inherit the same instance down the tree if possible
- assertSame(body.style, firstP.style);
- assertSame(firstP.style, secondP.style);
- assertSame(secondP.style, secondSpan.style);
-
- // if a backgroundColor is involved it does not help
- assertNotSame(thirdP.style.getInheritableStyle(), thirdSpan.style);
- }
-
public void testNamspaceConfusionDoesNotHurt() throws IOException {
TtmlSubtitle subtitle = getSubtitle(NAMESPACE_CONFUSION_TTML_FILE);
assertEquals(2, subtitle.getEventTimeCount());
@@ -373,6 +252,60 @@ public final class TtmlParserTest extends InstrumentationTestCase {
}
+ private void assertSpans(TtmlSubtitle subtitle, int second,
+ String text, String font, int fontStyle,
+ int backgroundColor, int color, boolean isUnderline,
+ boolean isLinethrough, Layout.Alignment alignment) {
+
+ long timeUs = second * 1000000;
+ List cues = subtitle.getCues(timeUs);
+
+ assertEquals(1, cues.size());
+ assertEquals(text, String.valueOf(cues.get(0).text));
+
+ assertEquals("single cue expected for timeUs: " + timeUs, 1, cues.size());
+ SpannableStringBuilder spannable = (SpannableStringBuilder) cues.get(0).text;
+
+ TypefaceSpan[] typefaceSpans = spannable.getSpans(0, spannable.length(), TypefaceSpan.class);
+ assertEquals(font, typefaceSpans[typefaceSpans.length - 1].getFamily());
+
+ StyleSpan[] styleSpans = spannable.getSpans(0, spannable.length(), StyleSpan.class);
+ assertEquals(fontStyle, styleSpans[styleSpans.length - 1].getStyle());
+
+ UnderlineSpan[] underlineSpans = spannable.getSpans(0, spannable.length(),
+ UnderlineSpan.class);
+ assertEquals(isUnderline ? "must be underlined" : "must not be underlined",
+ isUnderline ? 1 : 0, underlineSpans.length);
+
+ StrikethroughSpan[] striketroughSpans = spannable.getSpans(0, spannable.length(),
+ StrikethroughSpan.class);
+ assertEquals(isLinethrough ? "must be strikethrough" : "must not be strikethrough",
+ isLinethrough ? 1 : 0, striketroughSpans.length);
+
+ BackgroundColorSpan[] backgroundColorSpans =
+ spannable.getSpans(0, spannable.length(), BackgroundColorSpan.class);
+ if (backgroundColor != 0) {
+ assertEquals(backgroundColor, backgroundColorSpans[backgroundColorSpans.length - 1]
+ .getBackgroundColor());
+ } else {
+ assertEquals(0, backgroundColorSpans.length);
+ }
+
+ ForegroundColorSpan[] foregroundColorSpans =
+ spannable.getSpans(0, spannable.length(), ForegroundColorSpan.class);
+ assertEquals(color, foregroundColorSpans[foregroundColorSpans.length - 1].getForegroundColor());
+
+ if (alignment != null) {
+ AlignmentSpan.Standard[] alignmentSpans =
+ spannable.getSpans(0, spannable.length(), AlignmentSpan.Standard.class);
+ assertEquals(1, alignmentSpans.length);
+ assertEquals(alignment, alignmentSpans[0].getAlignment());
+ } else {
+ assertEquals(0, spannable.getSpans
+ (0, spannable.length(), AlignmentSpan.Standard.class).length);
+ }
+ }
+
private TtmlNode queryChildrenForTag(TtmlNode node, String tag, int pos) {
int count = 0;
for (int i = 0; i < node.getChildCount(); i++) {
diff --git a/library/src/androidTest/java/com/google/android/exoplayer/text/ttml/TtmlRenderUtilTest.java b/library/src/androidTest/java/com/google/android/exoplayer/text/ttml/TtmlRenderUtilTest.java
new file mode 100644
index 0000000000..bdfeda663b
--- /dev/null
+++ b/library/src/androidTest/java/com/google/android/exoplayer/text/ttml/TtmlRenderUtilTest.java
@@ -0,0 +1,111 @@
+package com.google.android.exoplayer.text.ttml;
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.graphics.Color;
+import android.test.InstrumentationTestCase;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Unit test for TtmlRenderUtil
+ */
+public class TtmlRenderUtilTest extends InstrumentationTestCase {
+
+ public void testResolveStyleNoStyleAtAll() {
+ assertNull(TtmlRenderUtil.resolveStyle(null, null, null));
+ }
+ public void testResolveStyleSingleReferentialStyle() {
+ Map globalStyles = getGlobalStyles();
+ String[] styleIds = {"s0"};
+
+ assertSame(globalStyles.get("s0"),
+ TtmlRenderUtil.resolveStyle(null, styleIds, globalStyles));
+ }
+ public void testResolveStyleMultipleReferentialStyles() {
+ Map globalStyles = getGlobalStyles();
+ String[] styleIds = {"s0", "s1"};
+
+ TtmlStyle resolved = TtmlRenderUtil.resolveStyle(null, styleIds, globalStyles);
+ assertNotSame(globalStyles.get("s0"), resolved);
+ assertNotSame(globalStyles.get("s1"), resolved);
+ assertNull(resolved.getId());
+
+ // inherited from s0
+ assertEquals(Color.BLACK, resolved.getBackgroundColor());
+ // inherited from s1
+ assertEquals(Color.RED, resolved.getColor());
+ // merged from s0 and s1
+ assertEquals(TtmlStyle.STYLE_BOLD_ITALIC, resolved.getStyle());
+ }
+
+ public void testResolveMergeSingleReferentialStyleIntoInlineStyle() {
+ Map globalStyles = getGlobalStyles();
+ String[] styleIds = {"s0"};
+ TtmlStyle style = new TtmlStyle();
+ style.setBackgroundColor(Color.YELLOW);
+
+ TtmlStyle resolved = TtmlRenderUtil.resolveStyle(style, styleIds, globalStyles);
+ assertSame(style, resolved);
+
+ // inline attribute not overridden
+ assertEquals(Color.YELLOW, resolved.getBackgroundColor());
+ // inherited from referential style
+ assertEquals(TtmlStyle.STYLE_BOLD, resolved.getStyle());
+ }
+
+
+ public void testResolveMergeMultipleReferentialStylesIntoInlineStyle() {
+ Map globalStyles = getGlobalStyles();
+ String[] styleIds = {"s0", "s1"};
+ TtmlStyle style = new TtmlStyle();
+ style.setBackgroundColor(Color.YELLOW);
+
+ TtmlStyle resolved = TtmlRenderUtil.resolveStyle(style, styleIds, globalStyles);
+ assertSame(style, resolved);
+
+ // inline attribute not overridden
+ assertEquals(Color.YELLOW, resolved.getBackgroundColor());
+ // inherited from both referential style
+ assertEquals(TtmlStyle.STYLE_BOLD_ITALIC, resolved.getStyle());
+ }
+
+ public void testResolveStyleOnlyInlineStyle() {
+ TtmlStyle inlineStyle = new TtmlStyle();
+ assertSame(inlineStyle, TtmlRenderUtil.resolveStyle(inlineStyle, null, null));
+ }
+
+ private Map getGlobalStyles() {
+ Map globalStyles = new HashMap<>();
+
+ TtmlStyle s0 = new TtmlStyle();
+ s0.setId("s0");
+ s0.setBackgroundColor(Color.BLACK);
+ s0.setBold(true);
+ globalStyles.put(s0.getId(), s0);
+
+ TtmlStyle s1 = new TtmlStyle();
+ s1.setId("s1");
+ s1.setBackgroundColor(Color.RED);
+ s1.setColor(Color.RED);
+ s1.setItalic(true);
+ globalStyles.put(s1.getId(), s1);
+
+ return globalStyles;
+ }
+
+}
diff --git a/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlNode.java b/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlNode.java
index 0eb282db5c..d393875c57 100644
--- a/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlNode.java
+++ b/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlNode.java
@@ -15,20 +15,12 @@
*/
package com.google.android.exoplayer.text.ttml;
-import android.text.Spannable;
import android.text.SpannableStringBuilder;
-import android.text.Spanned;
-import android.text.style.AlignmentSpan;
-import android.text.style.BackgroundColorSpan;
-import android.text.style.ForegroundColorSpan;
-import android.text.style.StrikethroughSpan;
-import android.text.style.StyleSpan;
-import android.text.style.TypefaceSpan;
-import android.text.style.UnderlineSpan;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.TreeSet;
/**
@@ -82,22 +74,28 @@ import java.util.TreeSet;
public final long startTimeUs;
public final long endTimeUs;
public final TtmlStyle style;
+ private String[] styleIds;
private List children;
+ private int start;
+ private int end;
- public static TtmlNode buildTextNode(String text, TtmlStyle style) {
- return new TtmlNode(null, applyTextElementSpacePolicy(text), UNDEFINED_TIME,
- UNDEFINED_TIME, style);
+ public static TtmlNode buildTextNode(String text) {
+ return new TtmlNode(null, TtmlRenderUtil.applyTextElementSpacePolicy(text), UNDEFINED_TIME,
+ UNDEFINED_TIME, null, null);
}
- public static TtmlNode buildNode(String tag, long startTimeUs, long endTimeUs, TtmlStyle style) {
- return new TtmlNode(tag, null, startTimeUs, endTimeUs, style);
+ public static TtmlNode buildNode(String tag, long startTimeUs, long endTimeUs,
+ TtmlStyle style, String[] styleIds) {
+ return new TtmlNode(tag, null, startTimeUs, endTimeUs, style, styleIds);
}
- private TtmlNode(String tag, String text, long startTimeUs, long endTimeUs, TtmlStyle style) {
+ private TtmlNode(String tag, String text, long startTimeUs, long endTimeUs,
+ TtmlStyle style, String[] styleIds) {
this.tag = tag;
this.text = text;
this.style = style;
+ this.styleIds = styleIds;
this.isTextNode = text != null;
this.startTimeUs = startTimeUs;
this.endTimeUs = endTimeUs;
@@ -159,8 +157,14 @@ import java.util.TreeSet;
}
}
- public CharSequence getText(long timeUs) {
- SpannableStringBuilder builder = getText(timeUs, new SpannableStringBuilder(), false);
+ public String[] getStyleIds() {
+ return styleIds;
+ }
+
+ public CharSequence getText(long timeUs, Map globalStyles) {
+ SpannableStringBuilder builder = new SpannableStringBuilder();
+ traverseForText(timeUs, builder, false);
+ traverseForStyle(builder, globalStyles);
// Having joined the text elements, we need to do some final cleanup on the result.
// 1. Collapse multiple consecutive spaces into a single space.
int builderLength = builder.length();
@@ -208,12 +212,12 @@ import java.util.TreeSet;
return builder;
}
- private SpannableStringBuilder getText(long timeUs, SpannableStringBuilder builder,
+ private SpannableStringBuilder traverseForText(long timeUs, SpannableStringBuilder builder,
boolean descendsPNode) {
+ start = builder.length();
+ end = start;
if (isTextNode && descendsPNode) {
- int start = builder.length();
builder.append(text);
- applyStylesToSpan(builder, start, builder.length(), style);
} else if (TAG_BR.equals(tag) && descendsPNode) {
builder.append('\n');
} else if (TAG_METADATA.equals(tag)) {
@@ -221,79 +225,27 @@ import java.util.TreeSet;
} else if (isActive(timeUs)) {
boolean isPNode = TAG_P.equals(tag);
for (int i = 0; i < getChildCount(); ++i) {
- getChild(i).getText(timeUs, builder, descendsPNode || isPNode);
+ getChild(i).traverseForText(timeUs, builder, descendsPNode || isPNode);
}
if (isPNode) {
- endParagraph(builder);
+ TtmlRenderUtil.endParagraph(builder);
}
+ end = builder.length();
}
return builder;
}
- private static void applyStylesToSpan(SpannableStringBuilder builder,
- int start, int end, TtmlStyle style) {
-
- if (style.getStyle() != TtmlStyle.UNSPECIFIED) {
- builder.setSpan(new StyleSpan(style.getStyle()), start, end,
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ private void traverseForStyle(SpannableStringBuilder builder,
+ Map globalStyles) {
+ if (start != end) {
+ TtmlStyle resolvedStyle = TtmlRenderUtil.resolveStyle(style, styleIds, globalStyles);
+ if (resolvedStyle != null) {
+ TtmlRenderUtil.applyStylesToSpan(builder, start, end, resolvedStyle);
+ }
+ for (int i = 0; i < getChildCount(); ++i) {
+ getChild(i).traverseForStyle(builder, globalStyles);
+ }
}
- if (style.isLinethrough()) {
- builder.setSpan(new StrikethroughSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- if (style.isUnderline()) {
- builder.setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- if (style.hasColorSpecified()) {
- builder.setSpan(new ForegroundColorSpan(style.getColor()), start, end,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- if (style.hasBackgroundColorSpecified()) {
- builder.setSpan(new BackgroundColorSpan(style.getBackgroundColor()), start, end,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- if (style.getFontFamily() != null) {
- builder.setSpan(new TypefaceSpan(style.getFontFamily()), start, end,
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- if (style.getTextAlign() != null) {
- builder.setSpan(new AlignmentSpan.Standard(style.getTextAlign()), start, end,
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- }
-
- /**
- * Invoked when the end of a paragraph is encountered. Adds a newline if there are one or more
- * non-space characters since the previous newline.
- *
- * @param builder The builder.
- */
- private static void endParagraph(SpannableStringBuilder builder) {
- int position = builder.length() - 1;
- while (position >= 0 && builder.charAt(position) == ' ') {
- position--;
- }
- if (position >= 0 && builder.charAt(position) != '\n') {
- builder.append('\n');
- }
- }
-
- /**
- * Applies the appropriate space policy to the given text element.
- *
- * @param in The text element to which the policy should be applied.
- * @return The result of applying the policy to the text element.
- */
- private static String applyTextElementSpacePolicy(String in) {
- // Removes carriage return followed by line feed. See: http://www.w3.org/TR/xml/#sec-line-ends
- String out = in.replaceAll("\r\n", "\n");
- // Apply suppress-at-line-break="auto" and
- // white-space-treatment="ignore-if-surrounding-linefeed"
- out = out.replaceAll(" *\n *", "\n");
- // Apply linefeed-treatment="treat-as-space"
- out = out.replaceAll("\n", " ");
- // Apply white-space-collapse="true"
- out = out.replaceAll("[ \t\\x0B\f\r]+", " ");
- return out;
}
}
diff --git a/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java b/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java
index 2379eed5bf..a9e6c1b99a 100644
--- a/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java
+++ b/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java
@@ -127,7 +127,7 @@ public final class TtmlParser implements SubtitleParser {
parseHeader(xmlParser, globalStyles);
} else {
try {
- TtmlNode node = parseNode(xmlParser, parent, globalStyles);
+ TtmlNode node = parseNode(xmlParser, parent);
nodeStack.addLast(node);
if (parent != null) {
parent.addChild(node);
@@ -143,10 +143,10 @@ public final class TtmlParser implements SubtitleParser {
}
}
} else if (eventType == XmlPullParser.TEXT) {
- parent.addChild(TtmlNode.buildTextNode(xmlParser.getText(), parent.style));
+ parent.addChild(TtmlNode.buildTextNode(xmlParser.getText()));
} else if (eventType == XmlPullParser.END_TAG) {
if (xmlParser.getName().equals(TtmlNode.TAG_TT)) {
- ttmlSubtitle = new TtmlSubtitle(nodeStack.getLast());
+ ttmlSubtitle = new TtmlSubtitle(nodeStack.getLast(), globalStyles);
}
nodeStack.removeLast();
}
@@ -176,7 +176,7 @@ public final class TtmlParser implements SubtitleParser {
String parentStyleId = xmlParser.getAttributeValue(null, ATTR_STYLE);
TtmlStyle style = parseStyleAttributes(xmlParser, new TtmlStyle());
if (parentStyleId != null) {
- String[] ids = parentStyleId.split(" ");
+ String[] ids = parseStyleIds(parentStyleId);
for (int i = 0; i < ids.length; i++) {
style.chain(globalStyles.get(ids[i]));
}
@@ -189,6 +189,10 @@ public final class TtmlParser implements SubtitleParser {
return globalStyles;
}
+ private String[] parseStyleIds(String parentStyleIds) {
+ return parentStyleIds.split("\\s+");
+ }
+
private TtmlStyle parseStyleAttributes(XmlPullParser parser, TtmlStyle style) {
int attributeCount = parser.getAttributeCount();
for (int i = 0; i < attributeCount; i++) {
@@ -282,23 +286,14 @@ public final class TtmlParser implements SubtitleParser {
return MimeTypes.APPLICATION_TTML.equals(mimeType);
}
- private TtmlNode parseNode(XmlPullParser parser, TtmlNode parent,
- Map globalStyles) throws ParserException {
+ private TtmlNode parseNode(XmlPullParser parser, TtmlNode parent) throws ParserException {
long duration = 0;
long startTime = TtmlNode.UNDEFINED_TIME;
long endTime = TtmlNode.UNDEFINED_TIME;
+ String[] styleIds = null;
int attributeCount = parser.getAttributeCount();
TtmlStyle style = parseStyleAttributes(parser, null);
- boolean hasInlineStyles = style != null;
- if (parent != null && parent.style != null) {
- if (hasInlineStyles) {
- style.inherit(parent.style);
- } else {
- style = parent.style.getInheritableStyle();
- }
- }
for (int i = 0; i < attributeCount; i++) {
- // TODO: check if it is safe to remove the namespace prefix
String attr = ParserUtil.removeNamespacePrefix(parser.getAttributeName(i));
String value = parser.getAttributeValue(i);
if (attr.equals(ATTR_BEGIN)) {
@@ -312,32 +307,10 @@ public final class TtmlParser implements SubtitleParser {
DEFAULT_FRAMERATE, DEFAULT_SUBFRAMERATE, DEFAULT_TICKRATE);
} else if (attr.equals(ATTR_STYLE)) {
// IDREFS: potentially multiple space delimited ids
- String[] ids = value.split(" ");
- if (style == null) {
- // use global style without overriding
- if (ids.length == 1) {
- style = globalStyles.get(value);
- } else if (ids.length > 1){
- style = new TtmlStyle();
- for (int j = 0; j < ids.length; j++) {
- style.chain(globalStyles.get(ids[j]));
- }
- }
- } else if (hasInlineStyles) {
- // local attributes inherits from global style
- for (int j = 0; j < ids.length; j++) {
- style.chain(globalStyles.get(ids[j]));
- }
- } else if (ids.length > 1 || (ids.length == 1 && style != globalStyles.get(ids[0]))) {
- // merge global style and parent styles
- TtmlStyle inheritedStyles = style;
- style = new TtmlStyle();
- for (int j = 0; j < ids.length; j++) {
- style.chain(globalStyles.get(ids[j]));
- }
- style.inherit(inheritedStyles);
+ String[] ids = parseStyleIds(value);
+ if (ids.length > 0) {
+ styleIds = ids;
}
-
} else {
// Do nothing.
}
@@ -359,7 +332,7 @@ public final class TtmlParser implements SubtitleParser {
endTime = parent.endTimeUs;
}
}
- return TtmlNode.buildNode(parser.getName(), startTime, endTime, style);
+ return TtmlNode.buildNode(parser.getName(), startTime, endTime, style, styleIds);
}
private static boolean isSupportedTag(String tag) {
diff --git a/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlRenderUtil.java b/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlRenderUtil.java
new file mode 100644
index 0000000000..638e47d276
--- /dev/null
+++ b/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlRenderUtil.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.exoplayer.text.ttml;
+
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.AlignmentSpan;
+import android.text.style.BackgroundColorSpan;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.TypefaceSpan;
+import android.text.style.UnderlineSpan;
+
+import java.util.Map;
+
+/**
+ * Package internal utility class to render styled TtmlNode
s.
+ */
+/* package */ final class TtmlRenderUtil {
+
+ /* spans which are always the same can be reused to avoid object creation */
+ private static final StrikethroughSpan STRIKETHROUGH_SPAN = new StrikethroughSpan();
+ private static final UnderlineSpan UNDERLINE_SPAN = new UnderlineSpan();
+ private static final StyleSpan[] STYLE_SPANS = new StyleSpan[] {
+ new StyleSpan(TtmlStyle.STYLE_NORMAL),
+ new StyleSpan(TtmlStyle.STYLE_BOLD),
+ new StyleSpan(TtmlStyle.STYLE_ITALIC),
+ new StyleSpan(TtmlStyle.STYLE_BOLD_ITALIC),
+ };
+
+ public static TtmlStyle resolveStyle(TtmlStyle style, String[] styleIds,
+ Map globalStyles) {
+ if (style == null && styleIds == null) {
+ // no styles at all
+ return null;
+ } else if (style == null && styleIds.length == 1) {
+ // only one single referential style present
+ return globalStyles.get(styleIds[0]);
+ } else if (style == null && styleIds.length > 1) {
+ // only multiple referential styles present
+ TtmlStyle chainedStyle = new TtmlStyle();
+ for (int i = 0; i < styleIds.length; i++) {
+ chainedStyle.chain(globalStyles.get(styleIds[i]));
+ }
+ return chainedStyle;
+ } else if (style != null && styleIds != null && styleIds.length == 1) {
+ // merge a single referential style into inline style
+ return style.chain(globalStyles.get(styleIds[0]));
+ } else if (style != null && styleIds != null && styleIds.length > 1) {
+ // merge multiple referential styles into inline style
+ for (int i = 0; i < styleIds.length; i++) {
+ style.chain(globalStyles.get(styleIds[i]));
+ }
+ return style;
+ }
+ // only inline styles available
+ return style;
+ }
+
+ public static void applyStylesToSpan(SpannableStringBuilder builder,
+ int start, int end, TtmlStyle style) {
+
+ if (style.getStyle() != TtmlStyle.UNSPECIFIED) {
+ builder.setSpan(STYLE_SPANS[style.getStyle()], start, end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ if (style.isLinethrough()) {
+ builder.setSpan(STRIKETHROUGH_SPAN, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ if (style.isUnderline()) {
+ builder.setSpan(UNDERLINE_SPAN, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ if (style.hasColorSpecified()) {
+ builder.setSpan(new ForegroundColorSpan(style.getColor()), start, end,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ if (style.hasBackgroundColorSpecified()) {
+ builder.setSpan(new BackgroundColorSpan(style.getBackgroundColor()), start, end,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ if (style.getFontFamily() != null) {
+ builder.setSpan(new TypefaceSpan(style.getFontFamily()), start, end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ if (style.getTextAlign() != null) {
+ builder.setSpan(new AlignmentSpan.Standard(style.getTextAlign()), start, end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+ /**
+ * Invoked when the end of a paragraph is encountered. Adds a newline if there are one or more
+ * non-space characters since the previous newline.
+ *
+ * @param builder The builder.
+ */
+ /* package */ static void endParagraph(SpannableStringBuilder builder) {
+ int position = builder.length() - 1;
+ while (position >= 0 && builder.charAt(position) == ' ') {
+ position--;
+ }
+ if (position >= 0 && builder.charAt(position) != '\n') {
+ builder.append('\n');
+ }
+ }
+
+ /**
+ * Applies the appropriate space policy to the given text element.
+ *
+ * @param in The text element to which the policy should be applied.
+ * @return The result of applying the policy to the text element.
+ */
+ /* package */ static String applyTextElementSpacePolicy(String in) {
+ // Removes carriage return followed by line feed. See: http://www.w3.org/TR/xml/#sec-line-ends
+ String out = in.replaceAll("\r\n", "\n");
+ // Apply suppress-at-line-break="auto" and
+ // white-space-treatment="ignore-if-surrounding-linefeed"
+ out = out.replaceAll(" *\n *", "\n");
+ // Apply linefeed-treatment="treat-as-space"
+ out = out.replaceAll("\n", " ");
+ // Apply white-space-collapse="true"
+ out = out.replaceAll("[ \t\\x0B\f\r]+", " ");
+ return out;
+ }
+
+ private TtmlRenderUtil() {}
+
+}
diff --git a/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlSubtitle.java b/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlSubtitle.java
index f29338cd43..a989bf86f8 100644
--- a/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlSubtitle.java
+++ b/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlSubtitle.java
@@ -21,6 +21,7 @@ import com.google.android.exoplayer.util.Util;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
/**
* A representation of a TTML subtitle.
@@ -29,9 +30,12 @@ public final class TtmlSubtitle implements Subtitle {
private final TtmlNode root;
private final long[] eventTimesUs;
+ private final Map globalStyles;
- public TtmlSubtitle(TtmlNode root) {
+ public TtmlSubtitle(TtmlNode root, Map globalStyles) {
this.root = root;
+ this.globalStyles = globalStyles != null
+ ? Collections.unmodifiableMap(globalStyles) : Collections.emptyMap();
this.eventTimesUs = root.getEventTimesUs();
}
@@ -63,7 +67,7 @@ public final class TtmlSubtitle implements Subtitle {
@Override
public List getCues(long timeUs) {
- CharSequence cueText = root.getText(timeUs);
+ CharSequence cueText = root.getText(timeUs, globalStyles);
if (cueText == null) {
return Collections.emptyList();
} else {
@@ -72,4 +76,8 @@ public final class TtmlSubtitle implements Subtitle {
}
}
+ /* @VisibleForTesting */
+ /* package */ Map getGlobalStyles() {
+ return globalStyles;
+ }
}