Skip to content

Commit

Permalink
feat(text): Partially support tts:textOutline
Browse files Browse the repository at this point in the history
Unfortunately, modern browsers do not support any CSS property that
directly maps to tts:textOutline. However, the non-standard (but still
commonly implemented) -webkit-text-stroke-width and
-webkit-text-stroke-color properties, when used together, can
replicate the functionality of tts:textOutline, with the exception of
the optional blur radius.

Closes shaka-project#3612

Change-Id: I863d09fc447765646bd405c59b0b20960362a594
  • Loading branch information
theodab committed Sep 14, 2021
1 parent a7f4db7 commit 911ce6d
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 2 deletions.
14 changes: 14 additions & 0 deletions externs/shaka/text.js
Expand Up @@ -297,6 +297,20 @@ shaka.extern.Cue = class {
*/
this.fontFamily;

/**
* Text stroke color as a CSS color, e.g. "#FFFFFF" or "white".
* @type {string}
* @exportDoc
*/
this.textStrokeColor;

/**
* Text stroke width as a CSS stroke-width value.
* @type {string}
* @exportDoc
*/
this.textStrokeWidth;

/**
* Text letter spacing as a CSS letter-spacing value.
* @type {string}
Expand Down
12 changes: 12 additions & 0 deletions lib/text/cue.js
Expand Up @@ -144,6 +144,18 @@ shaka.text.Cue = class {
*/
this.border = '';

/**
* @override
* @exportInterface
*/
this.textStrokeColor = '';

/**
* @override
* @exportInterface
*/
this.textStrokeWidth = '';

/**
* @override
* @exportInterface
Expand Down
27 changes: 25 additions & 2 deletions lib/text/ttml_text_parser.js
Expand Up @@ -509,7 +509,6 @@ shaka.text.TtmlTextParser = class {

const color = TtmlTextParser.getStyleAttribute_(
cueElement, region, styles, 'color', shouldInheritRegionStyles);

if (color) {
cue.color = color;
}
Expand Down Expand Up @@ -562,7 +561,6 @@ shaka.text.TtmlTextParser = class {

const fontSize = TtmlTextParser.getStyleAttribute_(
cueElement, region, styles, 'fontSize', shouldInheritRegionStyles);

if (fontSize) {
const isValidFontSizeUnit =
fontSize.match(TtmlTextParser.unitValues_) ||
Expand Down Expand Up @@ -600,6 +598,31 @@ shaka.text.TtmlTextParser = class {
}
}

const textOutline = TtmlTextParser.getStyleAttribute_(
cueElement, region, styles, 'textOutline', shouldInheritRegionStyles);
if (textOutline) {
// tts:textOutline isn't natively supported by browsers, but it can be
// mostly replicated using the non-standard -webkit-text-stroke-width and
// -webkit-text-stroke-color properties.
const split = textOutline.split(' ');
if (split[0].match(TtmlTextParser.unitValues_)) {
// There is no defined color, so default to the text color.
cue.textStrokeColor = cue.color;
} else {
cue.textStrokeColor = split[0];
split.shift();
}
if (split[0] && split[0].match(TtmlTextParser.unitValues_)) {
cue.textStrokeWidth = split[0];
} else {
// If there is no width, or the width is not a number, don't draw a
// border.
cue.textStrokeColor = '';
}
// There is an optional blur radius also, but we have no way of
// replicating that, so ignore it.
}

const letterSpacing = TtmlTextParser.getStyleAttribute_(
cueElement, region, styles, 'letterSpacing', shouldInheritRegionStyles);
if (letterSpacing && letterSpacing.match(TtmlTextParser.unitValues_)) {
Expand Down
2 changes: 2 additions & 0 deletions lib/text/ui_text_displayer.js
Expand Up @@ -320,6 +320,8 @@ shaka.text.UITextDisplayer = class {
style = span.style;
}

style.webkitTextStrokeColor = cue.textStrokeColor;
style.webkitTextStrokeWidth = cue.textStrokeWidth;
style.backgroundColor = cue.backgroundColor;
style.border = cue.border;
style.color = cue.color;
Expand Down
47 changes: 47 additions & 0 deletions test/text/ttml_text_parser_unit.js
Expand Up @@ -1211,6 +1211,8 @@ describe('TtmlTextParser', () => {
fontStyle: Cue.fontStyle.ITALIC,
lineHeight: '20px',
fontSize: '10em',
textStrokeColor: 'blue',
textStrokeWidth: '3px',
},
{
startTime: 2,
Expand All @@ -1222,6 +1224,7 @@ describe('TtmlTextParser', () => {
'<tt xmlns:tts="http://www.w3.org/ns/ttml#styling">' +
'<styling>' +
'<style xml:id="s1" tts:color="red" ' +
'tts:textOutline="blue 3px" ' +
'tts:backgroundColor="blue" ' +
'tts:fontWeight="bold" ' +
'tts:fontFamily="Times New Roman" ' +
Expand All @@ -1240,6 +1243,50 @@ describe('TtmlTextParser', () => {
{periodStart: 0, segmentStart: 0, segmentEnd: 0});
});

it('uses text color if tts:textOutline does not specify color', () => {
verifyHelper(
[
{
startTime: 1,
endTime: 2,
payload: 'Test',
color: 'red',
textStrokeColor: 'red',
textStrokeWidth: '3px',
},
],
'<tt xmlns:tts="http://www.w3.org/ns/ttml#styling">' +
'<styling>' +
'<style xml:id="s1" tts:color="red" ' +
'tts:textOutline="3px" />' +
'</styling>' +
'<body region="subtitleArea"><div>' +
'<p begin="00:01.00" end="00:02.00" style="s1">Test</p>' +
'</div></body></tt>',
{periodStart: 0, segmentStart: 0, segmentEnd: 0});
});

it('does not add an outline if tts:textOutline only contains color', () => {
verifyHelper(
[
{
startTime: 1,
endTime: 2,
payload: 'Test',
color: 'red',
},
],
'<tt xmlns:tts="http://www.w3.org/ns/ttml#styling">' +
'<styling>' +
'<style xml:id="s1" tts:color="red" ' +
'tts:textOutline="blue" />' +
'</styling>' +
'<body region="subtitleArea"><div>' +
'<p begin="00:01.00" end="00:02.00" style="s1">Test</p>' +
'</div></body></tt>',
{periodStart: 0, segmentStart: 0, segmentEnd: 0});
});

// Regression test for #2623
it('does not apply background colors to containers', () => {
verifyHelper(
Expand Down

0 comments on commit 911ce6d

Please sign in to comment.