Пример #1
0
    def paint_on_active_caption(self, time_code: SmpteTimeCode):
        """Initialize active caption for paint-on style"""
        active_style = SccCaptionStyle.PaintOn
        copied_lines = []
        cursor = self.active_cursor

        if self.has_active_caption():
            active_style = self.active_caption.get_caption_style()
            cursor = self.active_caption.get_cursor()

            # Copy buffered lines
            copied_lines = self.active_caption.copy_lines()

            # Push active to model if there is one
            self.push_active_caption_to_model(time_code)

        # Initialize new buffered caption
        self.active_caption = SccCaptionParagraph(self.safe_area_x_offset,
                                                  self.safe_area_y_offset,
                                                  active_style)
        self.initialize_active_caption(time_code)

        if len(copied_lines) > 0:
            # Set remaining lines to the new buffered caption
            self.active_caption.set_lines(copied_lines)

        self.active_caption.set_cursor_at(cursor[0], cursor[1])
Пример #2
0
    def test_region_prefix(self):
        doc = ContentDocument()

        caption_paragraph = SccCaptionParagraph()
        paragraph_region = _SccParagraphRegion(caption_paragraph, doc)
        self.assertEqual("region", paragraph_region._get_region_prefix())

        caption_paragraph = SccCaptionParagraph(
            caption_style=SccCaptionStyle.PaintOn)
        paragraph_region = _SccParagraphRegion(caption_paragraph, doc)
        self.assertEqual("paint", paragraph_region._get_region_prefix())

        caption_paragraph = SccCaptionParagraph(
            caption_style=SccCaptionStyle.PopOn)
        paragraph_region = _SccParagraphRegion(caption_paragraph, doc)
        self.assertEqual("pop", paragraph_region._get_region_prefix())

        caption_paragraph = SccCaptionParagraph(
            caption_style=SccCaptionStyle.RollUp)
        paragraph_region = _SccParagraphRegion(caption_paragraph, doc)
        self.assertEqual("rollup", paragraph_region._get_region_prefix())
Пример #3
0
  def process_preamble_address_code(self, pac: SccPreambleAddressCode, time_code: SccTimeCode):
    """Processes SCC Preamble Address Code it to the map to model"""
    if self.current_caption is None:
      raise ValueError("No current SCC caption initialized")

    pac_row = pac.get_row()
    pac_indent = pac.get_indent()

    if self.current_caption.get_caption_style() is SccCaptionStyle.PaintOn:
      if self.current_caption.get_current_text() is not None \
          and self.safe_area_y_offset + pac_row == self.current_caption.get_row_offset() + 1:
        # Creates a new Paragraph if the new caption is contiguous (Paint-On)
        self.set_current_to_previous()

        self.current_caption = SccCaptionParagraph(self.safe_area_x_offset, self.safe_area_y_offset, SccCaptionStyle.PaintOn)
        self.init_current_caption(time_code)

      elif len(self.previous_captions) > 0 and self.previous_captions[0].get_current_text() is not None \
          and self.safe_area_y_offset + pac_row == self.previous_captions[0].get_row_offset():
        # Pushes and erases displayed row so that it can be replaced by current row (Paint-On)
        self.push_previous_caption(self.current_caption.get_begin())

    self.current_caption.new_caption_text()

    if self.current_caption.get_caption_style() is SccCaptionStyle.RollUp:
      # Ignore PACs for rows 5-11, but get indent from PACs for rows 1-4 and 12-15. (Roll-Up)
      if pac_row in range(5, 12):
        self.current_caption.apply_current_text_offsets()
        return

      self.current_caption.set_column_offset(pac_indent)
    else:
      self.current_caption.set_row_offset(pac_row)
      self.current_caption.set_column_offset(pac_indent)

    self.current_caption.get_current_text().add_style_property(StyleProperties.Color, pac.get_color())
    self.current_caption.get_current_text().add_style_property(StyleProperties.FontStyle, pac.get_font_style())
    self.current_caption.get_current_text().add_style_property(StyleProperties.TextDecoration, pac.get_text_decoration())

    self.current_caption.apply_current_text_offsets()
