def cell_resolution(self): cr = model.CellResolutionType(rows=10, columns=20) self.assertEqual(cr.columns, 20) self.assertEqual(cr.rows, 10) with self.assertRaises(ValueError): cr = model.CellResolutionType(rows=0, columns=20)
def test_copy_to(self): src = model.ContentDocument() src.set_display_aspect_ratio(Fraction(16, 9)) src.set_active_area(model.ActiveAreaType(0.1, 0.15, 0.8, 0.7)) src.set_px_resolution(model.PixelResolutionType(height=480, width=640)) src.set_lang("fr") src.set_cell_resolution(model.CellResolutionType(rows=10, columns=20)) src.put_initial_value(styles.StyleProperties.Color, styles.ColorType((12, 23, 43, 56))) dest = model.ContentDocument() src.copy_to(dest) self.assertEqual(dest.get_display_aspect_ratio(), src.get_display_aspect_ratio()) self.assertEqual(dest.get_active_area(), src.get_active_area()) self.assertEqual(dest.get_px_resolution(), src.get_px_resolution()) self.assertEqual(dest.get_lang(), src.get_lang()) self.assertEqual(dest.get_cell_resolution(), src.get_cell_resolution()) self.assertSequenceEqual(list(dest.iter_initial_values()), list(src.iter_initial_values()))
def extract(ttml_element) -> model.CellResolutionType: cr = ttml_element.attrib.get(CellResolutionAttribute.qn) if cr is not None: m = CellResolutionAttribute._CELL_RESOLUTION_RE.match(cr) if m is not None: return model.CellResolutionType(int(m.group(1)), int(m.group(2))) LOGGER.error("ttp:cellResolution invalid syntax") # default value in TTML return model.CellResolutionType(rows=15, columns=32)
def test_init(self): d = model.ContentDocument() self.assertIsNone(d.get_body()) self.assertEqual(d.get_px_resolution(), model.PixelResolutionType(width=1920, height=1080)) self.assertEqual(len(d.iter_initial_values()), 0) self.assertEqual(len(d.iter_regions()), 0) self.assertEqual(d.get_cell_resolution(), model.CellResolutionType(rows=15, columns=32))
def from_model(model_doc: model.ContentDocument) -> et.Element: tt_element = et.Element(TTElement.qn) imsc_attr.XMLLangAttribute.set(tt_element, model_doc.get_lang()) if model_doc.get_cell_resolution() != model.CellResolutionType( rows=15, columns=32): imsc_attr.CellResolutionAttribute.set( tt_element, model_doc.get_cell_resolution()) has_px = False all_elements = list(model_doc.iter_regions()) if model_doc.get_body() is not None: all_elements.extend(model_doc.get_body().dfs_iterator()) for element in all_elements: for model_style_prop in element.iter_styles(): if StyleProperties.BY_MODEL_PROP[model_style_prop].has_px( element.get_style(model_style_prop)): has_px = True break if has_px: break if model_doc.get_px_resolution() is not None and has_px: imsc_attr.ExtentAttribute.set(tt_element, model_doc.get_px_resolution()) if model_doc.get_active_area() is not None: imsc_attr.ActiveAreaAttribute.set(tt_element, model_doc.get_active_area()) # Write the <head> section first head_element = HeadElement.from_model(model_doc) if head_element is not None: tt_element.append(head_element) model_body = model_doc.get_body() if model_body is not None: body_element = BodyElement.from_model(model_body) if body_element is not None: tt_element.append(body_element) return tt_element
def from_model( model_doc: model.ContentDocument, frame_rate: typing.Optional[Fraction], time_expression_syntax: imsc_attr.TimeExpressionSyntaxEnum, progress_callback: typing.Callable[[numbers.Real], typing.NoReturn] ) -> et.Element: '''Converts the data model to an IMSC document contained in an ElementTree Element''' ctx = TTMLElement.WritingContext(frame_rate, time_expression_syntax) tt_element = et.Element(TTElement.qn) imsc_attr.XMLLangAttribute.set(tt_element, model_doc.get_lang()) if model_doc.get_cell_resolution() != model.CellResolutionType(rows=15, columns=32): imsc_attr.CellResolutionAttribute.set(tt_element, model_doc.get_cell_resolution()) has_px = False all_elements = list(model_doc.iter_regions()) if model_doc.get_body() is not None: all_elements.extend(model_doc.get_body().dfs_iterator()) for element in all_elements: for model_style_prop in element.iter_styles(): if StyleProperties.BY_MODEL_PROP[model_style_prop].has_px(element.get_style(model_style_prop)): has_px = True break for animation_step in element.iter_animation_steps(): if StyleProperties.BY_MODEL_PROP[animation_step.style_property].has_px(animation_step.value): has_px = True break if has_px: break if model_doc.get_px_resolution() is not None and has_px: imsc_attr.ExtentAttribute.set(tt_element, model_doc.get_px_resolution()) if model_doc.get_active_area() is not None: imsc_attr.ActiveAreaAttribute.set(tt_element, model_doc.get_active_area()) if model_doc.get_display_aspect_ratio() is not None: imsc_attr.DisplayAspectRatioAttribute.set(tt_element, model_doc.get_display_aspect_ratio()) if frame_rate is not None: imsc_attr.FrameRateAttribute.set(tt_element, frame_rate) # Write the <head> section first head_element = HeadElement.from_model(ctx, model_doc) progress_callback(0.5) if head_element is not None: tt_element.append(head_element) model_body = model_doc.get_body() if model_body is not None: body_element = BodyElement.from_model(ctx, model_body) if body_element is not None: tt_element.append(body_element) progress_callback(1.0) return tt_element
def __init__(self, gsi_block: bytes, disable_fill_line_gap: bool = False, disable_line_padding: bool = False, start_tc: typing.Optional[str] = None, font_stack: typing.Tuple[typing.Union[ str, styles.GenericFontFamilyType]] = None, max_row_count: typing.Optional[typing.Union[int, str]] = None): self.gsi = _GSIBlock._make( struct.unpack( '3s8sc2s2s32s32s32s32s32s32s16s6s6s2s5s5s3s2s2s1s8s8s1s1s3s32s32s32s75x576s', gsi_block)) self.doc = model.ContentDocument() self.doc.set_cell_resolution( model.CellResolutionType( columns=round(100 * DEFAULT_TELETEXT_COLS / (100 - 2 * DEFAULT_HORIZONTAL_SAFE_MARGIN_PCT)), rows=round(100 * DEFAULT_TELETEXT_ROWS / (100 - 2 * DEFAULT_VERTICAL_SAFE_MARGIN_PCT)))) self.doc.set_active_area( model.ActiveAreaType( left_offset=DEFAULT_HORIZONTAL_SAFE_MARGIN_PCT / 100, top_offset=DEFAULT_VERTICAL_SAFE_MARGIN_PCT / 100, width=1 - 2 * DEFAULT_HORIZONTAL_SAFE_MARGIN_PCT / 100, height=1 - 2 * DEFAULT_VERTICAL_SAFE_MARGIN_PCT / 100)) self.body = model.Body(self.doc) if not disable_fill_line_gap: self.body.set_style(styles.StyleProperties.FillLineGap, True) if not disable_line_padding: self.body.set_style( styles.StyleProperties.LinePadding, styles.LengthType(LINE_PADDING_LENGTH_C, styles.LengthType.Units.c)) if font_stack is not None: self.body.set_style(styles.StyleProperties.FontFamily, font_stack) else: self.body.set_style(styles.StyleProperties.FontFamily, DEFAULT_FONT_STACK) self.doc.set_body(self.body) self.sgn_to_div_map = {} self.last_sn = None self.is_in_extension = False self.tti_tf = None self.fps = _DFC_FRACTION_MAP.get(self.gsi.DFC) if self.fps is None: LOGGER.error("Unknown GSI DFC value %s, defaulting to 25 fps", self.gsi.DFC) self.fps = Fraction(25) else: LOGGER.debug("GSI DFC: %s", self.gsi.DFC) self.cct = self.gsi.CCT LOGGER.debug("GSI CCT: %s", self.gsi.CCT) try: self.tti_count = int(self.gsi.TNB) LOGGER.debug("GSI TNB: %s", self.gsi.TNB) except ValueError: LOGGER.error("Invalid TNB field value: %s", self.gsi.TNB) self.tti_count = sys.maxsize self.language = _LC_BCP47_MAP.get(self.gsi.LC) if self.language is None: LOGGER.warning( "Unknown LC value: %s, defaulting to 'unspecified''", self.gsi.LC) self.language = "" else: LOGGER.debug("GSI LC: %s", self.gsi.LC) self.doc.set_lang(self.language) if start_tc is None: self.start_offset = 0 elif start_tc == "TCP": try: self.start_offset = SmpteTimeCode( int(self.gsi.TCP[0:2]), int(self.gsi.TCP[2:4]), int(self.gsi.TCP[4:6]), int(self.gsi.TCP[6:8]), self.get_fps()).to_temporal_offset() LOGGER.debug("GSI TCP: %s", self.gsi.TCP) except ValueError: LOGGER.error("Invalid TCP value: %s", self.gsi.tcp) self.start_offset = 0 else: try: self.start_offset = SmpteTimeCode.parse( start_tc, self.get_fps()).to_temporal_offset() except ValueError: LOGGER.error("Invalid start_tc value") raise if max_row_count is None or self.is_teletext(): self.max_row_count = DEFAULT_TELETEXT_ROWS elif isinstance(max_row_count, str) and max_row_count == "MNR": try: self.max_row_count = int(self.gsi.MNR) LOGGER.debug("GSI MNR: %s", self.gsi.MNR) except ValueError: LOGGER.error("Invalid MNR value: %s", self.gsi.MNR) self.start_offset = DEFAULT_TELETEXT_ROWS else: self.max_row_count = max_row_count # p_element for use across cumulative subtitles self.cur_p_element = None