From 0dd57de1f1459fc4208e83d0454e8aa93c61ceb7 Mon Sep 17 00:00:00 2001 From: ibaker Date: Mon, 11 May 2020 13:53:49 +0100 Subject: [PATCH] Parse TTML's tts:textAlign into Cue.textAlign, not an AlignmentSpan The tts:textAlign property only applies to

elements, which correspond 1:1 with ExoPlayer Cue objects, so we can use Cue.textAlignment to store this info instead of encoding it in the span-styled text. This will mean that TTML subtitles used with SubtitleView#setApplyEmbeddedStyles(false) will start respecting the tts:textAlign properties from the source data (currently this information is stripped when we remove all span styling). I think this is working-as-intended, we respect alignment of other subtitle types (e.g. WebVTT) when applyEmbeddedStyles=false. We also respect all other 'positioning' related properties in this case e.g. Cue.position and Cue.line. PiperOrigin-RevId: 310895499 --- .../exoplayer2/text/ttml/TtmlNode.java | 4 +- .../exoplayer2/text/ttml/TtmlRenderUtil.java | 12 ------ .../exoplayer2/text/ttml/TtmlDecoderTest.java | 40 ++++++++++++++++--- .../assets/ttml/inherit_global_and_parent.xml | 2 +- testdata/src/test/assets/ttml/text_align.xml | 29 ++++++++++++++ 5 files changed, 67 insertions(+), 20 deletions(-) create mode 100644 testdata/src/test/assets/ttml/text_align.xml diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlNode.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlNode.java index 7b1dda10fd..02019e8dd5 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlNode.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlNode.java @@ -380,7 +380,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } if (resolvedStyle != null) { TtmlRenderUtil.applyStylesToSpan(text, start, end, resolvedStyle, parent); - regionOutput.setVerticalType(resolvedStyle.getVerticalType()); + regionOutput + .setTextAlignment(resolvedStyle.getTextAlign()) + .setVerticalType(resolvedStyle.getVerticalType()); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRenderUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRenderUtil.java index e5ba2c9c1c..e343c57840 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRenderUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRenderUtil.java @@ -15,12 +15,10 @@ */ package com.google.android.exoplayer2.text.ttml; -import android.text.Layout.Alignment; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.style.AbsoluteSizeSpan; -import android.text.style.AlignmentSpan; import android.text.style.BackgroundColorSpan; import android.text.style.ForegroundColorSpan; import android.text.style.RelativeSizeSpan; @@ -162,16 +160,6 @@ import java.util.Map; // Do nothing break; } - - @Nullable Alignment textAlign = style.getTextAlign(); - if (textAlign != null) { - SpanUtil.addOrReplaceSpan( - builder, - new AlignmentSpan.Standard(textAlign), - start, - end, - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } if (style.getTextCombine()) { SpanUtil.addOrReplaceSpan( builder, diff --git a/library/core/src/test/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java b/library/core/src/test/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java index 071d34e5d0..9b21261f5f 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java @@ -60,6 +60,7 @@ public final class TtmlDecoderTest { private static final String BITMAP_REGION_FILE = "ttml/bitmap_percentage_region.xml"; private static final String BITMAP_PIXEL_REGION_FILE = "ttml/bitmap_pixel_region.xml"; private static final String BITMAP_UNSUPPORTED_REGION_FILE = "ttml/bitmap_unsupported_region.xml"; + private static final String TEXT_ALIGN_FILE = "ttml/text_align.xml"; private static final String VERTICAL_TEXT_FILE = "ttml/vertical_text.xml"; private static final String TEXT_COMBINE_FILE = "ttml/text_combine.xml"; private static final String RUBIES_FILE = "ttml/rubies.xml"; @@ -194,9 +195,6 @@ public final class TtmlDecoderTest { assertThat(firstCueText) .hasForegroundColorSpanBetween(0, firstCueText.length()) .withColor(ColorParser.parseTtmlColor("lime")); - assertThat(firstCueText) - .hasAlignmentSpanBetween(0, firstCueText.length()) - .withAlignment(Layout.Alignment.ALIGN_CENTER); Spanned secondCueText = getOnlyCueTextAtTimeUs(subtitle, 20_000_000); assertThat(secondCueText.toString()).isEqualTo("text 2"); @@ -210,9 +208,6 @@ public final class TtmlDecoderTest { assertThat(secondCueText) .hasForegroundColorSpanBetween(0, secondCueText.length()) .withColor(0xFFFFFF00); - assertThat(secondCueText) - .hasAlignmentSpanBetween(0, secondCueText.length()) - .withAlignment(Layout.Alignment.ALIGN_CENTER); } @Test @@ -575,6 +570,39 @@ public final class TtmlDecoderTest { assertThat(cue.bitmapHeight).isEqualTo(Cue.DIMEN_UNSET); } + @Test + public void textAlign() throws IOException, SubtitleDecoderException { + TtmlSubtitle subtitle = getSubtitle(TEXT_ALIGN_FILE); + + Cue firstCue = getOnlyCueAtTimeUs(subtitle, 10_000_000); + assertThat(firstCue.text.toString()).isEqualTo("Start alignment"); + assertThat(firstCue.textAlignment).isEqualTo(Layout.Alignment.ALIGN_NORMAL); + + Cue secondCue = getOnlyCueAtTimeUs(subtitle, 20_000_000); + assertThat(secondCue.text.toString()).isEqualTo("Left alignment"); + assertThat(secondCue.textAlignment).isEqualTo(Layout.Alignment.ALIGN_NORMAL); + + Cue thirdCue = getOnlyCueAtTimeUs(subtitle, 30_000_000); + assertThat(thirdCue.text.toString()).isEqualTo("Center alignment"); + assertThat(thirdCue.textAlignment).isEqualTo(Layout.Alignment.ALIGN_CENTER); + + Cue fourthCue = getOnlyCueAtTimeUs(subtitle, 40_000_000); + assertThat(fourthCue.text.toString()).isEqualTo("Right alignment"); + assertThat(fourthCue.textAlignment).isEqualTo(Layout.Alignment.ALIGN_OPPOSITE); + + Cue fifthCue = getOnlyCueAtTimeUs(subtitle, 50_000_000); + assertThat(fifthCue.text.toString()).isEqualTo("End alignment"); + assertThat(fifthCue.textAlignment).isEqualTo(Layout.Alignment.ALIGN_OPPOSITE); + + Cue sixthCue = getOnlyCueAtTimeUs(subtitle, 60_000_000); + assertThat(sixthCue.text.toString()).isEqualTo("Justify alignment (unsupported)"); + assertThat(sixthCue.textAlignment).isNull(); + + Cue seventhCue = getOnlyCueAtTimeUs(subtitle, 70_000_000); + assertThat(seventhCue.text.toString()).isEqualTo("No textAlign property"); + assertThat(seventhCue.textAlignment).isNull(); + } + @Test public void verticalText() throws IOException, SubtitleDecoderException { TtmlSubtitle subtitle = getSubtitle(VERTICAL_TEXT_FILE); diff --git a/testdata/src/test/assets/ttml/inherit_global_and_parent.xml b/testdata/src/test/assets/ttml/inherit_global_and_parent.xml index 126bfcdba2..50ef19440d 100644 --- a/testdata/src/test/assets/ttml/inherit_global_and_parent.xml +++ b/testdata/src/test/assets/ttml/inherit_global_and_parent.xml @@ -14,7 +14,7 @@ tts:color="yellow"/> - +

+ +
+

Start alignment

+
+
+

Left alignment

+
+
+

Center alignment

+
+
+

Right alignment

+
+
+

End alignment

+
+
+

Justify alignment (unsupported)

+
+
+

No textAlign property

+
+ +