Пример #4
0
class _SccContext:
    def __init__(self, config: Optional[SccReaderConfiguration] = None):
        # Caption paragraphs container
        self.div: Optional[Div] = None

        # Caption paragraphs counter
        self.count: int = 0

        # Screen safe area offsets
        self.safe_area_x_offset: int = 0
        self.safe_area_y_offset: int = 0

        # Previously read SCC word value
        self.previous_code = 0
        self.previous_code_type = None

        # Buffered caption being built
        self.buffered_caption: Optional[SccCaptionParagraph] = None
        # Captions being displayed
        self.active_caption: Optional[SccCaptionParagraph] = None
        # Caption style (Pop-on, Roll-up, Paint-on) currently processed
        self.current_style: Optional[SccCaptionStyle] = None

        # Roll-up caption number of lines
        self.roll_up_depth: int = 0

        # Cursor position in the active area
        self.active_cursor: (int, int) = (0, 0)

        self.current_text_decoration = None
        self.current_color = None
        self.current_font_style = None

        # Text alignment
        self.text_alignment = TextAlignment.AUTO if config is None else config.text_align

    def set_safe_area(self, safe_area_x_offset: int, safe_area_y_offset: int):
        """Sets the safe area"""
        self.safe_area_x_offset = safe_area_x_offset
        self.safe_area_y_offset = safe_area_y_offset

    def has_active_caption(self) -> bool:
        """Returns whether captions are being displayed or not"""
        return self.active_caption is not None

    def set_buffered_caption_begin_time(self, time_code: SmpteTimeCode):
        """Initializes the current buffered caption with begin time"""
        if self.buffered_caption is not None:
            self.buffered_caption.set_begin(time_code)

    def initialize_active_caption(self, begin_time_code: SmpteTimeCode):
        """Initializes the current active caption with id and begin time"""
        if self.active_caption is not None:
            if not self.active_caption.get_id():
                self.count += 1
                self.active_caption.set_id("caption" + str(self.count))

            self.active_caption.set_begin(begin_time_code)

    def push_buffered_to_active_captions(self):
        """Send the current buffered caption to the active captions list"""
        if self.buffered_caption is not None and self.buffered_caption.get_current_text(
        ):
            if not self.buffered_caption.get_id():
                self.count += 1
                self.buffered_caption.set_id("caption" + str(self.count))

            self.active_caption = self.buffered_caption
            self.buffered_caption = None

    def flip_buffered_to_active_captions(
            self, time_code: Optional[SmpteTimeCode] = None):
        """
    Flip the current buffered caption with the last active captions list,
    and push to model if an end time code is specified.
    """
        temporary_caption = None

        if self.has_active_caption():
            temporary_caption = self.active_caption

            if time_code is not None:
                # End of display of active captions
                if self.has_active_caption():
                    self.push_active_caption_to_model(time_code)

        self.push_buffered_to_active_captions()

        if temporary_caption is not None:
            self.buffered_caption = temporary_caption

    def push_active_caption_to_model(self,
                                     time_code: SmpteTimeCode,
                                     clear_active_caption: bool = True):
        """Sets end time to the last active caption, and pushes it into the data model"""
        if self.has_active_caption():
            self.active_cursor = self.active_caption.get_cursor()

            previous_caption = self.active_caption
            previous_caption.set_end(time_code)

            if clear_active_caption:
                self.active_caption = None

            self.div.push_child(
                previous_caption.to_paragraph(self.div.get_doc()))

    def paint_on_active_caption(self, time_code: SmpteTimeCode):
        """Initialize active caption for paint-on style"""
        active_style = SccCaptionStyle.PaintOn
        copied_lines = []
        cursor = self.active_cursor

        if self.has_active_caption():
            active_style = self.active_caption.get_caption_style()
            cursor = self.active_caption.get_cursor()

            # Copy buffered lines
            copied_lines = self.active_caption.copy_lines()

            # Push active to model if there is one
            self.push_active_caption_to_model(time_code)

        # Initialize new buffered caption
        self.active_caption = SccCaptionParagraph(self.safe_area_x_offset,
                                                  self.safe_area_y_offset,
                                                  active_style)
        self.initialize_active_caption(time_code)

        if len(copied_lines) > 0:
            # Set remaining lines to the new buffered caption
            self.active_caption.set_lines(copied_lines)

        self.active_caption.set_cursor_at(cursor[0], cursor[1])

    def process_preamble_address_code(self, pac: SccPreambleAddressCode,
                                      time_code: SmpteTimeCode):
        """Processes SCC Preamble Address Code it to the map to model"""

        pac_row = pac.get_row()
        pac_indent = pac.get_indent()

        if self.current_style is SccCaptionStyle.PaintOn:

            self.paint_on_active_caption(time_code)

            if self.active_caption.get_caption_style(
            ) is SccCaptionStyle.PaintOn:
                # Clear target row on Paint-On style
                target_row = self.active_caption.get_lines().get(pac_row)
                if target_row is not None:
                    target_row.clear()

            self.active_caption.set_cursor_at(pac_row, pac_indent)

            if self.active_caption.get_current_text() is None:
                self.active_caption.new_caption_text()

        elif self.current_style is SccCaptionStyle.RollUp:

            if not self.has_active_caption():
                # If there is no current active caption, initialize an empty new paragraph
                self.active_caption = SccCaptionParagraph(
                    self.safe_area_x_offset, self.safe_area_y_offset,
                    SccCaptionStyle.RollUp)
                self.initialize_active_caption(time_code)

            # Ignore PACs for rows 5-11, but get indent from PACs for rows 1-4 and 12-15. (Roll-Up)
            if pac_row in range(5, 12):
                self.active_caption.set_cursor_at(ROLL_UP_BASE_ROW)
                self.active_caption.new_caption_text()
                return

            # Force roll-up paragraph to belong to the same region
            self.active_caption.set_cursor_at(ROLL_UP_BASE_ROW, pac_indent)

            self.active_caption.new_caption_text()

        else:  # Pop-On Style

            if self.buffered_caption is None:
                self.buffered_caption = SccCaptionParagraph(
                    self.safe_area_x_offset, self.safe_area_y_offset,
                    SccCaptionStyle.PopOn)

            # set cursor in paragraph and create line or text if necessary
            self.buffered_caption.set_cursor_at(pac_row, pac_indent)

            self.buffered_caption.new_caption_text()

        self.current_color = pac.get_color()
        self.current_font_style = pac.get_font_style()
        self.current_text_decoration = pac.get_text_decoration()

        if self.has_active_caption():
            self.active_cursor = self.active_caption.get_cursor()

    def process_mid_row_code(self, mid_row_code: SccMidRowCode,
                             time_code: SmpteTimeCode):
        """Processes SCC Mid-Row Code to map it to the model"""

        # If the Paint-On or Roll-Up style is activated, write directly on active caption
        processed_caption = self.buffered_caption
        if self.current_style in (SccCaptionStyle.PaintOn,
                                  SccCaptionStyle.RollUp):
            processed_caption = self.active_caption

        if processed_caption is None:
            raise ValueError("No current SCC caption initialized")

        color = mid_row_code.get_color()
        font_style = mid_row_code.get_font_style()
        text_decoration = mid_row_code.get_text_decoration()

        if self.previous_code_type is not SccMidRowCode:
            # In case of multiple mid-row codes, move right only after the first code

            # If there is already text on the current line
            if processed_caption.get_current_text() is not None \
                and processed_caption.get_current_text().get_text() != "":

                # In case of paint-on replacing text
                if self.current_style is SccCaptionStyle.PaintOn \
                    and processed_caption.get_current_line().get_cursor() < processed_caption.get_current_line().get_length():
                    processed_caption.append_text(" ")

                else:
                    if text_decoration is None:
                        processed_caption.new_caption_text()
                        processed_caption.append_text(" ")
                    else:
                        processed_caption.append_text(" ")
                        processed_caption.new_caption_text()

            else:
                processed_caption.append_text(" ")

            self.current_color = color
            self.current_font_style = font_style
            self.current_text_decoration = text_decoration

        else:
            if color is not None:
                self.current_color = color
            if font_style is not None:
                self.current_font_style = font_style
            if text_decoration is not None:
                self.current_text_decoration = text_decoration

            processed_caption.append_text(" ")
            processed_caption.new_caption_text()

        if processed_caption.get_caption_style() is SccCaptionStyle.PaintOn:
            processed_caption.get_current_text().set_begin(time_code)

    def process_attribute_code(self, attribute_code: SccAttributeCode):
        """Processes SCC Attribute Code to map it to the model"""

        # If the Paint-On or Roll-Up style is activated, write directly on active caption
        processed_caption = self.buffered_caption
        if self.current_style in (SccCaptionStyle.PaintOn,
                                  SccCaptionStyle.RollUp):
            processed_caption = self.active_caption

        if processed_caption is None or processed_caption.get_current_text(
        ) is None:
            raise ValueError("No current SCC caption nor content initialized")

        if processed_caption.get_current_text(
        ) is not None and processed_caption.get_current_text().get_text():
            processed_caption.new_caption_text()

        if attribute_code.is_background():
            processed_caption.get_current_text().add_style_property(
                StyleProperties.BackgroundColor, attribute_code.get_color())
        else:
            processed_caption.get_current_text().add_style_property(
                StyleProperties.Color, attribute_code.get_color())

        processed_caption.get_current_text().add_style_property(
            StyleProperties.TextDecoration,
            attribute_code.get_text_decoration())

    def process_control_code(self, control_code: SccControlCode,
                             time_code: SmpteTimeCode):
        """Processes SCC Control Code to map it to the model"""

        processed_caption = self.buffered_caption

        if control_code is SccControlCode.RCL:
            # Start a new Pop-On caption
            self.current_style = SccCaptionStyle.PopOn

        elif control_code is SccControlCode.RDC:
            # Start a new Paint-On caption
            self.current_style = SccCaptionStyle.PaintOn

        elif control_code in (SccControlCode.RU2, SccControlCode.RU3,
                              SccControlCode.RU4):
            # Start a new Roll-Up caption
            self.current_style = SccCaptionStyle.RollUp

            if control_code is SccControlCode.RU2:
                self.roll_up_depth = 2

            elif control_code is SccControlCode.RU3:
                self.roll_up_depth = 3

            elif control_code is SccControlCode.RU4:
                self.roll_up_depth = 4

        else:
            # If the Paint-On or Roll-Up style is activated, write directly on active caption
            if self.current_style in (SccCaptionStyle.PaintOn,
                                      SccCaptionStyle.RollUp):
                processed_caption = self.active_caption

        if control_code is SccControlCode.EOC:
            # Display caption (Pop-On)
            self.set_buffered_caption_begin_time(time_code)
            self.flip_buffered_to_active_captions(time_code)

            if self.has_active_caption():
                # Set text alignment
                if self.text_alignment == TextAlignment.AUTO:
                    text_alignment = self.active_caption.guess_text_alignment()
                else:
                    text_alignment = self.text_alignment.text_align

                # Apply text alignment
                self.active_caption.add_style_property(
                    StyleProperties.TextAlign, text_alignment)

        elif control_code is SccControlCode.EDM:
            # Erase displayed captions
            if self.has_active_caption():
                if time_code is not None:
                    # End time is exclusive in the model, set it to the next frame
                    end_time_code = copy.copy(time_code)
                    end_time_code.add_frames()
                else:
                    end_time_code = time_code

                self.push_active_caption_to_model(end_time_code)

        elif control_code is SccControlCode.ENM:
            # Erase buffered caption
            self.buffered_caption = None

        elif control_code is SccControlCode.TO1:
            processed_caption.indent_cursor(1)

        elif control_code is SccControlCode.TO2:
            processed_caption.indent_cursor(2)

        elif control_code is SccControlCode.TO3:
            processed_caption.indent_cursor(3)

        elif control_code is SccControlCode.CR:
            # Roll the displayed caption up one row (Roll-Up)

            if self.has_active_caption():
                # Push active caption to model (but don't erase it)
                self.push_active_caption_to_model(time_code, False)
                # Roll the active caption up
                self.active_caption.roll_up()
                # Get the remaining lines to initialize the following caption with the expected depth
                previous_lines = self.active_caption.get_last_caption_lines(
                    self.roll_up_depth - 1)

                # Initialize the new caption with the previous lines
                self.active_caption = SccCaptionParagraph(
                    self.safe_area_x_offset, self.safe_area_y_offset,
                    SccCaptionStyle.RollUp)
                self.initialize_active_caption(time_code)
                self.active_caption.set_lines(previous_lines)

                self.active_caption.set_cursor_at(self.active_cursor[0],
                                                  self.active_cursor[1])

        elif control_code is SccControlCode.DER:
            # Delete to End of Row (Paint-On)
            # The DER may be issued from any point on a row to delete all displayable characters, transparent
            # spaces, and mid-row codes from (and including) the current cell to the end of the row.
            # Not used in this implementation since this SCC reader does not map the text overlapping into
            # the model (i.e. a row is erased when a PAC is received, so before a new caption is written onto it).
            pass

        elif control_code is SccControlCode.BS:
            # Backspace
            # When a Backspace is received, the cursor moves to the left one column position erasing
            # the character or Mid-Row Code occupying that location, unless the cursor is in Column 1
            processed_caption.get_current_text().backspace()

    def process_text(self, word: str, time_code: SmpteTimeCode):
        """Processes SCC text words"""
        if self.current_style is SccCaptionStyle.PaintOn:
            if word.startswith(" "):

                if self.active_caption.get_caption_style(
                ) is not SccCaptionStyle.PaintOn:
                    self.paint_on_active_caption(time_code)
                    self.active_caption.append_text(word)

                else:
                    self.active_caption.new_caption_text()
                    self.active_caption.append_text(word)
                    self.active_caption.get_current_text().set_begin(time_code)

            elif word.endswith(" "):
                self.active_caption.append_text(word)

                if self.active_caption.get_caption_style(
                ) is not SccCaptionStyle.PaintOn:
                    self.paint_on_active_caption(time_code)
                else:
                    self.active_caption.new_caption_text()
                    self.active_caption.get_current_text().set_begin(time_code)

            else:
                if not self.has_active_caption():
                    self.paint_on_active_caption(time_code)

                self.active_caption.append_text(word)

            self.active_caption.get_current_text().add_style_property(
                StyleProperties.Color, self.current_color)
            self.active_caption.get_current_text().add_style_property(
                StyleProperties.FontStyle, self.current_font_style)
            self.active_caption.get_current_text().add_style_property(
                StyleProperties.TextDecoration, self.current_text_decoration)

        elif self.current_style is SccCaptionStyle.RollUp:
            self.active_caption.append_text(word)

            self.active_caption.get_current_text().add_style_property(
                StyleProperties.Color, self.current_color)
            self.active_caption.get_current_text().add_style_property(
                StyleProperties.FontStyle, self.current_font_style)
            self.active_caption.get_current_text().add_style_property(
                StyleProperties.TextDecoration, self.current_text_decoration)

        else:
            self.buffered_caption.append_text(word)

            self.buffered_caption.get_current_text().add_style_property(
                StyleProperties.Color, self.current_color)
            self.buffered_caption.get_current_text().add_style_property(
                StyleProperties.FontStyle, self.current_font_style)
            self.buffered_caption.get_current_text().add_style_property(
                StyleProperties.TextDecoration, self.current_text_decoration)

        if self.has_active_caption():
            self.active_cursor = self.active_caption.get_cursor()

    def flush(self, time_code: Optional[SmpteTimeCode] = None):
        """Flushes the remaining current caption"""
        if self.has_active_caption():
            self.push_active_caption_to_model(time_code)

        if self.buffered_caption is not None:
            # Remove the buffered caption
            self.buffered_caption = None

    def process_line(self, line: SccLine) -> SmpteTimeCode:
        """Converts the SCC line to the data model"""

        debug = str(line.time_code) + "\t"

        for scc_word in line.scc_words:

            if self.previous_code == scc_word.value:
                continue

            line.time_code.add_frames()

            if scc_word.value == 0x0000:
                continue

            if scc_word.byte_1 < 0x20:

                control_code = SccControlCode.find(scc_word.value)
                if control_code is not None \
                    and control_code is SccControlCode.find(self.previous_code):
                    # Skip duplicated control code from 'Field 2'
                    line.time_code.add_frames(-1)
                    continue

                attribute_code = SccAttributeCode.find(scc_word.value)
                mid_row_code = SccMidRowCode.find(scc_word.value)
                pac = SccPreambleAddressCode.find(scc_word.byte_1,
                                                  scc_word.byte_2)
                spec_char = SccSpecialCharacter.find(scc_word.value)
                extended_char = SccExtendedCharacter.find(scc_word.value)

                if pac is not None:
                    debug += "[PAC|" + str(pac.get_row()) + "|" + str(
                        pac.get_indent())
                    if pac.get_color() is not None:
                        debug += "|" + str(pac.get_color())
                    if pac.get_font_style() is not None:
                        debug += "|I"
                    if pac.get_text_decoration() is not None:
                        debug += "|U"
                    debug += "/" + hex(scc_word.value) + "]"
                    self.process_preamble_address_code(pac, line.time_code)
                    self.previous_code_type = type(pac)

                elif attribute_code is not None:
                    debug += "[ATC/" + hex(scc_word.value) + "]"
                    self.process_attribute_code(attribute_code)
                    self.previous_code_type = type(attribute_code)

                elif mid_row_code is not None:
                    debug += "[MRC|" + mid_row_code.get_name() + "/" + hex(
                        scc_word.value) + "]"
                    self.process_mid_row_code(mid_row_code, line.time_code)
                    self.previous_code_type = type(mid_row_code)

                elif control_code is not None:
                    debug += "[CC|" + control_code.get_name() + "/" + hex(
                        scc_word.value) + "]"
                    self.process_control_code(control_code, line.time_code)
                    self.previous_code_type = type(control_code)

                elif spec_char is not None:
                    word = spec_char.get_unicode_value()
                    debug += word
                    self.process_text(word, line.time_code)
                    self.previous_code_type = type(spec_char)

                elif extended_char is not None:
                    if self.current_style in (SccCaptionStyle.PaintOn,
                                              SccCaptionStyle.RollUp):
                        self.active_caption.get_current_text().backspace()
                    else:
                        self.buffered_caption.get_current_text().backspace()

                    word = extended_char.get_unicode_value()
                    debug += word
                    self.process_text(word, line.time_code)
                    self.previous_code_type = type(extended_char)

                else:
                    debug += "[??/" + hex(scc_word.value) + "]"
                    LOGGER.warning("Unsupported SCC word: %s",
                                   hex(scc_word.value))
                    self.previous_code_type = None

            else:
                word = scc_word.to_text()
                debug += word
                self.process_text(word, line.time_code)
                self.previous_code_type = str

            self.previous_code = scc_word.value

        LOGGER.debug(debug)

        return line.time_code
