initial commit for underline and strikeout

This commit is contained in:
Abel Jimenez 2021-04-21 18:19:38 -07:00
parent c01d26f86b
commit 852101a43f
6 changed files with 118 additions and 7 deletions

View File

@ -509,7 +509,7 @@
{ {
"name": "SubStation Alpha positioning", "name": "SubStation Alpha positioning",
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/video-avc-baseline-480.mp4", "uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/video-avc-baseline-480.mp4",
"subtitle_uri": "https://storage.googleapis.com/exoplayer-test-media-1/ssa/test-subs-position.ass", "subtitle_uri": "https://drive.google.com/uc?export=download&id=16IrvtynQ6-ANRpRX7hU6xEQeFU91LmXl",
"subtitle_mime_type": "text/x-ssa", "subtitle_mime_type": "text/x-ssa",
"subtitle_language": "en" "subtitle_language": "en"
}, },

View File

@ -22,7 +22,9 @@ import android.graphics.Typeface;
import android.text.Layout; import android.text.Layout;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.style.ForegroundColorSpan; import android.text.style.ForegroundColorSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import android.text.style.UnderlineSpan;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Cue;
@ -340,6 +342,20 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
/* end= */ spannableText.length(), /* end= */ spannableText.length(),
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
if (style.underline) {
spannableText.setSpan(
new UnderlineSpan(),
/* start= */ 0,
/* end= */ spannableText.length(),
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (style.strikeout) {
spannableText.setSpan(
new StrikethroughSpan(),
/* start= */ 0,
/* end= */ spannableText.length(),
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} }
@SsaStyle.SsaAlignment int alignment; @SsaStyle.SsaAlignment int alignment;

View File

@ -22,6 +22,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.PointF; import android.graphics.PointF;
import android.os.Binder;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.ColorInt; import androidx.annotation.ColorInt;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
@ -95,6 +96,8 @@ import java.util.regex.Pattern;
public final float fontSize; public final float fontSize;
public final boolean bold; public final boolean bold;
public final boolean italic; public final boolean italic;
public final boolean underline;
public final boolean strikeout;
private SsaStyle( private SsaStyle(
String name, String name,
@ -102,13 +105,17 @@ import java.util.regex.Pattern;
@Nullable @ColorInt Integer primaryColor, @Nullable @ColorInt Integer primaryColor,
float fontSize, float fontSize,
boolean bold, boolean bold,
boolean italic) { boolean italic,
boolean underline,
boolean strikeout) {
this.name = name; this.name = name;
this.alignment = alignment; this.alignment = alignment;
this.primaryColor = primaryColor; this.primaryColor = primaryColor;
this.fontSize = fontSize; this.fontSize = fontSize;
this.bold = bold; this.bold = bold;
this.italic = italic; this.italic = italic;
this.underline = underline;
this.strikeout = strikeout;
} }
@Nullable @Nullable
@ -136,10 +143,16 @@ import java.util.regex.Pattern;
? parseFontSize(styleValues[format.fontSizeIndex].trim()) ? parseFontSize(styleValues[format.fontSizeIndex].trim())
: Cue.DIMEN_UNSET, : Cue.DIMEN_UNSET,
format.boldIndex != C.INDEX_UNSET format.boldIndex != C.INDEX_UNSET
? parseBoldOrItalic(styleValues[format.boldIndex].trim()) ? parseBooleanData(styleValues[format.boldIndex].trim())
: false, : false,
format.italicIndex != C.INDEX_UNSET format.italicIndex != C.INDEX_UNSET
? parseBoldOrItalic(styleValues[format.italicIndex].trim()) ? parseBooleanData(styleValues[format.italicIndex].trim())
: false,
format.underlineIndex != C.INDEX_UNSET
? parseBooleanData(styleValues[format.underlineIndex].trim())
: false,
format.strikeoutIndex != C.INDEX_UNSET
? parseBooleanData(styleValues[format.strikeoutIndex].trim())
: false); : false);
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.w(TAG, "Skipping malformed 'Style:' line: '" + styleLine + "'", e); Log.w(TAG, "Skipping malformed 'Style:' line: '" + styleLine + "'", e);
@ -226,12 +239,12 @@ import java.util.regex.Pattern;
} }
} }
private static boolean parseBoldOrItalic(String boldOrItalic) { private static boolean parseBooleanData(String booleanData) {
try { try {
int value = Integer.parseInt(boldOrItalic); int value = Integer.parseInt(booleanData);
return value == 1 || value == -1; return value == 1 || value == -1;
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
Log.w(TAG, "Failed to parse bold/italic: '" + boldOrItalic + "'", e); Log.w(TAG, "Failed to parse boolean data: '" + booleanData + "'", e);
return false; return false;
} }
} }
@ -250,6 +263,8 @@ import java.util.regex.Pattern;
public final int fontSizeIndex; public final int fontSizeIndex;
public final int boldIndex; public final int boldIndex;
public final int italicIndex; public final int italicIndex;
public final int underlineIndex;
public final int strikeoutIndex;
public final int length; public final int length;
private Format( private Format(
@ -259,6 +274,8 @@ import java.util.regex.Pattern;
int fontSizeIndex, int fontSizeIndex,
int boldIndex, int boldIndex,
int italicIndex, int italicIndex,
int underlineIndex,
int strikeoutIndex,
int length) { int length) {
this.nameIndex = nameIndex; this.nameIndex = nameIndex;
this.alignmentIndex = alignmentIndex; this.alignmentIndex = alignmentIndex;
@ -266,6 +283,8 @@ import java.util.regex.Pattern;
this.fontSizeIndex = fontSizeIndex; this.fontSizeIndex = fontSizeIndex;
this.boldIndex = boldIndex; this.boldIndex = boldIndex;
this.italicIndex = italicIndex; this.italicIndex = italicIndex;
this.underlineIndex = underlineIndex;
this.strikeoutIndex = strikeoutIndex;
this.length = length; this.length = length;
} }
@ -282,6 +301,8 @@ import java.util.regex.Pattern;
int fontSizeIndex = C.INDEX_UNSET; int fontSizeIndex = C.INDEX_UNSET;
int boldIndex = C.INDEX_UNSET; int boldIndex = C.INDEX_UNSET;
int italicIndex = C.INDEX_UNSET; int italicIndex = C.INDEX_UNSET;
int underlineIndex = C.INDEX_UNSET;
int strikeoutIndex = C.INDEX_UNSET;
String[] keys = String[] keys =
TextUtils.split(styleFormatLine.substring(SsaDecoder.FORMAT_LINE_PREFIX.length()), ","); TextUtils.split(styleFormatLine.substring(SsaDecoder.FORMAT_LINE_PREFIX.length()), ",");
for (int i = 0; i < keys.length; i++) { for (int i = 0; i < keys.length; i++) {
@ -304,6 +325,12 @@ import java.util.regex.Pattern;
case "italic": case "italic":
italicIndex = i; italicIndex = i;
break; break;
case "underline":
underlineIndex = i;
break;
case "strikeout":
strikeoutIndex = i;
break;
} }
} }
return nameIndex != C.INDEX_UNSET return nameIndex != C.INDEX_UNSET
@ -314,6 +341,8 @@ import java.util.regex.Pattern;
fontSizeIndex, fontSizeIndex,
boldIndex, boldIndex,
italicIndex, italicIndex,
underlineIndex,
strikeoutIndex,
keys.length) keys.length)
: null; : null;
} }

