From baab7c1ddbffcbdeb53c3933b8177cd35101a007 Mon Sep 17 00:00:00 2001 From: Tatsiana Gelahova Date: Wed, 4 Mar 2020 01:51:16 +0300 Subject: [PATCH 01/14] Add support of cellResolution fontSize --- externs/shaka/text.js | 16 +++++++++ lib/text/cue.js | 22 ++++++++++++ lib/text/ttml_text_parser.js | 45 +++++++++++++++++++++--- test/text/ttml_text_parser_unit.js | 37 ++++++++++++++++++++ ui/text_displayer.js | 56 +++++++++++++++++++++++++++++- 5 files changed, 171 insertions(+), 5 deletions(-) diff --git a/externs/shaka/text.js b/externs/shaka/text.js index df9a77f9c1..a70d94f01f 100644 --- a/externs/shaka/text.js +++ b/externs/shaka/text.js @@ -246,6 +246,22 @@ shaka.extern.Cue = class { */ this.backgroundColor; + /** + * The number of horizontal cells into which the Root Container + * Region area is divided + * @type {!number} + * @exportDoc + */ + this.cellResolutionRows; + + /** + * The number of vertical cells into which the Root Container + * Region area is divided + * @type {!number} + * @exportDoc + */ + this.cellResolutionColumns; + /** * Image background represented by any string that would be * accepted in image HTML element. diff --git a/lib/text/cue.js b/lib/text/cue.js index 06ba0c925f..b65a94c823 100644 --- a/lib/text/cue.js +++ b/lib/text/cue.js @@ -200,6 +200,18 @@ shaka.text.Cue = class { * @exportInterface */ this.spacer = false; + + /** + * @override + * @exportInterface + */ + this.cellResolutionRows = Cue.cellResolution.ROWS; + + /** + * @override + * @exportInterface + */ + this.cellResolutionColumns = Cue.cellResolution.COLUMNS; } }; @@ -296,6 +308,16 @@ shaka.text.Cue.fontWeight = { 'BOLD': 700, }; +/** + * Default values for cue cellResolution + * + * @enum {number} + * @export + */ +shaka.text.Cue.cellResolution = { + 'COLUMNS': 32, + 'ROWS': 15, +}; /** * @enum {string} diff --git a/lib/text/ttml_text_parser.js b/lib/text/ttml_text_parser.js index 69a682b287..068e43931b 100644 --- a/lib/text/ttml_text_parser.js +++ b/lib/text/ttml_text_parser.js @@ -76,6 +76,7 @@ shaka.text.TtmlTextParser = class { let tickRate = null; let spaceStyle = null; let extent = null; + let cellResolution = null; const tts = xml.getElementsByTagName('tt'); const tt = tts[0]; // TTML should always have tt element. @@ -91,6 +92,7 @@ shaka.text.TtmlTextParser = class { frameRateMultiplier = XmlUtils.getAttributeNS(tt, ttpNs, 'frameRateMultiplier'); tickRate = XmlUtils.getAttributeNS(tt, ttpNs, 'tickRate'); + cellResolution = XmlUtils.getAttributeNS(tt, ttpNs, 'cellResolution'); spaceStyle = tt.getAttribute('xml:space') || 'default'; extent = tt.getAttribute('tts:extent'); } @@ -107,6 +109,9 @@ shaka.text.TtmlTextParser = class { const rateInfo = new TtmlTextParser.RateInfo_( frameRate, subFrameRate, frameRateMultiplier, tickRate); + const cellResolutionInfo = + TtmlTextParser.getCellResolution_(cellResolution); + const metadataElements = TtmlTextParser.getLeafNodes_( tt.getElementsByTagName('metadata')[0]); const styles = TtmlTextParser.getLeafNodes_( @@ -127,7 +132,8 @@ shaka.text.TtmlTextParser = class { for (const node of textNodes) { const cue = TtmlTextParser.parseCue_( node, time.periodStart, rateInfo, metadataElements, styles, - regionElements, cueRegions, whitespaceTrim, false); + regionElements, cueRegions, whitespaceTrim, false, + cellResolutionInfo); if (cue) { ret.push(cue); } @@ -239,12 +245,13 @@ shaka.text.TtmlTextParser = class { * @param {!Array.} cueRegions * @param {boolean} whitespaceTrim * @param {boolean} isNested + * @param {Object} cellResolutionInfo * @return {shaka.text.Cue} * @private */ static parseCue_( cueElement, offset, rateInfo, metadataElements, styles, regionElements, - cueRegions, whitespaceTrim, isNested) { + cueRegions, whitespaceTrim, isNested, cellResolutionInfo) { if (isNested && cueElement.nodeName == 'br') { const cue = new shaka.text.Cue(0, 0, ''); cue.spacer = true; @@ -322,7 +329,8 @@ shaka.text.TtmlTextParser = class { regionElements, cueRegions, whitespaceTrim, - /* isNested= */ true + /* isNested= */ true, + cellResolutionInfo, ); if (nestedCue) { @@ -334,6 +342,9 @@ shaka.text.TtmlTextParser = class { const cue = new shaka.text.Cue(start, end, payload); cue.nestedCues = nestedCues; + cue.cellResolutionRows = cellResolutionInfo.rows; + cue.cellResolutionColumns = cellResolutionInfo.columns; + // Get other properties if available. const regionElement = shaka.text.TtmlTextParser.getElementsFromCollection_( cueElement, 'region', regionElements, /* prefix= */ '')[0]; @@ -543,7 +554,8 @@ shaka.text.TtmlTextParser = class { const fontSize = TtmlTextParser.getStyleAttribute_( cueElement, region, styles, 'fontSize'); - if (fontSize && fontSize.match(TtmlTextParser.unitValues_)) { + // TODO: correct regular expression + if (fontSize) { cue.fontSize = fontSize; } @@ -933,6 +945,31 @@ shaka.text.TtmlTextParser = class { return (milliseconds / 1000) + seconds + (minutes * 60) + (hours * 3600); } + + /** + * + * @param {?string} cellResolution + * @return {Object} + * @private + */ + static getCellResolution_(cellResolution) { + const {COLUMNS, ROWS} = shaka.text.Cue.cellResolution; + + // If not specified, the number of columns and rows must be considered + // to be 32 and 15 respectively + let columns = COLUMNS; + let rows = ROWS; + + if (cellResolution) { + const regExp = /^(\d+) (\d+)$/.exec(cellResolution); + + if (regExp !== null) { + columns = parseInt(regExp[1], 10); + rows = parseInt(regExp[2], 10); + } + } + return {columns, rows}; + } }; /** diff --git a/test/text/ttml_text_parser_unit.js b/test/text/ttml_text_parser_unit.js index 729e11c607..a9dcfc11e5 100644 --- a/test/text/ttml_text_parser_unit.js +++ b/test/text/ttml_text_parser_unit.js @@ -913,6 +913,43 @@ describe('TtmlTextParser', () => { {periodStart: 0, segmentStart: 0, segmentEnd: 0}); }); + describe('getCellResolution_', () => { + const {cellResolution} = shaka.text.Cue; + + const DEFAULT = { + columns: cellResolution.COLUMNS, + rows: cellResolution.ROWS, + }; + + const {getCellResolution_} = shaka.text.TtmlTextParser; + + it('should return default values if cellResolution was not provided', + () => { + expect(getCellResolution_('')).toEqual(DEFAULT); + expect(getCellResolution_(null)).toEqual(DEFAULT); + }); + + it('should return default values if provided cellResolution is not valid', + () => { + expect(getCellResolution_('')).toEqual(DEFAULT); + expect(getCellResolution_('-50 14')).toEqual(DEFAULT); + expect(getCellResolution_('60 incorrect')).toEqual(DEFAULT); + expect(getCellResolution_('incorrect50 14')).toEqual(DEFAULT); + expect(getCellResolution_('50 14incorrect')).toEqual(DEFAULT); + }); + + it('should return columns and rows by provided cell resolution', () => { + expect(getCellResolution_('60 20')).toEqual({ + columns: 60, + rows: 20, + }); + expect(getCellResolution_('10 30')).toEqual({ + columns: 10, + rows: 30, + }); + }); + }); + /** * @param {!Array} cues diff --git a/ui/text_displayer.js b/ui/text_displayer.js index 934ddccc6e..fff9d74d61 100644 --- a/ui/text_displayer.js +++ b/ui/text_displayer.js @@ -268,11 +268,15 @@ shaka.ui.TextDisplayer = class { captionsStyle.margin = '0'; } + const {convertLengthValue_} = shaka.ui.TextDisplayer; + captionsStyle.fontFamily = cue.fontFamily; captionsStyle.fontWeight = cue.fontWeight.toString(); - captionsStyle.fontSize = cue.fontSize; captionsStyle.fontStyle = cue.fontStyle; captionsStyle.letterSpacing = cue.letterSpacing; + captionsStyle.fontSize = convertLengthValue_( + cue.fontSize, cue, this.videoContainer_ + ); // The line attribute defines the positioning of the text container inside // the video container. @@ -361,4 +365,54 @@ shaka.ui.TextDisplayer = class { panelStyle.height = cue.size + '%'; } } + + /** + * Returns info about provided lengthValue + * @example 100px => { value: 100, unit: 'px' } + * @param {?string} lengthValue + * + * @return {?{ value: number, unit: string }} + * @private + */ + static getLengthValueInfo_(lengthValue) { + const regexp = new RegExp(/(\d*\.?\d+)([a-z]+|%+)/).exec(lengthValue); + + if (!regexp) { + return null; + } + + return { + value: Number(regexp[1]), + unit: regexp[2], + }; + } + + /** + * Converts length value + * + * @param {string} lengthValue + * @param {!shaka.extern.Cue} cue + * @param {HTMLElement} videoContainer + * @return {string} + * @private + */ + static convertLengthValue_(lengthValue, cue, videoContainer) { + const {getLengthValueInfo_} = shaka.ui.TextDisplayer; + + const lengthValueInfo = getLengthValueInfo_(lengthValue); + + if (!lengthValueInfo) { + return lengthValue; + } + + const {unit, value} = getLengthValueInfo_(lengthValue); + + if (unit === 'c') { + const containerHeight = videoContainer.clientHeight; + + return (containerHeight * (1 /cue.cellResolutionRows) * value) + 'px'; + } + + return lengthValue; + } }; From a8d5c6423e423a9f3aa964d803950631672aa5c0 Mon Sep 17 00:00:00 2001 From: Tatsiana Date: Wed, 4 Mar 2020 12:18:28 +0300 Subject: [PATCH 02/14] Update unit tests for parsing cellResolution from ttml cue --- test/text/ttml_text_parser_unit.js | 88 ++++++++++++++++-------------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/test/text/ttml_text_parser_unit.js b/test/text/ttml_text_parser_unit.js index a9dcfc11e5..f01fe13247 100644 --- a/test/text/ttml_text_parser_unit.js +++ b/test/text/ttml_text_parser_unit.js @@ -792,8 +792,8 @@ describe('TtmlTextParser', () => { verifyHelper( [ { - startTime: 62.05, - endTime: 3723.2, + startTime: 1, + endTime: 2, payload: 'Test', color: 'red', backgroundColor: 'blue', @@ -803,6 +803,12 @@ describe('TtmlTextParser', () => { lineHeight: '20px', fontSize: '10em', }, + { + startTime: 2, + endTime: 4, + payload: 'Test 2', + fontSize: '0.80c', + }, ], '' + '' + @@ -813,12 +819,14 @@ describe('TtmlTextParser', () => { 'tts:fontStyle="italic" ' + 'tts:lineHeight="20px" ' + 'tts:fontSize="10em"/>' + + '