Пример #5
0
    def process_control_code(self, control_code: SccControlCode,
                             time_code: SmpteTimeCode):
        """Processes SCC Control Code to map it to the model"""

        processed_caption = self.buffered_caption

        if control_code is SccControlCode.RCL:
            # Start a new Pop-On caption
            self.current_style = SccCaptionStyle.PopOn

        elif control_code is SccControlCode.RDC:
            # Start a new Paint-On caption
            self.current_style = SccCaptionStyle.PaintOn

        elif control_code in (SccControlCode.RU2, SccControlCode.RU3,
                              SccControlCode.RU4):
            # Start a new Roll-Up caption
            self.current_style = SccCaptionStyle.RollUp

            if control_code is SccControlCode.RU2:
                self.roll_up_depth = 2

            elif control_code is SccControlCode.RU3:
                self.roll_up_depth = 3

            elif control_code is SccControlCode.RU4:
                self.roll_up_depth = 4

        else:
            # If the Paint-On or Roll-Up style is activated, write directly on active caption
            if self.current_style in (SccCaptionStyle.PaintOn,
                                      SccCaptionStyle.RollUp):
                processed_caption = self.active_caption

        if control_code is SccControlCode.EOC:
            # Display caption (Pop-On)
            self.set_buffered_caption_begin_time(time_code)
            self.flip_buffered_to_active_captions(time_code)

            if self.has_active_caption():
                # Set text alignment
                if self.text_alignment == TextAlignment.AUTO:
                    text_alignment = self.active_caption.guess_text_alignment()
                else:
                    text_alignment = self.text_alignment.text_align

                # Apply text alignment
                self.active_caption.add_style_property(
                    StyleProperties.TextAlign, text_alignment)

        elif control_code is SccControlCode.EDM:
            # Erase displayed captions
            if self.has_active_caption():
                if time_code is not None:
                    # End time is exclusive in the model, set it to the next frame
                    end_time_code = copy.copy(time_code)
                    end_time_code.add_frames()
                else:
                    end_time_code = time_code

                self.push_active_caption_to_model(end_time_code)

        elif control_code is SccControlCode.ENM:
            # Erase buffered caption
            self.buffered_caption = None

        elif control_code is SccControlCode.TO1:
            processed_caption.indent_cursor(1)

        elif control_code is SccControlCode.TO2:
            processed_caption.indent_cursor(2)

        elif control_code is SccControlCode.TO3:
            processed_caption.indent_cursor(3)

        elif control_code is SccControlCode.CR:
            # Roll the displayed caption up one row (Roll-Up)

            if self.has_active_caption():
                # Push active caption to model (but don't erase it)
                self.push_active_caption_to_model(time_code, False)
                # Roll the active caption up
                self.active_caption.roll_up()
                # Get the remaining lines to initialize the following caption with the expected depth
                previous_lines = self.active_caption.get_last_caption_lines(
                    self.roll_up_depth - 1)

                # Initialize the new caption with the previous lines
                self.active_caption = SccCaptionParagraph(
                    self.safe_area_x_offset, self.safe_area_y_offset,
                    SccCaptionStyle.RollUp)
                self.initialize_active_caption(time_code)
                self.active_caption.set_lines(previous_lines)

                self.active_caption.set_cursor_at(self.active_cursor[0],
                                                  self.active_cursor[1])

        elif control_code is SccControlCode.DER:
            # Delete to End of Row (Paint-On)
            # The DER may be issued from any point on a row to delete all displayable characters, transparent
            # spaces, and mid-row codes from (and including) the current cell to the end of the row.
            # Not used in this implementation since this SCC reader does not map the text overlapping into
            # the model (i.e. a row is erased when a PAC is received, so before a new caption is written onto it).
            pass

        elif control_code is SccControlCode.BS:
            # Backspace
            # When a Backspace is received, the cursor moves to the left one column position erasing
            # the character or Mid-Row Code occupying that location, unless the cursor is in Column 1
            processed_caption.get_current_text().backspace()
