static addStyle_(
      cue, cueElement, region, imageElement, imageUri, styles,
      isNested, isLeaf) {
    const TtmlTextParser = shaka.text.TtmlTextParser;
    const TXml = shaka.util.TXml;
    const Cue = shaka.text.Cue;

    // Styles should be inherited from regions, if a style property is not
    // associated with a Content element (or an anonymous span).
    const shouldInheritRegionStyles = isNested || isLeaf;

    const direction = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'direction', shouldInheritRegionStyles);
    if (direction == 'rtl') {
      cue.direction = Cue.direction.HORIZONTAL_RIGHT_TO_LEFT;
    }

    // Direction attribute specifies one-dimensional writing direction
    // (left to right or right to left). Writing mode specifies that
    // plus whether text is vertical or horizontal.
    // They should not contradict each other. If they do, we give
    // preference to writing mode.
    const writingMode = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'writingMode', shouldInheritRegionStyles);
    // Set cue's direction if the text is horizontal, and cue's writingMode if
    // it's vertical.
    if (writingMode == 'tb' || writingMode == 'tblr') {
      cue.writingMode = Cue.writingMode.VERTICAL_LEFT_TO_RIGHT;
    } else if (writingMode == 'tbrl') {
      cue.writingMode = Cue.writingMode.VERTICAL_RIGHT_TO_LEFT;
    } else if (writingMode == 'rltb' || writingMode == 'rl') {
      cue.direction = Cue.direction.HORIZONTAL_RIGHT_TO_LEFT;
    } else if (writingMode) {
      cue.direction = Cue.direction.HORIZONTAL_LEFT_TO_RIGHT;
    }

    const align = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'textAlign', true);
    if (align) {
      cue.positionAlign = TtmlTextParser.textAlignToPositionAlign_.get(align);
      cue.lineAlign = TtmlTextParser.textAlignToLineAlign_.get(align);

      goog.asserts.assert(align.toUpperCase() in Cue.textAlign,
          align.toUpperCase() + ' Should be in Cue.textAlign values!');

      cue.textAlign = Cue.textAlign[align.toUpperCase()];
    } else {
      // Default value is START in the TTML spec: https://bit.ly/32OGmvo
      // But to make the subtitle render consistent with other players and the
      // shaka.text.Cue we use CENTER
      cue.textAlign = Cue.textAlign.CENTER;
    }

    const displayAlign = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'displayAlign', true);
    if (displayAlign) {
      goog.asserts.assert(displayAlign.toUpperCase() in Cue.displayAlign,
          displayAlign.toUpperCase() +
                          ' Should be in Cue.displayAlign values!');
      cue.displayAlign = Cue.displayAlign[displayAlign.toUpperCase()];
    }

    const color = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'color', shouldInheritRegionStyles);
    if (color) {
      cue.color = TtmlTextParser.convertTTMLrgbaToHTMLrgba_(color);
    }

    // Background color should not be set on a container.  If this is a nested
    // cue, you can set the background.  If it's a top-level that happens to
    // also be a leaf, you can set the background.
    // See https://github.com/shaka-project/shaka-player/issues/2623
    // This used to be handled in the displayer, but that is confusing.  The Cue
    // structure should reflect what you want to happen in the displayer, and
    // the displayer shouldn't have to know about TTML.
    const backgroundColor = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'backgroundColor',
        shouldInheritRegionStyles);
    if (backgroundColor) {
      cue.backgroundColor =
        TtmlTextParser.convertTTMLrgbaToHTMLrgba_(backgroundColor);
    }

    const border = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'border', shouldInheritRegionStyles);
    if (border) {
      cue.border = border;
    }

    const fontFamily = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'fontFamily', shouldInheritRegionStyles);
    // See https://github.com/sandflow/imscJS/blob/1.1.3/src/main/js/html.js#L1384
    if (fontFamily) {
      switch (fontFamily) {
        case 'monospaceSerif':
          cue.fontFamily = 'Courier New,Liberation Mono,Courier,monospace';
          break;
        case 'proportionalSansSerif':
          cue.fontFamily = 'Arial,Helvetica,Liberation Sans,sans-serif';
          break;
        case 'sansSerif':
          cue.fontFamily = 'sans-serif';
          break;
        case 'monospaceSansSerif':
          // cspell: disable-next-line
          cue.fontFamily = 'Consolas,monospace';
          break;
        case 'proportionalSerif':
          cue.fontFamily = 'serif';
          break;
        default:
          cue.fontFamily = fontFamily.split(',').filter((font) => {
            return font != 'default';
          }).join(',');
          break;
      }
    }

    const fontWeight = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'fontWeight', shouldInheritRegionStyles);
    if (fontWeight && fontWeight == 'bold') {
      cue.fontWeight = Cue.fontWeight.BOLD;
    }

    const wrapOption = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'wrapOption', shouldInheritRegionStyles);
    if (wrapOption && wrapOption == 'noWrap') {
      cue.wrapLine = false;
    } else {
      cue.wrapLine = true;
    }

    const lineHeight = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'lineHeight', shouldInheritRegionStyles);
    if (lineHeight && lineHeight.match(TtmlTextParser.unitValues_)) {
      cue.lineHeight = lineHeight;
    }

    const fontSize = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'fontSize', shouldInheritRegionStyles);
    if (fontSize) {
      const isValidFontSizeUnit =
          fontSize.match(TtmlTextParser.unitValues_) ||
          fontSize.match(TtmlTextParser.percentValue_);

      if (isValidFontSizeUnit) {
        cue.fontSize = fontSize;
      }
    }

    const fontStyle = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'fontStyle', shouldInheritRegionStyles);
    if (fontStyle) {
      goog.asserts.assert(fontStyle.toUpperCase() in Cue.fontStyle,
          fontStyle.toUpperCase() +
                          ' Should be in Cue.fontStyle values!');
      cue.fontStyle = Cue.fontStyle[fontStyle.toUpperCase()];
    }

    if (imageElement) {
      // According to the spec, we should use imageType (camelCase), but
      // historically we have checked for imagetype (lowercase).
      // This was the case since background image support was first introduced
      // in PR #1859, in April 2019, and first released in v2.5.0.
      // Now we check for both, although only imageType (camelCase) is to spec.
      const backgroundImageType =
          imageElement.attributes['imageType'] ||
          imageElement.attributes['imagetype'];
      const backgroundImageEncoding = imageElement.attributes['encoding'];
      const backgroundImageData = (TXml.getTextContents(imageElement)).trim();
      if (backgroundImageType == 'PNG' &&
          backgroundImageEncoding == 'Base64' &&
          backgroundImageData) {
        cue.backgroundImage = 'data:image/png;base64,' + backgroundImageData;
      }
    } else if (imageUri) {
      cue.backgroundImage = imageUri;
    }

    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 =
            TtmlTextParser.convertTTMLrgbaToHTMLrgba_(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_)) {
      cue.letterSpacing = letterSpacing;
    }

    const linePadding = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'linePadding', shouldInheritRegionStyles);
    if (linePadding && linePadding.match(TtmlTextParser.unitValues_)) {
      cue.linePadding = linePadding;
    }

    const opacity = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'opacity', shouldInheritRegionStyles);
    if (opacity) {
      cue.opacity = parseFloat(opacity);
    }

    // Text decoration is an array of values which can come both from the
    // element's style or be inherited from elements' parent nodes. All of those
    // values should be applied as long as they don't contradict each other. If
    // they do, elements' own style gets preference.
    const textDecorationRegion = TtmlTextParser.getStyleAttributeFromRegion_(
        region, styles, 'textDecoration');
    if (textDecorationRegion) {
      TtmlTextParser.addTextDecoration_(cue, textDecorationRegion);
    }

    const textDecorationElement = TtmlTextParser.getStyleAttributeFromElement_(
        cueElement, styles, 'textDecoration');
    if (textDecorationElement) {
      TtmlTextParser.addTextDecoration_(cue, textDecorationElement);
    }

    const textCombine = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'textCombine', shouldInheritRegionStyles);
    if (textCombine) {
      cue.textCombineUpright = textCombine;
    }

    const ruby = TtmlTextParser.getStyleAttribute_(
        cueElement, region, styles, 'ruby', shouldInheritRegionStyles);
    switch (ruby) {
      case 'container':
        cue.rubyTag = 'ruby';
        break;
      case 'text':
        cue.rubyTag = 'rt';
        break;
    }
  }
Adds applicable style properties to a cue.

@param {!shaka.text.Cue} cue
@param {!shaka.extern.xml.Node} cueElement
@param {shaka.extern.xml.Node} region
@param {shaka.extern.xml.Node} imageElement
@param {?string} imageUri
@param {!Array<!shaka.extern.xml.Node>} styles
@param {boolean} isNested
@param {boolean} isLeaf
@private