From 49dfe66bb3202f84b6928cdf69915ff0b2b54705 Mon Sep 17 00:00:00 2001 From: Denise LaFayette Date: Fri, 14 May 2021 16:35:43 -0700 Subject: [PATCH] Preserve Japanese language features when not applying embedded styling Fix bug where rubies and boutens are missing, tate-chu-yoko is rendered incorrectly when SubtitleView.setApplyEmbeddedStyles(false). This method should only affect styling elements and not remove any language features. --- .../android/exoplayer2/ui/SubtitleView.java | 7 ++- .../exoplayer2/ui/SubtitleViewUtils.java | 28 +++++++++ .../exoplayer2/ui/SubtitleViewUtilsTest.java | 63 +++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 library/ui/src/test/java/com/google/android/exoplayer2/ui/SubtitleViewUtilsTest.java diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java index c82cac109a..7908de8a60 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java @@ -386,7 +386,12 @@ public final class SubtitleView extends FrameLayout implements TextOutput { cue.buildUpon().setTextSize(Cue.DIMEN_UNSET, Cue.TYPE_UNSET).clearWindowColor(); if (cueText != null) { // Remove all spans, regardless of type. - strippedCue.setText(cueText.toString()); + strippedCue.setText(new SpannableString(cueText.toString())); + if (cueText instanceof Spanned) { + SubtitleViewUtils + .preserveJapaneseLanguageFeatures((SpannableString)strippedCue.getText(), + (Spanned) cueText); + } } return strippedCue.build(); } else if (!applyEmbeddedFontSizes) { diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleViewUtils.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleViewUtils.java index 24b5e30b2e..38dd6ff93c 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleViewUtils.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleViewUtils.java @@ -16,7 +16,13 @@ */ package com.google.android.exoplayer2.ui; +import android.text.Spannable; +import android.text.Spanned; import com.google.android.exoplayer2.text.Cue; +import com.google.android.exoplayer2.text.span.HorizontalTextInVerticalContextSpan; +import com.google.android.exoplayer2.text.span.RubySpan; +import com.google.android.exoplayer2.text.span.SpanUtil; +import com.google.android.exoplayer2.text.span.TextEmphasisSpan; /** Utility class for subtitle layout logic. */ /* package */ final class SubtitleViewUtils { @@ -48,5 +54,27 @@ import com.google.android.exoplayer2.text.Cue; } } + public static void preserveJapaneseLanguageFeatures(Spannable copy, Spanned original) { + RubySpan[] absSpans = + original.getSpans(0, original.length(), RubySpan.class); + for (RubySpan rubySpan : absSpans) { + SpanUtil.addOrReplaceSpan(copy, rubySpan, original.getSpanStart(rubySpan), + original.getSpanEnd(rubySpan), original.getSpanFlags(rubySpan)); + } + TextEmphasisSpan[] textEmphasisSpans = + original.getSpans(0, original.length(), TextEmphasisSpan.class); + for (TextEmphasisSpan textEmphasisSpan : textEmphasisSpans) { + SpanUtil.addOrReplaceSpan(copy, textEmphasisSpan, original.getSpanStart(textEmphasisSpan), + original.getSpanEnd(textEmphasisSpan), original.getSpanFlags(textEmphasisSpan)); + } + HorizontalTextInVerticalContextSpan[] horizontalTextInVerticalContextSpans = + original.getSpans(0, original.length(), HorizontalTextInVerticalContextSpan.class); + + for (HorizontalTextInVerticalContextSpan span : horizontalTextInVerticalContextSpans) { + SpanUtil.addOrReplaceSpan(copy, span, original.getSpanStart(span), + original.getSpanEnd(span), original.getSpanFlags(span)); + } + } + private SubtitleViewUtils() {} } diff --git a/library/ui/src/test/java/com/google/android/exoplayer2/ui/SubtitleViewUtilsTest.java b/library/ui/src/test/java/com/google/android/exoplayer2/ui/SubtitleViewUtilsTest.java new file mode 100644 index 0000000000..66d5a5ecc7 --- /dev/null +++ b/library/ui/src/test/java/com/google/android/exoplayer2/ui/SubtitleViewUtilsTest.java @@ -0,0 +1,63 @@ +package com.google.android.exoplayer2.ui; + +import static com.google.android.exoplayer2.testutil.truth.SpannedSubject.assertThat; + +import android.text.SpannableString; +import android.text.Spanned; +import android.text.style.UnderlineSpan; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.android.exoplayer2.text.span.HorizontalTextInVerticalContextSpan; +import com.google.android.exoplayer2.text.span.RubySpan; +import com.google.android.exoplayer2.text.span.TextAnnotation; +import com.google.android.exoplayer2.text.span.TextEmphasisSpan; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class SubtitleViewUtilsTest { + + @Test + public void testPreserveJapaneseLanguageFeatures() { + SpannableString spanned = new SpannableString("TextEmphasis おはよ Ruby ございます 123 Underline"); + spanned.setSpan( + new TextEmphasisSpan( + TextEmphasisSpan.MARK_SHAPE_CIRCLE, + TextEmphasisSpan.MARK_FILL_FILLED, + TextAnnotation.POSITION_BEFORE), + "Text emphasis ".length(), + "Text emphasis おはよ".length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + spanned.setSpan( + new RubySpan("おはよ", TextAnnotation.POSITION_BEFORE), + "TextEmphasis おはよ Ruby ".length(), + "TextEmphasis おはよ Ruby ございます".length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + spanned.setSpan( + new HorizontalTextInVerticalContextSpan(), + "TextEmphasis おはよ Ruby ございます ".length(), + "TextEmphasis おはよ Ruby ございます 123".length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + spanned.setSpan( + new UnderlineSpan(), + "TextEmphasis おはよ Ruby ございます 123".length(), + "TextEmphasis おはよ Ruby ございます 123 Underline".length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + SpannableString spannable = new SpannableString(spanned.toString()); + assertThat(spannable).hasNoTextEmphasisSpanBetween(0, spannable.length()); + + SubtitleViewUtils.preserveJapaneseLanguageFeatures(spannable, spanned); + assertThat(spannable) + .hasTextEmphasisSpanBetween("Text emphasis ".length(), "Text emphasis おはよ".length()); + assertThat(spannable).hasRubySpanBetween("TextEmphasis おはよ Ruby ".length(), + "TextEmphasis おはよ Ruby ございます".length()); + assertThat(spannable) + .hasHorizontalTextInVerticalContextSpanBetween("TextEmphasis おはよ Ruby ございます ".length(), + "TextEmphasis おはよ Ruby ございます 123".length()); + + assertThat(spannable).hasNoUnderlineSpanBetween(0, spannable.length()); + } +}