17053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta/*
27053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta * Copyright (C) 2014 The Android Open Source Project
37053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta *
47053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License");
57053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta * you may not use this file except in compliance with the License.
67053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta * You may obtain a copy of the License at
77053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta *
87053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta *      http://www.apache.org/licenses/LICENSE-2.0
97053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta *
107053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta * Unless required by applicable law or agreed to in writing, software
117053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS,
127053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta * See the License for the specific language governing permissions and
147053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta * limitations under the License.
157053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta */
167053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
177053919a3a55ad1b58e6db701afda18850037732Deepanshu Guptapackage android.text;
187053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
19476e582d2ffdf25102d4c55f8c242baa3d21d37fDeepanshu Guptaimport android.annotation.NonNull;
207053919a3a55ad1b58e6db701afda18850037732Deepanshu Guptaimport android.text.Primitive.PrimitiveType;
217053919a3a55ad1b58e6db701afda18850037732Deepanshu Guptaimport android.text.StaticLayout.LineBreaks;
227053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
237053919a3a55ad1b58e6db701afda18850037732Deepanshu Guptaimport java.util.ArrayList;
247053919a3a55ad1b58e6db701afda18850037732Deepanshu Guptaimport java.util.List;
257053919a3a55ad1b58e6db701afda18850037732Deepanshu Guptaimport java.util.ListIterator;
267053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
277053919a3a55ad1b58e6db701afda18850037732Deepanshu Guptaimport static android.text.Primitive.PrimitiveType.PENALTY_INFINITY;
287053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
297053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
307053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta// Based on the native implementation of OptimizingLineBreaker in
317053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
327053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta/**
337053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta * A more complex version of line breaking where we try to prevent the right edge from being too
347053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta * jagged.
357053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta */
367053919a3a55ad1b58e6db701afda18850037732Deepanshu Guptapublic class OptimizingLineBreaker extends LineBreaker {
377053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
387053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    public OptimizingLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth,
397053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            @NonNull TabStops tabStops) {
407053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        super(primitives, lineWidth, tabStops);
417053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    }
427053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
437053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    @Override
447053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    public void computeBreaks(@NonNull LineBreaks breakInfo) {
457053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        int numBreaks = mPrimitives.size();
467053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        assert numBreaks > 0;
477053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        if (numBreaks == 1) {
487053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            // This can be true only if it's an empty paragraph.
497053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            Primitive p = mPrimitives.get(0);
507053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            assert p.type == PrimitiveType.PENALTY;
517053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            breakInfo.breaks = new int[]{0};
527053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            breakInfo.widths = new float[]{p.width};
5326d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien            breakInfo.flags = new int[]{0};
547053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            return;
557053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        }
567053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        Node[] opt = new Node[numBreaks];
577053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        opt[0] = new Node(-1, 0, 0, 0, false);
587053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        opt[numBreaks - 1] = new Node(-1, 0, 0, 0, false);
597053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
607053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        ArrayList<Integer> active = new ArrayList<Integer>();
617053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        active.add(0);
627053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        int lastBreak = 0;
637053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        for (int i = 0; i < numBreaks; i++) {
647053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            Primitive p = mPrimitives.get(i);
657053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            if (p.type == PrimitiveType.PENALTY) {
667053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                boolean finalBreak = (i + 1 == numBreaks);
677053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                Node bestBreak = null;
687053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
697053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                for (ListIterator<Integer> it = active.listIterator(); it.hasNext();
707053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                        /* incrementing done in loop */) {
717053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    int pos = it.next();
727053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    int lines = opt[pos].mPrevCount;
737053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    float maxWidth = mLineWidth.getLineWidth(lines);
747053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    // we have to compute metrics every time --
757053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    // we can't really pre-compute this stuff and just deal with breaks
767053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    // because of the way tab characters work, this makes it computationally
777053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    // harder, but this way, we can still optimize while treating tab characters
787053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    // correctly
797053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    LineMetrics lineMetrics = computeMetrics(pos, i);
807053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    if (lineMetrics.mPrintedWidth <= maxWidth) {
817053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                        float demerits = computeDemerits(maxWidth, lineMetrics.mPrintedWidth,
827053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                                finalBreak, p.penalty) + opt[pos].mDemerits;
837053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                        if (bestBreak == null || demerits < bestBreak.mDemerits) {
847053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                            if (bestBreak == null) {
857053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                                bestBreak = new Node(pos, opt[pos].mPrevCount + 1, demerits,
867053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                                        lineMetrics.mPrintedWidth, lineMetrics.mHasTabs);
877053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                            } else {
887053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                                bestBreak.mPrev = pos;
897053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                                bestBreak.mPrevCount = opt[pos].mPrevCount + 1;
907053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                                bestBreak.mDemerits = demerits;
917053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                                bestBreak.mWidth = lineMetrics.mPrintedWidth;
927053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                                bestBreak.mHasTabs = lineMetrics.mHasTabs;
937053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                            }
947053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                        }
957053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    } else {
967053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                        it.remove();
977053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    }
987053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                }
997053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                if (p.penalty == -PENALTY_INFINITY) {
1007053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    active.clear();
1017053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                }
1027053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                if (bestBreak != null) {
1037053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    opt[i] = bestBreak;
1047053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    active.add(i);
1057053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    lastBreak = i;
1067053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                }
1077053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                if (active.isEmpty()) {
1087053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    // we can't give up!
1097053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    LineMetrics lineMetrics = new LineMetrics();
1107053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    int lines = opt[lastBreak].mPrevCount;
1117053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    float maxWidth = mLineWidth.getLineWidth(lines);
1127053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    int breakIndex = desperateBreak(lastBreak, numBreaks, maxWidth, lineMetrics);
1137053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    opt[breakIndex] = new Node(lastBreak, lines + 1, 0 /*doesn't matter*/,
1147053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                            lineMetrics.mWidth, lineMetrics.mHasTabs);
1157053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    active.add(breakIndex);
1167053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    lastBreak = breakIndex;
1177053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    i = breakIndex; // incremented by i++
1187053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                }
1197053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            }
1207053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        }
1217053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
1227053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        int idx = numBreaks - 1;
1237053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        int count = opt[idx].mPrevCount;
1247053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        resize(breakInfo, count);
1257053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        while (opt[idx].mPrev != -1) {
1267053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            count--;
1277053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            assert count >=0;
1287053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
1297053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            breakInfo.breaks[count] = mPrimitives.get(idx).location;
1307053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            breakInfo.widths[count] = opt[idx].mWidth;
13126d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien            breakInfo.flags [count] = opt[idx].mHasTabs ? TAB_MASK : 0;
1327053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            idx = opt[idx].mPrev;
1337053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        }
1347053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    }
1357053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
1367053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    private static void resize(LineBreaks lineBreaks, int size) {
1377053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        if (lineBreaks.breaks.length == size) {
1387053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            return;
1397053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        }
1407053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        int[] breaks = new int[size];
1417053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        float[] widths = new float[size];
14226d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        int[] flags = new int[size];
1437053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
1447053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        int toCopy = Math.min(size, lineBreaks.breaks.length);
1457053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        System.arraycopy(lineBreaks.breaks, 0, breaks, 0, toCopy);
1467053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        System.arraycopy(lineBreaks.widths, 0, widths, 0, toCopy);
1477053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        System.arraycopy(lineBreaks.flags, 0, flags, 0, toCopy);
1487053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
1497053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        lineBreaks.breaks = breaks;
1507053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        lineBreaks.widths = widths;
1517053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        lineBreaks.flags = flags;
1527053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    }
1537053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
1547053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    @NonNull
1557053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    private LineMetrics computeMetrics(int start, int end) {
1567053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        boolean f = false;
1577053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        float w = 0, pw = 0;
1587053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        for (int i = start; i < end; i++) {
1597053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            Primitive p = mPrimitives.get(i);
1607053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) {
1617053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                w += p.width;
1627053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                if (p.type == PrimitiveType.BOX) {
1637053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    pw = w;
1647053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                }
1657053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            } else if (p.type == PrimitiveType.VARIABLE) {
1667053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                w = mTabStops.width(w);
1677053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                f = true;
1687053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            }
1697053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        }
1707053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        return new LineMetrics(w, pw, f);
1717053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    }
1727053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
1737053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    private static float computeDemerits(float maxWidth, float width, boolean finalBreak,
1747053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            float penalty) {
1757053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        float deviation = finalBreak ? 0 : maxWidth - width;
1767053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        return (deviation * deviation) + penalty;
1777053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    }
1787053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
1797053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    /**
1807053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta     * @return the last break position or -1 if failed.
1817053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta     */
1827053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    @SuppressWarnings("ConstantConditions")  // method too complex to be analyzed.
1837053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    private int desperateBreak(int start, int limit, float maxWidth,
1847053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            @NonNull LineMetrics lineMetrics) {
1857053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        float w = 0, pw = 0;
1867053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        boolean breakFound = false;
1877053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        int breakIndex = 0, firstTabIndex = Integer.MAX_VALUE;
1887053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        for (int i = start; i < limit; i++) {
1897053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            Primitive p = mPrimitives.get(i);
1907053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
1917053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) {
1927053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                w += p.width;
1937053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                if (p.type == PrimitiveType.BOX) {
1947053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    pw = w;
1957053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                }
1967053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            } else if (p.type == PrimitiveType.VARIABLE) {
1977053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                w = mTabStops.width(w);
1987053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                firstTabIndex = Math.min(firstTabIndex, i);
1997053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            }
2007053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
2017053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            if (pw > maxWidth && breakFound) {
2027053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                break;
2037053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            }
2047053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
2057053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            // must make progress
2067053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            if (i > start &&
2077053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                    (p.type == PrimitiveType.PENALTY || p.type == PrimitiveType.WORD_BREAK)) {
2087053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                breakFound = true;
2097053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta                breakIndex = i;
2107053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            }
2117053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        }
2127053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
2137053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        if (breakFound) {
2147053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            lineMetrics.mWidth = w;
2157053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            lineMetrics.mPrintedWidth = pw;
2167053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            lineMetrics.mHasTabs = (start <= firstTabIndex && firstTabIndex < breakIndex);
2177053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            return breakIndex;
2187053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        } else {
2197053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            return -1;
2207053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        }
2217053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    }
2227053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
2237053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    private static class LineMetrics {
2247053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        /** Actual width of the line. */
2257053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        float mWidth;
2267053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        /** Width of the line minus trailing whitespace. */
2277053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        float mPrintedWidth;
2287053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        boolean mHasTabs;
2297053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
2307053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        public LineMetrics() {
2317053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        }
2327053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
2337053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        public LineMetrics(float width, float printedWidth, boolean hasTabs) {
2347053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            mWidth = width;
2357053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            mPrintedWidth = printedWidth;
2367053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            mHasTabs = hasTabs;
2377053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        }
2387053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    }
2397053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
2407053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    /**
2417053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta     * A struct to store the info about a break.
2427053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta     */
2437053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    @SuppressWarnings("SpellCheckingInspection")  // For the word struct.
2447053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    private static class Node {
2457053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        // -1 for the first node.
2467053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        int mPrev;
2477053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        // number of breaks so far.
2487053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        int mPrevCount;
2497053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        float mDemerits;
2507053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        float mWidth;
2517053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        boolean mHasTabs;
2527053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta
2537053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        public Node(int prev, int prevCount, float demerits, float width, boolean hasTabs) {
2547053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            mPrev = prev;
2557053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            mPrevCount = prevCount;
2567053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            mDemerits = demerits;
2577053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            mWidth = width;
2587053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta            mHasTabs = hasTabs;
2597053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta        }
2607053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    }
2617053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta}
262