From d72d2f7aa1751c2ad63271a594f2fc1c84bac0be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20NO=C3=8BL?= Date: Tue, 10 Oct 2023 17:14:43 +0200 Subject: [PATCH] SCC: supports left-hand cursor movements on current line --- src/main/python/ttconv/scc/caption_line.py | 18 ++++++--- .../python/ttconv/scc/caption_paragraph.py | 13 ++++++- src/main/python/ttconv/scc/caption_text.py | 5 +++ src/test/python/test_scc_reader.py | 38 +++++++++++++++++++ 4 files changed, 66 insertions(+), 8 deletions(-) diff --git a/src/main/python/ttconv/scc/caption_line.py b/src/main/python/ttconv/scc/caption_line.py index 10b40aa8..20e89991 100644 --- a/src/main/python/ttconv/scc/caption_line.py +++ b/src/main/python/ttconv/scc/caption_line.py @@ -58,9 +58,9 @@ def add_text(self, text: Union[SccCaptionText, str]): if self._current_text is None: # Initialize a new text element if necessary - self._texts.append(SccCaptionText(text)) + self._texts.append(SccCaptionText()) self._current_text = self._texts[-1] - self._cursor = self._current_text.get_length() + self._append_text(text) else: remaining_text = text @@ -71,8 +71,7 @@ def add_text(self, text: Union[SccCaptionText, str]): text_to_write = remaining_text[:available] # Replace current text element content - self._current_text.append(text_to_write) - self.set_cursor(self._cursor + len(text_to_write)) + self._append_text(text_to_write) remaining_text = remaining_text[available:] # If some text remains on the last text element @@ -80,12 +79,19 @@ def add_text(self, text: Union[SccCaptionText, str]): assert self._current_text is self._texts[-1] # Replace and append to current text element content - self._current_text.append(remaining_text) - self.set_cursor(self._cursor + len(remaining_text)) + self._append_text(remaining_text) else: raise ValueError("Unsupported text type for SCC caption line") + def _append_text(self, text: str): + """Appends text and update cursor position""" + self._current_text.append(text) + if self._cursor < 0: + self._cursor = 0 + + self.set_cursor(self._cursor + len(text)) + def indent(self, indent: int): """Indent current line""" self._indent += indent diff --git a/src/main/python/ttconv/scc/caption_paragraph.py b/src/main/python/ttconv/scc/caption_paragraph.py index 6b30eabd..84b971c7 100644 --- a/src/main/python/ttconv/scc/caption_paragraph.py +++ b/src/main/python/ttconv/scc/caption_paragraph.py @@ -167,7 +167,7 @@ def set_cursor_at(self, row: int, indent: Optional[int] = None): self._current_line = self._caption_lines.get(row) if indent is not None: - self._current_line.set_cursor(self._cursor[1] - self._current_line.get_indent()) + self._update_current_line_cursor() def get_cursor(self) -> (int, int): """Returns cursor coordinates""" @@ -181,7 +181,16 @@ def indent_cursor(self, indent: int): # If the current line is empty, set cursor indent as a line tabulation self._current_line.indent(indent) else: - self._current_line.set_cursor(self._cursor[1] - self._current_line.get_indent()) + self._update_current_line_cursor() + + def _update_current_line_cursor(self): + """Updates cursor position on current line""" + new_cursor_position = self._cursor[1] - self._current_line.get_indent() + + if new_cursor_position < 0: + self._current_line.indent(new_cursor_position) + + self._current_line.set_cursor(new_cursor_position) def get_lines(self) -> Dict[int, SccCaptionLine]: """Returns the paragraph lines per row""" diff --git a/src/main/python/ttconv/scc/caption_text.py b/src/main/python/ttconv/scc/caption_text.py index 886f1430..17ebcc0b 100644 --- a/src/main/python/ttconv/scc/caption_text.py +++ b/src/main/python/ttconv/scc/caption_text.py @@ -79,6 +79,11 @@ def is_empty(self) -> bool: def append(self, text: str): """Add or replace text content at cursor position""" + if self._cursor < 0: + # Insert space characters before current text + self._text = ' ' * -self._cursor + self._text + self._cursor = 0 + # print("Append text: ", text, "to", self._text, "at", self._cursor) self._text = self._text[:self._cursor] + text + self._text[(self._cursor + len(text)):] self._cursor += len(text) diff --git a/src/test/python/test_scc_reader.py b/src/test/python/test_scc_reader.py index 74fa312a..6563b905 100644 --- a/src/test/python/test_scc_reader.py +++ b/src/test/python/test_scc_reader.py @@ -1192,5 +1192,43 @@ def test_scc_content_parsing_from_preamble_address_code(self): "consectetur adipiscing elit.") self.assertEqual(region_1, p_list[0].get_region()) + def test_scc_with_negative_cursor(self): + scc_content = """Scenarist_SCC V1.0 +00:00:01:00 94AE 94AE 9420 9420 94F8 94F8 45E5 E5E3 68A1 94F4 94F4 D3E3 61F2 79A1 942C 942C 942F 942F +00:00:02:00 942F 942F +""" + scc_disassembly_expected = """\ +00:00:01:00 {ENM}{ENM}{RCL}{RCL}{1516}{1516}Eeech!{1508}{1508}Scary!{EDM}{EDM}{EOC}{EOC} +00:00:02:00 {EOC}{EOC} +""" + scc_disassembly = to_disassembly(scc_content) + self.assertEqual(scc_disassembly_expected, scc_disassembly) + + doc = to_model(scc_content) + + self.assertIsNotNone(doc) + + region_1 = doc.get_region("pop1") + self.assertIsNotNone(region_1) + self.check_region_origin(region_1, 12, 16, doc.get_cell_resolution()) + self.check_region_extent(region_1, 14, 1, doc.get_cell_resolution()) + self.check_element_style(region_1, StyleProperties.DisplayAlign, DisplayAlignType.before) + self.check_element_style(region_1, StyleProperties.ShowBackground, ShowBackgroundType.whenActive) + + body = doc.get_body() + self.assertIsNotNone(body) + + div_list = list(body) + self.assertEqual(1, len(div_list)) + div = div_list[0] + self.assertIsNotNone(div) + + p_list = list(div) + self.assertEqual(1, len(p_list)) + + self.check_caption(p_list[0], "caption1", "00:00:01:12", "00:00:02:01", "Scary! Eeech!") + self.assertEqual(region_1, p_list[0].get_region()) + + if __name__ == '__main__': unittest.main()