@@ -598,6 +598,78 @@ def test_line_padding_rw_raises(self):
598598 with self .assertRaises (ValueError ):
599599 IMSC11TextFilter ().process (doc )
600600
601+ # Spatial overlap checks
602+
603+ def _make_two_region_doc (self , ox1 , oy1 , ew1 , eh1 , ox2 , oy2 , ew2 , eh2 ):
604+ """Builds a doc with two regions each containing active content.
605+
606+ All coordinates and sizes are in percent units.
607+ """
608+ doc = model .ContentDocument ()
609+ body = model .Body (doc )
610+ doc .set_body (body )
611+ div = model .Div (doc )
612+ body .push_child (div )
613+
614+ for idx , (ox , oy , ew , eh ) in enumerate (
615+ [(ox1 , oy1 , ew1 , eh1 ), (ox2 , oy2 , ew2 , eh2 )]
616+ ):
617+ region = model .Region (f"r{ idx } " , doc )
618+ region .set_style (
619+ styles .StyleProperties .Origin ,
620+ styles .CoordinateType (
621+ x = styles .LengthType (ox , styles .LengthType .Units .pct ),
622+ y = styles .LengthType (oy , styles .LengthType .Units .pct ),
623+ ),
624+ )
625+ region .set_style (
626+ styles .StyleProperties .Extent ,
627+ styles .ExtentType (
628+ width = styles .LengthType (ew , styles .LengthType .Units .pct ),
629+ height = styles .LengthType (eh , styles .LengthType .Units .pct ),
630+ ),
631+ )
632+ doc .put_region (region )
633+
634+ p = model .P (doc )
635+ p .set_begin (Fraction (0 ))
636+ p .set_end (Fraction (5 ))
637+ p .set_region (region )
638+ div .push_child (p )
639+ span = model .Span (doc )
640+ p .push_child (span )
641+ text = model .Text (doc , "Hello" )
642+ span .push_child (text )
643+
644+ return doc
645+
646+ def test_overlapping_regions_raises (self ):
647+ # r0: x=[0,60], y=[0,50] r1: x=[40,100], y=[0,50] → overlap at x=[40,60]
648+ doc = self ._make_two_region_doc (0 , 0 , 60 , 50 , 40 , 0 , 60 , 50 )
649+ with self .assertRaises (ValueError ):
650+ IMSC11TextFilter ().process (doc )
651+
652+ def test_abutting_regions_horizontal_passes (self ):
653+ # r0: x=[0,50], y=[0,100] r1: x=[50,100], y=[0,100] → share edge, no overlap
654+ doc = self ._make_two_region_doc (0 , 0 , 50 , 100 , 50 , 0 , 50 , 100 )
655+ filt = IMSC11TextFilter ()
656+ filt .process (doc )
657+ self .assertIn (IMSC_11_TEXT_PROFILE_DESIGNATOR , doc .get_content_profiles ())
658+
659+ def test_abutting_regions_vertical_passes (self ):
660+ # r0: x=[0,100], y=[0,50] r1: x=[0,100], y=[50,100] → share edge, no overlap
661+ doc = self ._make_two_region_doc (0 , 0 , 100 , 50 , 0 , 50 , 100 , 50 )
662+ filt = IMSC11TextFilter ()
663+ filt .process (doc )
664+ self .assertIn (IMSC_11_TEXT_PROFILE_DESIGNATOR , doc .get_content_profiles ())
665+
666+ def test_non_overlapping_regions_with_gap_passes (self ):
667+ # r0: x=[0,40], y=[0,100] r1: x=[60,100], y=[0,100] → gap at x=[40,60]
668+ doc = self ._make_two_region_doc (0 , 0 , 40 , 100 , 60 , 0 , 40 , 100 )
669+ filt = IMSC11TextFilter ()
670+ filt .process (doc )
671+ self .assertIn (IMSC_11_TEXT_PROFILE_DESIGNATOR , doc .get_content_profiles ())
672+
601673 # Filter auto-registration
602674 def test_filter_registered_by_name (self ):
603675 filt_cls = DocumentFilter .get_filter_by_name ("imsc11text" )
0 commit comments