diff --git a/docs/changelog.rst b/docs/changelog.rst index 400e9cdd..a058b2d3 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,14 +2,14 @@ Changelog --------- 2.2.13 ^^^^^^ -- Mid-row codes only add spaces only if there isn't one before -- Mid-row codes only add spaces only if affects the text in the same row (not adding if after previous text follows break or breaks) -- Remove spaces to the end of the lines -- Change error message for the 32 character limit. +- Mid-row codes only add spaces only if there isn't one before. +- Mid-row codes add spaces only if affects the text in the same row (not adding if it follows break or PACS). +- Remove spaces to the end of the lines. - Close italics on receiving another style setting command. - Throw an CaptionReadNoCaptions error in case of empty input file are provided -- Properly add breaks (it was only for jumps to next row). Now it adds as many breaks as the difference between row numbers. - Ignore repositioning commands which are not followed by any text before breaks. +- Mid-row codes will not add the space if is in front of punctuation. +- Fix a bug with background codes when InstructionNodeCreator collection is empty. 2.2.12 ^^^^^^ diff --git a/pycaption/scc/__init__.py b/pycaption/scc/__init__.py index b28444a0..398745ed 100644 --- a/pycaption/scc/__init__.py +++ b/pycaption/scc/__init__.py @@ -85,20 +85,35 @@ from copy import deepcopy from pycaption.base import BaseReader, BaseWriter, CaptionNode, CaptionSet -from pycaption.exceptions import (CaptionLineLengthError, - CaptionReadNoCaptions, - CaptionReadTimingError, InvalidInputError) - -from .constants import (CHARACTER_TO_CODE, CHARACTERS, COMMANDS, - CUE_STARTING_COMMAND, EXTENDED_CHARS, HEADER, - MICROSECONDS_PER_CODEWORD, - PAC_BYTES_TO_POSITIONING_MAP, PAC_HIGH_BYTE_BY_ROW, - PAC_LOW_BYTE_BY_ROW_RESTRICTED, - PAC_TAB_OFFSET_COMMANDS, SPECIAL_CHARS, - SPECIAL_OR_EXTENDED_CHAR_TO_CODE) +from pycaption.exceptions import ( + CaptionLineLengthError, + CaptionReadNoCaptions, + CaptionReadTimingError, + InvalidInputError, +) + +from .constants import ( + CHARACTER_TO_CODE, + CHARACTERS, + COMMANDS, + CUE_STARTING_COMMAND, + EXTENDED_CHARS, + HEADER, + MICROSECONDS_PER_CODEWORD, + PAC_BYTES_TO_POSITIONING_MAP, + PAC_HIGH_BYTE_BY_ROW, + PAC_LOW_BYTE_BY_ROW_RESTRICTED, + PAC_TAB_OFFSET_COMMANDS, + SPECIAL_CHARS, + SPECIAL_OR_EXTENDED_CHAR_TO_CODE, +) from .specialized_collections import CaptionCreator # noqa: F401 -from .specialized_collections import (InstructionNodeCreator, NotifyingDict, - PopOnCue, TimingCorrectingCaptionList) +from .specialized_collections import ( + InstructionNodeCreator, + NotifyingDict, + PopOnCue, + TimingCorrectingCaptionList, +) from .state_machines import DefaultProvidingPositionTracker @@ -236,7 +251,9 @@ def read(self, content, lang="en-US", simulate_roll_up=False, offset=0): for caption in self.caption_stash._collection: caption_start = caption.to_real_caption().format_start() caption_text = "".join(caption.to_real_caption().get_text_nodes()) - text_too_long = [line for line in caption_text.split("\n") if len(line) > 32] + text_too_long = [ + line for line in caption_text.split("\n") if len(line) > 32 + ] if caption_start in lines_too_long: lines_too_long[caption_start] = text_too_long else: @@ -313,9 +330,10 @@ def _translate_line(self, line): for idx, word in enumerate(word_list): word = word.strip() if len(word) == 4: - self._translate_word(word=word) + next_command = word_list[idx + 1] if idx + 1 < len(word_list) else None + self._translate_word(word=word, next_command=next_command) - def _translate_word(self, word): + def _translate_word(self, word, next_command=None): if self._handle_double_command(word): # count frames for timing self.time_translator.increment_frames() @@ -324,7 +342,7 @@ def _translate_word(self, word): # TODO - check that all the positioning commands are here, or use # some other strategy to determine if the word is a command. if word in COMMANDS or _is_pac_command(word): - self._translate_command(word=word) + self._translate_command(word=word, next_command=next_command) # second, check if word is a special character elif word in SPECIAL_CHARS: @@ -396,7 +414,7 @@ def _translate_extended_char(self, word): # add to buffer self.buffer.add_chars(EXTENDED_CHARS[word]) - def _translate_command(self, word): + def _translate_command(self, word, next_command=None): # if command is pop_up if word == "9420": self.buffer_dict.set_active("pop") @@ -462,7 +480,7 @@ def _translate_command(self, word): # If command is not one of the aforementioned, add it to buffer else: - self.buffer.interpret_command(command=word) + self.buffer.interpret_command(command=word, next_command=next_command) def _translate_characters(self, word): # split word into the 2 bytes diff --git a/pycaption/scc/specialized_collections.py b/pycaption/scc/specialized_collections.py index 85bad6ba..a5e132a8 100644 --- a/pycaption/scc/specialized_collections.py +++ b/pycaption/scc/specialized_collections.py @@ -14,14 +14,14 @@ BACKGROUND_COLOR_CODES, COMMANDS, EXTENDED_CHARS, + ITALICS_COMMANDS, MICROSECONDS_PER_CODEWORD, MID_ROW_CODES, PAC_BYTES_TO_POSITIONING_MAP, PAC_TAB_OFFSET_COMMANDS, - ITALICS_COMMANDS, - UNDERLINE_COMMANDS, PLAIN_TEXT_COMMANDS, - STYLE_SETTING_COMMANDS + STYLE_SETTING_COMMANDS, + UNDERLINE_COMMANDS, ) PopOnCue = collections.namedtuple("PopOnCue", "buffer, start, end") @@ -309,7 +309,9 @@ def __init__(self, collection=None, position_tracker=None): else: self._collection = collection - self.last_style = None # can be italic on or italic off as we only support italics + self.last_style = ( + None # can be italic on or italic off as we only support italics + ) self._position_tracer = position_tracker def is_empty(self): @@ -340,10 +342,9 @@ def add_chars(self, *chars): # handle a simple line break if self._position_tracer.is_linebreak_required(): - for _ in range(self._position_tracer._breaks_required): - self._collection.append( - _InstructionNode.create_break(position=current_position) - ) + self._collection.append( + _InstructionNode.create_break(position=current_position) + ) self._position_tracer.acknowledge_linebreak_consumed() node = _InstructionNode.create_text(current_position) self._collection.append(node) @@ -374,7 +375,7 @@ def get_style_for_command(command): # only remaining possibility is plain text return "plaintext" - def interpret_command(self, command): + def interpret_command(self, command, next_command=None): """Given a command determines whether to turn italics on or off, or to set the positioning @@ -382,6 +383,7 @@ def interpret_command(self, command): :type command: str or a PAC_TAB_OFFSET_COMMANDS + :type next_command: the command that follows next """ self._update_positioning(command) @@ -394,7 +396,7 @@ def interpret_command(self, command): # which will be deleted when the code is applied. # ex: 2080 97ad 94a1 if ( - len(self._collection) > 1 + len(self._collection) > 0 and self._collection[-1].is_text_node() and self._collection[-1].text[-1].isspace() ): @@ -410,10 +412,9 @@ def interpret_command(self, command): # it should open italic tag # if break is required, break then add style tag if self._position_tracer.is_linebreak_required(): - for _ in range(self._position_tracer._breaks_required): - self._collection.append( - _InstructionNode.create_break(position=current_position) - ) + self._collection.append( + _InstructionNode.create_break(position=current_position) + ) self._position_tracer.acknowledge_linebreak_consumed() self._collection.append( _InstructionNode.create_italics_style(current_position) @@ -432,23 +433,28 @@ def interpret_command(self, command): ) self.last_style = "italics off" if self._position_tracer.is_linebreak_required(): - for _ in range(self._position_tracer._breaks_required): - self._collection.append( - _InstructionNode.create_break(position=current_position) - ) + self._collection.append( + _InstructionNode.create_break(position=current_position) + ) self._position_tracer.acknowledge_linebreak_consumed() # handle mid-row codes that follows a text node + # don't add space if the next command adds one of + # ['.', '!', '?', ','] + punctuation = ["ae", "a1", "bf", "2c"] + next_is_punctuation = next_command and next_command[:2] in punctuation prev_text_node = self.get_previous_text_node() prev_node_is_break = prev_text_node is not None and any( - x.is_explicit_break() for x in self._collection[self._collection.index(prev_text_node):] + x.is_explicit_break() + for x in self._collection[self._collection.index(prev_text_node) :] ) if ( - command in MID_ROW_CODES and - prev_text_node and not - prev_node_is_break and not - prev_text_node.text[-1].isspace() and - command not in PAC_TAB_OFFSET_COMMANDS + command in MID_ROW_CODES + and prev_text_node + and not prev_node_is_break + and not prev_text_node.text[-1].isspace() + and command not in PAC_TAB_OFFSET_COMMANDS + and not next_is_punctuation ): if self.last_style == "italics off": # need to open italics tag, add a space @@ -465,8 +471,8 @@ def _update_positioning(self, command): :type command: str """ - prev_positioning = self._position_tracer.default if command in PAC_TAB_OFFSET_COMMANDS: + prev_positioning = self._position_tracer.default tab_offset = PAC_TAB_OFFSET_COMMANDS[command] positioning = (prev_positioning[0], prev_positioning[1] + tab_offset) else: @@ -751,6 +757,12 @@ def _format_italics(collection): new_collection = _remove_noop_italics(new_collection) # remove spaces to the end of the lines + new_collection = _remove_spaces_at_end_of_the_line(new_collection) + + return new_collection + + +def _remove_spaces_at_end_of_the_line(collection: list[_InstructionNode]): for idx, node in enumerate(collection): if ( idx > 0 @@ -762,7 +774,7 @@ def _format_italics(collection): # handle last node if collection[-1].is_text_node(): collection[-1].text = collection[-1].text.rstrip() - return new_collection + return collection def _remove_noop_on_off_italics(collection): diff --git a/pycaption/scc/state_machines.py b/pycaption/scc/state_machines.py index fed5e508..7353eff8 100644 --- a/pycaption/scc/state_machines.py +++ b/pycaption/scc/state_machines.py @@ -5,13 +5,14 @@ class _PositioningTracker: """Helps determine the positioning of a node, having kept track of positioning-related commands. """ + def __init__(self, positioning=None): """ :param positioning: positioning information (row, column) :type positioning: tuple[int] """ self._positions = [positioning] - self._breaks_required = 0 + self._break_required = False self._repositioning_required = False # Since the actual column is not applied when encountering a line break # this attribute is used to store it and determine by comparison if the @@ -35,18 +36,18 @@ def update_positioning(self, positioning): return row, col = current - if self._breaks_required: + if self._break_required: col = self._last_column new_row, new_col = positioning is_tab_offset = new_row == row and col + 1 <= new_col <= col + 3 # One line below will be treated as line break, not repositioning - if new_row > row: + if new_row == row + 1: self._positions.append((new_row, col)) - self._breaks_required = new_row - row + self._break_required = 1 self._last_column = new_col # Tab offsets after line breaks will be ignored to avoid repositioning - elif self._breaks_required and is_tab_offset: + elif self._break_required and is_tab_offset: return else: # Reset the "current" position altogether. @@ -64,9 +65,7 @@ def get_current_position(self): :raise: CaptionReadSyntaxError """ if not any(self._positions): - raise CaptionReadSyntaxError( - 'No Preamble Address Code [PAC] was provided' - ) + raise CaptionReadSyntaxError("No Preamble Address Code [PAC] was provided") else: return self._positions[0] @@ -86,17 +85,18 @@ def is_linebreak_required(self): """If the current position is simply one line below the previous. :rtype: bool """ - return self._breaks_required > 0 + return self._break_required def acknowledge_linebreak_consumed(self): """Call to acknowledge that the line required was consumed""" - self._breaks_required = 0 + self._break_required = False class DefaultProvidingPositionTracker(_PositioningTracker): """A _PositioningTracker that provides if needed a default value (14, 0), or uses the last positioning value set anywhere in the document """ + default = (14, 0) def __init__(self, positioning=None, default=None): diff --git a/setup.py b/setup.py index 3d4bd260..a139f657 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ setup( name='pycaption', - version='2.2.12.dev6', + version='2.2.12.dev7', description='Closed caption converter', long_description=open(README_PATH).read(), author='Joe Norton', diff --git a/tests/fixtures/dfxp.py b/tests/fixtures/dfxp.py index d0a052d0..864f3e7a 100644 --- a/tests/fixtures/dfxp.py +++ b/tests/fixtures/dfxp.py @@ -729,6 +729,7 @@ def sample_dfxp_to_render_with_only_default_positioning_input(): """ + ## # When converting from DFXP to DFXP, notice the extra region "r0" is added, to # support the spam that sets the "tts:textAlign" attribute. @@ -914,9 +915,16 @@ def sample_dfxp_from_scc_output(): - - - + + + + + + + + + + @@ -925,41 +933,40 @@ def sample_dfxp_from_scc_output(): abab

