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