@@ -426,6 +426,209 @@ void main() async {
426426 expect (paragraph.getBoxesForRange (0 , 0 ), isEmpty);
427427 });
428428
429+ testEachMeasurement ('getBoxesForRange multi-line' , () {
430+ final ParagraphBuilder builder = ParagraphBuilder (ParagraphStyle (
431+ fontFamily: 'Ahem' ,
432+ fontStyle: FontStyle .normal,
433+ fontWeight: FontWeight .normal,
434+ fontSize: 10 ,
435+ textDirection: TextDirection .ltr,
436+ ));
437+ builder.addText ('abcd\n ' );
438+ builder.addText ('abcdefg\n ' );
439+ builder.addText ('ab' );
440+ final Paragraph paragraph = builder.build ();
441+ paragraph.layout (const ParagraphConstraints (width: 100 ));
442+
443+ // In the dom-based measurement (except Firefox), there will be some
444+ // discrepancies around line ends.
445+ final isDiscrepancyExpected =
446+ ! TextMeasurementService .enableExperimentalCanvasImplementation &&
447+ browserEngine != BrowserEngine .firefox;
448+
449+ // First line: "abcd\n"
450+
451+ // At the beginning of the first line.
452+ expect (
453+ paragraph.getBoxesForRange (0 , 0 ),
454+ < TextBox > [],
455+ );
456+ // At the end of the first line.
457+ expect (
458+ paragraph.getBoxesForRange (4 , 4 ),
459+ < TextBox > [],
460+ );
461+ // Between "b" and "c" in the first line.
462+ expect (
463+ paragraph.getBoxesForRange (2 , 2 ),
464+ < TextBox > [],
465+ );
466+ // The range "ab" in the first line.
467+ expect (
468+ paragraph.getBoxesForRange (0 , 2 ),
469+ < TextBox > [
470+ TextBox .fromLTRBD (0.0 , 0.0 , 20.0 , 10.0 , TextDirection .ltr),
471+ ],
472+ );
473+ // The range "bc" in the first line.
474+ expect (
475+ paragraph.getBoxesForRange (1 , 3 ),
476+ < TextBox > [
477+ TextBox .fromLTRBD (10.0 , 0.0 , 30.0 , 10.0 , TextDirection .ltr),
478+ ],
479+ );
480+ // The range "d" in the first line.
481+ expect (
482+ paragraph.getBoxesForRange (3 , 4 ),
483+ < TextBox > [
484+ TextBox .fromLTRBD (30.0 , 0.0 , 40.0 , 10.0 , TextDirection .ltr),
485+ ],
486+ );
487+ // The range "\n" in the first line.
488+ expect (
489+ paragraph.getBoxesForRange (4 , 5 ),
490+ < TextBox > [
491+ TextBox .fromLTRBD (40.0 , 0.0 , 40.0 , 10.0 , TextDirection .ltr),
492+ ],
493+ );
494+ // The range "cd\n" in the first line.
495+ expect (
496+ paragraph.getBoxesForRange (2 , 5 ),
497+ < TextBox > [
498+ TextBox .fromLTRBD (20.0 , 0.0 , 40.0 , 10.0 , TextDirection .ltr),
499+ if (isDiscrepancyExpected)
500+ TextBox .fromLTRBD (40.0 , 0.0 , 40.0 , 10.0 , TextDirection .ltr),
501+ ],
502+ );
503+
504+ // Second line: "abcdefg\n"
505+
506+ // At the beginning of the second line.
507+ expect (
508+ paragraph.getBoxesForRange (5 , 5 ),
509+ < TextBox > [],
510+ );
511+ // At the end of the second line.
512+ expect (
513+ paragraph.getBoxesForRange (12 , 12 ),
514+ < TextBox > [],
515+ );
516+ // The range "efg" in the second line.
517+ expect (
518+ paragraph.getBoxesForRange (9 , 12 ),
519+ < TextBox > [
520+ TextBox .fromLTRBD (40.0 , 10.0 , 70.0 , 20.0 , TextDirection .ltr),
521+ ],
522+ );
523+ // The range "bcde" in the second line.
524+ expect (
525+ paragraph.getBoxesForRange (6 , 10 ),
526+ < TextBox > [
527+ TextBox .fromLTRBD (10.0 , 10.0 , 50.0 , 20.0 , TextDirection .ltr),
528+ ],
529+ );
530+ // The range "fg\n" in the second line.
531+ expect (
532+ paragraph.getBoxesForRange (10 , 13 ),
533+ < TextBox > [
534+ TextBox .fromLTRBD (50.0 , 10.0 , 70.0 , 20.0 , TextDirection .ltr),
535+ if (isDiscrepancyExpected)
536+ TextBox .fromLTRBD (70.0 , 10.0 , 70.0 , 20.0 , TextDirection .ltr),
537+ ],
538+ );
539+
540+ // Last (third) line: "ab"
541+
542+ // At the beginning of the last line.
543+ expect (
544+ paragraph.getBoxesForRange (13 , 13 ),
545+ < TextBox > [],
546+ );
547+ // At the end of the last line.
548+ expect (
549+ paragraph.getBoxesForRange (15 , 15 ),
550+ < TextBox > [],
551+ );
552+ // The range "a" in the last line.
553+ expect (
554+ paragraph.getBoxesForRange (14 , 15 ),
555+ < TextBox > [
556+ TextBox .fromLTRBD (10.0 , 20.0 , 20.0 , 30.0 , TextDirection .ltr),
557+ ],
558+ );
559+ // The range "ab" in the last line.
560+ expect (
561+ paragraph.getBoxesForRange (13 , 15 ),
562+ < TextBox > [
563+ TextBox .fromLTRBD (0.0 , 20.0 , 20.0 , 30.0 , TextDirection .ltr),
564+ ],
565+ );
566+
567+
568+ // Combine multiple lines
569+
570+ // The range "cd\nabc".
571+ expect (
572+ paragraph.getBoxesForRange (2 , 8 ),
573+ < TextBox > [
574+ TextBox .fromLTRBD (20.0 , 0.0 , 40.0 , 10.0 , TextDirection .ltr),
575+ if (isDiscrepancyExpected)
576+ TextBox .fromLTRBD (40.0 , 0.0 , 40.0 , 10.0 , TextDirection .ltr),
577+ TextBox .fromLTRBD (0.0 , 10.0 , 30.0 , 20.0 , TextDirection .ltr),
578+ ],
579+ );
580+
581+ // The range "\nabcd".
582+ expect (
583+ paragraph.getBoxesForRange (4 , 9 ),
584+ < TextBox > [
585+ TextBox .fromLTRBD (40.0 , 0.0 , 40.0 , 10.0 , TextDirection .ltr),
586+ TextBox .fromLTRBD (0.0 , 10.0 , 40.0 , 20.0 , TextDirection .ltr),
587+ ],
588+ );
589+
590+ // The range "d\nabcdefg\na".
591+ expect (
592+ paragraph.getBoxesForRange (3 , 14 ),
593+ < TextBox > [
594+ TextBox .fromLTRBD (30.0 , 0.0 , 40.0 , 10.0 , TextDirection .ltr),
595+ if (isDiscrepancyExpected)
596+ TextBox .fromLTRBD (40.0 , 0.0 , 40.0 , 10.0 , TextDirection .ltr),
597+ TextBox .fromLTRBD (0.0 , 10.0 , 70.0 , 20.0 , TextDirection .ltr),
598+ if (isDiscrepancyExpected)
599+ TextBox .fromLTRBD (70.0 , 10.0 , 70.0 , 20.0 , TextDirection .ltr),
600+ TextBox .fromLTRBD (0.0 , 20.0 , 10.0 , 30.0 , TextDirection .ltr),
601+ ],
602+ );
603+
604+ // The range "abcd\nabcdefg\n".
605+ expect (
606+ paragraph.getBoxesForRange (0 , 13 ),
607+ < TextBox > [
608+ TextBox .fromLTRBD (0.0 , 0.0 , 40.0 , 10.0 , TextDirection .ltr),
609+ if (isDiscrepancyExpected)
610+ TextBox .fromLTRBD (40.0 , 0.0 , 40.0 , 10.0 , TextDirection .ltr),
611+ TextBox .fromLTRBD (0.0 , 10.0 , 70.0 , 20.0 , TextDirection .ltr),
612+ if (isDiscrepancyExpected)
613+ TextBox .fromLTRBD (70.0 , 10.0 , 70.0 , 20.0 , TextDirection .ltr),
614+ ],
615+ );
616+
617+ // The range "abcd\nabcdefg\nab".
618+ expect (
619+ paragraph.getBoxesForRange (0 , 15 ),
620+ < TextBox > [
621+ TextBox .fromLTRBD (0.0 , 0.0 , 40.0 , 10.0 , TextDirection .ltr),
622+ if (isDiscrepancyExpected)
623+ TextBox .fromLTRBD (40.0 , 0.0 , 40.0 , 10.0 , TextDirection .ltr),
624+ TextBox .fromLTRBD (0.0 , 10.0 , 70.0 , 20.0 , TextDirection .ltr),
625+ if (isDiscrepancyExpected)
626+ TextBox .fromLTRBD (70.0 , 10.0 , 70.0 , 20.0 , TextDirection .ltr),
627+ TextBox .fromLTRBD (0.0 , 20.0 , 20.0 , 30.0 , TextDirection .ltr),
628+ ],
629+ );
630+ });
631+
429632 test ('longestLine' , () {
430633 // [Paragraph.longestLine] is only supported by canvas-based measurement.
431634 TextMeasurementService .enableExperimentalCanvasImplementation = true ;
0 commit comments