- cdcd


+ cdcd +

+

efef

-

-



+

ghgh
ijij
klkl

-

-




+

mnmn

-

+

opop

-

+

qrqr

-

-


+

stst
uvuv
wxwx

-

-





+

yzyz

-

- 0101




+

+ 0101 +

+

2323

-

-




+

4545
6767
8989 @@ -1519,4 +1526,4 @@ def sample_dfxp_default_styling_p_tags():

-""" \ No newline at end of file +""" diff --git a/tests/test_scc.py b/tests/test_scc.py index fd8c7a1e..b6076202 100644 --- a/tests/test_scc.py +++ b/tests/test_scc.py @@ -2,11 +2,12 @@ from pycaption import CaptionNode, CaptionReadNoCaptions, SCCReader from pycaption.exceptions import CaptionLineLengthError, CaptionReadTimingError -from pycaption.geometry import (HorizontalAlignmentEnum, UnitEnum, - VerticalAlignmentEnum) +from pycaption.geometry import HorizontalAlignmentEnum, UnitEnum, VerticalAlignmentEnum from pycaption.scc.constants import MICROSECONDS_PER_CODEWORD -from pycaption.scc.specialized_collections import (InstructionNodeCreator, - TimingCorrectingCaptionList) +from pycaption.scc.specialized_collections import ( + InstructionNodeCreator, + TimingCorrectingCaptionList, +) from pycaption.scc.state_machines import DefaultProvidingPositionTracker from tests.mixins import ReaderTestingMixIn @@ -70,14 +71,16 @@ def test_positioning(self, sample_scc_multiple_positioning): expected_positioning = [ ((10.0, UnitEnum.PERCENT), (77.0, UnitEnum.PERCENT)), ((40.0, UnitEnum.PERCENT), (5.0, UnitEnum.PERCENT)), - ((40.0, UnitEnum.PERCENT), (5.0, UnitEnum.PERCENT)), - ((40.0, UnitEnum.PERCENT), (5.0, UnitEnum.PERCENT)), + ((70.0, UnitEnum.PERCENT), (23.0, UnitEnum.PERCENT)), + ((20.0, UnitEnum.PERCENT), (47.0, UnitEnum.PERCENT)), + ((20.0, UnitEnum.PERCENT), (89.0, UnitEnum.PERCENT)), ((40.0, UnitEnum.PERCENT), (53.0, UnitEnum.PERCENT)), ((70.0, UnitEnum.PERCENT), (17.0, UnitEnum.PERCENT)), - ((70.0, UnitEnum.PERCENT), (17.0, UnitEnum.PERCENT)), - ((70.0, UnitEnum.PERCENT), (17.0, UnitEnum.PERCENT)), + ((20.0, UnitEnum.PERCENT), (35.0, UnitEnum.PERCENT)), + ((20.0, UnitEnum.PERCENT), (83.0, UnitEnum.PERCENT)), ((70.0, UnitEnum.PERCENT), (11.0, UnitEnum.PERCENT)), - ((70.0, UnitEnum.PERCENT), (11.0, UnitEnum.PERCENT)) + ((40.0, UnitEnum.PERCENT), (41.0, UnitEnum.PERCENT)), + ((20.0, UnitEnum.PERCENT), (71.0, UnitEnum.PERCENT)), ] actual_positioning = [ @@ -276,18 +279,18 @@ def test_line_too_long(self, sample_scc_with_line_too_long): assert str_to_check in exc_info.value.args[0].split("\n")[2] def test_mid_row_codes_not_adding_space_before_text( - self, - sample_scc_mid_row_before_text_pop, - sample_scc_mid_row_before_text_roll, - sample_scc_mid_row_before_text_paint + self, + sample_scc_mid_row_before_text_pop, + sample_scc_mid_row_before_text_roll, + sample_scc_mid_row_before_text_paint, ): # if mid-row code is before any text in the cue, no space # should be added - expected_lines = ['AB AB'] # no space before first A + expected_lines = ["AB AB"] # no space before first A for caption in [ sample_scc_mid_row_before_text_pop, sample_scc_mid_row_before_text_roll, - sample_scc_mid_row_before_text_paint + sample_scc_mid_row_before_text_paint, ]: caption_set = SCCReader().read(caption) actual_lines = [ @@ -299,19 +302,19 @@ def test_mid_row_codes_not_adding_space_before_text( assert expected_lines == actual_lines def test_mid_row_codes_adding_space_after_text_if_there_is_none_closing_style( - self, - sample_scc_mid_row_following_text_no_text_before_italics_off_pop, - sample_scc_mid_row_following_text_no_text_before_italics_off_roll, - sample_scc_mid_row_following_text_no_text_before_italics_off_paint + self, + sample_scc_mid_row_following_text_no_text_before_italics_off_pop, + sample_scc_mid_row_following_text_no_text_before_italics_off_roll, + sample_scc_mid_row_following_text_no_text_before_italics_off_paint, ): # if there's no space in between text nodes it should add one # since 9120 is closing italics, the space will be added before # second text node - expected_lines = ['AB', ' AB'] + expected_lines = ["AB", " AB"] for caption in [ sample_scc_mid_row_following_text_no_text_before_italics_off_pop, sample_scc_mid_row_following_text_no_text_before_italics_off_roll, - sample_scc_mid_row_following_text_no_text_before_italics_off_paint + sample_scc_mid_row_following_text_no_text_before_italics_off_paint, ]: caption_set = SCCReader().read(caption) actual_lines = [ @@ -323,19 +326,19 @@ def test_mid_row_codes_adding_space_after_text_if_there_is_none_closing_style( assert expected_lines == actual_lines def test_mid_row_codes_adding_space_after_text_if_there_is_none_opening_style( - self, - sample_scc_mid_row_following_text_no_text_before_italics_on_pop, - sample_scc_mid_row_following_text_no_text_before_italics_on_roll, - sample_scc_mid_row_following_text_no_text_before_italics_on_paint + self, + sample_scc_mid_row_following_text_no_text_before_italics_on_pop, + sample_scc_mid_row_following_text_no_text_before_italics_on_roll, + sample_scc_mid_row_following_text_no_text_before_italics_on_paint, ): # if there's no space in between text nodes it should add one # since 91ae is opening italics, the space will be added at the end # of the first text node - expected_lines = ['AB ', 'AB'] + expected_lines = ["AB ", "AB"] for caption in [ sample_scc_mid_row_following_text_no_text_before_italics_on_pop, sample_scc_mid_row_following_text_no_text_before_italics_on_roll, - sample_scc_mid_row_following_text_no_text_before_italics_on_paint + sample_scc_mid_row_following_text_no_text_before_italics_on_paint, ]: caption_set = SCCReader().read(caption) actual_lines = [ @@ -347,18 +350,18 @@ def test_mid_row_codes_adding_space_after_text_if_there_is_none_opening_style( assert expected_lines == actual_lines def test_mid_row_codes_not_adding_space_if_there_is_one_before( - self, - sample_scc_mid_row_with_space_before_pop, - sample_scc_mid_row_with_space_before_roll, - sample_scc_mid_row_with_space_before_paint + self, + sample_scc_mid_row_with_space_before_pop, + sample_scc_mid_row_with_space_before_roll, + sample_scc_mid_row_with_space_before_paint, ): # if mid-row code following a text node that ends in space # no additional space will be added - expected_lines = ['AB ', 'AB'] - for caption in[ + expected_lines = ["AB ", "AB"] + for caption in [ sample_scc_mid_row_with_space_before_pop, sample_scc_mid_row_with_space_before_roll, - sample_scc_mid_row_with_space_before_paint + sample_scc_mid_row_with_space_before_paint, ]: # no additional space added (will not be 'AB ') caption_set = SCCReader().read(caption) @@ -371,16 +374,16 @@ def test_mid_row_codes_not_adding_space_if_there_is_one_before( assert expected_lines == actual_lines def test_removing_spaces_at_end_of_lines( - self, - sample_scc_with_spaces_at_eol_pop, - sample_scc_with_spaces_at_eol_roll, - sample_scc_with_spaces_at_eol_paint + self, + sample_scc_with_spaces_at_eol_pop, + sample_scc_with_spaces_at_eol_roll, + sample_scc_with_spaces_at_eol_paint, ): - expected_lines = ['AB', 'AB', 'AB'] + expected_lines = ["AB", "AB", "AB"] for caption in [ sample_scc_with_spaces_at_eol_pop, sample_scc_with_spaces_at_eol_roll, - sample_scc_with_spaces_at_eol_paint + sample_scc_with_spaces_at_eol_paint, ]: caption_set = SCCReader().read(caption) actual_lines = [ @@ -582,11 +585,13 @@ def check_closing_italics_closing_on_style_change(node_creator): node_creator.interpret_command("9140") # row 01, col 0 plaintext node_creator.interpret_command("9120") # plaintext = italics off node_creator.interpret_command("9120") # plaintext = italics off - assert len(node_creator._collection) == 0 # will get cleaned by format_italics + assert ( + len(node_creator._collection) == 0 + ) # will get cleaned by format_italics node_creator.interpret_command("91ce") # row 01, col 0 italics assert len(node_creator._collection) == 1 assert node_creator._collection[0].sets_italics_on() - node_creator.add_chars("a", 'b') # write ab + node_creator.add_chars("a", "b") # write ab assert node_creator.last_style == "italics on" assert node_creator._collection[-1].is_text_node() assert node_creator._collection[-1].text == "ab" @@ -597,18 +602,18 @@ def check_closing_italics_closing_on_style_change(node_creator): assert node_creator._collection[-1].text == " " node_creator.interpret_command("91ae") # italics again # it should re-open italics - node_creator.add_chars("c", 'd') + node_creator.add_chars("c", "d") assert node_creator.last_style == "italics on" assert node_creator._collection[-1].text == "cd" # let's break the line now, and keep the style node_creator.interpret_command("916e") # row 02, column 00 with italics - node_creator.add_chars("e", 'f') + node_creator.add_chars("e", "f") assert node_creator.last_style == "italics on" assert node_creator._collection[-1].text == "ef" # let's break the line now, but change the style this time # it should close italics then break node_creator.interpret_command("9252") # row 03, column 04 with plaintext - node_creator.add_chars("g", 'h') + node_creator.add_chars("g", "h") assert node_creator.last_style == "italics off" assert node_creator._collection[-1].text == "gh" # check that we have a closed italic before break @@ -650,24 +655,22 @@ def test_closing_italics_closing_on_style_change(self): def test_remove_noon_off_on_italics(self): from pycaption.scc.specialized_collections import ( - _InstructionNode, _remove_noon_off_on_italics + _InstructionNode, + _remove_noon_off_on_italics, ) + position_tracker = DefaultProvidingPositionTracker().default node_creator = InstructionNodeCreator( position_tracker=(DefaultProvidingPositionTracker()) ) node_creator.interpret_command("9420") node_creator.interpret_command("9140") # row 01, col 0 plaintext - node_creator.add_chars('a', 'b') + node_creator.add_chars("a", "b") node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=False - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=False) ) node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=True - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=True) ) # last two nodes should be assert node_creator._collection[-2].sets_italics_off() @@ -682,15 +685,11 @@ def test_remove_noon_off_on_italics(self): # check if there's text in between close/open italics node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=False - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=False) ) - node_creator.add_chars('c', 'd') + node_creator.add_chars("c", "d") node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=True - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=True) ) assert node_creator._collection[-3].sets_italics_off() @@ -705,22 +704,20 @@ def test_remove_noon_off_on_italics(self): def test_skip_redundant_italics_nodes(self): from pycaption.scc.specialized_collections import ( - _InstructionNode, _skip_redundant_italics_nodes + _InstructionNode, + _skip_redundant_italics_nodes, ) + position_tracker = DefaultProvidingPositionTracker().default node_creator = InstructionNodeCreator( position_tracker=(DefaultProvidingPositionTracker()) ) node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=False - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=False) ) node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=True - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=True) ) new_collection = _skip_redundant_italics_nodes(node_creator._collection) # should remove italics off @@ -732,15 +729,11 @@ def test_skip_redundant_italics_nodes(self): position_tracker=(DefaultProvidingPositionTracker()) ) node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=False - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=False) ) - node_creator.add_chars('f', 'o', 'o') + node_creator.add_chars("f", "o", "o") node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=True - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=True) ) new_collection = _skip_redundant_italics_nodes(node_creator._collection) # should remove italics off @@ -753,35 +746,23 @@ def test_skip_redundant_italics_nodes(self): position_tracker=(DefaultProvidingPositionTracker()) ) node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=True - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=True) ) - node_creator.add_chars('f', 'o', 'o') + node_creator.add_chars("f", "o", "o") node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=True - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=True) ) node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=True - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=True) ) node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=False - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=False) ) node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=False - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=False) ) node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=True - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=True) ) new_collection = _skip_redundant_italics_nodes(node_creator._collection) # should open italics once, write foo, close italics once then re-open italics @@ -793,8 +774,10 @@ def test_skip_redundant_italics_nodes(self): def test_close_italics_before_repositioning(self): from pycaption.scc.specialized_collections import ( - _InstructionNode, _close_italics_before_repositioning + _close_italics_before_repositioning, + _InstructionNode, ) + position_tracker = DefaultProvidingPositionTracker().default node_creator = InstructionNodeCreator( position_tracker=(DefaultProvidingPositionTracker()) @@ -802,16 +785,14 @@ def test_close_italics_before_repositioning(self): # set italics node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=True - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=True) ) - node_creator.add_chars('f', 'o', 'o') + node_creator.add_chars("f", "o", "o") # reposition node_creator._collection.append( _InstructionNode.create_repositioning_command(position_tracker) ) - node_creator.add_chars('b', 'a', 'r') + node_creator.add_chars("b", "a", "r") new_collection = _close_italics_before_repositioning(node_creator._collection) assert new_collection[0].sets_italics_on() @@ -823,8 +804,10 @@ def test_close_italics_before_repositioning(self): def test_ensure_final_italics_node_closes(self): from pycaption.scc.specialized_collections import ( - _InstructionNode, _ensure_final_italics_node_closes + _ensure_final_italics_node_closes, + _InstructionNode, ) + position_tracker = DefaultProvidingPositionTracker().default node_creator = InstructionNodeCreator( position_tracker=(DefaultProvidingPositionTracker()) @@ -832,11 +815,9 @@ def test_ensure_final_italics_node_closes(self): # set italics node_creator._collection.append( - _InstructionNode.create_italics_style( - position_tracker, turn_on=True - ) + _InstructionNode.create_italics_style(position_tracker, turn_on=True) ) - node_creator.add_chars('f', 'o', 'o') + node_creator.add_chars("f", "o", "o") node_creator._collection.append( _InstructionNode.create_repositioning_command(position_tracker) ) @@ -849,11 +830,11 @@ def test_ensure_final_italics_node_closes(self): node_creator._collection.append( _InstructionNode.create_break(position=position_tracker) ) - node_creator.add_chars('b', 'a', 'r') + node_creator.add_chars("b", "a", "r") node_creator._collection.append( _InstructionNode.create_break(position=position_tracker) ) - node_creator.add_chars('b', 'a', 'z') + node_creator.add_chars("b", "a", "z") new_collection = _ensure_final_italics_node_closes(node_creator._collection) # it should close italics at the end assert new_collection[-1].sets_italics_off() diff --git a/tests/test_scc_conversion.py b/tests/test_scc_conversion.py index 67dc1fb9..ed7a81c9 100644 --- a/tests/test_scc_conversion.py +++ b/tests/test_scc_conversion.py @@ -1,9 +1,13 @@ import pytest from pycaption import ( - SCCReader, SCCWriter, SRTReader, SRTWriter, DFXPWriter, WebVTTWriter, + DFXPWriter, + SCCReader, + SCCWriter, + SRTReader, + SRTWriter, + WebVTTWriter, ) - from tests.mixins import CaptionSetTestingMixIn # This is quite fuzzy at the moment. @@ -31,17 +35,18 @@ def test_srt_to_scc_to_srt_conversion(self, sample_srt_ascii): class TestSCCtoDFXP: - def test_scc_to_dfxp(self, sample_dfxp_from_scc_output, - sample_scc_multiple_positioning): + def test_scc_to_dfxp( + self, sample_dfxp_from_scc_output, sample_scc_multiple_positioning + ): caption_set = SCCReader().read(sample_scc_multiple_positioning) - dfxp = DFXPWriter( - relativize=False, fit_to_screen=False).write(caption_set) - + dfxp = DFXPWriter(relativize=False, fit_to_screen=False).write(caption_set) assert sample_dfxp_from_scc_output == dfxp def test_dfxp_is_valid_xml_when_scc_source_has_weird_italic_commands( - self, sample_dfxp_with_properly_closing_spans_output, - sample_scc_created_dfxp_with_wrongly_closing_spans): + self, + sample_dfxp_with_properly_closing_spans_output, + sample_scc_created_dfxp_with_wrongly_closing_spans, + ): caption_set = SCCReader().read( sample_scc_created_dfxp_with_wrongly_closing_spans ) @@ -51,8 +56,8 @@ def test_dfxp_is_valid_xml_when_scc_source_has_weird_italic_commands( assert dfxp == sample_dfxp_with_properly_closing_spans_output def test_dfxp_is_valid_xml_when_scc_source_has_ampersand_character( - self, sample_dfxp_with_ampersand_character, - sample_scc_with_ampersand_character): + self, sample_dfxp_with_ampersand_character, sample_scc_with_ampersand_character + ): caption_set = SCCReader().read(sample_scc_with_ampersand_character) dfxp = DFXPWriter().write(caption_set) @@ -62,10 +67,11 @@ def test_dfxp_is_valid_xml_when_scc_source_has_ampersand_character( class TestSCCToWebVTT: def test_webvtt_newlines_are_properly_rendered( - self, sample_webvtt_from_scc_properly_writes_newlines_output, - scc_that_generates_webvtt_with_proper_newlines): - caption_set = SCCReader().read( - scc_that_generates_webvtt_with_proper_newlines) + self, + sample_webvtt_from_scc_properly_writes_newlines_output, + scc_that_generates_webvtt_with_proper_newlines, + ): + caption_set = SCCReader().read(scc_that_generates_webvtt_with_proper_newlines) webvtt = WebVTTWriter().write(caption_set) assert webvtt == sample_webvtt_from_scc_properly_writes_newlines_output