View File

@ -50,6 +50,8 @@ public final class SsaDecoderTest {
private static final String STYLE_COLORS = "media/ssa/style_colors"; private static final String STYLE_COLORS = "media/ssa/style_colors";
private static final String STYLE_FONT_SIZE = "media/ssa/style_font_size"; private static final String STYLE_FONT_SIZE = "media/ssa/style_font_size";
private static final String STYLE_BOLD_ITALIC = "media/ssa/style_bold_italic"; private static final String STYLE_BOLD_ITALIC = "media/ssa/style_bold_italic";
private static final String STYLE_UNDERLINE = "media/ssa/style_underline";
private static final String STYLE_STRIKEOUT = "media/ssa/style_strikeout";
@Test @Test
public void decodeEmpty() throws IOException { public void decodeEmpty() throws IOException {
@ -356,6 +358,38 @@ public final class SsaDecoderTest {
SpannedSubject.assertThat(thirdCueText).hasBoldItalicSpanBetween(0, thirdCueText.length()); SpannedSubject.assertThat(thirdCueText).hasBoldItalicSpanBetween(0, thirdCueText.length());
} }
@Test
public void decodeUnderline() throws IOException {
SsaDecoder decoder = new SsaDecoder();
byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_UNDERLINE);
Subtitle subtitle = decoder.decode(bytes, bytes.length, false);
assertThat(subtitle.getEventTimeCount()).isEqualTo(4);
Spanned firstCueText =
(Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(0))).text;
SpannedSubject.assertThat(firstCueText).hasUnderlineSpanBetween(0, firstCueText.length());
Spanned secondCueText =
(Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(2))).text;
SpannedSubject.assertThat(secondCueText).hasNoUnderlineSpanBetween(0, secondCueText.length());
}
@Test
public void decodeStrikeout() throws IOException {
SsaDecoder decoder = new SsaDecoder();
byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_STRIKEOUT);
Subtitle subtitle = decoder.decode(bytes, bytes.length, false);
assertThat(subtitle.getEventTimeCount()).isEqualTo(4);
Spanned firstCueText =
(Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(0))).text;
SpannedSubject.assertThat(firstCueText).hasStrikethroughSpanBetween(0, firstCueText.length());
Spanned secondCueText =
(Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(2))).text;
SpannedSubject.assertThat(secondCueText).hasNoStrikethroughSpanBetween(0, secondCueText.length());
}
private static void assertTypicalCue1(Subtitle subtitle, int eventIndex) { private static void assertTypicalCue1(Subtitle subtitle, int eventIndex) {
assertThat(subtitle.getEventTime(eventIndex)).isEqualTo(0); assertThat(subtitle.getEventTime(eventIndex)).isEqualTo(0);
assertThat(subtitle.getCues(subtitle.getEventTime(eventIndex)).get(0).text.toString()) assertThat(subtitle.getCues(subtitle.getEventTime(eventIndex)).get(0).text.toString())

View File

@ -0,0 +1,16 @@
[Script Info]
Title: SSA/ASS Test
Original Script: Abel
Script Type: V4.00+
PlayResX: 1280
PlayResY: 720
[V4+ Styles]
Format: Name ,Strikeout
Style: WithStrikeout ,-1
Style: WithoutStrikeout ,0
[Events]
Format: Start ,End ,Style ,Text
Dialogue: 0:00:01.00,0:00:03.00,WithStrikeout ,First line with Strikeout.
Dialogue: 0:00:05.00,0:00:07.00,WithoutStrikeout ,Second line without Strikeout.

View File

@ -0,0 +1,16 @@
[Script Info]
Title: SSA/ASS Test
Original Script: Abel
Script Type: V4.00+
PlayResX: 1280
PlayResY: 720
[V4+ Styles]
Format: Name ,Underline
Style: WithUnderline ,-1
Style: WithoutUnderline ,0
[Events]
Format: Start ,End ,Style ,Text
Dialogue: 0:00:01.00,0:00:03.00,WithUnderline ,First line with Underline.
Dialogue: 0:00:05.00,0:00:07.00,WithoutUnderline ,Second line without Underline.