Пример #6
0
    def process_preamble_address_code(self, pac: SccPreambleAddressCode,
                                      time_code: SmpteTimeCode):
        """Processes SCC Preamble Address Code it to the map to model"""

        pac_row = pac.get_row()
        pac_indent = pac.get_indent()

        if self.current_style is SccCaptionStyle.PaintOn:

            self.paint_on_active_caption(time_code)

            if self.active_caption.get_caption_style(
            ) is SccCaptionStyle.PaintOn:
                # Clear target row on Paint-On style
                target_row = self.active_caption.get_lines().get(pac_row)
                if target_row is not None:
                    target_row.clear()

            self.active_caption.set_cursor_at(pac_row, pac_indent)

            if self.active_caption.get_current_text() is None:
                self.active_caption.new_caption_text()

        elif self.current_style is SccCaptionStyle.RollUp:

            if not self.has_active_caption():
                # If there is no current active caption, initialize an empty new paragraph
                self.active_caption = SccCaptionParagraph(
                    self.safe_area_x_offset, self.safe_area_y_offset,
                    SccCaptionStyle.RollUp)
                self.initialize_active_caption(time_code)

            # Ignore PACs for rows 5-11, but get indent from PACs for rows 1-4 and 12-15. (Roll-Up)
            if pac_row in range(5, 12):
                self.active_caption.set_cursor_at(ROLL_UP_BASE_ROW)
                self.active_caption.new_caption_text()
                return

            # Force roll-up paragraph to belong to the same region
            self.active_caption.set_cursor_at(ROLL_UP_BASE_ROW, pac_indent)

            self.active_caption.new_caption_text()

        else:  # Pop-On Style

            if self.buffered_caption is None:
                self.buffered_caption = SccCaptionParagraph(
                    self.safe_area_x_offset, self.safe_area_y_offset,
                    SccCaptionStyle.PopOn)

            # set cursor in paragraph and create line or text if necessary
            self.buffered_caption.set_cursor_at(pac_row, pac_indent)

            self.buffered_caption.new_caption_text()

        self.current_color = pac.get_color()
        self.current_font_style = pac.get_font_style()
        self.current_text_decoration = pac.get_text_decoration()

        if self.has_active_caption():
            self.active_cursor = self.active_caption.get_cursor()
