private void reflow(CharSequence s, int where, int before, int after) { if (s != mBase) return; CharSequence text = mDisplay; int len = text.length(); // seek back to the start of the paragraph int find = TextUtils.lastIndexOf(text, '\n', where - 1); if (find < 0) find = 0; else find = find + 1; { int diff = where - find; before += diff; after += diff; where -= diff; } // seek forward to the end of the paragraph int look = TextUtils.indexOf(text, '\n', where + after); if (look < 0) look = len; else look++; // we want the index after the \n int change = look - (where + after); before += change; after += change; // seek further out to cover anything that is forced to wrap together if (text instanceof Spanned) { Spanned sp = (Spanned) text; boolean again; do { again = false; Object[] force = sp.getSpans(where, where + after, WrapTogetherSpan.class); for (int i = 0; i < force.length; i++) { int st = sp.getSpanStart(force[i]); int en = sp.getSpanEnd(force[i]); if (st < where) { again = true; int diff = where - st; before += diff; after += diff; where -= diff; } if (en > where + after) { again = true; int diff = en - (where + after); before += diff; after += diff; } } } while (again); } // find affected region of old layout int startline = getLineForOffset(where); int startv = getLineTop(startline); int endline = getLineForOffset(where + before); if (where + after == len) endline = getLineCount(); int endv = getLineTop(endline); boolean islast = (endline == getLineCount()); // generate new layout for affected text StaticLayout reflowed; synchronized (sLock) { reflowed = sStaticLayout; sStaticLayout = null; } if (reflowed == null) { reflowed = new StaticLayout(null); } else { reflowed.prepare(); } reflowed.generate( text, where, where + after, getPaint(), getWidth(), getTextDirectionHeuristic(), getSpacingMultiplier(), getSpacingAdd(), false, true, mEllipsizedWidth, mEllipsizeAt); int n = reflowed.getLineCount(); // If the new layout has a blank line at the end, but it is not // the very end of the buffer, then we already have a line that // starts there, so disregard the blank line. if (where + after != len && reflowed.getLineStart(n - 1) == where + after) n--; // remove affected lines from old layout mInts.deleteAt(startline, endline - startline); mObjects.deleteAt(startline, endline - startline); // adjust offsets in layout for new height and offsets int ht = reflowed.getLineTop(n); int toppad = 0, botpad = 0; if (mIncludePad && startline == 0) { toppad = reflowed.getTopPadding(); mTopPadding = toppad; ht -= toppad; } if (mIncludePad && islast) { botpad = reflowed.getBottomPadding(); mBottomPadding = botpad; ht += botpad; } mInts.adjustValuesBelow(startline, START, after - before); mInts.adjustValuesBelow(startline, TOP, startv - endv + ht); // insert new layout int[] ints; if (mEllipsize) { ints = new int[COLUMNS_ELLIPSIZE]; ints[ELLIPSIS_START] = ELLIPSIS_UNDEFINED; } else { ints = new int[COLUMNS_NORMAL]; } Directions[] objects = new Directions[1]; for (int i = 0; i < n; i++) { ints[START] = reflowed.getLineStart(i) | (reflowed.getParagraphDirection(i) << DIR_SHIFT) | (reflowed.getLineContainsTab(i) ? TAB_MASK : 0); int top = reflowed.getLineTop(i) + startv; if (i > 0) top -= toppad; ints[TOP] = top; int desc = reflowed.getLineDescent(i); if (i == n - 1) desc += botpad; ints[DESCENT] = desc; objects[0] = reflowed.getLineDirections(i); if (mEllipsize) { ints[ELLIPSIS_START] = reflowed.getEllipsisStart(i); ints[ELLIPSIS_COUNT] = reflowed.getEllipsisCount(i); } mInts.insertAt(startline + i, ints); mObjects.insertAt(startline + i, objects); } synchronized (sLock) { sStaticLayout = reflowed; reflowed.finish(); } }
public List<Integer> getPageOffsets(CharSequence text, boolean includePageNumbers) { if (text == null) { return new ArrayList<Integer>(); } List<Integer> pageOffsets = new ArrayList<Integer>(); TextPaint textPaint = bookView.getInnerView().getPaint(); int boundedWidth = bookView.getInnerView().getWidth(); LOG.debug("Page width: " + boundedWidth); StaticLayout layout = layoutFactory.create(text, textPaint, boundedWidth, bookView.getLineSpacing()); LOG.debug("Layout height: " + layout.getHeight()); layout.draw(new Canvas()); // Subtract the height of the top margin int pageHeight = bookView.getHeight() - bookView.getVerticalMargin(); if (includePageNumbers) { String bottomSpace = "0\n"; StaticLayout numLayout = layoutFactory.create(bottomSpace, textPaint, boundedWidth, bookView.getLineSpacing()); numLayout.draw(new Canvas()); // Subtract the height needed to show page numbers, or the // height of the margin, whichever is more pageHeight = pageHeight - Math.max(numLayout.getHeight(), bookView.getVerticalMargin()); } else { // Just subtract the bottom margin pageHeight = pageHeight - bookView.getVerticalMargin(); } LOG.debug("Got pageHeight " + pageHeight); int totalLines = layout.getLineCount(); int topLineNextPage = -1; int pageStartOffset = 0; while (topLineNextPage < totalLines - 1) { LOG.debug("Processing line " + topLineNextPage + " / " + totalLines); int topLine = layout.getLineForOffset(pageStartOffset); topLineNextPage = layout.getLineForVertical(layout.getLineTop(topLine) + pageHeight); LOG.debug("topLine " + topLine + " / " + topLineNextPage); if (topLineNextPage == topLine) { // If lines are bigger than can fit on a page topLineNextPage = topLine + 1; } int pageEnd = layout.getLineEnd(topLineNextPage - 1); LOG.debug("pageStartOffset=" + pageStartOffset + ", pageEnd=" + pageEnd); if (pageEnd > pageStartOffset) { if (text.subSequence(pageStartOffset, pageEnd).toString().trim().length() > 0) { pageOffsets.add(pageStartOffset); } pageStartOffset = layout.getLineStart(topLineNextPage); } } return pageOffsets; }