14ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta/* 24ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * Copyright (C) 2014 The Android Open Source Project 34ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * 44ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License"); 54ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * you may not use this file except in compliance with the License. 64ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * You may obtain a copy of the License at 74ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * 84ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * http://www.apache.org/licenses/LICENSE-2.0 94ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * 104ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * Unless required by applicable law or agreed to in writing, software 114ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS, 124ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * See the License for the specific language governing permissions and 144ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * limitations under the License. 154ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta */ 164ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 174ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptapackage android.text; 184ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 19442aee6bc1abfb143dcfa1ba60d696e576d066c4Deepanshu Guptaimport android.annotation.NonNull; 204ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport android.text.Primitive.PrimitiveType; 214ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport android.text.StaticLayout.LineBreaks; 224ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 234ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport java.util.ArrayList; 244ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport java.util.List; 254ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport java.util.ListIterator; 264ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 274ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport static android.text.Primitive.PrimitiveType.PENALTY_INFINITY; 284ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 294ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 304ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta// Based on the native implementation of OptimizingLineBreaker in 314ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 324ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta/** 334ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * A more complex version of line breaking where we try to prevent the right edge from being too 344ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * jagged. 354ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta */ 364ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptapublic class OptimizingLineBreaker extends LineBreaker { 374ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 384ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta public OptimizingLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth, 394ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta @NonNull TabStops tabStops) { 404ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta super(primitives, lineWidth, tabStops); 414ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 424ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 434ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta @Override 444ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta public void computeBreaks(@NonNull LineBreaks breakInfo) { 454ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int numBreaks = mPrimitives.size(); 464ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta assert numBreaks > 0; 474ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (numBreaks == 1) { 484ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta // This can be true only if it's an empty paragraph. 494ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta Primitive p = mPrimitives.get(0); 504ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta assert p.type == PrimitiveType.PENALTY; 514ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta breakInfo.breaks = new int[]{0}; 524ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta breakInfo.widths = new float[]{p.width}; 53ccbd4123e2a020629a2a0eb932d0bbd8c7224c0fRaph Levien breakInfo.flags = new int[]{0}; 544ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta return; 554ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 564ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta Node[] opt = new Node[numBreaks]; 574ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta opt[0] = new Node(-1, 0, 0, 0, false); 584ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta opt[numBreaks - 1] = new Node(-1, 0, 0, 0, false); 594ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 604ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta ArrayList<Integer> active = new ArrayList<Integer>(); 614ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta active.add(0); 624ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int lastBreak = 0; 634ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta for (int i = 0; i < numBreaks; i++) { 644ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta Primitive p = mPrimitives.get(i); 654ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (p.type == PrimitiveType.PENALTY) { 664ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta boolean finalBreak = (i + 1 == numBreaks); 674ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta Node bestBreak = null; 684ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 694ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta for (ListIterator<Integer> it = active.listIterator(); it.hasNext(); 704ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta /* incrementing done in loop */) { 714ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int pos = it.next(); 724ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int lines = opt[pos].mPrevCount; 734ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta float maxWidth = mLineWidth.getLineWidth(lines); 744ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta // we have to compute metrics every time -- 754ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta // we can't really pre-compute this stuff and just deal with breaks 764ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta // because of the way tab characters work, this makes it computationally 774ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta // harder, but this way, we can still optimize while treating tab characters 784ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta // correctly 794ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta LineMetrics lineMetrics = computeMetrics(pos, i); 804ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (lineMetrics.mPrintedWidth <= maxWidth) { 814ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta float demerits = computeDemerits(maxWidth, lineMetrics.mPrintedWidth, 824ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta finalBreak, p.penalty) + opt[pos].mDemerits; 834ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (bestBreak == null || demerits < bestBreak.mDemerits) { 844ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (bestBreak == null) { 854ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta bestBreak = new Node(pos, opt[pos].mPrevCount + 1, demerits, 864ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta lineMetrics.mPrintedWidth, lineMetrics.mHasTabs); 874ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } else { 884ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta bestBreak.mPrev = pos; 894ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta bestBreak.mPrevCount = opt[pos].mPrevCount + 1; 904ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta bestBreak.mDemerits = demerits; 914ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta bestBreak.mWidth = lineMetrics.mPrintedWidth; 924ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta bestBreak.mHasTabs = lineMetrics.mHasTabs; 934ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 944ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 954ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } else { 964ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta it.remove(); 974ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 984ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 994ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (p.penalty == -PENALTY_INFINITY) { 1004ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta active.clear(); 1014ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1024ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (bestBreak != null) { 1034ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta opt[i] = bestBreak; 1044ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta active.add(i); 1054ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta lastBreak = i; 1064ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1074ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (active.isEmpty()) { 1084ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta // we can't give up! 1094ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta LineMetrics lineMetrics = new LineMetrics(); 1104ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int lines = opt[lastBreak].mPrevCount; 1114ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta float maxWidth = mLineWidth.getLineWidth(lines); 1124ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int breakIndex = desperateBreak(lastBreak, numBreaks, maxWidth, lineMetrics); 1134ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta opt[breakIndex] = new Node(lastBreak, lines + 1, 0 /*doesn't matter*/, 1144ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta lineMetrics.mWidth, lineMetrics.mHasTabs); 1154ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta active.add(breakIndex); 1164ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta lastBreak = breakIndex; 1174ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta i = breakIndex; // incremented by i++ 1184ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1194ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1204ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1214ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 1224ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int idx = numBreaks - 1; 1234ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int count = opt[idx].mPrevCount; 1244ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta resize(breakInfo, count); 1254ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta while (opt[idx].mPrev != -1) { 1264ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta count--; 1274ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta assert count >=0; 1284ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 1294ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta breakInfo.breaks[count] = mPrimitives.get(idx).location; 1304ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta breakInfo.widths[count] = opt[idx].mWidth; 131ccbd4123e2a020629a2a0eb932d0bbd8c7224c0fRaph Levien breakInfo.flags [count] = opt[idx].mHasTabs ? TAB_MASK : 0; 1324ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta idx = opt[idx].mPrev; 1334ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1344ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1354ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 1364ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta private static void resize(LineBreaks lineBreaks, int size) { 1374ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (lineBreaks.breaks.length == size) { 1384ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta return; 1394ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1404ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int[] breaks = new int[size]; 1414ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta float[] widths = new float[size]; 142ccbd4123e2a020629a2a0eb932d0bbd8c7224c0fRaph Levien int[] flags = new int[size]; 1434ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 1444ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int toCopy = Math.min(size, lineBreaks.breaks.length); 1454ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta System.arraycopy(lineBreaks.breaks, 0, breaks, 0, toCopy); 1464ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta System.arraycopy(lineBreaks.widths, 0, widths, 0, toCopy); 1474ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta System.arraycopy(lineBreaks.flags, 0, flags, 0, toCopy); 1484ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 1494ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta lineBreaks.breaks = breaks; 1504ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta lineBreaks.widths = widths; 1514ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta lineBreaks.flags = flags; 1524ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1534ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 1544ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta @NonNull 1554ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta private LineMetrics computeMetrics(int start, int end) { 1564ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta boolean f = false; 1574ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta float w = 0, pw = 0; 1584ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta for (int i = start; i < end; i++) { 1594ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta Primitive p = mPrimitives.get(i); 1604ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) { 1614ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta w += p.width; 1624ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (p.type == PrimitiveType.BOX) { 1634ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta pw = w; 1644ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1654ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } else if (p.type == PrimitiveType.VARIABLE) { 1664ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta w = mTabStops.width(w); 1674ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta f = true; 1684ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1694ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1704ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta return new LineMetrics(w, pw, f); 1714ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1724ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 1734ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta private static float computeDemerits(float maxWidth, float width, boolean finalBreak, 1744ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta float penalty) { 1754ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta float deviation = finalBreak ? 0 : maxWidth - width; 1764ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta return (deviation * deviation) + penalty; 1774ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1784ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 1794ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta /** 1804ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * @return the last break position or -1 if failed. 1814ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta */ 1824ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta @SuppressWarnings("ConstantConditions") // method too complex to be analyzed. 1834ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta private int desperateBreak(int start, int limit, float maxWidth, 1844ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta @NonNull LineMetrics lineMetrics) { 1854ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta float w = 0, pw = 0; 1864ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta boolean breakFound = false; 1874ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int breakIndex = 0, firstTabIndex = Integer.MAX_VALUE; 1884ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta for (int i = start; i < limit; i++) { 1894ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta Primitive p = mPrimitives.get(i); 1904ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 1914ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) { 1924ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta w += p.width; 1934ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (p.type == PrimitiveType.BOX) { 1944ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta pw = w; 1954ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1964ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } else if (p.type == PrimitiveType.VARIABLE) { 1974ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta w = mTabStops.width(w); 1984ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta firstTabIndex = Math.min(firstTabIndex, i); 1994ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 2004ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 2014ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (pw > maxWidth && breakFound) { 2024ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta break; 2034ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 2044ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 2054ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta // must make progress 2064ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (i > start && 2074ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta (p.type == PrimitiveType.PENALTY || p.type == PrimitiveType.WORD_BREAK)) { 2084ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta breakFound = true; 2094ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta breakIndex = i; 2104ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 2114ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 2124ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 2134ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (breakFound) { 2144ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta lineMetrics.mWidth = w; 2154ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta lineMetrics.mPrintedWidth = pw; 2164ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta lineMetrics.mHasTabs = (start <= firstTabIndex && firstTabIndex < breakIndex); 2174ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta return breakIndex; 2184ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } else { 2194ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta return -1; 2204ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 2214ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 2224ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 2234ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta private static class LineMetrics { 2244ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta /** Actual width of the line. */ 2254ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta float mWidth; 2264ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta /** Width of the line minus trailing whitespace. */ 2274ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta float mPrintedWidth; 2284ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta boolean mHasTabs; 2294ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 2304ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta public LineMetrics() { 2314ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 2324ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 2334ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta public LineMetrics(float width, float printedWidth, boolean hasTabs) { 2344ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta mWidth = width; 2354ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta mPrintedWidth = printedWidth; 2364ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta mHasTabs = hasTabs; 2374ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 2384ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 2394ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 2404ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta /** 2414ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * A struct to store the info about a break. 2424ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta */ 2434ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta @SuppressWarnings("SpellCheckingInspection") // For the word struct. 2444ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta private static class Node { 2454ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta // -1 for the first node. 2464ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int mPrev; 2474ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta // number of breaks so far. 2484ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int mPrevCount; 2494ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta float mDemerits; 2504ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta float mWidth; 2514ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta boolean mHasTabs; 2524ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 2534ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta public Node(int prev, int prevCount, float demerits, float width, boolean hasTabs) { 2544ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta mPrev = prev; 2554ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta mPrevCount = prevCount; 2564ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta mDemerits = demerits; 2574ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta mWidth = width; 2584ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta mHasTabs = hasTabs; 2594ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 2604ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 2614ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta} 262