Пример #7
0
class _SccContext:
  def __init__(self):
    self.div: Optional[Div] = None
    self.count: int = 0
    self.safe_area_x_offset: int = 0
    self.safe_area_y_offset: int = 0
    self.previous_code = 0
    self.current_caption: Optional[SccCaptionParagraph] = None
    self.previous_captions: List[SccCaptionParagraph] = []

  def set_safe_area(self, safe_area_x_offset: int, safe_area_y_offset: int):
    """Sets the safe area"""
    self.safe_area_x_offset = safe_area_x_offset
    self.safe_area_y_offset = safe_area_y_offset

  def set_current_to_previous(self):
    """Rotates current caption to previous caption"""
    if self.current_caption is not None and self.current_caption.get_current_text():

      if not self.current_caption.get_id():
        self.count += 1
        self.current_caption.set_id("caption" + str(self.count))

      self.previous_captions.append(self.current_caption)
      self.current_caption = None

  def init_current_caption(self, time_code: SccTimeCode):
    """Initializes the current caption with begin time"""
    if self.current_caption is not None:
      self.current_caption.set_begin(time_code)

  def push_previous_caption(self, time_code: SccTimeCode, index: int = 0):
    """Sets previous caption end time, pushes it into the data model and resets it"""
    if len(self.previous_captions) > 0:
      previous_caption = self.previous_captions.pop(index)
      previous_caption.set_end(time_code)

      if previous_caption.get_style_property(StyleProperties.BackgroundColor) is None:
        previous_caption.add_style_property(StyleProperties.BackgroundColor, NamedColors.black.value)

      self.div.push_child(previous_caption.to_paragraph(self.div.get_doc()))

  def process_preamble_address_code(self, pac: SccPreambleAddressCode, time_code: SccTimeCode):
    """Processes SCC Preamble Address Code it to the map to model"""
    if self.current_caption is None:
      raise ValueError("No current SCC caption initialized")

    pac_row = pac.get_row()
    pac_indent = pac.get_indent()

    if self.current_caption.get_caption_style() is SccCaptionStyle.PaintOn:
      if self.current_caption.get_current_text() is not None \
          and self.safe_area_y_offset + pac_row == self.current_caption.get_row_offset() + 1:
        # Creates a new Paragraph if the new caption is contiguous (Paint-On)
        self.set_current_to_previous()

        self.current_caption = SccCaptionParagraph(self.safe_area_x_offset, self.safe_area_y_offset, SccCaptionStyle.PaintOn)
        self.init_current_caption(time_code)

      elif len(self.previous_captions) > 0 and self.previous_captions[0].get_current_text() is not None \
          and self.safe_area_y_offset + pac_row == self.previous_captions[0].get_row_offset():
        # Pushes and erases displayed row so that it can be replaced by current row (Paint-On)
        self.push_previous_caption(self.current_caption.get_begin())

    self.current_caption.new_caption_text()

    if self.current_caption.get_caption_style() is SccCaptionStyle.RollUp:
      # Ignore PACs for rows 5-11, but get indent from PACs for rows 1-4 and 12-15. (Roll-Up)
      if pac_row in range(5, 12):
        self.current_caption.apply_current_text_offsets()
        return

      self.current_caption.set_column_offset(pac_indent)
    else:
      self.current_caption.set_row_offset(pac_row)
      self.current_caption.set_column_offset(pac_indent)

    self.current_caption.get_current_text().add_style_property(StyleProperties.Color, pac.get_color())
    self.current_caption.get_current_text().add_style_property(StyleProperties.FontStyle, pac.get_font_style())
    self.current_caption.get_current_text().add_style_property(StyleProperties.TextDecoration, pac.get_text_decoration())

    self.current_caption.apply_current_text_offsets()

  def process_mid_row_code(self, mid_row_code: SccMidRowCode):
    """Processes SCC Mid-Row Code to map it to the model"""
    if self.current_caption is None:
      raise ValueError("No current SCC caption initialized")

    if self.current_caption.get_current_text() is not None and self.current_caption.get_current_text().get_text():
      self.current_caption.new_caption_text()
      self.current_caption.apply_current_text_offsets()

    self.current_caption.get_current_text().add_style_property(StyleProperties.Color, mid_row_code.get_color())
    self.current_caption.get_current_text().add_style_property(StyleProperties.FontStyle, mid_row_code.get_font_style())
    self.current_caption.get_current_text().add_style_property(StyleProperties.TextDecoration, mid_row_code.get_text_decoration())

  def process_attribute_code(self, attribute_code: SccAttributeCode):
    """Processes SCC Attribute Code to map it to the model"""
    if self.current_caption is None or self.current_caption.get_current_text() is None:
      raise ValueError("No current SCC caption nor content initialized")

    if self.current_caption.get_current_text() is not None and self.current_caption.get_current_text().get_text():
      self.current_caption.new_caption_text()
      self.current_caption.apply_current_text_offsets()

    if attribute_code.is_background():
      self.current_caption.get_current_text().add_style_property(StyleProperties.BackgroundColor, attribute_code.get_color())
    else:
      self.current_caption.get_current_text().add_style_property(StyleProperties.Color, attribute_code.get_color())

    self.current_caption.get_current_text().add_style_property(StyleProperties.TextDecoration, attribute_code.get_text_decoration())

  def process_control_code(self, control_code: SccControlCode, time_code: SccTimeCode):
    """Processes SCC Control Code to map it to the model"""

    if control_code is SccControlCode.RCL:
      # Start a new Pop-On caption
      if self.current_caption is not None and self.current_caption.get_current_text() is not None:
        self.set_current_to_previous()

      self.current_caption = SccCaptionParagraph(self.safe_area_x_offset, self.safe_area_y_offset, SccCaptionStyle.PopOn)

    elif control_code is SccControlCode.RDC:
      # Start a new Paint-On caption
      self.set_current_to_previous()

      self.current_caption = SccCaptionParagraph(self.safe_area_x_offset, self.safe_area_y_offset, SccCaptionStyle.PaintOn)
      self.init_current_caption(time_code)

    elif control_code in (SccControlCode.RU2, SccControlCode.RU3, SccControlCode.RU4):
      # Start a new Roll-Up caption
      self.set_current_to_previous()

      previous_last_lines: List[SccCaptionContent] = []
      if len(self.previous_captions) > 0:

        if control_code is SccControlCode.RU2:
          previous_last_lines = self.previous_captions[0].get_last_caption_lines(1)

        elif control_code is SccControlCode.RU3:
          previous_last_lines = self.previous_captions[0].get_last_caption_lines(2)

        elif control_code is SccControlCode.RU4:
          previous_last_lines = self.previous_captions[0].get_last_caption_lines(3)

        previous_last_lines.append(SccCaptionLineBreak())

      self.push_previous_caption(time_code)

      self.current_caption = SccCaptionParagraph(self.safe_area_x_offset, self.safe_area_y_offset, SccCaptionStyle.RollUp)
      self.current_caption.set_contents(previous_last_lines)
      self.init_current_caption(time_code)
      self.current_caption.apply_roll_up_row_offsets()

    elif control_code is SccControlCode.EOC:
      # Display caption (Pop-On)
      self.init_current_caption(time_code)
      self.set_current_to_previous()

    elif control_code in (SccControlCode.EDM, SccControlCode.ENM):
      # Erase displayed caption (Pop-On)
      if len(self.previous_captions) > 0:
        # Set line breaks depending on the position of the content

        contents = []
        initial_length = len(self.previous_captions[0].get_contents())

        for index in range(0, initial_length):
          content = self.previous_captions[0].get_contents()[index - 1]
          if not isinstance(content, SccCaptionText):
            continue

          next_content = self.previous_captions[0].get_contents()[index]
          if not isinstance(next_content, SccCaptionText):
            continue

          if next_content.is_contiguous(content):
            contents.append(SccCaptionLineBreak())

          contents.append(next_content)

        self.previous_captions[0].set_contents(contents)

      self.push_previous_caption(time_code)

    elif control_code is SccControlCode.TO1:
      self.current_caption.indent(1)

    elif control_code is SccControlCode.TO2:
      self.current_caption.indent(2)

    elif control_code is SccControlCode.TO3:
      self.current_caption.indent(3)

    elif control_code is SccControlCode.CR:
      # Roll the display up one row (Roll-Up)
      # Not used in this implementation since this SCC reader does not really map the roll-up effect,
      # but it erases the displayed paragraph and resets a new paragraph with the resulting rows.
      pass

    elif control_code is SccControlCode.DER:
      # Delete to End of Row (Paint-On)
      # The DER may be issued from any point on a row to delete all displayable characters, transparent
      # spaces, and mid-row codes from (and including) the current cell to the end of the row.
      # Not used in this implementation since this SCC reader does not map the text overlapping into
      # the model (i.e. a row is erased when a PAC is received, so before a new caption is written onto it).
      pass

  def process_text(self, word: str, time_code: SccTimeCode):
    """Processes SCC text words"""
    if self.current_caption.get_caption_style() is SccCaptionStyle.PaintOn:
      if word.startswith(" "):
        self.current_caption.new_caption_text()
        self.current_caption.get_current_text().append(word)
        self.current_caption.apply_current_text_offsets()
        self.current_caption.get_current_text().set_begin(time_code)
        return

      if word.endswith(" "):
        self.current_caption.get_current_text().append(word)
        self.current_caption.new_caption_text()
        self.current_caption.apply_current_text_offsets()
        self.current_caption.get_current_text().set_begin(time_code)
        return

    self.current_caption.get_current_text().append(word)

  def flush(self, time_code: SccTimeCode):
    """Flushes the remaining current caption"""
    if self.current_caption is not None:
      self.set_current_to_previous()
      while len(self.previous_captions) > 0:
        self.push_previous_caption(time_code)
      self.current_caption = None
