diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SpannedToHtmlConverter.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SpannedToHtmlConverter.java
index f534b9e397..e06edd8ded 100644
--- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SpannedToHtmlConverter.java
+++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SpannedToHtmlConverter.java
@@ -19,6 +19,7 @@ package com.google.android.exoplayer2.ui;
import android.graphics.Typeface;
import android.text.Html;
import android.text.Spanned;
+import android.text.style.AbsoluteSizeSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
@@ -66,8 +67,12 @@ import java.util.regex.Pattern;
*
WebView/Chromium (the intended destination of this HTML) gracefully handles overlapping
* tags and usually renders the same result as spanned text in a TextView.
*
+ *
+ * @param text The (possibly span-styled) text to convert to HTML.
+ * @param displayDensity The screen density of the device. WebView treats 1 CSS px as one Android
+ * dp, so to convert size values from Android px to CSS px we need to know the screen density.
*/
- public static String convert(@Nullable CharSequence text) {
+ public static String convert(@Nullable CharSequence text, float displayDensity) {
if (text == null) {
return "";
}
@@ -75,7 +80,7 @@ import java.util.regex.Pattern;
return escapeHtml(text);
}
Spanned spanned = (Spanned) text;
- SparseArray spanTransitions = findSpanTransitions(spanned);
+ SparseArray spanTransitions = findSpanTransitions(spanned, displayDensity);
StringBuilder html = new StringBuilder(spanned.length());
int previousTransition = 0;
@@ -100,11 +105,12 @@ import java.util.regex.Pattern;
return html.toString();
}
- private static SparseArray findSpanTransitions(Spanned spanned) {
+ private static SparseArray findSpanTransitions(
+ Spanned spanned, float displayDensity) {
SparseArray spanTransitions = new SparseArray<>();
for (Object span : spanned.getSpans(0, spanned.length(), Object.class)) {
- @Nullable String openingTag = getOpeningTag(span);
+ @Nullable String openingTag = getOpeningTag(span, displayDensity);
@Nullable String closingTag = getClosingTag(span);
int spanStart = spanned.getSpanStart(span);
int spanEnd = spanned.getSpanEnd(span);
@@ -120,7 +126,7 @@ import java.util.regex.Pattern;
}
@Nullable
- private static String getOpeningTag(Object span) {
+ private static String getOpeningTag(Object span, float displayDensity) {
if (span instanceof ForegroundColorSpan) {
ForegroundColorSpan colorSpan = (ForegroundColorSpan) span;
return Util.formatInvariant(
@@ -132,6 +138,13 @@ import java.util.regex.Pattern;
HtmlUtils.toCssRgba(colorSpan.getBackgroundColor()));
} else if (span instanceof HorizontalTextInVerticalContextSpan) {
return "";
+ } else if (span instanceof AbsoluteSizeSpan) {
+ AbsoluteSizeSpan absoluteSizeSpan = (AbsoluteSizeSpan) span;
+ float sizeCssPx =
+ absoluteSizeSpan.getDip()
+ ? absoluteSizeSpan.getSize()
+ : absoluteSizeSpan.getSize() / displayDensity;
+ return Util.formatInvariant("", sizeCssPx);
} else if (span instanceof TypefaceSpan) {
@Nullable String fontFamily = ((TypefaceSpan) span).getFamily();
return fontFamily != null
@@ -171,7 +184,8 @@ import java.util.regex.Pattern;
private static String getClosingTag(Object span) {
if (span instanceof ForegroundColorSpan
|| span instanceof BackgroundColorSpan
- || span instanceof HorizontalTextInVerticalContextSpan) {
+ || span instanceof HorizontalTextInVerticalContextSpan
+ || span instanceof AbsoluteSizeSpan) {
return "";
} else if (span instanceof TypefaceSpan) {
@Nullable String fontFamily = ((TypefaceSpan) span).getFamily();
diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleWebView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleWebView.java
index 480d057212..ee081f384e 100644
--- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleWebView.java
+++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleWebView.java
@@ -309,7 +309,9 @@ import java.util.List;
horizontalTranslatePercent,
verticalTranslatePercent))
.append(Util.formatInvariant("", backgroundColorCss))
- .append(SpannedToHtmlConverter.convert(cue.text))
+ .append(
+ SpannedToHtmlConverter.convert(
+ cue.text, getContext().getResources().getDisplayMetrics().density))
.append("")
.append("");
}
diff --git a/library/ui/src/test/java/com/google/android/exoplayer2/ui/SpannedToHtmlConverterTest.java b/library/ui/src/test/java/com/google/android/exoplayer2/ui/SpannedToHtmlConverterTest.java
index b6a6c5a323..c64d5cabcf 100644
--- a/library/ui/src/test/java/com/google/android/exoplayer2/ui/SpannedToHtmlConverterTest.java
+++ b/library/ui/src/test/java/com/google/android/exoplayer2/ui/SpannedToHtmlConverterTest.java
@@ -22,21 +22,31 @@ import android.graphics.Color;
import android.graphics.Typeface;
import android.text.SpannableString;
import android.text.Spanned;
+import android.text.style.AbsoluteSizeSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.text.style.UnderlineSpan;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.text.span.HorizontalTextInVerticalContextSpan;
import com.google.android.exoplayer2.text.span.RubySpan;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
/** Tests for {@link SpannedToHtmlConverter}. */
@RunWith(AndroidJUnit4.class)
public class SpannedToHtmlConverterTest {
+ private final float displayDensity;
+
+ public SpannedToHtmlConverterTest() {
+ displayDensity =
+ ApplicationProvider.getApplicationContext().getResources().getDisplayMetrics().density;
+ }
+
@Test
public void convert_supportsForegroundColorSpan() {
SpannableString spanned = new SpannableString("String with colored section");
@@ -46,7 +56,7 @@ public class SpannedToHtmlConverterTest {
"String with colored".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- String html = SpannedToHtmlConverter.convert(spanned);
+ String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
assertThat(html)
.isEqualTo("String with colored section");
@@ -61,7 +71,7 @@ public class SpannedToHtmlConverterTest {
"String with highlighted".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- String html = SpannedToHtmlConverter.convert(spanned);
+ String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
assertThat(html)
.isEqualTo(
@@ -78,7 +88,7 @@ public class SpannedToHtmlConverterTest {
"Vertical text with 123".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- String html = SpannedToHtmlConverter.convert(spanned);
+ String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
assertThat(html)
.isEqualTo(
@@ -86,6 +96,40 @@ public class SpannedToHtmlConverterTest {
+ "horizontal numbers");
}
+ // Set the screen density so we see that px are handled differently to dp.
+ @Config(qualifiers = "xhdpi")
+ @Test
+ public void convert_supportsAbsoluteSizeSpan_px() {
+ SpannableString spanned = new SpannableString("String with 10px section");
+ spanned.setSpan(
+ new AbsoluteSizeSpan(/* size= */ 10, /* dip= */ false),
+ "String with ".length(),
+ "String with 10px".length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
+
+ // 10 Android px are converted to 5 CSS px because WebView treats 1 CSS px as 1 Android dp
+ // and we're using screen density xhdpi i.e. density=2.
+ assertThat(html).isEqualTo("String with 10px section");
+ }
+
+ // Set the screen density so we see that px are handled differently to dp.
+ @Config(qualifiers = "xhdpi")
+ @Test
+ public void convert_supportsAbsoluteSizeSpan_dp() {
+ SpannableString spanned = new SpannableString("String with 10dp section");
+ spanned.setSpan(
+ new AbsoluteSizeSpan(/* size= */ 10, /* dip= */ true),
+ "String with ".length(),
+ "String with 10dp".length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
+
+ assertThat(html).isEqualTo("String with 10dp section");
+ }
+
@Test
public void convert_supportsTypefaceSpan() {
SpannableString spanned = new SpannableString("String with Times New Roman section");
@@ -95,7 +139,7 @@ public class SpannedToHtmlConverterTest {
"String with Times New Roman".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- String html = SpannedToHtmlConverter.convert(spanned);
+ String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
assertThat(html)
.isEqualTo(
@@ -112,7 +156,7 @@ public class SpannedToHtmlConverterTest {
"String with unstyled".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- String html = SpannedToHtmlConverter.convert(spanned);
+ String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
assertThat(html).isEqualTo("String with unstyled section");
}
@@ -137,7 +181,7 @@ public class SpannedToHtmlConverterTest {
"String with bold, italic and bold-italic".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- String html = SpannedToHtmlConverter.convert(spanned);
+ String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
assertThat(html)
.isEqualTo(
@@ -159,7 +203,7 @@ public class SpannedToHtmlConverterTest {
"String with over-annotated and under-annotated".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- String html = SpannedToHtmlConverter.convert(spanned);
+ String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
assertThat(html)
.isEqualTo(
@@ -185,28 +229,31 @@ public class SpannedToHtmlConverterTest {
"String with underlined".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- String html = SpannedToHtmlConverter.convert(spanned);
+ String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
assertThat(html).isEqualTo("String with underlined section.");
}
@Test
public void convert_escapesHtmlInUnspannedString() {
- String html = SpannedToHtmlConverter.convert("String with bold tags");
+ String html = SpannedToHtmlConverter.convert("String with bold tags", displayDensity);
assertThat(html).isEqualTo("String with <b>bold</b> tags");
}
@Test
public void convert_handlesLinebreakInUnspannedString() {
- String html = SpannedToHtmlConverter.convert("String with\nnew line and\r\ncrlf style too");
+ String html =
+ SpannedToHtmlConverter.convert(
+ "String with\nnew line and\r\ncrlf style too", displayDensity);
assertThat(html).isEqualTo("String with
new line and
crlf style too");
}
@Test
public void convert_doesntConvertAmpersandLineFeedToBrTag() {
- String html = SpannedToHtmlConverter.convert("String with
new line ampersand code");
+ String html =
+ SpannedToHtmlConverter.convert("String with
new line ampersand code", displayDensity);
assertThat(html).isEqualTo("String with new line ampersand code");
}
@@ -220,14 +267,16 @@ public class SpannedToHtmlConverterTest {
"String with unrecognised".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- String html = SpannedToHtmlConverter.convert(spanned);
+ String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
assertThat(html).isEqualTo("String with <foo>unrecognised</foo> tags");
}
@Test
public void convert_handlesLinebreakInSpannedString() {
- String html = SpannedToHtmlConverter.convert("String with\nnew line and\r\ncrlf style too");
+ String html =
+ SpannedToHtmlConverter.convert(
+ "String with\nnew line and\r\ncrlf style too", displayDensity);
assertThat(html).isEqualTo("String with
new line and
crlf style too");
}
@@ -236,7 +285,7 @@ public class SpannedToHtmlConverterTest {
public void convert_convertsNonAsciiCharactersToAmpersandCodes() {
String html =
SpannedToHtmlConverter.convert(
- new SpannableString("Strìng with 優しいの non-ASCII characters"));
+ new SpannableString("Strìng with 優しいの non-ASCII characters"), displayDensity);
assertThat(html)
.isEqualTo("Strìng with 優しいの non-ASCII characters");
@@ -256,7 +305,7 @@ public class SpannedToHtmlConverterTest {
"String with unrecognised".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- String html = SpannedToHtmlConverter.convert(spanned);
+ String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
assertThat(html).isEqualTo("String with unrecognised span");
}
@@ -270,7 +319,7 @@ public class SpannedToHtmlConverterTest {
spanned.setSpan(new StyleSpan(Typeface.ITALIC), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spanned.setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- String html = SpannedToHtmlConverter.convert(spanned);
+ String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
assertThat(html).isEqualTo("String with italic-bold-underlined section");
}
@@ -287,7 +336,7 @@ public class SpannedToHtmlConverterTest {
"String with italic and bold".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- String html = SpannedToHtmlConverter.convert(spanned);
+ String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
assertThat(html).isEqualTo("String with italic and bold section");
}
@@ -306,7 +355,7 @@ public class SpannedToHtmlConverterTest {
"String with italic and bold section".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- String html = SpannedToHtmlConverter.convert(spanned);
+ String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
assertThat(html).isEqualTo("String with italic and bold section");
}