fix: 折り返し表示時、特定条件で例外が発生
折り返し表示時で、描画トークンが描画領域右端にオーバーラップするとき、
その中で画面上に表示されない後半の文字を切り捨てる処理がある。
この処理において、切り捨て位置を描画トークンの内容ではなく Document
の内容から判断していた。その結果、切断位置が描画トークンの長さよりも
後ろになることがあり、そのような場合に ArgumentOutOfRangeException
を発生することとなっていた。
本件はフォーラム #42910 で報告いただいた不具合の修正となる (Special
Thanks: @kkato233)。また、trunk (master) でのみ顕在化する不具合であり
1.7 以前のバージョンでは発生しない。
https://github.com/sgryjp/azuki/commit/fa4b3e461b4f521e6955bd45fe7fd0bf55054db3
| @@ -491,6 +491,21 @@ | ||
| 491 | 491 | return index; |
| 492 | 492 | } |
| 493 | 493 | |
| 494 | + public static int NextGraphemeClusterIndex(string text, int index) | |
| 495 | + { | |
| 496 | + Debug.Assert(text != null); | |
| 497 | + Debug.Assert(0 <= index); | |
| 498 | + Debug.Assert(index < text.Length); | |
| 499 | + | |
| 500 | + do | |
| 501 | + { | |
| 502 | + index++; | |
| 503 | + } | |
| 504 | + while (index < text.Length && IsUndividableIndex(text, index)); | |
| 505 | + | |
| 506 | + return index; | |
| 507 | + } | |
| 508 | + | |
| 494 | 509 | public static int PrevGraphemeClusterIndex( IList<char> text, int index ) |
| 495 | 510 | { |
| 496 | 511 | Debug.Assert( text != null ); |
| @@ -731,19 +731,23 @@ | ||
| 731 | 731 | // (if the clip-rect's right boundary is NOT the text area's right boundary, |
| 732 | 732 | // we must write one more char so that the peeking char appears at the boundary.) |
| 733 | 733 | |
| 734 | - // try to get graphically peeking (drawn over the border line) char | |
| 735 | - var peekingChar = new TextSegment( visCharCount, TextUtil.NextGraphemeClusterIndex(Document.InternalBuffer, visCharCount) ); | |
| 734 | + // find the graphically peeking character at the right end of the view | |
| 735 | + // and discard characters following it | |
| 736 | + if( visCharCount < token.Length ) | |
| 737 | + { | |
| 738 | + var peekingChar = new TextSegment( visCharCount, TextUtil.NextGraphemeClusterIndex(token, visCharCount) ); | |
| 736 | 739 | |
| 737 | - // calculate right end coordinate of the peeking char | |
| 738 | - if( peekingChar.IsEmpty == false ) | |
| 739 | - { | |
| 740 | - peekingCharRight = MeasureTokenEndX( g, peekingChar, visPartRight ); | |
| 740 | + // calculate right end coordinate of the peeking char | |
| 741 | + if( peekingChar.IsEmpty == false ) | |
| 742 | + { | |
| 743 | + peekingCharRight = MeasureTokenEndX( g, peekingChar, visPartRight ); | |
| 744 | + } | |
| 745 | + | |
| 746 | + // cut trailing extra | |
| 747 | + token = token.Substring( 0, visCharCount+peekingChar.Length ); | |
| 748 | + tokenEndPos.X = (peekingCharRight != 0) ? peekingCharRight : visPartRight; | |
| 741 | 749 | } |
| 742 | 750 | |
| 743 | - // cut trailing extra | |
| 744 | - token = token.Substring( 0, visCharCount+peekingChar.Length ); | |
| 745 | - tokenEndPos.X = (peekingCharRight != 0) ? peekingCharRight : visPartRight; | |
| 746 | - | |
| 747 | 751 | // to terminate this loop, set token end position to invalid one |
| 748 | 752 | end = Int32.MaxValue; |
| 749 | 753 | } |