def setUp(self): self.doc = model.ContentDocument() r1 = model.Region("r1", self.doc) r1.set_style(styles.StyleProperties.ShowBackground, styles.ShowBackgroundType.whenActive) self.doc.put_region(r1) b = model.Body(self.doc) b.set_region(r1) self.doc.set_body(b) div1 = model.Div(self.doc) b.push_child(div1) p1 = model.P(self.doc) p1.set_begin(1) p1.set_end(3) div1.push_child(p1) span1 = model.Span(self.doc) span1.push_child(model.Text(self.doc, "hello")) p1.push_child(span1) span2 = model.Span(self.doc) span2.set_begin(1) span2.push_child(model.Text(self.doc, "bye")) p1.push_child(span2)
def test_push_child(self): s = model.Span() s.push_child(model.Br()) s.push_child(model.Span()) s.push_child(model.Text()) with self.assertRaises(TypeError): s.push_child(model.P())
def from_xml( parent_ctx: typing.Optional[TTMLElement.ParsingContext], xml_elem: et.Element ) -> typing.Optional[SpanElement.ParsingContext]: span_ctx = SpanElement.ParsingContext(SpanElement, parent_ctx, model.Span(parent_ctx.doc)) span_ctx.process(parent_ctx, xml_elem) return span_ctx
def test_default_region(self): doc = model.ContentDocument() b = model.Body(doc) doc.set_body(b) div1 = model.Div(doc) b.push_child(div1) p1 = model.P(doc) div1.push_child(p1) span1 = model.Span(doc) span1.push_child(model.Text(doc, "hello")) p1.push_child(span1) isd = ISD.from_model(doc, 0) self.assertEqual(len(isd), 1) regions = list(isd.iter_regions()) self.assertEqual(regions[0].get_id(), ISD.DEFAULT_REGION_ID) p = regions[0][0][0][0] self.assertEqual(len(p), 1) self.assertEqual(p[0][0].get_text(), "hello")
def test_tts_writing_extent_when_body_has_extents(self): doc = model.ContentDocument() body = model.Body(doc) div = model.Div(doc) div.set_style( styles.StyleProperties.Extent, get_extent_from_dimensions(123, 456, styles.LengthType.Units.px)) p = model.P(doc) span = model.Span(doc) text = model.Text(doc) text.set_text("asdf") span.push_child(text) p.push_child(span) div.push_child(p) body.push_child(div) doc.set_body(body) r = model.Region("hello", doc) r.set_style( styles.StyleProperties.Extent, get_extent_from_dimensions(123, 456, styles.LengthType.Units.px)) doc.put_region(r) tree_from_model = imsc_writer.from_model(doc) extent = tree_from_model.getroot().attrib.get( f"{{{imsc_styles.StyleProperties.Extent.ns}}}{imsc_styles.StyleProperties.Extent.local_name}" ) self.assertEqual(extent, '1920px 1080px')
def test_compute_style_property(self): doc = model.ContentDocument() r1 = model.Region("r1", doc) r1.set_style(styles.StyleProperties.FontSize, styles.LengthType(value=50, units=styles.LengthType.Units.pct)) doc.put_region(r1) b = model.Body(doc) b.set_style(styles.StyleProperties.FontSize, styles.LengthType(value=50, units=styles.LengthType.Units.pct)) b.set_region(r1) doc.set_body(b) div1 = model.Div(doc) b.push_child(div1) p1 = model.P(doc) div1.push_child(p1) span1 = model.Span(doc) p1.push_child(span1) t1 = model.Text(doc, "hello") span1.push_child(t1) isd = ISD.from_model(doc, 0) region = list(isd.iter_regions())[0] span = region[0][0][0][0] fs: styles.LengthType = span.get_style(styles.StyleProperties.FontSize) self.assertAlmostEqual(fs.value, 25 / doc.get_cell_resolution().rows) self.assertEqual(fs.units, styles.LengthType.Units.rh)
def handle_starttag(self, tag, attrs): span = model.Span(self.parent.get_doc()) self.parent.push_child(span) self.parent = span if tag.lower() in ("b", "bold"): span.set_style(styles.StyleProperties.FontWeight, styles.FontWeightType.bold) elif tag.lower() in ("i", "italic"): span.set_style(styles.StyleProperties.FontStyle, styles.FontStyleType.italic) elif tag.lower() in ("u", "underline"): span.set_style(styles.StyleProperties.TextDecoration, styles.TextDecorationType(underline=True)) elif tag.lower() == "font": for attr in attrs: if attr[0] == "color": color = parse_color(attr[1]) break else: LOGGER.warning("Font tag without a color attribute at line %s", self.line_num) return if color is None: LOGGER.warning("Unknown color %s at line %s", attrs["color"], self.line_num) return span.set_style(styles.StyleProperties.Color, color) else: LOGGER.warning("Unknown tag %s at line %s", tag, self.line_num) return
def make_anonymous_span(doc, text): s = model.Span(doc) t = model.Text(doc, text) s.push_child(t) return s
def handle_data(self, data): lines = data.split("\n") for i, line in enumerate(lines): if i > 0: self.parent.push_child(model.Br(self.parent.get_doc())) span = model.Span(self.parent.get_doc()) span.push_child(model.Text(self.parent.get_doc(), line)) self.parent.push_child(span)
def test_push_child(self): p = model.P() p.push_child(model.Br()) p.push_child(model.Span()) p.push_child(model.Ruby()) with self.assertRaises(TypeError): p.push_child(model.Text())
def make_anonymous_span(document, model_element, span_text): '''Creates an anonymous span in the element `model_element` from the text contained in `span_text` ''' if isinstance(model_element, model.Span): return model.Text(document, span_text) s = model.Span(document) s.set_space(model_element.get_space()) s.set_lang(model_element.get_lang()) s.push_child(model.Text(document, span_text)) return s
def test_text_decoration_inheritance(self): doc = model.ContentDocument() r1 = model.Region("r1", doc) r1.set_style( styles.StyleProperties.TextDecoration, styles.TextDecorationType( line_through=False, underline=True, overline=True ) ) doc.put_region(r1) b = model.Body(doc) b.set_style( styles.StyleProperties.TextDecoration, styles.TextDecorationType( overline=False ) ) b.set_region(r1) doc.set_body(b) div1 = model.Div(doc) b.push_child(div1) p1 = model.P(doc) div1.push_child(p1) span1 = model.Span(doc) p1.push_child(span1) t1 = model.Text(doc, "hello") span1.push_child(t1) isd = ISD.from_model(doc, 0) region = list(isd.iter_regions())[0] span = region[0][0][0][0] self.assertEqual( span.get_style(styles.StyleProperties.TextDecoration), styles.TextDecorationType( line_through=False, underline=True, overline=False ) )
def process(context, inherited_space, inherited_lang, ttml_element): element = model.Span(context.doc) # process attributes element.set_space( XMLSpaceAttribute.extract(ttml_element) or inherited_space) element.set_lang( XMLLangAttribute.extract(ttml_element) or inherited_lang) ContentElement.process_region_property(context, ttml_element, element) ContentElement.process_style_properties(context, ttml_element, element) # process head text node if ttml_element.text is not None: element.push_child(model.Text(context.doc, ttml_element.text)) # process children elements for ttml_child_element in ttml_element: child_element = ContentElement.process(context, element.get_space(), element.get_lang(), ttml_child_element) if child_element is not None: if not isinstance(child_element, (model.Span, model.Br)): LOGGER.error( "Children of p must be span or br or text instances") else: element.push_child(child_element) # process tail text node if ttml_child_element.tail: element.push_child( model.Text(context.doc, ttml_child_element.tail)) return element
def start_span(self): if self.span is None: self.span = model.Span(self.parent.get_doc()) self.span.set_style(styles.StyleProperties.BackgroundColor, self.get_bg_color()) self.span.set_style(styles.StyleProperties.Color, self.get_fg_color()) if self.get_underline(): self.span.set_style( styles.StyleProperties.TextDecoration, styles.TextDecorationType(underline=True) ) if self.get_italic(): self.span.set_style( styles.StyleProperties.FontStyle, styles.FontStyleType.italic )
def test_body_only(self): doc = model.ContentDocument() body = model.Body(doc) div = model.Div(doc) p = model.P(doc) span = model.Span(doc) text = model.Text(doc) text.set_text("asdf") span.push_child(text) p.push_child(span) div.push_child(p) body.push_child(div) doc.set_body(body) # write the document out to a file imsc_writer.from_model(doc).write('build/BodyElement.out.ttml', encoding='utf-8', xml_declaration=True)
def test_tts_writing_no_extent_when_body_has_no_extents(self): doc = model.ContentDocument() body = model.Body(doc) div = model.Div(doc) p = model.P(doc) span = model.Span(doc) text = model.Text(doc) text.set_text("asdf") span.push_child(text) p.push_child(span) div.push_child(p) body.push_child(div) doc.set_body(body) tree_from_model = imsc_writer.from_model(doc) extent = tree_from_model.getroot().attrib.get( f"{{{imsc_styles.StyleProperties.Extent.ns}}}{imsc_styles.StyleProperties.Extent.local_name}" ) self.assertEqual(extent, None)
def process_tti_block(self, tti_block: bytes): """Processes a single TTI block """ if tti_block is None: raise ValueError("tti_block should not be None") tti = _TTIBlock._make(struct.unpack('<BHBBBBBBBBBBBBB112s', tti_block)) LOGGER.debug("Subtitle SN: %s", tti.SN) LOGGER.debug(" EBN: %s", tti.EBN) LOGGER.debug(" CS: %s", tti.CS) LOGGER.debug(" SGN: %s", tti.SGN) LOGGER.debug(" JC: %s", tti.JC) LOGGER.debug(" VP: %s", tti.VP) if 0xEF < tti.EBN < 0xFF: # skip user data and reserved blocks return if not self.is_in_extension: self.tti_tf = b'' self.tti_tf += tti.TF.strip(b'\x8f') is_double_height_characters = tf.has_double_height_char(self.tti_tf) # continue accumulating if we have an extension block if tti.EBN != 0xFF: self.is_in_extension = True return self.is_in_extension = False # apply program offset try: tci = SmpteTimeCode(tti.TCIh, tti.TCIm, tti.TCIs, tti.TCIf, self.get_fps()) tco = SmpteTimeCode(tti.TCOh, tti.TCOm, tti.TCOs, tti.TCOf, self.get_fps()) except ValueError: LOGGER.error("Invalid TTI timecode") return begin_time = tci.to_temporal_offset() - self.start_offset if begin_time < 0: LOGGER.debug( "Skipping subtitle because TCI is less than start time") return LOGGER.debug(" Time in: %s", tci) end_time = tco.to_temporal_offset() - self.start_offset if end_time < begin_time: LOGGER.error("Subtitle TCO is less than TCI") return LOGGER.debug(" Time out: %s", tco) # create a new subtitle if SN changes and we are not in cumulative mode if tti.SN is not self.last_sn and tti.CS in (0x00, 0x01): self.last_sn = tti.SN # find the div to which the subtitle belongs, based on SGN div_element = self.sgn_to_div_map.get(tti.SGN) # create the div if it does not exist if div_element is None: div_element = model.Div(self.doc) self.body.push_child(div_element) self.sgn_to_div_map[tti.SGN] = div_element # create the p that will hold the subtitle self.cur_p_element = model.P(self.doc) if tti.JC == 0x01: self.cur_p_element.set_style(styles.StyleProperties.TextAlign, styles.TextAlignType.start) elif tti.JC == 0x03: self.cur_p_element.set_style(styles.StyleProperties.TextAlign, styles.TextAlignType.end) else: self.cur_p_element.set_style(styles.StyleProperties.TextAlign, styles.TextAlignType.center) self.cur_p_element.set_style( styles.StyleProperties.LineHeight, styles.LengthType(DEFAULT_LINE_HEIGHT_PCT, styles.LengthType.Units.pct)) if self.is_teletext() and not is_double_height_characters: font_size = DEFAULT_SINGLE_HEIGHT_FONT_SIZE_PCT else: font_size = DEFAULT_DOUBLE_HEIGHT_FONT_SIZE_PCT self.cur_p_element.set_style( styles.StyleProperties.FontSize, styles.LengthType(font_size, styles.LengthType.Units.pct)) safe_area_height = round(100 - DEFAULT_VERTICAL_SAFE_MARGIN_PCT * 2) safe_area_width = round(100 - DEFAULT_HORIZONTAL_SAFE_MARGIN_PCT * 2) # assume that VP < max number of rows/2 means bottom-aligned and otherwise top-aligned # probably should offer an option to override this if tti.VP < self.get_max_row_count() // 2: # top-aligned large region r_y = DEFAULT_VERTICAL_SAFE_MARGIN_PCT + ( (tti.VP - 1) / self.get_max_row_count()) * safe_area_height r_height = 100 - DEFAULT_VERTICAL_SAFE_MARGIN_PCT - r_y region = _get_region_from_model( self.doc, round(DEFAULT_HORIZONTAL_SAFE_MARGIN_PCT), r_y, safe_area_width, r_height, styles.DisplayAlignType.before) else: line_count = tf.line_count(self.tti_tf, is_double_height_characters) vp = tti.VP line_height = 2 if is_double_height_characters else 1 r_y = DEFAULT_VERTICAL_SAFE_MARGIN_PCT r_height = ((vp + line_count * line_height - 1) / self.get_max_row_count()) * safe_area_height region = _get_region_from_model( self.doc, round(DEFAULT_HORIZONTAL_SAFE_MARGIN_PCT), r_y, safe_area_width, r_height, styles.DisplayAlignType.after) self.cur_p_element.set_region(region) div_element.push_child(self.cur_p_element) if tti.CS in (0x01, 0x02, 0x03): # create a nested span if we are in cumulative mode sub_element = model.Span(self.doc) self.cur_p_element.push_child(sub_element) else: sub_element = self.cur_p_element sub_element.set_begin(begin_time) sub_element.set_end(end_time) LOGGER.debug(" TF: %s", self.tti_tf) tf.to_model(sub_element, self.is_teletext(), self.get_cct(), self.tti_tf) if tti.CS in (0x01, 0x02): sub_element.push_child(model.Br(self.doc))
def setUp(self): self.doc = model.ContentDocument() a1 = model.DiscreteAnimationStep( style_property=styles.StyleProperties.Color, begin=None, end=None, value=styles.NamedColors.red.value ) a2 = model.DiscreteAnimationStep( style_property=styles.StyleProperties.Color, begin=Fraction(1), end=None, value=styles.NamedColors.green.value ) a3 = model.DiscreteAnimationStep( style_property=styles.StyleProperties.Color, begin=Fraction(2), end=None, value=styles.NamedColors.blue.value ) r1 = model.Region("r1", self.doc) self.doc.put_region(r1) # r2: sig times = {2, 9} r2 = model.Region("r2", self.doc) r2.set_begin(Fraction(2)) r2.set_end(Fraction(9)) r2.add_animation_step(a1) self.doc.put_region(r2) # b: sig times = {1, 10} b = model.Body(self.doc) b.set_begin(Fraction(1)) b.set_end(Fraction(10)) self.doc.set_body(b) # div1: offset = 1, sig times = {2, 4} div1 = model.Div(self.doc) div1.add_animation_step(a2) div1.set_begin(Fraction(3)) div1.set_region(r1) b.push_child(div1) # div2: offset = 1, sig times = {10} div2 = model.Div(self.doc) div2.set_end(Fraction(12)) div2.set_region(r2) b.push_child(div2) # p1: offset = 1, sig times = {} p1 = model.P(self.doc) div2.push_child(p1) # span1: offset = 1, sig times = {3} span1 = model.Span(self.doc) span1.add_animation_step(a3) p1.push_child(span1) t1 = model.Text(self.doc, "hello") span1.push_child(t1)
def test_push_child(self): t = model.Text() with self.assertRaises(RuntimeError): t.push_child(model.Span())