1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.android.exoplayer2.text.ttml; 17 18 import android.graphics.Typeface; 19 import android.text.Layout; 20 import androidx.annotation.IntDef; 21 import androidx.annotation.Nullable; 22 import com.google.android.exoplayer2.text.Cue; 23 import com.google.android.exoplayer2.text.Cue.VerticalType; 24 import com.google.android.exoplayer2.text.span.RubySpan; 25 import java.lang.annotation.Documented; 26 import java.lang.annotation.Retention; 27 import java.lang.annotation.RetentionPolicy; 28 29 /** 30 * Style object of a <code>TtmlNode</code> 31 */ 32 /* package */ final class TtmlStyle { 33 34 public static final int UNSPECIFIED = -1; 35 36 @Documented 37 @Retention(RetentionPolicy.SOURCE) 38 @IntDef( 39 flag = true, 40 value = {UNSPECIFIED, STYLE_NORMAL, STYLE_BOLD, STYLE_ITALIC, STYLE_BOLD_ITALIC}) 41 public @interface StyleFlags {} 42 43 public static final int STYLE_NORMAL = Typeface.NORMAL; 44 public static final int STYLE_BOLD = Typeface.BOLD; 45 public static final int STYLE_ITALIC = Typeface.ITALIC; 46 public static final int STYLE_BOLD_ITALIC = Typeface.BOLD_ITALIC; 47 48 @Documented 49 @Retention(RetentionPolicy.SOURCE) 50 @IntDef({UNSPECIFIED, FONT_SIZE_UNIT_PIXEL, FONT_SIZE_UNIT_EM, FONT_SIZE_UNIT_PERCENT}) 51 public @interface FontSizeUnit {} 52 53 public static final int FONT_SIZE_UNIT_PIXEL = 1; 54 public static final int FONT_SIZE_UNIT_EM = 2; 55 public static final int FONT_SIZE_UNIT_PERCENT = 3; 56 57 @Documented 58 @Retention(RetentionPolicy.SOURCE) 59 @IntDef({UNSPECIFIED, OFF, ON}) 60 private @interface OptionalBoolean {} 61 62 private static final int OFF = 0; 63 private static final int ON = 1; 64 65 @Documented 66 @Retention(RetentionPolicy.SOURCE) 67 @IntDef({UNSPECIFIED, RUBY_TYPE_CONTAINER, RUBY_TYPE_BASE, RUBY_TYPE_TEXT, RUBY_TYPE_DELIMITER}) 68 public @interface RubyType {} 69 70 public static final int RUBY_TYPE_CONTAINER = 1; 71 public static final int RUBY_TYPE_BASE = 2; 72 public static final int RUBY_TYPE_TEXT = 3; 73 public static final int RUBY_TYPE_DELIMITER = 4; 74 75 @Nullable private String fontFamily; 76 private int fontColor; 77 private boolean hasFontColor; 78 private int backgroundColor; 79 private boolean hasBackgroundColor; 80 @OptionalBoolean private int linethrough; 81 @OptionalBoolean private int underline; 82 @OptionalBoolean private int bold; 83 @OptionalBoolean private int italic; 84 @FontSizeUnit private int fontSizeUnit; 85 private float fontSize; 86 @Nullable private String id; 87 @RubyType private int rubyType; 88 @RubySpan.Position private int rubyPosition; 89 @Nullable private Layout.Alignment textAlign; 90 @OptionalBoolean private int textCombine; 91 @Cue.VerticalType private int verticalType; 92 TtmlStyle()93 public TtmlStyle() { 94 linethrough = UNSPECIFIED; 95 underline = UNSPECIFIED; 96 bold = UNSPECIFIED; 97 italic = UNSPECIFIED; 98 fontSizeUnit = UNSPECIFIED; 99 rubyType = UNSPECIFIED; 100 rubyPosition = RubySpan.POSITION_UNKNOWN; 101 textCombine = UNSPECIFIED; 102 verticalType = Cue.TYPE_UNSET; 103 } 104 105 /** 106 * Returns the style or {@link #UNSPECIFIED} when no style information is given. 107 * 108 * @return {@link #UNSPECIFIED}, {@link #STYLE_NORMAL}, {@link #STYLE_BOLD}, {@link #STYLE_BOLD} 109 * or {@link #STYLE_BOLD_ITALIC}. 110 */ getStyle()111 @StyleFlags public int getStyle() { 112 if (bold == UNSPECIFIED && italic == UNSPECIFIED) { 113 return UNSPECIFIED; 114 } 115 return (bold == ON ? STYLE_BOLD : STYLE_NORMAL) 116 | (italic == ON ? STYLE_ITALIC : STYLE_NORMAL); 117 } 118 isLinethrough()119 public boolean isLinethrough() { 120 return linethrough == ON; 121 } 122 setLinethrough(boolean linethrough)123 public TtmlStyle setLinethrough(boolean linethrough) { 124 this.linethrough = linethrough ? ON : OFF; 125 return this; 126 } 127 isUnderline()128 public boolean isUnderline() { 129 return underline == ON; 130 } 131 setUnderline(boolean underline)132 public TtmlStyle setUnderline(boolean underline) { 133 this.underline = underline ? ON : OFF; 134 return this; 135 } 136 setBold(boolean bold)137 public TtmlStyle setBold(boolean bold) { 138 this.bold = bold ? ON : OFF; 139 return this; 140 } 141 setItalic(boolean italic)142 public TtmlStyle setItalic(boolean italic) { 143 this.italic = italic ? ON : OFF; 144 return this; 145 } 146 147 @Nullable getFontFamily()148 public String getFontFamily() { 149 return fontFamily; 150 } 151 setFontFamily(@ullable String fontFamily)152 public TtmlStyle setFontFamily(@Nullable String fontFamily) { 153 this.fontFamily = fontFamily; 154 return this; 155 } 156 getFontColor()157 public int getFontColor() { 158 if (!hasFontColor) { 159 throw new IllegalStateException("Font color has not been defined."); 160 } 161 return fontColor; 162 } 163 setFontColor(int fontColor)164 public TtmlStyle setFontColor(int fontColor) { 165 this.fontColor = fontColor; 166 hasFontColor = true; 167 return this; 168 } 169 hasFontColor()170 public boolean hasFontColor() { 171 return hasFontColor; 172 } 173 getBackgroundColor()174 public int getBackgroundColor() { 175 if (!hasBackgroundColor) { 176 throw new IllegalStateException("Background color has not been defined."); 177 } 178 return backgroundColor; 179 } 180 setBackgroundColor(int backgroundColor)181 public TtmlStyle setBackgroundColor(int backgroundColor) { 182 this.backgroundColor = backgroundColor; 183 hasBackgroundColor = true; 184 return this; 185 } 186 hasBackgroundColor()187 public boolean hasBackgroundColor() { 188 return hasBackgroundColor; 189 } 190 191 /** 192 * Chains this style to referential style. Local properties which are already set are never 193 * overridden. 194 * 195 * @param ancestor the referential style to inherit from 196 */ chain(@ullable TtmlStyle ancestor)197 public TtmlStyle chain(@Nullable TtmlStyle ancestor) { 198 return inherit(ancestor, true); 199 } 200 201 /** 202 * Inherits from an ancestor style. Properties like <i>tts:backgroundColor</i> which are not 203 * inheritable are not inherited as well as properties which are already set locally are never 204 * overridden. 205 * 206 * @param ancestor the ancestor style to inherit from 207 */ inherit(@ullable TtmlStyle ancestor)208 public TtmlStyle inherit(@Nullable TtmlStyle ancestor) { 209 return inherit(ancestor, false); 210 } 211 inherit(@ullable TtmlStyle ancestor, boolean chaining)212 private TtmlStyle inherit(@Nullable TtmlStyle ancestor, boolean chaining) { 213 if (ancestor != null) { 214 if (!hasFontColor && ancestor.hasFontColor) { 215 setFontColor(ancestor.fontColor); 216 } 217 if (bold == UNSPECIFIED) { 218 bold = ancestor.bold; 219 } 220 if (italic == UNSPECIFIED) { 221 italic = ancestor.italic; 222 } 223 if (fontFamily == null && ancestor.fontFamily != null) { 224 fontFamily = ancestor.fontFamily; 225 } 226 if (linethrough == UNSPECIFIED) { 227 linethrough = ancestor.linethrough; 228 } 229 if (underline == UNSPECIFIED) { 230 underline = ancestor.underline; 231 } 232 if (rubyPosition == RubySpan.POSITION_UNKNOWN) { 233 rubyPosition = ancestor.rubyPosition; 234 } 235 if (textAlign == null && ancestor.textAlign != null) { 236 textAlign = ancestor.textAlign; 237 } 238 if (textCombine == UNSPECIFIED) { 239 textCombine = ancestor.textCombine; 240 } 241 if (fontSizeUnit == UNSPECIFIED) { 242 fontSizeUnit = ancestor.fontSizeUnit; 243 fontSize = ancestor.fontSize; 244 } 245 // attributes not inherited as of http://www.w3.org/TR/ttml1/ 246 if (chaining && !hasBackgroundColor && ancestor.hasBackgroundColor) { 247 setBackgroundColor(ancestor.backgroundColor); 248 } 249 if (chaining && rubyType == UNSPECIFIED && ancestor.rubyType != UNSPECIFIED) { 250 rubyType = ancestor.rubyType; 251 } 252 if (chaining && verticalType == Cue.TYPE_UNSET && ancestor.verticalType != Cue.TYPE_UNSET) { 253 setVerticalType(ancestor.verticalType); 254 } 255 } 256 return this; 257 } 258 setId(@ullable String id)259 public TtmlStyle setId(@Nullable String id) { 260 this.id = id; 261 return this; 262 } 263 264 @Nullable getId()265 public String getId() { 266 return id; 267 } 268 setRubyType(@ubyType int rubyType)269 public TtmlStyle setRubyType(@RubyType int rubyType) { 270 this.rubyType = rubyType; 271 return this; 272 } 273 274 @RubyType getRubyType()275 public int getRubyType() { 276 return rubyType; 277 } 278 setRubyPosition(@ubySpan.Position int position)279 public TtmlStyle setRubyPosition(@RubySpan.Position int position) { 280 this.rubyPosition = position; 281 return this; 282 } 283 284 @RubySpan.Position getRubyPosition()285 public int getRubyPosition() { 286 return rubyPosition; 287 } 288 289 @Nullable getTextAlign()290 public Layout.Alignment getTextAlign() { 291 return textAlign; 292 } 293 setTextAlign(@ullable Layout.Alignment textAlign)294 public TtmlStyle setTextAlign(@Nullable Layout.Alignment textAlign) { 295 this.textAlign = textAlign; 296 return this; 297 } 298 299 /** Returns true if the source entity has {@code tts:textCombine=all}. */ getTextCombine()300 public boolean getTextCombine() { 301 return textCombine == ON; 302 } 303 setTextCombine(boolean combine)304 public TtmlStyle setTextCombine(boolean combine) { 305 this.textCombine = combine ? ON : OFF; 306 return this; 307 } 308 setFontSize(float fontSize)309 public TtmlStyle setFontSize(float fontSize) { 310 this.fontSize = fontSize; 311 return this; 312 } 313 setFontSizeUnit(int fontSizeUnit)314 public TtmlStyle setFontSizeUnit(int fontSizeUnit) { 315 this.fontSizeUnit = fontSizeUnit; 316 return this; 317 } 318 getFontSizeUnit()319 @FontSizeUnit public int getFontSizeUnit() { 320 return fontSizeUnit; 321 } 322 getFontSize()323 public float getFontSize() { 324 return fontSize; 325 } 326 setVerticalType(@erticalType int verticalType)327 public TtmlStyle setVerticalType(@VerticalType int verticalType) { 328 this.verticalType = verticalType; 329 return this; 330 } 331 332 @VerticalType getVerticalType()333 public int getVerticalType() { 334 return verticalType; 335 } 336 } 337