void setGlyphXPositions(bool isRTL) { double position = 0; // logClustersIndex indexes logClusters for the first (or last when // RTL) codepoint of the current glyph. Each time we advance a glyph, // we skip over all the codepoints that contributed to the current // glyph. unsigned logClustersIndex = isRTL ? m_item.num_glyphs - 1 : 0; for (int iter = 0; iter < m_item.num_glyphs; ++iter) { // Glyphs are stored in logical order, but for layout purposes we // always go left to right. int i = isRTL ? m_item.num_glyphs - iter - 1 : iter; m_glyphs16[i] = m_item.glyphs[i]; double offsetX = truncateFixedPointToInteger(m_item.offsets[i].x); m_xPositions[i] = m_offsetX + position + offsetX; double advance = truncateFixedPointToInteger(m_item.advances[i]); // The first half of the conjuction works around the case where // output glyphs aren't associated with any codepoints by the // clusters log. if (logClustersIndex < m_item.item.length && isWordBreak(m_item.item.pos + logClustersIndex, isRTL)) { advance += m_wordSpacingAdjustment; if (m_padding > 0) { unsigned toPad = roundf(m_padPerWordBreak + m_padError); m_padError += m_padPerWordBreak - toPad; if (m_padding < toPad) toPad = m_padding; m_padding -= toPad; advance += toPad; } } // We would like to add m_letterSpacing after each cluster, but I // don't know where the cluster information is. This is typically // fine for Roman languages, but breaks more complex languages // terribly. // advance += m_letterSpacing; if (isRTL) { while (logClustersIndex > 0 && logClusters()[logClustersIndex] == i) logClustersIndex--; } else { while (logClustersIndex < m_item.item.length && logClusters()[logClustersIndex] == i) logClustersIndex++; } position += advance; } m_pixelWidth = position; m_offsetX += m_pixelWidth; }
// Return the rectangle for selecting the given range of code-points in the TextRun. FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int height, int from, int to) const { int fromX = -1, toX = -1; ComplexTextController controller(run, 0, 0, this); controller.setWordSpacingAdjustment(wordSpacing()); controller.setLetterSpacingAdjustment(letterSpacing()); controller.setPadding(run.expansion()); if (run.rtl()) { // See FIXME in drawComplexText. controller.reset(controller.widthOfFullRun()); controller.setPadding(run.expansion()); } // Iterate through the script runs in logical order, searching for the run covering the positions of interest. while (controller.nextScriptRun() && (fromX == -1 || toX == -1)) { if (fromX == -1 && from >= 0 && static_cast<unsigned>(from) < controller.numCodePoints()) { // |from| is within this script run. So we index the clusters log to // find which glyph this code-point contributed to and find its x // position. int glyph = controller.logClusters()[from]; fromX = controller.positions()[glyph].x(); if (controller.rtl()) fromX += truncateFixedPointToInteger(controller.advances()[glyph]); } else from -= controller.numCodePoints(); if (toX == -1 && to >= 0 && static_cast<unsigned>(to) < controller.numCodePoints()) { int glyph = controller.logClusters()[to]; toX = controller.positions()[glyph].x(); if (controller.rtl()) toX += truncateFixedPointToInteger(controller.advances()[glyph]); } else to -= controller.numCodePoints(); } // The position in question might be just after the text. if (fromX == -1) fromX = controller.offsetX(); if (toX == -1) toX = controller.offsetX(); ASSERT(fromX != -1 && toX != -1); if (fromX < toX) return FloatRect(point.x() + fromX, point.y(), toX - fromX, height); return FloatRect(point.x() + toX, point.y(), fromX - toX, height); }
static int glyphIndexForXPositionInScriptRun(const TextRunWalker& walker, int x) { const HB_Fixed* advances = walker.advances(); int glyphIndex; if (walker.rtl()) { for (glyphIndex = walker.length() - 1; glyphIndex >= 0; --glyphIndex) { if (x < truncateFixedPointToInteger(advances[glyphIndex])) break; x -= truncateFixedPointToInteger(advances[glyphIndex]); } } else { for (glyphIndex = 0; glyphIndex < walker.length(); ++glyphIndex) { if (x < truncateFixedPointToInteger(advances[glyphIndex])) break; x -= truncateFixedPointToInteger(advances[glyphIndex]); } } return glyphIndex; }
void ComplexTextController::setGlyphPositions(bool isRTL) { const double rtlFlip = isRTL ? -1 : 1; double position = 0; // logClustersIndex indexes logClusters for the first codepoint of the current glyph. // Each time we advance a glyph, we skip over all the codepoints that contributed to the current glyph. int logClustersIndex = 0; // Iterate through the glyphs in logical order, flipping for RTL where necessary. // Glyphs are positioned starting from m_offsetX; in RTL mode they go leftwards from there. for (size_t i = 0; i < m_item.num_glyphs; ++i) { while (static_cast<unsigned>(logClustersIndex) < m_item.item.length && m_item.log_clusters[logClustersIndex] < i) logClustersIndex++; // If the current glyph is just after a space, add in the word spacing. position += determineWordBreakSpacing(logClustersIndex); m_glyphs16[i] = m_item.glyphs[i]; double offsetX = truncateFixedPointToInteger(m_item.offsets[i].x); double offsetY = truncateFixedPointToInteger(m_item.offsets[i].y); double advance = truncateFixedPointToInteger(m_item.advances[i]); if (isRTL) offsetX -= advance; m_positions[i].set(m_offsetX + (position * rtlFlip) + offsetX, m_startingY + offsetY); if (m_currentFontData->isZeroWidthSpaceGlyph(m_glyphs16[i])) continue; // At the end of each cluster, add in the letter spacing. if (i + 1 == m_item.num_glyphs || m_item.attributes[i + 1].clusterStart) position += m_letterSpacing; position += advance; } m_pixelWidth = std::max(position, 0.0); m_offsetX += m_pixelWidth * rtlFlip; }
FloatRect ComplexTextController::selectionRect(const FloatPoint& point, int height, int from, int to) { int fromX = -1, toX = -1; // Iterate through the script runs in logical order, searching for the run covering the positions of interest. while (nextScriptRun() && (fromX == -1 || toX == -1)) { if (fromX == -1 && from >= 0 && static_cast<unsigned>(from) < numCodePoints()) { // |from| is within this script run. So we index the clusters log to // find which glyph this code-point contributed to and find its x // position. int glyph = m_item.log_clusters[from]; fromX = positions()[glyph].x(); if (rtl()) fromX += truncateFixedPointToInteger(m_item.advances[glyph]); } else from -= numCodePoints(); if (toX == -1 && to >= 0 && static_cast<unsigned>(to) < numCodePoints()) { int glyph = m_item.log_clusters[to]; toX = positions()[glyph].x(); if (rtl()) toX += truncateFixedPointToInteger(m_item.advances[glyph]); } else to -= numCodePoints(); } // The position in question might be just after the text. if (fromX == -1) fromX = offsetX(); if (toX == -1) toX = offsetX(); ASSERT(fromX != -1 && toX != -1); if (fromX < toX) return FloatRect(point.x() + fromX, point.y(), toX - fromX, height); return FloatRect(point.x() + toX, point.y(), fromX - toX, height); }
static int glyphIndexForXPositionInScriptRun(const ComplexTextController& controller, int targetX) { // Iterate through the glyphs in logical order, seeing whether targetX falls between the previous // position and halfway through the current glyph. // FIXME: this code probably belongs in ComplexTextController. int lastX = controller.offsetX() - (controller.rtl() ? -controller.width() : controller.width()); for (int glyphIndex = 0; static_cast<unsigned>(glyphIndex) < controller.length(); ++glyphIndex) { int advance = truncateFixedPointToInteger(controller.advances()[glyphIndex]); int nextX = static_cast<int>(controller.positions()[glyphIndex].x()) + advance / 2; if (std::min(nextX, lastX) <= targetX && targetX <= std::max(nextX, lastX)) return glyphIndex; lastX = nextX; } return controller.length() - 1; }
void setGlyphXPositions(bool isRTL) { m_pixelWidth = 0; for (unsigned i = 0; i < m_item.num_glyphs; ++i) { int index; if (isRTL) index = m_item.num_glyphs - (i + 1); else index = i; m_glyphs16[i] = m_item.glyphs[i]; m_xPositions[index] = m_offsetX + m_pixelWidth; m_pixelWidth += truncateFixedPointToInteger(m_item.advances[index]); } m_offsetX += m_pixelWidth; }
// Return the rectangle for selecting the given range of code-points in the TextRun. FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int height, int from, int to) const { int fromX = -1, toX = -1, fromAdvance = -1, toAdvance = -1; TextRunWalker walker(run, 0, this); // Base will point to the x offset for the current script run. Note that, in // the LTR case, width will be 0. int base = walker.rtl() ? walker.widthOfFullRun() : 0; const int leftEdge = base; // We want to enumerate the script runs in code point order in the following // code. This call also resets |walker|. walker.setBackwardsIteration(false); while (walker.nextScriptRun() && (fromX == -1 || toX == -1)) { // TextRunWalker will helpfully accululate the x offsets for different // script runs for us. For this code, however, we always want the x offsets // to start from zero so we call this before each script run. walker.setXOffsetToZero(); if (walker.rtl()) base -= walker.width(); if (fromX == -1 && from < walker.numCodePoints()) { // |from| is within this script run. So we index the clusters log to // find which glyph this code-point contributed to and find its x // position. int glyph = walker.logClusters()[from]; fromX = base + walker.xPositions()[glyph]; fromAdvance = walker.advances()[glyph]; } else from -= walker.numCodePoints(); if (toX == -1 && to < walker.numCodePoints()) { int glyph = walker.logClusters()[to]; toX = base + walker.xPositions()[glyph]; toAdvance = walker.advances()[glyph]; } else to -= walker.numCodePoints(); if (!walker.rtl()) base += walker.width(); } // The position in question might be just after the text. const int rightEdge = base; if (fromX == -1 && !from) fromX = leftEdge; else if (walker.rtl()) fromX += truncateFixedPointToInteger(fromAdvance); if (toX == -1 && !to) toX = rightEdge; else if (!walker.rtl()) toX += truncateFixedPointToInteger(toAdvance); ASSERT(fromX != -1 && toX != -1); if (fromX < toX) return FloatRect(point.x() + fromX, point.y(), toX - fromX, height); return FloatRect(point.x() + toX, point.y(), fromX - toX, height); }