32
32
from typing import Optional , List , Dict , Union
33
33
34
34
from ttconv .model import Region , ContentDocument , P , Br , Span , Text
35
- from ttconv .scc .content import SccCaptionText , SccCaptionLine
36
- from ttconv .scc .style import SccCaptionStyle
35
+ from ttconv .scc .caption_line import SccCaptionLine
36
+ from ttconv .scc .caption_style import SccCaptionStyle
37
+ from ttconv .scc .caption_text import SccCaptionText
37
38
from ttconv .scc .utils import get_position_from_offsets , get_extent_from_dimensions , convert_cells_to_percentages
38
39
from ttconv .style_properties import CoordinateType , ExtentType , StyleProperties , LengthType , DisplayAlignType , ShowBackgroundType , \
39
40
TextAlignType , NamedColors
51
52
class SccCaptionParagraph :
52
53
"""Caption paragraph"""
53
54
55
+ @staticmethod
56
+ def default (caption_style : SccCaptionStyle = SccCaptionStyle .Unknown ):
57
+ """Initializes a default caption paragraph"""
58
+ return SccCaptionParagraph (caption_style = caption_style )
59
+
54
60
def __init__ (self , safe_area_x_offset : int = 0 , safe_area_y_offset : int = 0 ,
55
61
caption_style : SccCaptionStyle = SccCaptionStyle .Unknown ):
56
62
self ._caption_id : str = ""
@@ -69,6 +75,8 @@ def __init__(self, safe_area_x_offset: int = 0, safe_area_y_offset: int = 0,
69
75
self ._current_line : Optional [SccCaptionLine ] = None
70
76
# Lines per row in the active area (will be separated by line-breaks)
71
77
self ._caption_lines : Dict [int , SccCaptionLine ] = {}
78
+ # Initialize first default line
79
+ self .new_caption_line ()
72
80
73
81
self ._caption_style : SccCaptionStyle = caption_style
74
82
self ._style_properties = {}
@@ -85,15 +93,15 @@ def set_begin(self, time_code):
85
93
"""Sets caption begin time code"""
86
94
self ._begin = copy .copy (time_code )
87
95
88
- def get_begin (self ) -> SmpteTimeCode :
96
+ def get_begin (self ) -> Optional [ SmpteTimeCode ] :
89
97
"""Returns the caption begin time code"""
90
98
return self ._begin
91
99
92
100
def set_end (self , time_code ):
93
101
"""Sets caption end time code"""
94
102
self ._end = copy .copy (time_code )
95
103
96
- def get_end (self ) -> SmpteTimeCode :
104
+ def get_end (self ) -> Optional [ SmpteTimeCode ] :
97
105
"""Returns the caption end time code"""
98
106
return self ._end
99
107
@@ -105,18 +113,20 @@ def get_safe_area_y_offset(self):
105
113
"""Returns the safe area y offset"""
106
114
return self ._safe_area_y_offset
107
115
116
+ def set_caption_style (self , caption_style : SccCaptionStyle ):
117
+ """Sets the caption style"""
118
+ self ._caption_style = caption_style
119
+
108
120
def get_caption_style (self ) -> SccCaptionStyle :
109
121
"""Returns the caption style"""
110
122
return self ._caption_style
111
123
112
- def get_current_line (self ) -> Optional [ SccCaptionLine ] :
124
+ def get_current_line (self ) -> SccCaptionLine :
113
125
"""Returns the current caption line"""
114
126
return self ._current_line
115
127
116
- def get_current_text (self ) -> Optional [ SccCaptionText ] :
128
+ def get_current_text (self ) -> SccCaptionText :
117
129
"""Returns the current caption text"""
118
- if self ._current_line is None :
119
- return None
120
130
return self ._current_line .get_current_text ()
121
131
122
132
def append_text (self , text : str ):
@@ -150,9 +160,14 @@ def get_style_property(self, style_property) -> Optional:
150
160
def set_cursor_at (self , row : int , indent : Optional [int ] = None ):
151
161
"""Set cursor position and initialize a new line if necessary"""
152
162
153
- # Remove current line if empty (useless)
154
- if self ._current_line is not None and self ._current_line .is_empty ():
155
- del self ._caption_lines [self ._current_line .get_row ()]
163
+ if self ._caption_lines .get (self ._current_line .get_row ()) is not None :
164
+ # Set current line if necessary
165
+ if self ._caption_lines .get (self ._current_line .get_row ()) is not self ._current_line :
166
+ self ._current_line = self ._caption_lines .get (self ._current_line .get_row ())
167
+
168
+ # Remove current line if empty (i.e. useless)
169
+ if self ._current_line .is_empty ():
170
+ del self ._caption_lines [self ._current_line .get_row ()]
156
171
157
172
self ._cursor = (row , indent if indent is not None else 0 )
158
173
@@ -162,7 +177,7 @@ def set_cursor_at(self, row: int, indent: Optional[int] = None):
162
177
self ._current_line = self ._caption_lines .get (row )
163
178
164
179
if indent is not None :
165
- self ._current_line . set_cursor ( self . _cursor [ 1 ] - self . _current_line . get_indent () )
180
+ self ._update_current_line_cursor ( )
166
181
167
182
def get_cursor (self ) -> (int , int ):
168
183
"""Returns cursor coordinates"""
@@ -176,12 +191,29 @@ def indent_cursor(self, indent: int):
176
191
# If the current line is empty, set cursor indent as a line tabulation
177
192
self ._current_line .indent (indent )
178
193
else :
179
- self ._current_line .set_cursor (self ._cursor [1 ] - self ._current_line .get_indent ())
194
+ self ._update_current_line_cursor ()
195
+
196
+ def _update_current_line_cursor (self ):
197
+ """Updates cursor position on current line"""
198
+ new_cursor_position = self ._cursor [1 ] - self ._current_line .get_indent ()
199
+
200
+ if new_cursor_position < 0 :
201
+ self ._current_line .indent (new_cursor_position )
202
+
203
+ self ._current_line .set_cursor (new_cursor_position )
180
204
181
205
def get_lines (self ) -> Dict [int , SccCaptionLine ]:
182
206
"""Returns the paragraph lines per row"""
183
207
return self ._caption_lines
184
208
209
+ def is_empty (self ) -> bool :
210
+ """Returns whether the paragraph has no content"""
211
+ return self ._get_length () == 0
212
+
213
+ def _get_length (self ) -> int :
214
+ """Returns the total length of contained text"""
215
+ return sum ([line .get_length () for line in self ._caption_lines .values ()])
216
+
185
217
def copy_lines (self ) -> Dict [int , SccCaptionLine ]:
186
218
"""Copy paragraph lines (without time attributes)"""
187
219
lines_copy = {}
@@ -199,10 +231,6 @@ def copy_lines(self) -> Dict[int, SccCaptionLine]:
199
231
200
232
def new_caption_text (self ):
201
233
"""Appends a new caption text content, and keeps reference on it"""
202
- if self ._current_line is None :
203
- LOGGER .warning ("Add a new caption line to add new caption text" )
204
- self .new_caption_line ()
205
-
206
234
self ._current_line .add_text (SccCaptionText ())
207
235
208
236
def new_caption_line (self ):
@@ -227,7 +255,7 @@ def roll_up(self):
227
255
228
256
def get_origin (self ) -> CoordinateType :
229
257
"""Computes and returns the current paragraph origin, based on its content"""
230
- if len ( self ._caption_lines ) > 0 :
258
+ if not self .is_empty () :
231
259
x_offsets = [text .get_indent () for text in self ._caption_lines .values ()]
232
260
y_offsets = [text .get_row () - 1 for text in self ._caption_lines .values ()]
233
261
@@ -237,7 +265,7 @@ def get_origin(self) -> CoordinateType:
237
265
238
266
def get_extent (self ) -> ExtentType :
239
267
"""Computes and returns the current paragraph extent, based on its content"""
240
- if len ( self ._caption_lines ) == 0 :
268
+ if self .is_empty () :
241
269
return get_extent_from_dimensions (0 , 0 )
242
270
243
271
paragraph_rows = self ._caption_lines .keys ()
@@ -260,6 +288,9 @@ def guess_text_alignment(self) -> TextAlignType:
260
288
def get_line_right_offset (line : SccCaptionLine ) -> int :
261
289
return SCC_ROOT_CELL_RESOLUTION_COLUMNS - (line .get_indent () + line .get_length ())
262
290
291
+ if self .is_empty ():
292
+ return TextAlignType .start
293
+
263
294
# look for longest line
264
295
longest_line = max (self ._caption_lines .values (), key = lambda line : line .get_length ())
265
296
0 commit comments