def ordered_styles(self, dataset): """ This function figures out the chain of styles. WARNING: Do not call this before the semantic validation of tt/head/styling is finished. Otherwise your style may not have been found yet! :param dataset: Semantic dataset :return: a list of styles applicable in order """ if self._styling_lock.locked(): raise SemanticValidationError(ERR_SEMANTIC_STYLE_CIRCLE.format( style=self.id )) with self._styling_lock: if self._ordered_styles is not None: return self._ordered_styles ordered_styles = [self] if self.style is not None: for style_id in self.style: try: style_elem = dataset['tt_element'].get_element_by_id(elem_id=style_id, elem_type=style_type) cascading_styles = style_elem.ordered_styles(dataset=dataset) for style_elem in cascading_styles: if style_elem in ordered_styles: continue ordered_styles.append(style_elem) except LookupError: raise SemanticValidationError(ERR_SEMANTIC_STYLE_MISSING.format( style=style_id )) self._ordered_styles = ordered_styles return ordered_styles
def _semantic_register_id(self, dataset): ebid = dataset['elements_by_id'] if self.id is not None: if self.id in ebid: raise SemanticValidationError( ERR_SEMANTIC_ID_UNIQUENESS.format(id=self.id)) ebid[self.id] = self
def _semantic_set_region(self, dataset, region_type): if self.region is not None: try: region = dataset['tt_element'].get_element_by_id(self.region, region_type) dataset['region'] = region self._validated_region = region except LookupError: raise SemanticValidationError(ERR_SEMANTIC_REGION_MISSING.format( region=self.region ))
def __semantic_test_time_base_clock_attrs_absent(self): clock_attrs = [ 'clockMode' ] extra_attrs = self._semantic_attributes_present(clock_attrs) if extra_attrs: raise SemanticValidationError( ERR_SEMANTIC_VALIDATION_MISSING_ATTRIBUTES.format( elem_name='tt:tt', attr_names=extra_attrs ) )
def get_element_by_id(self, elem_id, elem_type=None): """ Lookup an element and return it. Optionally type is checked as well. :param elem_id: :param elem_type: :return: """ if self._elements_by_id is None: raise SemanticValidationError(ERR_SEMANTIC_VALIDATION_EXPECTED) element = self._elements_by_id.get(elem_id, None) if element is None or elem_type is not None and not isinstance(element, elem_type): raise LookupError(ERR_SEMANTIC_ELEMENT_BY_ID_MISSING.format(id=elem_id)) return element
def __semantic_test_smpte_attrs_absent(self): smpte_attrs = [ 'dropMode', 'markerMode' ] extra_attrs = self._semantic_attributes_present(smpte_attrs) if extra_attrs: raise SemanticValidationError( ERR_SEMANTIC_VALIDATION_INVALID_ATTRIBUTES.format( elem_name='tt:tt', attr_names=extra_attrs ) )
def __semantic_test_smpte_attrs_present(self): smpte_attrs = [ 'frameRate', # 'frameRateMultiplier', 'dropMode', 'markerMode' ] missing_attrs = self._semantic_attributes_missing(smpte_attrs) if missing_attrs: raise SemanticValidationError( ERR_SEMANTIC_VALIDATION_MISSING_ATTRIBUTES.format( elem_name='tt:tt', attr_names=missing_attrs ) )
def _semantic_timebase_validation(self, dataset, element_content): time_base = dataset['tt_element'].timeBase if self.begin is not None: if hasattr(self.begin, 'compatible_timebases'): # Check typing of begin attribute against the timebase timebases = self.begin.compatible_timebases() if time_base not in timebases['begin']: raise SemanticValidationError( ERR_SEMANTIC_VALIDATION_TIMING_TYPE.format( attr_type=type(self.begin), attr_value=self.begin, attr_name='begin', time_base=time_base)) if self.end is not None: if hasattr(self.end, 'compatible_timebases'): # Check typing of end attribute against the timebase timebases = self.end.compatible_timebases() if time_base not in timebases['end']: raise SemanticValidationError( ERR_SEMANTIC_VALIDATION_TIMING_TYPE.format( attr_type=type(self.end), attr_value=self.end, attr_name='end', time_base=time_base))
def _semantic_timebase_validation(self, dataset, element_content): super(BodyTimingValidationMixin, self)._semantic_timebase_validation(dataset, element_content) time_base = dataset['tt_element'].timeBase if self.dur is not None: if hasattr(self.dur, 'compatible_timebases'): # Check typing of dur attribute against the timebase timebases = self.dur.compatible_timebases() if time_base not in timebases['dur']: raise SemanticValidationError( ERR_SEMANTIC_VALIDATION_TIMING_TYPE.format( attr_type=type(self.dur), attr_value=self.dur, attr_name='dur', time_base=time_base))
def _semantic_collect_applicable_styles(self, dataset, style_type): referenced_styles = [] inherited_styles = [] region_styles = [] if self.style is not None: # Styles cascade for style_id in self.style: try: style = dataset['tt_element'].get_element_by_id( elem_id=style_id, elem_type=style_type) for style_binding in style.ordered_styles(dataset=dataset): if style_binding not in referenced_styles: referenced_styles.append(style_binding) except LookupError: raise SemanticValidationError( ERR_SEMANTIC_STYLE_MISSING.format(style=style_id)) # Push this validated set onto the stack for children to use for style_list in dataset['styles_stack']: # Traverse all the styles encountered at our parent elements for inh_style in style_list: if inh_style not in referenced_styles and inh_style not in inherited_styles: inherited_styles.append(inh_style) region = dataset.get('region', None) self._inherited_region = region if region is not None: # At last apply any region styles we may find for region_style in region.validated_styles: if region_style not in referenced_styles and region_style not in inherited_styles: region_styles.append(region_style) self._referenced_styles = referenced_styles self._validated_styles = referenced_styles + inherited_styles + region_styles
def _semantic_collect_applicable_styles(self, dataset, style_type, parent_binding, defer_font_size=False, extra_referenced_styles=None): """ This function identifies the styling dependency chain for the styled element in question. :param dataset: Semantic dataset :param style_type: the style_type to be used in the process (there are different style types for EBU-TT D and live). :param parent_binding: The immediate parent of the styled element in the document structure :param defer_font_size: If True then fontsize can stay percentage in case it could not be calculated :param extra_referenced_styles: Used by region to inject its extra style attributes :return: """ self._specified_style = None self._computed_style = None self._parent_computed_style = None referenced_styles = [] inherited_styles = [] region_styles = [] if extra_referenced_styles is None: extra_referenced_styles = [] if self.style is not None: # Styles cascade for style_id in self.style: try: style = dataset['tt_element'].get_element_by_id(elem_id=style_id, elem_type=style_type) for style_binding in style.ordered_styles(dataset=dataset): if style_binding not in referenced_styles: referenced_styles.append(style_binding) except LookupError: raise SemanticValidationError(ERR_SEMANTIC_STYLE_MISSING.format(style=style_id)) # Push this validated set onto the stack for children to use for style_list in dataset['styles_stack']: # Traverse all the styles encountered at our parent elements for inh_style in style_list: if inh_style not in referenced_styles and inh_style not in inherited_styles: inherited_styles.append(inh_style) region = dataset.get('region', None) self._inherited_region = region if region is not None: # At last apply any region styles we may find for region_style in region.validated_styles: if region_style not in referenced_styles and region_style not in inherited_styles: region_styles.append(region_style) self._referenced_styles = referenced_styles self._inherited_styles = inherited_styles self._region_styles = region_styles self._validated_styles = referenced_styles + inherited_styles + region_styles if parent_binding is not None and hasattr(parent_binding, 'computed_style'): parent_computed_style = parent_binding.computed_style else: parent_computed_style = None if region is not None and hasattr(region, 'computed_style'): region_computed_style = region.computed_style else: region_computed_style = None # Let's resolve the specified styles # Make sure the extra style attributes supersede the rest of the referenced styles self._specified_style = self._compatible_style_type.resolve_styles(extra_referenced_styles + referenced_styles) # Let's generate the computed style of the element self._computed_style = self._compatible_style_type.compute_style( self._specified_style, parent_computed_style, region_computed_style, dataset, defer_font_size )
def validated_styles(self): if self._validated_styles is None: raise SemanticValidationError(ERR_SEMANTIC_VALIDATION_EXPECTED) return self._validated_styles