diff --git a/library/src/androidTest/assets/ttml/multiple_regions.xml b/library/src/androidTest/assets/ttml/multiple_regions.xml
index edc704d8b1..3bde2c99b5 100644
--- a/library/src/androidTest/assets/ttml/multiple_regions.xml
+++ b/library/src/androidTest/assets/ttml/multiple_regions.xml
@@ -8,7 +8,7 @@
-
+
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 ce2282ab86..04686b959c 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
@@ -156,10 +156,13 @@ public final class TtmlParserTest extends InstrumentationTestCase {
assertEquals("lorem", ttmlCue.text.toString());
assertEquals(10.f / 100.f, ttmlCue.position);
assertEquals(10.f / 100.f, ttmlCue.line);
+ assertEquals(20.f / 100.f, ttmlCue.size);
+
ttmlCue = output.get(1);
assertEquals("amet", ttmlCue.text.toString());
assertEquals(60.f / 100.f, ttmlCue.position);
assertEquals(10.f / 100.f, ttmlCue.line);
+ assertEquals(20.f / 100.f, ttmlCue.size);
output = subtitle.getCues(5000000);
assertEquals(1, output.size());
@@ -167,6 +170,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
assertEquals("ipsum", ttmlCue.text.toString());
assertEquals(40.f / 100.f, ttmlCue.position);
assertEquals(40.f / 100.f, ttmlCue.line);
+ assertEquals(20.f / 100.f, ttmlCue.size);
output = subtitle.getCues(9000000);
assertEquals(1, output.size());
@@ -174,6 +178,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
assertEquals("dolor", ttmlCue.text.toString());
assertEquals(10.f / 100.f, ttmlCue.position);
assertEquals(80.f / 100.f, ttmlCue.line);
+ assertEquals(Cue.DIMEN_UNSET, ttmlCue.size);
output = subtitle.getCues(21000000);
assertEquals(1, output.size());
@@ -181,6 +186,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
assertEquals("She first said this", ttmlCue.text.toString());
assertEquals(45.f / 100.f, ttmlCue.position);
assertEquals(45.f / 100.f, ttmlCue.line);
+ assertEquals(35.f / 100.f, ttmlCue.size);
output = subtitle.getCues(25000000);
ttmlCue = output.get(0);
assertEquals("She first said this\nThen this", ttmlCue.text.toString());
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 4dd33407c1..abf9a4643c 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
@@ -53,6 +53,7 @@ import java.util.TreeSet;
public static final String ANONYMOUS_REGION_ID = "";
public static final String ATTR_ID = "id";
public static final String ATTR_TTS_BACKGROUND_COLOR = "backgroundColor";
+ public static final String ATTR_TTS_EXTENT = "extent";
public static final String ATTR_TTS_FONT_STYLE = "fontStyle";
public static final String ATTR_TTS_FONT_SIZE = "fontSize";
public static final String ATTR_TTS_FONT_FAMILY = "fontFamily";
@@ -180,7 +181,7 @@ import java.util.TreeSet;
for (Entry entry : regionOutputs.entrySet()) {
TtmlRegion region = regionMap.get(entry.getKey());
cues.add(new Cue(cleanUpText(entry.getValue()), null, region.line, Cue.TYPE_UNSET,
- Cue.TYPE_UNSET, region.position, Cue.TYPE_UNSET, Cue.DIMEN_UNSET));
+ Cue.TYPE_UNSET, region.position, Cue.TYPE_UNSET, region.width));
}
return cues;
}
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 984bc6d160..e0907d0364 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
@@ -17,6 +17,7 @@ package com.google.android.exoplayer.text.ttml;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.ParserException;
+import com.google.android.exoplayer.text.Cue;
import com.google.android.exoplayer.text.SimpleSubtitleParser;
import com.google.android.exoplayer.util.ColorParser;
import com.google.android.exoplayer.util.ParserUtil;
@@ -74,9 +75,8 @@ public final class TtmlParser extends SimpleSubtitleParser {
+ "(?:(\\.[0-9]+)|:([0-9][0-9])(?:\\.([0-9]+))?)?$");
private static final Pattern OFFSET_TIME =
Pattern.compile("^([0-9]+(?:\\.[0-9]+)?)(h|m|s|ms|f|t)$");
- private static final Pattern FONT_SIZE =
- Pattern.compile("^(([0-9]*.)?[0-9]+)(px|em|%)$");
- private static final Pattern ORIGIN_COORDINATES =
+ private static final Pattern FONT_SIZE = Pattern.compile("^(([0-9]*.)?[0-9]+)(px|em|%)$");
+ private static final Pattern PERCENTAGE_COORDINATES =
Pattern.compile("^(\\d+\\.?\\d*?)% (\\d+\\.?\\d*?)%$");
// TODO: read and apply the following attributes if specified.
@@ -183,23 +183,42 @@ public final class TtmlParser extends SimpleSubtitleParser {
return globalStyles;
}
+ /**
+ * Parses a region declaration. Supports origin and extent definition but only when defined in
+ * terms of percentage of the viewport. Regions that do not correctly declare origin are ignored.
+ */
private Pair parseRegionAttributes(XmlPullParser xmlParser) {
String regionId = ParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID);
String regionOrigin = ParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_ORIGIN);
+ String regionExtent = ParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_EXTENT);
if (regionOrigin == null || regionId == null) {
return null;
}
- Matcher originMatcher = ORIGIN_COORDINATES.matcher(regionOrigin);
+ float position = Cue.DIMEN_UNSET;
+ float line = Cue.DIMEN_UNSET;
+ Matcher originMatcher = PERCENTAGE_COORDINATES.matcher(regionOrigin);
if (originMatcher.matches()) {
try {
- float position = Float.parseFloat(originMatcher.group(1)) / 100.f;
- float line = Float.parseFloat(originMatcher.group(2)) / 100.f;
- return new Pair<>(regionId, new TtmlRegion(position, line));
+ position = Float.parseFloat(originMatcher.group(1)) / 100.f;
+ line = Float.parseFloat(originMatcher.group(2)) / 100.f;
} catch (NumberFormatException e) {
- Log.w(TAG, "Ignoring malformed region declaration: '" + regionOrigin + "'", e);
+ Log.w(TAG, "Ignoring region with malformed origin: '" + regionOrigin + "'", e);
+ position = Cue.DIMEN_UNSET;
}
}
- return null;
+ float width = Cue.DIMEN_UNSET;
+ if (regionExtent != null) {
+ Matcher extentMatcher = PERCENTAGE_COORDINATES.matcher(regionExtent);
+ if (extentMatcher.matches()) {
+ try {
+ width = Float.parseFloat(extentMatcher.group(1)) / 100.f;
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Ignoring malformed region extent: '" + regionExtent + "'", e);
+ }
+ }
+ }
+ return position != Cue.DIMEN_UNSET ? new Pair<>(regionId, new TtmlRegion(position, line, width))
+ : null;
}
private String[] parseStyleIds(String parentStyleIds) {
@@ -387,10 +406,11 @@ public final class TtmlParser extends SimpleSubtitleParser {
matcher = FONT_SIZE.matcher(expression);
} else if (expressions.length == 2){
matcher = FONT_SIZE.matcher(expressions[1]);
- Log.w(TAG, "multiple values in fontSize attribute. Picking the second "
- + "value for vertical font size and ignoring the first.");
+ Log.w(TAG, "Multiple values in fontSize attribute. Picking the second value for vertical font"
+ + " size and ignoring the first.");
} else {
- throw new ParserException();
+ throw new ParserException("Invalid number of entries for fontSize: " + expressions.length
+ + ".");
}
if (matcher.matches()) {
@@ -406,11 +426,11 @@ public final class TtmlParser extends SimpleSubtitleParser {
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PERCENT);
break;
default:
- throw new ParserException();
+ throw new ParserException("Invalid unit for fontSize: '" + unit + "'.");
}
out.setFontSize(Float.valueOf(matcher.group(1)));
} else {
- throw new ParserException();
+ throw new ParserException("Invalid expression for fontSize: '" + expression + "'.");
}
}
diff --git a/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlRegion.java b/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlRegion.java
index 757d134ad7..135018a861 100644
--- a/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlRegion.java
+++ b/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlRegion.java
@@ -24,14 +24,16 @@ import com.google.android.exoplayer.text.Cue;
public final float position;
public final float line;
+ public final float width;
public TtmlRegion() {
- this(Cue.DIMEN_UNSET, Cue.DIMEN_UNSET);
+ this(Cue.DIMEN_UNSET, Cue.DIMEN_UNSET, Cue.DIMEN_UNSET);
}
- public TtmlRegion(float position, float line) {
+ public TtmlRegion(float position, float line, float width) {
this.position = position;
this.line = line;
+ this.width = width;
}
}