Пример #8
0
  def process_control_code(self, control_code: SccControlCode, time_code: SccTimeCode):
    """Processes SCC Control Code to map it to the model"""

    if control_code is SccControlCode.RCL:
      # Start a new Pop-On caption
      if self.current_caption is not None and self.current_caption.get_current_text() is not None:
        self.set_current_to_previous()

      self.current_caption = SccCaptionParagraph(self.safe_area_x_offset, self.safe_area_y_offset, SccCaptionStyle.PopOn)

    elif control_code is SccControlCode.RDC:
      # Start a new Paint-On caption
      self.set_current_to_previous()

      self.current_caption = SccCaptionParagraph(self.safe_area_x_offset, self.safe_area_y_offset, SccCaptionStyle.PaintOn)
      self.init_current_caption(time_code)

    elif control_code in (SccControlCode.RU2, SccControlCode.RU3, SccControlCode.RU4):
      # Start a new Roll-Up caption
      self.set_current_to_previous()

      previous_last_lines: List[SccCaptionContent] = []
      if len(self.previous_captions) > 0:

        if control_code is SccControlCode.RU2:
          previous_last_lines = self.previous_captions[0].get_last_caption_lines(1)

        elif control_code is SccControlCode.RU3:
          previous_last_lines = self.previous_captions[0].get_last_caption_lines(2)

        elif control_code is SccControlCode.RU4:
          previous_last_lines = self.previous_captions[0].get_last_caption_lines(3)

        previous_last_lines.append(SccCaptionLineBreak())

      self.push_previous_caption(time_code)

      self.current_caption = SccCaptionParagraph(self.safe_area_x_offset, self.safe_area_y_offset, SccCaptionStyle.RollUp)
      self.current_caption.set_contents(previous_last_lines)
      self.init_current_caption(time_code)
      self.current_caption.apply_roll_up_row_offsets()

    elif control_code is SccControlCode.EOC:
      # Display caption (Pop-On)
      self.init_current_caption(time_code)
      self.set_current_to_previous()

    elif control_code in (SccControlCode.EDM, SccControlCode.ENM):
      # Erase displayed caption (Pop-On)
      if len(self.previous_captions) > 0:
        # Set line breaks depending on the position of the content

        contents = []
        initial_length = len(self.previous_captions[0].get_contents())

        for index in range(0, initial_length):
          content = self.previous_captions[0].get_contents()[index - 1]
          if not isinstance(content, SccCaptionText):
            continue

          next_content = self.previous_captions[0].get_contents()[index]
          if not isinstance(next_content, SccCaptionText):
            continue

          if next_content.is_contiguous(content):
            contents.append(SccCaptionLineBreak())

          contents.append(next_content)

        self.previous_captions[0].set_contents(contents)

      self.push_previous_caption(time_code)

    elif control_code is SccControlCode.TO1:
      self.current_caption.indent(1)

    elif control_code is SccControlCode.TO2:
      self.current_caption.indent(2)

    elif control_code is SccControlCode.TO3:
      self.current_caption.indent(3)

    elif control_code is SccControlCode.CR:
      # Roll the display up one row (Roll-Up)
      # Not used in this implementation since this SCC reader does not really map the roll-up effect,
      # but it erases the displayed paragraph and resets a new paragraph with the resulting rows.
      pass

    elif control_code is SccControlCode.DER:
      # Delete to End of Row (Paint-On)
      # The DER may be issued from any point on a row to delete all displayable characters, transparent
      # spaces, and mid-row codes from (and including) the current cell to the end of the row.
      # Not used in this implementation since this SCC reader does not map the text overlapping into
      # the model (i.e. a row is erased when a PAC is received, so before a new caption is written onto it).
      pass
Пример #9
0
    def test_matching_region(self):
        doc = ContentDocument()
        doc_columns = 40
        doc_rows = 19
        doc.set_cell_resolution(
            CellResolutionType(rows=doc_rows, columns=doc_columns))

        safe_area_x_offset = 4
        safe_area_y_offset = 2

        caption_paragraph = SccCaptionParagraph(safe_area_x_offset,
                                                safe_area_y_offset)
        caption_paragraph.set_cursor_at(4, 7)
        caption_paragraph.new_caption_text()

        caption_paragraph.get_current_text().append("A 20-char long line.")

        origin = caption_paragraph.get_origin()
        self.assertEqual(11, origin.x.value)
        self.assertEqual(5, origin.y.value)

        extent = caption_paragraph.get_extent()
        self.assertEqual(20, extent.width.value)
        self.assertEqual(1, extent.height.value)

        paragraph_region = _SccParagraphRegion(caption_paragraph, doc)

        self.assertIsNone(paragraph_region._find_matching_region())
        region = paragraph_region._create_matching_region()

        self.assertEqual("region1", region.get_id())
        self.assertTrue(paragraph_region._has_same_origin_as_region(region))
        self.assertEqual(region, paragraph_region._find_matching_region())

        self.assertEqual(ShowBackgroundType.whenActive,
                         region.get_style(StyleProperties.ShowBackground))

        region_extent = region.get_style(StyleProperties.Extent)

        self.assertEqual(50, region_extent.width.value)
        self.assertEqual(63, region_extent.height.value)

        caption_paragraph.set_cursor_at(5, 7)
        caption_paragraph.new_caption_text()
        caption_paragraph.get_current_text().append(
            "This is another 34-char long line.")

        origin = caption_paragraph.get_origin()
        self.assertEqual(11, origin.x.value)
        self.assertEqual(5, origin.y.value)

        extent = caption_paragraph.get_extent()
        self.assertEqual(34, extent.width.value)
        self.assertEqual(2, extent.height.value)

        paragraph_region = _SccParagraphRegion(caption_paragraph, doc)

        self.assertEqual(region, paragraph_region._find_matching_region())
        paragraph_region._extend_region_to_paragraph(region)
        self.assertTrue(paragraph_region._has_same_origin_as_region(region))

        region_extent = region.get_style(StyleProperties.Extent)

        self.assertEqual(62, region_extent.width.value)
        self.assertEqual(63, region_extent.height.value)
