diff --git a/.travis.yml b/.travis.yml index c610dfd76..fc61f43dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,44 +1,20 @@ language: python python: - - '2.7' + - '3.7' branches: except: - gh-pages -before_install: - - echo "Checking for java" - - java -version - - openssl aes-256-cbc -K $encrypted_f18bb5b3c3d4_key -iv $encrypted_f18bb5b3c3d4_iv -in publish-key.enc -out ~/.ssh/publish-key -d - - chmod u=rw,og= ~/.ssh/publish-key - - echo "Host github.com" >> ~/.ssh/config - - echo " IdentityFile ~/.ssh/publish-key" >> ~/.ssh/config - - sudo apt-get -qq update - - sudo apt-get install graphviz - - git --version - - git remote set-url origin git@github.com:ebu/ebu-tt-live-toolkit.git - - git fetch origin -f gh-pages:gh-pages - install: - - pip install pip==9.0.1 - - make - - python setup.py develop - - pip install coveralls - - pip install ghp-import + - pip install -U pip + - make init script: - - pyxbgen --binding-root=./ebu_tt_live/bindings -m __init__ --schema-root=./ebu_tt_live/xsd/ -r -u ebutt_all.xsd - - python setup.py test - - export PR=https://api.github.com/repos/$TRAVIS_REPO_SLUG/pulls/$TRAVIS_PULL_REQUEST - - export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo `curl -s $PR | jq -r .head.ref`; fi) - - echo "TRAVIS_BRANCH=$TRAVIS_BRANCH, PR=$PR, BRANCH=$BRANCH" - -after_success: - - coveralls + - make test + - echo "Compiling documentation" + - make + - sudo apt-get -qq update + - sudo apt-get install graphviz - python setup.py build_sphinx - - if [ $BRANCH == "master" ]; then ghp-import -n -p -m "Update gh-pages." docs/build; fi - -notifications: - slack: - secure: RLs+ufYg4HcZfaTl73tTKKVja8DJ9LGk1WO+7B1qa4Fgpv0c/3uXcM+6tvDXllTlqcfLHgum0vbzvE4yapo0em0g/m75t1pEfkh5Pfrcas8IKAM9jg/xycWdYoAkKLRvNOcS4amO29FlzOHFaSSjX+V8JK8Mpm+QM2eGnqIbJokaXgBf/bQxHqGSRvYCeSisEG0f79Mn/0v5OOlbvmetFfvcQVNZDtgpA1CDbvK99F/vQT3qZ04Rh/vRnlcZyH9PEp/TEEJ0yX0NhHYEDnuBKPc86Ack/YCQpT12Ej2xTWjgoUrqv1pcr+h0ltYPXnnCyETrlbeLgNIMUsGW579MThqXekBZ7byRK2DMfjyL5+UgPSmTOIzEg7Qbc1u0r1bUuEi7nsJ5Vf65QeIAcewVASOUKmda9ag0MidTR/VoAsMtf5MzbDifSa1aBHRxZJToshNJVY9V8/lJLdwsbqsG2H+8v8ZuzyRLNiaFhNQTZQWpRXMipvbTDnnVxwpeFiLAqAwEKO9KIyGijqv2yeVvi4WtYvwzM7/1y/ycu7kTDFemKKuKmW2rzzBKJ30vo79ufMQCmn0riKkpaNxBf8R3EUEr86A9yuFpo6/Qc47i3PkeD7MszDqhEAfxl1+sQssWXOdVy6ALFb7RZ6EczpPvAjYLLO0aPa8OPaZjy3aJr+8= diff --git a/docs/source/configurator.rst b/docs/source/configurator.rst index 93f63c54d..00ddc8737 100644 --- a/docs/source/configurator.rst +++ b/docs/source/configurator.rst @@ -125,6 +125,9 @@ Node type dependent options for [nodeN] : :: type="distributor" : No options + type="denester" + └─sequence_identifier : sequence identifier, default "Denester1" + Output carriage type dependent options for "carriage": :: type="direct" diff --git a/docs/source/denesting.rst b/docs/source/denesting.rst new file mode 100644 index 000000000..a673e5b24 --- /dev/null +++ b/docs/source/denesting.rst @@ -0,0 +1,37 @@ +Denesting of EBU-TT-Live documents +====================================== + +DenesterNode should be used when a EBU-TT-3 document has a div that contains +other divs, or a span contains another span, and those nested elements need +to be flattened, for example before conversion to EBU-TT-D. These elements +are not able to be nested inside each other in EBU-TT-D documents. + +When documents are Denested, any nested elements must be removed from +their parent elements, while retaining attributes they would have inherited. +To address this, the DenesterNode node processes the +document(s) with the +:py:func:`ebu_tt_live.node.denester.DenesterNode.recurse` and +:py:func:`ebu_tt_live.node.denester.DenesterNode.recurse_span` +functions. These will iterate through the file to locate the deepest +nested element, and create a new copy of it with its content and +expected inherited attributes. The end result is a file containing +these newly created divs/spans in place of the nested ones. + +Once the new divs and spans are created, divs that are sequential in +the list of divs and have the same attributes are combined into a single +div. This is done by the +:py:func:`ebu_tt_live.node.denester.DenesterNode.combine_divs` +function to reduce the number of divs in the resulting file. + +In the combined divs, every p element should have the same region +as its parent div. Any p elements that specify a different region +to their inherited region are removed here: as per TTML semantics, +such p elements are never presented. +The +:py:func:`ebu_tt_live.node.denester.DenesterNode.check_p_regions` +function iterates through the divs that have an assigned region and +removes any p where its region does not match. +It then removes the region attribute from any remaining p, as it will +inhert the region of its parent div and the attribute is unnecessary. +It will also remove any now-empty divs that exist as a result of having +their p elements removed. diff --git a/docs/source/ebu_tt_live.node.rst b/docs/source/ebu_tt_live.node.rst index fdd249b2f..9d4527776 100644 --- a/docs/source/ebu_tt_live.node.rst +++ b/docs/source/ebu_tt_live.node.rst @@ -80,3 +80,11 @@ node Package :members: :undoc-members: :show-inheritance: + +:mod:`denester` Module +---------------------- + +.. automodule:: ebu_tt_live.node.denester + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/inode.puml b/docs/source/inode.puml index 280a86b31..e1d039eab 100644 --- a/docs/source/inode.puml +++ b/docs/source/inode.puml @@ -70,6 +70,11 @@ class BufferDelayNode { +process_document() } +class DenesterNode{ + ..methods.. + +process_document() +} + class RetimingDelayNode{ ..methods.. +process_document() diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 13c353908..408143760 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -18,4 +18,5 @@ The components mimic the nodes and carriage mechanisms defined in the specificat timing_resolution segmentation deduplication + denesting conversion diff --git a/docs/source/scripts_and_their_functions.rst b/docs/source/scripts_and_their_functions.rst index 75ea12d8a..c0b0a4e0b 100644 --- a/docs/source/scripts_and_their_functions.rst +++ b/docs/source/scripts_and_their_functions.rst @@ -107,6 +107,17 @@ attributes are duplicated. For the default configuration of the node, see: ``ebu-run --admin.conf=ebu_tt_live/examples/config/deduplicator_fs.json`` +Denester Node +------------- +This node flattens nested ``div`` and ``span`` elements such that no +``div`` ends up containing a ``div`` and no ``span`` ends up containing +a ``span``. It also removes any ``p`` elements that specify a ``region`` +attribute that differs from a specified region on an ancester element. + +If nested ``div`` or ``span`` elements might be present in a document, the +Denester node should be used to flatten them before passing them to the +EBU-TT-D Encoder, because EBU-TT-D does not permit such nested elements. + Retiming Delay Node ------------------- This script modifies the times within each Document and issues them without diff --git a/ebu_tt_live/bindings/__init__.py b/ebu_tt_live/bindings/__init__.py index 8bc81f397..82f4a0212 100644 --- a/ebu_tt_live/bindings/__init__.py +++ b/ebu_tt_live/bindings/__init__.py @@ -100,6 +100,24 @@ class style_type(StyledElementMixin, IDMixin, SizingValidationMixin, SemanticVal } _default_attrs = None + def check_equal(self, other): + return (self.backgroundColor == other.backgroundColor and + self.padding == other.padding and + self.unicodeBidi == other.unicodeBidi and + self.color == other.color and + self.direction == other.direction and + self.fontFamily == other.fontFamily and + self.fontStyle == other.fontStyle and + self.fontWeight == other.fontWeight and + self.linePadding == other.linePadding and + self.multiRowAlign == other.multiRowAlign and + self.textAlign == other.textAlign and + self.textDecoration == other.textDecoration and + self.fontSize == other.fontSize and + self.lineHeight == other.lineHeight and + self.wrapOption == other.wrapOption) + + def __repr__(self): return '