1 /* 2 * Copyright 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.compose.ui.text.android 18 19 import android.text.TextDirectionHeuristic 20 import android.text.TextDirectionHeuristics 21 import android.text.TextPaint 22 import androidx.core.content.res.ResourcesCompat 23 import androidx.test.ext.junit.runners.AndroidJUnit4 24 import androidx.test.filters.SmallTest 25 import androidx.test.platform.app.InstrumentationRegistry 26 import androidx.testutils.fonts.R 27 import com.google.common.truth.Truth.assertThat 28 import org.junit.Test 29 import org.junit.runner.RunWith 30 31 /** 32 * In this test cases, use following notations: 33 * - L1-LF shows an example strong LTR character. 34 * - R1-RF shows an example strong RTL character 35 */ 36 @SmallTest 37 @OptIn(InternalPlatformTextApi::class) 38 @RunWith(AndroidJUnit4::class) 39 class LayoutGetHorizontalTest { 40 41 private val sampleTypeface = 42 ResourcesCompat.getFont( 43 InstrumentationRegistry.getInstrumentation().targetContext, 44 R.font.sample_font 45 ) 46 47 private val LTR = TextDirectionHeuristics.LTR 48 private val RTL = TextDirectionHeuristics.RTL 49 getUpstreamPrimaryHorizontalPositionnull50 private fun LayoutHelper.getUpstreamPrimaryHorizontalPosition(offset: Int) = 51 getHorizontalPosition(offset = offset, usePrimaryDirection = true, upstream = true) 52 53 private fun LayoutHelper.getDownstreamPrimaryHorizontalPosition(offset: Int) = 54 getHorizontalPosition(offset = offset, usePrimaryDirection = true, upstream = false) 55 56 private fun LayoutHelper.getUpstreamSecondaryHorizontalPosition(offset: Int) = 57 getHorizontalPosition(offset = offset, usePrimaryDirection = false, upstream = true) 58 59 private fun LayoutHelper.getDownstreamSecondaryHorizontalPosition(offset: Int) = 60 getHorizontalPosition(offset = offset, usePrimaryDirection = false, upstream = false) 61 62 private fun getLayout( 63 text: String, 64 fontSize: Int, 65 width: Int, 66 dir: TextDirectionHeuristic 67 ): LayoutHelper { 68 val paint = 69 TextPaint().apply { 70 textSize = fontSize.toFloat() 71 typeface = sampleTypeface 72 } 73 val layout = 74 StaticLayoutFactory.create(text = text, paint = paint, width = width, textDir = dir) 75 return LayoutHelper(layout) 76 } 77 78 @Test getHorizontal_LTRText_LTRParagraph_FirstCharacternull79 fun getHorizontal_LTRText_LTRParagraph_FirstCharacter() { 80 // The line break happens like as follows 81 // 82 // input (Logical): L1 L2 L3 L4 L5 L6 L7 L8 L9 LA LB LC 83 // 84 // |L1 L2 L3 L4 L5| 85 // |L6 L7 L8 L9 LA| 86 // |LB LC | 87 // 88 val layout = 89 getLayout( 90 text = "\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006A\u006B\u006C", 91 fontSize = 10, 92 width = 50, 93 dir = LTR 94 ) 95 96 val offset = 0 // Before L1 97 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 98 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 99 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 100 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 101 } 102 103 @Test getHorizontal_LTRText_LTRParagraph_LineBreakOffsetnull104 fun getHorizontal_LTRText_LTRParagraph_LineBreakOffset() { 105 // The line break happens like as follows 106 // 107 // input (Logical): L1 L2 L3 L4 L5 L6 L7 L8 L9 LA LB LC 108 // 109 // |L1 L2 L3 L4 L5| 110 // |L6 L7 L8 L9 LA| 111 // |LB LC | 112 // 113 val layout = 114 getLayout( 115 text = "\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006A\u006B\u006C", 116 fontSize = 10, 117 width = 50, 118 dir = LTR 119 ) 120 121 val offset = 5 // before L6 122 // If insert LX to first line, it will be |L1 L2 L3 L4 L5 LX| 123 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 124 // If insert RX to first line, it will be |L1 L2 L3 L4 L5 RX| 125 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 126 // If insert LX to second line, it will be |LX L6 L7 L8 L9 LA| 127 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 128 // If insert RX to second line, it will be |RX L6 L7 L8 L9 LA| 129 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 130 } 131 132 @Test getHorizontal_LTRText_LTRParagraph_LastOffsetnull133 fun getHorizontal_LTRText_LTRParagraph_LastOffset() { 134 // The line break happens like as follows 135 // 136 // input (Logical): L1 L2 L3 L4 L5 L6 L7 L8 L9 LA LB LC 137 // 138 // |L1 L2 L3 L4 L5| 139 // |L6 L7 L8 L9 LA| 140 // |LB LC | 141 // 142 val layout = 143 getLayout( 144 text = "\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006A\u006B\u006C", 145 fontSize = 10, 146 width = 50, 147 dir = LTR 148 ) 149 150 val offset = layout.layout.text.length // after LC 151 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(20) 152 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(20) 153 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(20) 154 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(20) 155 } 156 157 @Test getHorizontal_LTRText_LTRParagraph_Othernull158 fun getHorizontal_LTRText_LTRParagraph_Other() { 159 // The line break happens like as follows 160 // 161 // input (Logical): L1 L2 L3 L4 L5 L6 L7 L8 L9 LA LB LC 162 // 163 // |L1 L2 L3 L4 L5| 164 // |L6 L7 L8 L9 LA| 165 // |LB LC | 166 // 167 val layout = 168 getLayout( 169 text = "\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006A\u006B\u006C", 170 fontSize = 10, 171 width = 50, 172 dir = LTR 173 ) 174 175 val offset = 7 // before L8 176 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(20) 177 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(20) 178 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(20) 179 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(20) 180 } 181 182 @Test getHorizontal_LTRText_RTLParagraph_FirstCharacternull183 fun getHorizontal_LTRText_RTLParagraph_FirstCharacter() { 184 // The line break happens like as follows 185 // 186 // input (Logical): L1 L2 L3 L4 L5 L6 L7 L8 L9 LA LB LC 187 // 188 // |L1 L2 L3 L4 L5| 189 // |L6 L7 L8 L9 LA| 190 // | LB LC| 191 // 192 val layout = 193 getLayout( 194 text = "\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006A\u006B\u006C", 195 fontSize = 10, 196 width = 50, 197 dir = RTL 198 ) 199 200 val offset = 0 // before L1 201 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 202 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 203 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 204 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 205 } 206 207 @Test getHorizontal_LTRText_RTLParagraph_LineBreakOffsetnull208 fun getHorizontal_LTRText_RTLParagraph_LineBreakOffset() { 209 // The line break happens like as follows 210 // 211 // input (Logical): L1 L2 L3 L4 L5 L6 L7 L8 L9 LA LB LC 212 // 213 // |L1 L2 L3 L4 L5| 214 // |L6 L7 L8 L9 LA| 215 // | LB LC| 216 // 217 val layout = 218 getLayout( 219 text = "\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006A\u006B\u006C", 220 fontSize = 10, 221 width = 50, 222 dir = RTL 223 ) 224 225 val offset = 5 // before L6 == after L5 226 // If insert RX to the first line, it will be |RX L1 L2 L3 L4 L5| 227 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 228 // If insert LX to the first line, it will be |L1 L2 L3 L4 L5 LX| 229 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 230 // If insert RX to the second line, it will be |L6 L7 L8 L9 LA RX| 231 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 232 // If insert LX to the second line, it will be |LX L6 L7 L8 L9 LA| 233 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 234 } 235 236 @Test getHorizontal_LTRText_RTLParagraph_LastOffsetnull237 fun getHorizontal_LTRText_RTLParagraph_LastOffset() { 238 // The line break happens like as follows 239 // 240 // input (Logical): L1 L2 L3 L4 L5 L6 L7 L8 L9 LA LB LC 241 // 242 // |L1 L2 L3 L4 L5| 243 // |L6 L7 L8 L9 LA| 244 // | LB LC| 245 // 246 val layout = 247 getLayout( 248 text = "\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006A\u006B\u006C", 249 fontSize = 10, 250 width = 50, 251 dir = RTL 252 ) 253 254 val offset = layout.layout.text.length // after LC 255 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(30) 256 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 257 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(30) 258 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 259 } 260 261 @Test getHorizontal_LTRText_RTLParagraph_Othernull262 fun getHorizontal_LTRText_RTLParagraph_Other() { 263 // The line break happens like as follows 264 // 265 // input (Logical): L1 L2 L3 L4 L5 L6 L7 L8 L9 LA LB LC 266 // 267 // |L1 L2 L3 L4 L5| 268 // |L6 L7 L8 L9 LA| 269 // | LB LC| 270 // 271 val layout = 272 getLayout( 273 text = "\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006A\u006B\u006C", 274 fontSize = 10, 275 width = 50, 276 dir = RTL 277 ) 278 279 val offset = 7 // before L8 280 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(20) 281 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(20) 282 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(20) 283 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(20) 284 } 285 286 @Test getHorizontal_RTLText_RTLParagraph_FirstCharacternull287 fun getHorizontal_RTLText_RTLParagraph_FirstCharacter() { 288 // The line break happens like as follows 289 // 290 // input (Logical): R1 R2 R3 R4 R5 R6 R7 R8 R9 RA RB RC 291 // 292 // |R5 R4 R3 R2 R1| 293 // |RA R9 R8 R7 R6| 294 // | RC RB| 295 // 296 val layout = 297 getLayout( 298 text = "\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u05D7\u05D8\u05D9\u05DA\u05DB\u05DC", 299 fontSize = 10, 300 width = 50, 301 dir = RTL 302 ) 303 304 val offset = 0 // before R1 305 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 306 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 307 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 308 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 309 } 310 311 @Test getHorizontal_RTLText_RTLParagraph_LineBreakOffsetnull312 fun getHorizontal_RTLText_RTLParagraph_LineBreakOffset() { 313 // The line break happens like as follows 314 // 315 // input (Logical): R1 R2 R3 R4 R5 R6 R7 R8 R9 RA RB RC 316 // 317 // |R5 R4 R3 R2 R1| 318 // |RA R9 R8 R7 R6| 319 // | RC RB| 320 // 321 val layout = 322 getLayout( 323 text = "\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u05D7\u05D8\u05D9\u05DA\u05DB\u05DC", 324 fontSize = 10, 325 width = 50, 326 dir = RTL 327 ) 328 329 val offset = 5 // before R6 == after R5 330 // If insert RX to the first line, it will be |RX R5 R4 R3 R2 R1| 331 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 332 // If insert LX to the first line, it will be |LX R5 R4 R3 R2 R1| 333 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 334 // If insert RX to the second line, it will be |RA R9 R8 R7 R6 RX| 335 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 336 // If insert LX to the second line, it will be |RA R9 R8 R7 R6 LX| 337 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 338 } 339 340 @Test getHorizontal_RTLText_RTLParagraph_LastCharacternull341 fun getHorizontal_RTLText_RTLParagraph_LastCharacter() { 342 // The line break happens like as follows 343 // 344 // input (Logical): R1 R2 R3 R4 R5 R6 R7 R8 R9 RA RB RC 345 // 346 // |R5 R4 R3 R2 R1| 347 // |RA R9 R8 R7 R6| 348 // | RC RB| 349 // 350 val layout = 351 getLayout( 352 text = "\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u05D7\u05D8\u05D9\u05DA\u05DB\u05DC", 353 fontSize = 10, 354 width = 50, 355 dir = RTL 356 ) 357 358 val offset = layout.layout.text.length // after RC 359 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(30) 360 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(30) 361 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(30) 362 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(30) 363 } 364 365 @Test getHorizontal_RTLText_RTLParagraph_Othernull366 fun getHorizontal_RTLText_RTLParagraph_Other() { 367 // The line break happens like as follows 368 // 369 // input (Logical): R1 R2 R3 R4 R5 R6 R7 R8 R9 RA RB RC 370 // 371 // |R5 R4 R3 R2 R1| 372 // |RA R9 R8 R7 R6| 373 // | RC RB| 374 // 375 val layout = 376 getLayout( 377 text = "\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u05D7\u05D8\u05D9\u05DA\u05DB\u05DC", 378 fontSize = 10, 379 width = 50, 380 dir = RTL 381 ) 382 383 val offset = 7 // before R8 384 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(30) 385 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(30) 386 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(30) 387 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(30) 388 } 389 390 @Test getHorizontal_RTLText_LTRParagraph_FirstCharacternull391 fun getHorizontal_RTLText_LTRParagraph_FirstCharacter() { 392 // The line break happens like as follows 393 // 394 // input (Logical): R1 R2 R3 R4 R5 R6 R7 R8 R9 RA RB RC 395 // 396 // |R5 R4 R3 R2 R1| 397 // |RA R9 R8 R7 R6| 398 // |RC RB | 399 // 400 val layout = 401 getLayout( 402 text = "\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u05D7\u05D8\u05D9\u05DA\u05DB\u05DC", 403 fontSize = 10, 404 width = 50, 405 dir = LTR 406 ) 407 408 val offset = 0 // before R1 409 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 410 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 411 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 412 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 413 } 414 415 @Test getHorizontal_RTLText_LTRParagraph_LineBreakOffsetnull416 fun getHorizontal_RTLText_LTRParagraph_LineBreakOffset() { 417 // The line break happens like as follows 418 // 419 // input (Logical): R1 R2 R3 R4 R5 R6 R7 R8 R9 RA RB RC 420 // 421 // |R5 R4 R3 R2 R1| 422 // |RA R9 R8 R7 R6| 423 // |RC RB | 424 // 425 val layout = 426 getLayout( 427 text = "\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u05D7\u05D8\u05D9\u05DA\u05DB\u05DC", 428 fontSize = 10, 429 width = 50, 430 dir = LTR 431 ) 432 433 val offset = 5 // befroe R6 == after R5 434 // If insert LX to the first line, it will be |R5 R4 R3 R2 R1 LX| 435 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 436 // If insert RX to the first line, it will be |RX R5 R4 R3 R2 R1| 437 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 438 // If insert LX to the second line, it will be |LX RA R9 R8 R7 R6| 439 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 440 // If insert RX to the second line, it will be |RA R9 R8 R7 R6 RX| 441 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 442 } 443 444 @Test getHorizontal_RTLText_LTRParagraph_LastCharacternull445 fun getHorizontal_RTLText_LTRParagraph_LastCharacter() { 446 // The line break happens like as follows 447 // 448 // input (Logical): R1 R2 R3 R4 R5 R6 R7 R8 R9 RA RB RC 449 // 450 // |R5 R4 R3 R2 R1| 451 // |RA R9 R8 R7 R6| 452 // |RC RB | 453 // 454 val layout = 455 getLayout( 456 text = "\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u05D7\u05D8\u05D9\u05DA\u05DB\u05DC", 457 fontSize = 10, 458 width = 50, 459 dir = LTR 460 ) 461 462 val offset = layout.layout.text.length // after RB 463 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(20) 464 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 465 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(20) 466 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 467 } 468 469 @Test getHorizontal_RTLText_LTRParagraph_Othernull470 fun getHorizontal_RTLText_LTRParagraph_Other() { 471 // The line break happens like as follows 472 // 473 // input (Logical): R1 R2 R3 R4 R5 R6 R7 R8 R9 RA RB RC 474 // 475 // |R5 R4 R3 R2 R1| 476 // |RA R9 R8 R7 R6| 477 // |RC RB | 478 // 479 val layout = 480 getLayout( 481 text = "\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u05D7\u05D8\u05D9\u05DA\u05DB\u05DC", 482 fontSize = 10, 483 width = 50, 484 dir = LTR 485 ) 486 487 val offset = 7 // before R8 488 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(30) 489 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(30) 490 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(30) 491 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(30) 492 } 493 494 @Test getHorizontal_BidiText_BiDiTransitionNotLineBreakOffset_FromLTRToRTL_LTRParagraphnull495 fun getHorizontal_BidiText_BiDiTransitionNotLineBreakOffset_FromLTRToRTL_LTRParagraph() { 496 // The line break happens like as follows 497 // 498 // input (Logical): L1 R1 R2 R3 L2 L3 L5 L6 L7 L8 499 // 500 // |L1 R3 R2 R1 L2| 501 // |L3 L4 L5 L6 L7| 502 // |L8 L9 | 503 // 504 val layout = 505 getLayout( 506 text = "\u0061\u05D1\u05D2\u05D3\u0065\u0066\u0067\u0068\u0069\u006A\u006B\u006C", 507 fontSize = 10, 508 width = 50, 509 dir = LTR 510 ) 511 512 val offset = 1 // before R1 513 // If insert LX to first line, it will be |L1 LX R3 R2 R1 L2| 514 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(10) 515 // If insert RX to first line, it will be |L1 R3 R2 R1 RX L2| 516 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(40) 517 // If insert LX to first line, it will be |L1 LX R3 R2 R1 L2| 518 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(10) 519 // If insert RX to first line, it will be |L1 R3 R2 R1 RX L2| 520 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(40) 521 } 522 523 @Test getHorizontal_BidiText_BiDiTransitionNotLineBreakOffset_FromLTRToRTL_RTLParagraphnull524 fun getHorizontal_BidiText_BiDiTransitionNotLineBreakOffset_FromLTRToRTL_RTLParagraph() { 525 // The line break happens like as follows 526 // 527 // input (Logical): L1 R1 R2 R3 L2 L3 L5 L6 L7 L8 L9 528 // 529 // |L2 R3 R2 R1 L1| 530 // |L3 L4 L5 L6 L7| 531 // | L8 L9| 532 // 533 val layout = 534 getLayout( 535 text = "\u0061\u05D1\u05D2\u05D3\u0065\u0066\u0067\u0068\u0069\u006A\u006B\u006C", 536 fontSize = 10, 537 width = 50, 538 dir = RTL 539 ) 540 541 val offset = 1 // before R1 542 // If insert RX to first line, it will be |L2 R3 R2 R1 RX L1| 543 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(40) 544 // If insert LX to first line, it will be |L2 R3 R2 R1 L1 LX| 545 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 546 // If insert RX to first line, it will be |L2 R3 R2 R1 RX L1| 547 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(40) 548 // If insert LX to first line, it will be |L2 R3 R2 R1 L1 LX| 549 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 550 } 551 552 @Test getHorizontal_BidiText_BiDiTransitionNotLineBreakOffset_FromRTLtoLTR_RTLParagraphnull553 fun getHorizontal_BidiText_BiDiTransitionNotLineBreakOffset_FromRTLtoLTR_RTLParagraph() { 554 // The line break happens like as follows 555 // 556 // input (Logical): R1 L1 L2 L3 R2 R3 R4 R5 R6 R7 R8 R9 557 // 558 // |R2 L1 L2 L3 R1| 559 // |R7 R6 R5 R4 R3| 560 // | R9 R8| 561 // 562 val layout = 563 getLayout( 564 text = "\u05D0\u0061\u0062\u0063\u05D4\u05D5\u05D6\u05D7\u05D8\u05DA\u05DB\u05DC", 565 fontSize = 10, 566 width = 50, 567 dir = RTL 568 ) 569 570 val offset = 1 // before L1 571 // If insert RX to first line, it will be |R2 L1 L2 L3 RX R1| 572 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(40) 573 // If insert LX to first line, it will be |R2 LX L1 L2 L3 R1| 574 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(10) 575 // If insert RX to first line, it will be |R2 L1 L2 L3 RX R1| 576 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(40) 577 // If insert LX to first line, it will be |R2 LX L1 L2 L3 R1| 578 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(10) 579 } 580 581 @Test getHorizontal_BidiText_BiDiTransitionNotLineBreakOffset_FromRTLtoLTR_LTRParagraphnull582 fun getHorizontal_BidiText_BiDiTransitionNotLineBreakOffset_FromRTLtoLTR_LTRParagraph() { 583 // The line break happens like as follows 584 // 585 // input (Logical): R1 L1 L2 L3 R2 R3 R4 R5 R6 R7 R8 R9 586 // 587 // |R1 L1 L2 L3 R2| 588 // |R7 R6 R5 R4 R3| 589 // |R9 R8 | 590 // 591 val layout = 592 getLayout( 593 text = "\u05D0\u0061\u0062\u0063\u05D4\u05D5\u05D6\u05D7\u05D8\u05DA\u05DB\u05DC", 594 fontSize = 10, 595 width = 50, 596 dir = LTR 597 ) 598 599 val offset = 1 // before L1 600 // If insert LX to first line, it will be |R2 LX L1 L2 L3 R1| 601 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(10) 602 // If insert RX to first line, it will be |RX R1 L1 L2 L3 R2| 603 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 604 // If insert LX to first line, it will be |R2 LX L1 L2 L3 R1| 605 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(10) 606 // If insert RX to first line, it will be |RX R1 L1 L2 L3 R2| 607 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 608 } 609 610 @Test getHorizontal_BidiText_BiDiTransitionLineBreakOffset_FromLTRToRTL_LTRParagraphnull611 fun getHorizontal_BidiText_BiDiTransitionLineBreakOffset_FromLTRToRTL_LTRParagraph() { 612 // The line break happens like as follows 613 // 614 // input (Logical): L1 L2 L3 L4 L5 R1 R2 R3 R4 R5 R6 R7 615 // 616 // |L1 L2 L3 L4 L5| 617 // |R5 R4 R3 R2 R1| 618 // |R7 R6 | 619 // 620 val layout = 621 getLayout( 622 text = "\u0061\u0062\u0063\u0064\u0065\u05D5\u05D6\u05D7\u05D8\u05DA\u05DB\u05DC", 623 fontSize = 10, 624 width = 50, 625 dir = LTR 626 ) 627 628 val offset = 5 // before R1 == after L5 629 // If insert LX to first line, it will be |L1 L2 L3 L4 L5 LX| 630 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 631 // If insert RX to first line, it will be |L1 L2 L3 L4 L5 RX| 632 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 633 // If insert LX to second line, it will be |L1 R5 R4 R3 R2 R1| 634 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 635 // If insert RX to second line, it will be |R5 R4 R3 R2 R1 RX| 636 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 637 } 638 639 @Test getHorizontal_BidiText_BiDiTransitionLineBreakOffset_FromLTRToRTL_RTLParagraphnull640 fun getHorizontal_BidiText_BiDiTransitionLineBreakOffset_FromLTRToRTL_RTLParagraph() { 641 // The line break happens like as follows 642 // 643 // input (Logical): L1 L2 L3 L4 L5 R1 R2 R3 R4 R5 R6 R7 644 // 645 // |L1 L2 L3 L4 L5| 646 // |R5 R4 R3 R2 R1| 647 // | R7 R6| 648 // 649 val layout = 650 getLayout( 651 text = "\u0061\u0062\u0063\u0064\u0065\u05D5\u05D6\u05D7\u05D8\u05DA\u05DB\u05DC", 652 fontSize = 10, 653 width = 50, 654 dir = RTL 655 ) 656 657 val offset = 5 // before R1 == after L5 658 // If insert RX to first line, it will be |RX L1 L2 L3 L4 L5| 659 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 660 // If insert LX to first line, it will be |L1 L2 L3 L4 L5 LX| 661 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 662 // If insert RX to second line, it will be |R5 R4 R3 R2 R1 RX| 663 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 664 // If insert LX to second line, it will be |R5 R4 R3 R2 R1 LX| 665 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 666 } 667 668 @Test getHorizontal_BidiText_BiDiTransitionLineBreakOffset_FromRTLToLTR_RTLParagraphnull669 fun getHorizontal_BidiText_BiDiTransitionLineBreakOffset_FromRTLToLTR_RTLParagraph() { 670 // The line break happens like as follows 671 // 672 // input (Logical): R1 R2 R3 R4 R5 L1 L2 L3 L4 L5 L6 L7 673 // 674 // |R5 R4 R3 R2 R1| 675 // |L1 L2 L3 L4 L5| 676 // | L6 L7| 677 // 678 val layout = 679 getLayout( 680 text = "\u05D0\u05D1\u05D2\u05D3\u05D4\u0061\u0062\u0063\u0064\u0065\u0066\u0067", 681 fontSize = 10, 682 width = 50, 683 dir = RTL 684 ) 685 686 val offset = 5 // before L1 == after R5 687 // If insert RX to first line, it will be |RX R5 R4 R3 R2 R1| 688 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 689 // If insert LX to first line, it will be |LX R5 R4 R3 R2 R1| 690 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 691 // If insert RX to second line, it will be |L1 L2 L3 L4 L5 RX| 692 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 693 // If insert LX to second line, it will be |LX L1 L2 L3 L4 L5| 694 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 695 } 696 697 @Test getHorizontal_BidiText_BiDiTransitionLineBreakOffset_FromRTLToLTR_LTRParagraphnull698 fun getHorizontal_BidiText_BiDiTransitionLineBreakOffset_FromRTLToLTR_LTRParagraph() { 699 // The line break happens like as follows 700 // 701 // input (Logical): R1 R2 R3 R4 R5 L1 L2 L3 L4 L5 L6 L7 702 // 703 // |R5 R4 R3 R2 R1| 704 // |L1 L2 L3 L4 L5| 705 // |L6 L7 | 706 // 707 val layout = 708 getLayout( 709 text = "\u05D0\u05D1\u05D2\u05D3\u05D4\u0061\u0062\u0063\u0064\u0065\u0066\u0067", 710 fontSize = 10, 711 width = 50, 712 dir = LTR 713 ) 714 715 val offset = 5 // before L1 == after R5 716 // If insert LX to first line, it will be |R5 R4 R3 R2 R1 LX| 717 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 718 // If insert RX to first line, it will be |RX R5 R4 R3 R2 R1| 719 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 720 // If insert LX to second line, it will be |LX L1 L2 L3 L4 L5| 721 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 722 // If insert RX to second line, it will be |R1 L1 L2 L3 L4 L5| 723 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 724 } 725 726 @Test getHorizontal_BidiText_LineBreakOffset_FromMiddle_FromLTRToRTL_LTRParagraphnull727 fun getHorizontal_BidiText_LineBreakOffset_FromMiddle_FromLTRToRTL_LTRParagraph() { 728 // The line break happens like as follows 729 // 730 // input (Logical): L1 L2 R1 R2 R3 R4 R5 R6 R7 R8 R9 RA 731 // 732 // |L1 L2 R3 R2 R1| 733 // |R8 R7 R6 R5 R4| 734 // |RA R9 | 735 // 736 val layout = 737 getLayout( 738 "\u0061\u0062\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u05D7\u05D8\u05DA\u05DB", 739 10, 740 50, 741 LTR 742 ) 743 744 val offset = 5 // before R4 == after R3 745 // If insert LX to first line, it will be |L1 L2 R3 R2 R1 LX| 746 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 747 // If insert RX to first line, it will be |L1 L2 RX R3 R2 R1| 748 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(20) 749 // If insert LX to second line, it will be |LX R5 R7 R6 R5 R4| 750 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 751 // If insert RX to second line, it will be |R5 R7 R6 R5 R4 RX| 752 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 753 } 754 755 @Test getHorizontal_BidiText_LineBreakOffset_FromMiddle_FromLTRToRTL_RTLParagraphnull756 fun getHorizontal_BidiText_LineBreakOffset_FromMiddle_FromLTRToRTL_RTLParagraph() { 757 // The line break happens like as follows 758 // 759 // input (Logical): L1 L2 R1 R2 R3 R4 R5 R6 R7 R8 R9 RA 760 // 761 // |R3 R2 R1 L1 L2| 762 // |R8 R7 R6 R5 R4| 763 // | RA R9| 764 // 765 val layout = 766 getLayout( 767 "\u0061\u0062\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u05D7\u05D8\u05DA", 768 10, 769 50, 770 RTL 771 ) 772 773 val offset = 5 // before R4 == after R3 774 // If insert RX to first line, it will be |RX R3 R2 R1 L1 L2| 775 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 776 // If insert LX to first line, it will be |LX R3 R2 R1 L1 L2| 777 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 778 // If insert RX to second line, it will be |R8 R7 R6 R5 R4 RX| 779 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 780 // If insert LX to second line, it will be |R8 R7 R6 R5 R4 LX| 781 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 782 } 783 784 @Test getHorizontal_BidiText_LineBreakOffset_FromMiddle_FromRTLToLTR_LTRParagraphnull785 fun getHorizontal_BidiText_LineBreakOffset_FromMiddle_FromRTLToLTR_LTRParagraph() { 786 // The line break happens like as follows 787 // 788 // input (Logical): R1 R2 L1 L2 L3 L4 L5 L6 L7 L8 L9 LA 789 // 790 // |R2 R1 L1 L2 L3| 791 // |L4 L5 L6 L7 L8| 792 // |L9 RA | 793 // 794 val layout = 795 getLayout( 796 "\u05D0\u05D1\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006A", 797 10, 798 50, 799 LTR 800 ) 801 802 val offset = 5 // before L4 == after L3 803 // If insert LX to first line, it will be |R2 R1 L1 L2 L3 LX| 804 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 805 // If insert RX to first line, it will be |R2 R1 L1 L2 L3 RX| 806 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 807 // If insert LX to second line, it will be |LX L4 L5 L6 L7 L8| 808 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 809 // If insert RX to second line, it will be |RX L4 L5 L6 L7 L8| 810 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 811 } 812 813 @Test getHorizontal_BidiText_LineBreakOffset_FromMiddle_FromRTLToLTR_RTLParagraphnull814 fun getHorizontal_BidiText_LineBreakOffset_FromMiddle_FromRTLToLTR_RTLParagraph() { 815 // The line break happens like as follows 816 // 817 // input (Logical): R1 R2 L1 L2 L3 L4 L5 L6 L7 L8 L9 LA 818 // 819 // |L1 L2 L3 R2 R1| 820 // |L4 L5 L6 L7 L8| 821 // | L9 RA| 822 // 823 val layout = 824 getLayout( 825 "\u05D0\u05D1\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006A", 826 10, 827 50, 828 RTL 829 ) 830 831 val offset = 5 // before L4 == after L3 832 // If insert RX to first line, it will be |RX L1 L2 L3 R2 R1| 833 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 834 // If insert LX to first line, it will be |L1 L2 L3 L4 R2 R1| 835 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(30) 836 // If insert RX to second line, it will be |L4 L5 L6 L7 L8 RX| 837 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 838 // If insert LX to second line, it will be |LX L4 L5 L6 L7 L8| 839 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 840 } 841 842 @Test getHorizontal_BidiText_LineBreakOffset_ToMiddle_FromLTRToRTL_LTRParagraphnull843 fun getHorizontal_BidiText_LineBreakOffset_ToMiddle_FromLTRToRTL_LTRParagraph() { 844 // The line break happens like as follows 845 // 846 // input (Logical): L1 L2 L3 L4 L5 L6 L7 R1 R2 R3 R4 R5 847 // 848 // |L1 L2 L3 L4 L5| 849 // |L6 L7 R3 R2 R1| 850 // |R5 R4 | 851 // 852 val layout = 853 getLayout( 854 "\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u05D0\u05D1\u05D2\u05D3\u05D4", 855 10, 856 50, 857 LTR 858 ) 859 860 val offset = 5 // before L6 == after L5 861 // If insert LX to first line, it will be |L1 L2 L3 L4 L5 LX| 862 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 863 // If insert RX to first line, it will be |L1 L2 L3 L4 L5 RX| 864 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 865 // If insert LX to second line, it will be |LX L6 L7 R3 R2 R1| 866 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 867 // If insert RX to second line, it will be |RX L6 L7 R3 R2 R1| 868 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 869 } 870 871 @Test getHorizontal_BidiText_LineBreakOffset_ToMiddle_FromLTRToRTL_RTLParagraphnull872 fun getHorizontal_BidiText_LineBreakOffset_ToMiddle_FromLTRToRTL_RTLParagraph() { 873 // The line break happens like as follows 874 // 875 // input (Logical): L1 L2 L3 L4 L5 L6 L7 R1 R2 R3 R4 R5 876 // 877 // |L1 L2 L3 L4 L5| 878 // |R3 R2 R1 L6 L7| 879 // | R5 R4| 880 // 881 val layout = 882 getLayout( 883 "\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u05D0\u05D1\u05D2\u05D3\u05D4", 884 10, 885 50, 886 RTL 887 ) 888 889 val offset = 5 // before L6 == after L5 890 // If insert RX to first line, it will be |RX L1 L2 L3 L4 L5| 891 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 892 // If insert LX to first line, it will be |L1 L2 L3 L4 L5 LX| 893 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 894 // If insert RX to second line, it will be |R3 R2 R1 L6 L7 RX| 895 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 896 // If insert LX to second line, it will be |R3 R2 R1 LX L6 L7| 897 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(30) 898 } 899 900 @Test getHorizontal_BidiText_LineBreakOffset_ToMiddle_FromRTLToLTR_LTRParagraphnull901 fun getHorizontal_BidiText_LineBreakOffset_ToMiddle_FromRTLToLTR_LTRParagraph() { 902 // The line break happens like as follows 903 // 904 // input (Logical): R1 R2 R3 R4 R5 R6 R7 L1 L2 L3 L4 L5 905 // 906 // |R5 R4 R3 R2 R1| 907 // |R7 R6 L1 L2 L3| 908 // |L4 L5 | 909 // 910 val layout = 911 getLayout( 912 "\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u0061\u0062\u0063\u0064\u0065", 913 10, 914 50, 915 LTR 916 ) 917 918 val offset = 5 // before R6 == after R5 919 // If insert LX to first line, it will be |R5 R4 R3 R2 R1 LX| 920 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 921 // If insert RX to first line, it will be |RX R5 R4 R3 R2 R1| 922 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 923 // If insert LX to second line, it will be |LX R7 R6 L1 L2 L3| 924 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 925 // If insert RX to second line, it will be |R7 R6 RX L1 L2 L3| 926 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(20) 927 } 928 929 @Test getHorizontal_BidiText_LineBreakOffset_ToMiddle_FromRTLToLTR_RTLParagraphnull930 fun getHorizontal_BidiText_LineBreakOffset_ToMiddle_FromRTLToLTR_RTLParagraph() { 931 // The line break happens like as follows 932 // 933 // input (Logical): R1 R2 R3 R4 R5 R6 R7 L1 L2 L3 L4 L5 934 // 935 // |R5 R4 R3 R2 R1| 936 // |L1 L2 L3 R7 R6| 937 // | L4 L5| 938 // 939 val layout = 940 getLayout( 941 "\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5\u05D6\u0061\u0062\u0063\u0064\u0065", 942 10, 943 50, 944 RTL 945 ) 946 947 val offset = 5 // before R6 == after R5 948 // If insert RX to first line, it will be |RX R5 R4 R3 R2 R1| 949 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 950 // If insert LX to first line, it will be |LX R5 R4 R3 R2 R1| 951 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 952 // If insert RX to second line, it will be |L1 L2 L3 R7 R6 RX| 953 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 954 // If insert LX to second line, it will be |L1 L2 L3 R7 R6 LX| 955 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 956 } 957 958 @Test getHorizontal_BiDi_Whitspacenull959 fun getHorizontal_BiDi_Whitspace() { 960 // The line break happens like as follows 961 // 962 // input (Logical): R1 R2 SP R3 R4 R6 SP R7 R8 SP R9 RA 963 // 964 // |R4 R3 SP R2 R1| (SP) 965 // |L1 L2 SP L3 L4| (SP) 966 // |L5 L6 | 967 val layout = 968 getLayout( 969 "\u05D0\u05D1 \u05D2\u05D3 \u0061\u0062 \u0063\u0064 \u0065\u0066", 970 10, 971 50, 972 LTR 973 ) 974 975 val offset = 6 // before L1 == after SP 976 // If insert LX to first line, it will be |R4 R3 SP R2 R1 SP LX| 977 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 978 // If insert RX to first line, it will be |RX SP R4 R3 SP R2 R1| 979 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 980 // If insert LX to second line, it will be |LX L2 SP L3 L4| (SP) 981 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 982 // If insert RX to second line, it will be |RX L1 L2 SP L3 L4| (SP) 983 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 984 } 985 986 @Test getHorizontal_BiDi_singleLine_Whitespacenull987 fun getHorizontal_BiDi_singleLine_Whitespace() { 988 // The line break happens like as follows 989 // 990 // input (Logical): R1 R2 SP L1 L2 SP L3 L4 SP R3 R4 SP L5 L6 991 // 992 // |L1 L2 SP R2 R1| (SP) 993 // |L3 L4 SP R4 R3| (SP) 994 // |L5 L6 | 995 val layout = 996 getLayout( 997 "\u05D0\u05D1 \u0061\u0062 \u0063\u0064 \u05D3\u05D4 \u0065\u0066", 998 10, 999 50, 1000 LTR 1001 ) 1002 1003 val offset = 6 // before L3 == after SP 1004 // If insert LX to first line, it will be |L1 L2 SP R2 R1 SP LX| 1005 assertThat(layout.getUpstreamPrimaryHorizontalPosition(offset)).isEqualTo(50) 1006 // If insert RX to first line, it will be |L1 L2 SP R2 R1 SP RX| 1007 assertThat(layout.getUpstreamSecondaryHorizontalPosition(offset)).isEqualTo(50) 1008 // If insert LX to second line, it will be |LX L3 L4 SP R4 R3| (SP) 1009 assertThat(layout.getDownstreamPrimaryHorizontalPosition(offset)).isEqualTo(0) 1010 // If insert RX to second line, it will be |RX L3 L4 SP R4 R3| (SP) 1011 assertThat(layout.getDownstreamSecondaryHorizontalPosition(offset)).isEqualTo(0) 1012 } 1013 } 1014