Пример #10
0
    def test_content(self):
        caption_paragraph = SccCaptionParagraph()
        self.assertEqual(0, caption_paragraph._safe_area_x_offset)
        self.assertEqual(0, caption_paragraph._safe_area_y_offset)

        caption_paragraph = SccCaptionParagraph(4, 2)
        self.assertEqual(4, caption_paragraph._safe_area_x_offset)
        self.assertEqual(2, caption_paragraph._safe_area_y_offset)

        self.assertIsNone(caption_paragraph.get_current_text())
        self.assertEqual(0, len(caption_paragraph._caption_lines))

        caption_paragraph.new_caption_text()
        self.assertEqual(caption_paragraph.get_current_line(),
                         caption_paragraph.get_lines()[0])
        self.assertIsNotNone(caption_paragraph.get_current_text())
        self.assertEqual(1, len(caption_paragraph._caption_lines))

        caption_paragraph.set_cursor_at(4, 4)
        caption_paragraph.new_caption_text()
        self.assertEqual((4, 4), caption_paragraph.get_cursor())
        self.assertEqual(caption_paragraph.get_current_line(),
                         caption_paragraph.get_lines()[4])
        self.assertEqual(4, caption_paragraph.get_current_line().get_row())
        self.assertEqual(4, caption_paragraph.get_current_line().get_indent())
        self.assertEqual(0, caption_paragraph.get_current_line().get_cursor())
        self.assertEqual(0, caption_paragraph.get_current_text().get_cursor())

        caption_paragraph.indent_cursor(3)
        self.assertEqual((4, 7), caption_paragraph.get_cursor())
        self.assertEqual(4, caption_paragraph.get_current_line().get_row())
        self.assertEqual(7, caption_paragraph.get_current_line().get_indent())
        self.assertEqual(0, caption_paragraph.get_current_line().get_cursor())
        self.assertEqual(0, caption_paragraph.get_current_text().get_cursor())

        caption_paragraph.append_text("Hello")
        self.assertEqual(5, caption_paragraph.get_current_line().get_cursor())
        self.assertEqual(5, caption_paragraph.get_current_text().get_cursor())

        caption_paragraph.set_cursor_at(4, 10)
        self.assertEqual((4, 10), caption_paragraph.get_cursor())
        self.assertEqual(4, caption_paragraph.get_current_line().get_row())
        self.assertEqual(7, caption_paragraph.get_current_line().get_indent())

        self.assertEqual(3, caption_paragraph.get_current_line().get_cursor())
        self.assertEqual(3, caption_paragraph.get_current_text().get_cursor())

        caption_paragraph.indent_cursor(2)
        self.assertEqual((4, 12), caption_paragraph.get_cursor())
        self.assertEqual(4, caption_paragraph.get_current_line().get_row())
        self.assertEqual(7, caption_paragraph.get_current_line().get_indent())

        self.assertEqual(5, caption_paragraph.get_current_line().get_cursor())
        self.assertEqual(5, caption_paragraph.get_current_text().get_cursor())

        self.assertListEqual([], caption_paragraph.get_last_caption_lines(0))
        self.assertListEqual([caption_paragraph.get_current_line()],
                             caption_paragraph.get_last_caption_lines(1))

        caption_paragraph.set_cursor_at(2, 4)
        caption_paragraph.new_caption_text()
        caption_paragraph.append_text("World")
        self.assertEqual(5, caption_paragraph.get_current_line().get_cursor())
        self.assertEqual(5, caption_paragraph.get_current_text().get_cursor())

        self.assertRaisesRegex(RuntimeError,
                               "Cannot roll-Up Unknown-styled caption.",
                               caption_paragraph.roll_up)

        caption_paragraph._caption_style = SccCaptionStyle.PopOn
        self.assertRaisesRegex(RuntimeError,
                               "Cannot roll-Up PopOn-styled caption.",
                               caption_paragraph.roll_up)

        caption_paragraph._caption_style = SccCaptionStyle.RollUp
        caption_paragraph.roll_up()
        self.assertEqual(2, len(caption_paragraph.get_lines()))
        self.assertEqual(caption_paragraph.get_current_line(),
                         caption_paragraph.get_lines().get(1))
        self.assertEqual(1, caption_paragraph.get_lines().get(1).get_row())
        self.assertEqual(
            "World",
            caption_paragraph.get_lines().get(1).get_current_text().get_text())

        self.assertTrue(
            isinstance(caption_paragraph.get_lines().get(3), SccCaptionLine))
        self.assertEqual(3, caption_paragraph.get_lines().get(3).get_row())
        self.assertEqual(
            "Hello",
            caption_paragraph.get_lines().get(3).get_current_text().get_text())

        self.assertListEqual([], caption_paragraph.get_last_caption_lines(0))
        self.assertListEqual([caption_paragraph.get_lines().get(3)],
                             caption_paragraph.get_last_caption_lines(1))
        self.assertListEqual([
            caption_paragraph.get_lines().get(1),
            caption_paragraph.get_lines().get(3)
        ], caption_paragraph.get_last_caption_lines(2))

        caption_paragraph.set_cursor_at(15, 0)
        caption_paragraph.new_caption_text()
        caption_paragraph.append_text("!!!")
        self.assertEqual(3, caption_paragraph.get_current_line().get_cursor())
        self.assertEqual(3, caption_paragraph.get_current_text().get_cursor())

        caption_paragraph.roll_up()
        self.assertEqual(3, len(caption_paragraph.get_lines()))
        self.assertTrue(
            isinstance(caption_paragraph.get_lines().get(0), SccCaptionLine))
        self.assertEqual(0, caption_paragraph.get_lines().get(0).get_row())
        self.assertEqual(
            "World",
            caption_paragraph.get_lines().get(0).get_current_text().get_text())

        self.assertTrue(
            isinstance(caption_paragraph.get_lines().get(2), SccCaptionLine))
        self.assertEqual(2, caption_paragraph.get_lines().get(2).get_row())
        self.assertEqual(
            "Hello",
            caption_paragraph.get_lines().get(2).get_current_text().get_text())

        self.assertEqual(caption_paragraph.get_current_line(),
                         caption_paragraph.get_lines().get(14))
        self.assertEqual(14, caption_paragraph.get_lines().get(14).get_row())
        self.assertEqual(
            "!!!",
            caption_paragraph.get_lines().get(
                14).get_current_text().get_text())

        self.assertListEqual([], caption_paragraph.get_last_caption_lines(0))
        self.assertListEqual([caption_paragraph.get_lines().get(14)],
                             caption_paragraph.get_last_caption_lines(1))
        self.assertListEqual([
            caption_paragraph.get_lines().get(2),
            caption_paragraph.get_lines().get(14)
        ], caption_paragraph.get_last_caption_lines(2))
        self.assertListEqual([
            caption_paragraph.get_lines().get(0),
            caption_paragraph.get_lines().get(2),
            caption_paragraph.get_lines().get(14)
        ], caption_paragraph.get_last_caption_lines(3))
