Пример #1
0
    def test_push_child(self):
        b = model.Body()

        b.push_child(model.Div())

        with self.assertRaises(TypeError):
            b.push_child(model.P())
Пример #2
0
  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")
Пример #3
0
  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)
Пример #4
0
 def from_xml(
   parent_ctx: typing.Optional[TTMLElement.ParsingContext],
   xml_elem: et.Element
 ) -> typing.Optional[PElement.ParsingContext]:
   p_ctx = PElement.ParsingContext(PElement, parent_ctx, model.P(parent_ctx.doc))
   p_ctx.process(parent_ctx, xml_elem)
   return p_ctx
Пример #5
0
  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)
Пример #6
0
    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')
Пример #7
0
    def test_push_child(self):

        s = model.Rbc()

        s.push_child(model.Rb())

        with self.assertRaises(TypeError):
            s.push_child(model.P())
Пример #8
0
    def test_push_child(self):

        d1 = model.Div()

        d1.push_child(model.P())

        d1.push_child(model.Div())

        with self.assertRaises(TypeError):
            d1.push_child(model.Text())
Пример #9
0
    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())
Пример #10
0
    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())
Пример #11
0
  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
      )
    )
Пример #12
0
    def process(context, inherited_space, inherited_lang, ttml_element):

        element = model.P(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 children elements

        if ttml_element.text:
            element.push_child(
                SpanElement.make_anonymous_span(context.doc,
                                                ttml_element.text))

        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, model.Ruby)):

                    LOGGER.error(
                        "Children of p must be span, br or ruby instances")

                else:

                    element.push_child(child_element)

            if ttml_child_element.tail:
                element.push_child(
                    SpanElement.make_anonymous_span(context.doc,
                                                    ttml_child_element.tail))

        return element
Пример #13
0
    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)
Пример #14
0
    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)
Пример #15
0
  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)
Пример #16
0
    def test_push_child(self):

        r = model.Rtc()

        with self.assertRaises(RuntimeError):
            r.push_child(model.P())
Пример #17
0
    def test_push_child(self):

        r = model.Region("hello")

        with self.assertRaises(RuntimeError):
            r.push_child(model.P())
Пример #18
0
def to_model(data_file: typing.IO, _config = None, progress_callback=lambda _: None):
  """Converts an SRT document to the data model"""

  doc = model.ContentDocument()

  region = model.Region(_DEFAULT_REGION_ID, doc)
  region.set_style(
    styles.StyleProperties.Origin,
    styles.CoordinateType(
      x=styles.LengthType(5, styles.LengthType.Units.pct),
      y=styles.LengthType(5, styles.LengthType.Units.pct)
    )
  )
  region.set_style(
    styles.StyleProperties.Extent,
    styles.ExtentType(
      height=styles.LengthType(90, styles.LengthType.Units.pct),
      width=styles.LengthType(90, styles.LengthType.Units.pct)
    )
  )
  region.set_style(
    styles.StyleProperties.DisplayAlign,
    styles.DisplayAlignType.after
  )
  region.set_style(
    styles.StyleProperties.TextAlign,
    styles.TextAlignType.center
  )
  region.set_style(
    styles.StyleProperties.LineHeight,
    _DEFAULT_LINE_HEIGHT
  )
  region.set_style(
    styles.StyleProperties.FontFamily,
    _DEFAULT_FONT_STACK
  )
  region.set_style(
    styles.StyleProperties.FontSize,
    _DEFAULT_FONT_SIZE
  )
  region.set_style(
    styles.StyleProperties.Color,
    _DEFAULT_TEXT_COLOR
  )
  region.set_style(
    styles.StyleProperties.TextOutline,
    styles.TextOutlineType(
      _DEFAULT_OUTLINE_THICKNESS,
      _DEFAULT_OUTLINE_COLOR
    )
  )

  doc.put_region(region)

  body = model.Body(doc)
  body.set_region(region)

  doc.set_body(body)

  div = model.Div(doc)

  body.push_child(div)

  lines : str = data_file.readlines()

  state = _State.COUNTER
  current_p = None
 
  for line_index, line in enumerate(_none_terminated(lines)):

    if state is _State.COUNTER:
      if line is None:
        break

      if _EMPTY_RE.fullmatch(line):
        continue

      if _COUNTER_RE.search(line) is None:
        LOGGER.fatal("Missing subtitle counter at line %s", line_index)
        return None
      
      progress_callback(line_index/len(lines))

      state = _State.TC

      continue

    if state is _State.TC:
      if line is None:
        break

      m = _TIMECODE_RE.search(line)

      if m is None:
        LOGGER.fatal("Missing timecode at line %s", line_index)
        return None

      current_p = model.P(doc)

      current_p.set_begin(
        int(m.group('begin_h')) * 3600 + 
        int(m.group('begin_m')) * 60 + 
        int(m.group('begin_s')) +
        int(m.group('begin_ms')) / 1000
        )
    
      current_p.set_end(
        int(m.group('end_h')) * 3600 + 
        int(m.group('end_m')) * 60 + 
        int(m.group('end_s')) +
        int(m.group('end_ms')) / 1000
        )

      state = _State.TEXT

      continue

    if state in (_State.TEXT, _State.TEXT_MORE):

      if line is None or _EMPTY_RE.fullmatch(line):
        subtitle_text = subtitle_text.strip('\r\n')\
          .replace(r"\n\r", "\n")\
          .replace(r"{bold}", r"<bold>")\
          .replace(r"{/bold}", r"</bold>")\
          .replace(r"{italic}", r"<italic>")\
          .replace(r"{/italic}", r"</italic>")\
          .replace(r"{underline}", r"<underline>")\
          .replace(r"{/underline}", r"</underline>")

        parser = _TextParser(current_p, line_index)
        parser.feed(subtitle_text)
        parser.close()

        state = _State.COUNTER
        continue

      if state is _State.TEXT:
        div.push_child(current_p)
        subtitle_text = ""

      if state is _State.TEXT_MORE:
        current_p.push_child(model.Br(current_p.get_doc()))

      subtitle_text += line

      state = _State.TEXT_MORE

      continue

  return doc
Пример #19
0
    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))