private int getLogicalColumn(int startLogicalColumn, int offset) {
   assert offset >= startOffset;
   assert offset <= endOffset;
   int currentStartOffset = startOffset;
   for (LineFragment fragment : fragments) {
     int currentEndOffset = currentStartOffset + fragment.getLength();
     startLogicalColumn =
         fragment.offsetToLogicalColumn(
             startLogicalColumn, Math.min(offset, currentEndOffset) - currentStartOffset);
     currentStartOffset = currentEndOffset;
     if (offset <= currentStartOffset) break;
   }
   return startLogicalColumn;
 }
 // column is expected to be between startVisualColumn and endVisualColumn for this fragment
 int visualToLogicalColumn(int column) {
   int relativeLogicalColumn =
       delegate.visualToLogicalColumn(startX, getMinLogicalColumn(), column - startVisualColumn);
   return isRtl
       ? startLogicalColumn - relativeLogicalColumn
       : startLogicalColumn + relativeLogicalColumn;
 }
 // column is expected to be between minLogicalColumn and maxLogicalColumn for this fragment
 int logicalToVisualColumn(int column) {
   return startVisualColumn
       + delegate.logicalToVisualColumn(
           startX,
           getMinLogicalColumn(),
           isRtl ? startLogicalColumn - column : column - startLogicalColumn);
 }
 private BidiRun subRun(int targetStartOffset, int targetEndOffset) {
   assert targetStartOffset < endOffset;
   assert targetEndOffset > startOffset;
   if (targetStartOffset <= startOffset && targetEndOffset >= this.endOffset) {
     return this;
   }
   int start = Math.max(startOffset, targetStartOffset);
   int end = Math.min(endOffset, targetEndOffset);
   BidiRun run = new BidiRun(level, start, end);
   int offset = startOffset;
   for (LineFragment fragment : fragments) {
     if (end <= offset) break;
     int endOffset = offset + fragment.getLength();
     if (start < endOffset) {
       run.fragments.add(
           fragment.subFragment(
               Math.max(start, offset) - offset, Math.min(end, endOffset) - offset));
     }
     offset = endOffset;
   }
   return run;
 }
 // columns are visual (relative to fragment's start)
 void draw(Graphics2D g, float x, float y, int startRelativeColumn, int endRelativeColumn) {
   delegate.draw(g, x, y, startRelativeColumn, endRelativeColumn);
 }
 void draw(Graphics2D g, float x, float y) {
   delegate.draw(g, x, y, 0, getLength());
 }
 // column is expected to be between startVisualColumn and endVisualColumn for this fragment
 float visualColumnToX(int column) {
   return delegate.visualColumnToX(startX, column - startVisualColumn);
 }
 // x is expected to be between startX and endX for this fragment
 // returns array of two elements
 // - first one is visual column,
 // - second one is 1 if target location is closer to larger columns and 0 otherwise
 int[] xToVisualColumn(float x) {
   int[] column = delegate.xToVisualColumn(startX, x);
   column[0] += startVisualColumn;
   return column;
 }
 // both startOffset and offset are expected to be between minOffset and maxOffset for this
 // fragment
 float offsetToX(float startX, int startOffset, int offset) {
   return delegate.offsetToX(startX, getRelativeOffset(startOffset), getRelativeOffset(offset));
 }
 // offset is expected to be between minOffset and maxOffset for this fragment
 float offsetToX(int offset) {
   return delegate.offsetToX(startX, 0, getRelativeOffset(offset));
 }
 float getEndX() {
   return delegate.offsetToX(startX, 0, getLength());
 }
 int getVisualColumnCount() {
   return delegate.getVisualColumnCount(startX);
 }
 int getLogicalColumnCount() {
   return isRtl ? getLength() : delegate.getLogicalColumnCount(getMinLogicalColumn());
 }
 int getLength() {
   return delegate.getLength();
 }