Пример #11
0
    def test_to_paragraph(self):
        caption_paragraph = SccCaptionParagraph()
        doc = ContentDocument()

        self.assertRaisesRegex(TypeError,
                               "Element id must be a valid xml:id string",
                               caption_paragraph.to_paragraph, doc)

        caption_paragraph.set_id("test-id")

        origin = caption_paragraph.get_origin()
        self.assertEqual(0, origin.x.value)
        self.assertEqual(0, origin.y.value)

        extent = caption_paragraph.get_extent()
        self.assertEqual(0, extent.width.value)
        self.assertEqual(0, extent.height.value)

        paragraph = caption_paragraph.to_paragraph(doc)

        self.assertEqual("test-id", paragraph.get_id())
        self.assertEqual(doc, paragraph.get_doc())
        self.assertIsNone(paragraph.get_begin())
        self.assertIsNone(paragraph.get_end())

        children = list(paragraph)
        self.assertEqual(0, len(children))

        caption_paragraph.set_begin(SmpteTimeCode.parse("00:01:02:03", FPS_30))
        caption_paragraph.set_end(SmpteTimeCode.parse("00:02:03:04", FPS_30))

        caption_paragraph.set_cursor_at(0)
        caption_paragraph.new_caption_text()
        caption_paragraph.append_text("Hello")
        caption_paragraph.set_cursor_at(1)
        caption_paragraph.new_caption_text()
        caption_paragraph.append_text("World")

        paragraph = caption_paragraph.to_paragraph(doc)

        self.assertEqual("test-id", paragraph.get_id())
        self.assertEqual(doc, paragraph.get_doc())
        self.assertEqual(Fraction(1863, 30), paragraph.get_begin())
        self.assertEqual(Fraction(3694, 30), paragraph.get_end())

        children = list(paragraph)
        self.assertEqual(3, len(children))

        self.assertIsInstance(children[0], Span)
        self.assertEqual("Hello", list(children[0])[0].get_text())

        self.assertIsInstance(children[1], Br)

        self.assertIsInstance(children[2], Span)
        self.assertEqual("World", list(children[2])[0].get_text())
Пример #12
0
    def test_paragraph_content_alignment_detection_right(self):
        caption_paragraph = SccCaptionParagraph()

        caption_paragraph.set_cursor_at(10, 0)
        caption_paragraph.append_text("0123456789")

        caption_paragraph.set_cursor_at(11, 0)
        caption_paragraph.append_text("    012345")

        caption_paragraph.set_cursor_at(12, 0)
        caption_paragraph.append_text("      0123")

        self.assertEqual(TextAlignType.end,
                         caption_paragraph.guess_text_alignment())

        caption_paragraph = SccCaptionParagraph()

        caption_paragraph.set_cursor_at(10, 0)
        caption_paragraph.append_text("0123456789")

        caption_paragraph.set_cursor_at(11, 0)
        caption_paragraph.append_text("    012345")

        caption_paragraph.set_cursor_at(12, 4)
        caption_paragraph.append_text("  0123")

        self.assertEqual(TextAlignType.end,
                         caption_paragraph.guess_text_alignment())
Пример #13
0
    def test_paragraph_content_alignment_detection_center(self):
        caption_paragraph = SccCaptionParagraph()

        caption_paragraph.set_cursor_at(0, 2)
        caption_paragraph.append_text("0123456789")

        caption_paragraph.set_cursor_at(1, 2)
        caption_paragraph.append_text("  012345")

        caption_paragraph.set_cursor_at(2, 2)
        caption_paragraph.append_text("   0123")

        self.assertEqual(TextAlignType.center,
                         caption_paragraph.guess_text_alignment())

        caption_paragraph = SccCaptionParagraph()

        caption_paragraph.set_cursor_at(0, 0)
        caption_paragraph.append_text("  0123456789")

        caption_paragraph.set_cursor_at(1, 2)
        caption_paragraph.append_text("  012345")

        caption_paragraph.set_cursor_at(2, 5)
        caption_paragraph.append_text("0123")

        self.assertEqual(TextAlignType.center,
                         caption_paragraph.guess_text_alignment())
Пример #14
0
    def test_paragraph_alignment_detection_left(self):
        caption_paragraph = SccCaptionParagraph()

        caption_paragraph.set_cursor_at(0, 0)
        caption_paragraph.append_text("0123456789")

        caption_paragraph.set_cursor_at(1, 0)
        caption_paragraph.append_text("012345")

        caption_paragraph.set_cursor_at(2, 0)
        caption_paragraph.append_text("0123")

        self.assertEqual(TextAlignType.start,
                         caption_paragraph.guess_text_alignment())
Пример #15
0
    def process_control_code(self, control_code: SccControlCode,
                             time_code: SmpteTimeCode):
        """Processes SCC Control Code to map it to the model"""

        if control_code is SccControlCode.RCL:
            # Start a new Pop-On caption
            self.current_style = SccCaptionStyle.PopOn

        elif control_code is SccControlCode.RDC:
            # Start a new Paint-On caption
            self.current_style = SccCaptionStyle.PaintOn

        elif control_code in (SccControlCode.RU2, SccControlCode.RU3,
                              SccControlCode.RU4):
            # Start a new Roll-Up caption
            self.current_style = SccCaptionStyle.RollUp

            previous_last_lines: List[SccCaptionLine] = []
            if self.has_active_caption():

                if control_code is SccControlCode.RU2:
                    previous_last_lines = self.active_caption.get_last_caption_lines(
                        1)

                elif control_code is SccControlCode.RU3:
                    previous_last_lines = self.active_caption.get_last_caption_lines(
                        2)

                elif control_code is SccControlCode.RU4:
                    previous_last_lines = self.active_caption.get_last_caption_lines(
                        3)

            self.push_active_caption_to_model(time_code)

            self.active_caption = SccCaptionParagraph(self.safe_area_x_offset,
                                                      self.safe_area_y_offset,
                                                      SccCaptionStyle.RollUp)
            self.initialize_active_caption(time_code)

            self.active_caption.set_lines(previous_last_lines)

        elif control_code is SccControlCode.EOC:
            # Display caption (Pop-On)
            self.set_buffered_caption_begin_time(time_code)
            self.flip_buffered_to_active_captions(time_code)

            if self.has_active_caption():
                # Set text alignment
                if self.text_alignment == TextAlignment.AUTO:
                    text_alignment = self.active_caption.guess_text_alignment()
                else:
                    text_alignment = self.text_alignment.text_align

                # Apply text alignment
                self.active_caption.add_style_property(
                    StyleProperties.TextAlign, text_alignment)

        elif control_code is SccControlCode.EDM:
            # Erase displayed captions
            if self.has_active_caption():
                self.push_active_caption_to_model(time_code)

        elif control_code is SccControlCode.ENM:
            # Erase buffered caption
            self.buffered_caption = None

        elif control_code is SccControlCode.TO1:
            self.buffered_caption.indent_cursor(1)

        elif control_code is SccControlCode.TO2:
            self.buffered_caption.indent_cursor(2)

        elif control_code is SccControlCode.TO3:
            self.buffered_caption.indent_cursor(3)

        elif control_code is SccControlCode.CR:
            # Roll the display up one row (Roll-Up)
            self.active_caption.roll_up()

        elif control_code is SccControlCode.DER:
            # Delete to End of Row (Paint-On)
            # The DER may be issued from any point on a row to delete all displayable characters, transparent
            # spaces, and mid-row codes from (and including) the current cell to the end of the row.
            # Not used in this implementation since this SCC reader does not map the text overlapping into
            # the model (i.e. a row is erased when a PAC is received, so before a new caption is written onto it).
            pass

        elif control_code is SccControlCode.BS:
            # Backspace
            # When a Backspace is received, the cursor moves to the left one column position erasing
            # the character or Mid-Row Code occupying that location, unless the cursor is in Column 1
            self.buffered_caption.get_current_text().backspace()