GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle, const InlineTextBox* inlineTextBox) { ASSERT(!inlineTextBox || inlineTextBox->lineStyle() == lineStyle); TextDecoration decoration = lineStyle.textDecorationsInEffect(); if (decoration == TextDecorationNone) return GlyphOverflow(); float strokeThickness = textDecorationStrokeThickness(lineStyle.fontSize()); float controlPointDistance; float step; float wavyOffset; TextDecorationStyle decorationStyle = lineStyle.textDecorationStyle(); float height = lineStyle.fontCascade().fontMetrics().floatHeight(); GlyphOverflow overflowResult; if (decorationStyle == TextDecorationStyleWavy) { getWavyStrokeParameters(strokeThickness, controlPointDistance, step); wavyOffset = wavyOffsetFromDecoration(); overflowResult.left = strokeThickness; overflowResult.right = strokeThickness; } // These metrics must match where underlines get drawn. if (decoration & TextDecorationUnderline) { // Compensate for the integral ceiling in GraphicsContext::computeLineBoundsAndAntialiasingModeForText() int underlineOffset = 1; underlineOffset += computeUnderlineOffset(lineStyle.textUnderlinePosition(), lineStyle.fontMetrics(), inlineTextBox, strokeThickness); if (decorationStyle == TextDecorationStyleWavy) { extendIntToFloat(overflowResult.bottom, underlineOffset + wavyOffset + controlPointDistance + strokeThickness - height); extendIntToFloat(overflowResult.top, -(underlineOffset + wavyOffset - controlPointDistance - strokeThickness)); } else { extendIntToFloat(overflowResult.bottom, underlineOffset + strokeThickness - height); extendIntToFloat(overflowResult.top, -underlineOffset); } } if (decoration & TextDecorationOverline) { if (decorationStyle == TextDecorationStyleWavy) { extendIntToFloat(overflowResult.bottom, -wavyOffset + controlPointDistance + strokeThickness - height); extendIntToFloat(overflowResult.top, wavyOffset + controlPointDistance + strokeThickness); } else { extendIntToFloat(overflowResult.bottom, strokeThickness - height); // top is untouched } } if (decoration & TextDecorationLineThrough) { float baseline = lineStyle.fontMetrics().floatAscent(); if (decorationStyle == TextDecorationStyleWavy) { extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + controlPointDistance + strokeThickness - height); extendIntToFloat(overflowResult.top, -(2 * baseline / 3 - controlPointDistance - strokeThickness)); } else { extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + strokeThickness - height); extendIntToFloat(overflowResult.top, -(2 * baseline / 3)); } } return overflowResult; }
bool EllipsisBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { LayoutPoint adjustedLocation = accumulatedOffset + roundedLayoutPoint(topLeft()); // Hit test the markup box. if (InlineBox* markupBox = this->markupBox()) { RenderStyle* style = renderer().style(isFirstLineStyle()); LayoutUnit mtx = adjustedLocation.x() + m_logicalWidth - markupBox->x(); LayoutUnit mty = adjustedLocation.y() + style->fontMetrics().ascent() - (markupBox->y() + markupBox->renderer().style(isFirstLineStyle())->fontMetrics().ascent()); if (markupBox->nodeAtPoint(request, result, locationInContainer, LayoutPoint(mtx, mty), lineTop, lineBottom)) { renderer().updateHitTestResult(result, locationInContainer.point() - LayoutSize(mtx, mty)); return true; } } FloatPoint boxOrigin = locationIncludingFlipping(); boxOrigin.moveBy(accumulatedOffset); FloatRect boundsRect(boxOrigin, size()); if (visibleToHitTestRequest(request) && boundsRect.intersects(HitTestLocation::rectForPoint(locationInContainer.point(), 0, 0, 0, 0))) { renderer().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); if (!result.addNodeToRectBasedTestResult(renderer().node(), request, locationInContainer, boundsRect)) return true; } return false; }
IntRect SVGInlineTextBox::calculateBoundaries() const { FloatRect textRect; RenderText* textRenderer = this->textRenderer(); ASSERT(textRenderer); RenderStyle* style = textRenderer->style(); ASSERT(style); int baseline = baselinePosition(AlphabeticBaseline); int heightDifference = baseline - style->fontMetrics().ascent(); unsigned textFragmentsSize = m_textFragments.size(); for (unsigned i = 0; i < textFragmentsSize; ++i) { const SVGTextFragment& fragment = m_textFragments.at(i); FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fragment.height + heightDifference); if (!fragment.transform.isIdentity()) fragmentRect = fragment.transform.mapRect(fragmentRect); textRect.unite(fragmentRect); } return enclosingIntRect(textRect); }
LayoutUnit toUserUnits(const MathMLElement::Length& length, const RenderStyle& style, const LayoutUnit& referenceValue) { switch (length.type) { case MathMLElement::LengthType::Cm: return length.value * cssPixelsPerInch / 2.54f; case MathMLElement::LengthType::Em: return length.value * style.fontCascade().size(); case MathMLElement::LengthType::Ex: return length.value * style.fontMetrics().xHeight(); case MathMLElement::LengthType::In: return length.value * cssPixelsPerInch; case MathMLElement::LengthType::MathUnit: return length.value * style.fontCascade().size() / 18; case MathMLElement::LengthType::Mm: return length.value * cssPixelsPerInch / 25.4f; case MathMLElement::LengthType::Pc: return length.value * cssPixelsPerInch / 6; case MathMLElement::LengthType::Percentage: return referenceValue * length.value / 100; case MathMLElement::LengthType::Pt: return length.value * cssPixelsPerInch / 72; case MathMLElement::LengthType::Px: return length.value; case MathMLElement::LengthType::UnitLess: return referenceValue * length.value; case MathMLElement::LengthType::ParsingFailed: return referenceValue; default: ASSERT_NOT_REACHED(); return referenceValue; } }
void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { GraphicsContext* context = paintInfo.context; RenderStyle* style = m_renderer->style(isFirstLineStyle()); Color styleTextColor = style->visitedDependentColor(CSSPropertyWebkitTextFillColor); if (styleTextColor != context->fillColor()) context->setFillColor(styleTextColor, style->colorSpace()); Color textColor = styleTextColor; const Font& font = style->font(); if (selectionState() != RenderObject::SelectionNone) { paintSelection(context, paintOffset, style, font); // Select the correct color for painting the text. Color foreground = paintInfo.forceBlackText() ? Color::black : renderer()->selectionForegroundColor(); if (foreground.isValid() && foreground != styleTextColor) context->setFillColor(foreground, style->colorSpace()); } const ShadowData* shadow = style->textShadow(); bool hasShadow = shadow; if (hasShadow) { // FIXME: it would be better if we could get the shadows top-to-bottom from the style. Vector<const ShadowData*, 4> shadows; do { shadows.append(shadow); } while ((shadow = shadow->next())); DrawLooper drawLooper; drawLooper.addUnmodifiedContent(); for (int i = shadows.size() - 1; i >= 0; i--) { shadow = shadows[i]; int shadowX = isHorizontal() ? shadow->x() : shadow->y(); int shadowY = isHorizontal() ? shadow->y() : -shadow->x(); FloatSize offset(shadowX, shadowY); drawLooper.addShadow(offset, shadow->blur(), shadow->color(), DrawLooper::ShadowRespectsTransforms, DrawLooper::ShadowIgnoresAlpha); } context->setDrawLooper(drawLooper); } // FIXME: Why is this always LTR? Fix by passing correct text run flags below. FloatPoint boxOrigin(paintOffset); boxOrigin.move(x(), y()); FloatRect boxRect(boxOrigin, LayoutSize(logicalWidth(), logicalHeight())); FloatPoint textOrigin(boxOrigin.x(), boxOrigin.y() + style->fontMetrics().ascent()); TextRun textRun = RenderBlock::constructTextRun(renderer(), font, m_str, style, TextRun::AllowTrailingExpansion); TextRunPaintInfo textRunPaintInfo(textRun); textRunPaintInfo.bounds = boxRect; context->drawText(font, textRunPaintInfo, textOrigin); // Restore the regular fill color. if (styleTextColor != context->fillColor()) context->setFillColor(styleTextColor, style->colorSpace()); if (hasShadow) context->clearDrawLooper(); paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, style); }
const QFontMetrics &RenderText::metrics(bool firstLine) const { if( firstLine && hasFirstLine() ) { RenderStyle *pseudoStyle = style()->getPseudoStyle(RenderStyle::FIRST_LINE); if ( pseudoStyle ) return pseudoStyle->fontMetrics(); } return style()->fontMetrics(); }
void EllipsisBox::paintMarkupBox(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom, const RenderStyle& style) { InlineBox* markupBox = this->markupBox(); if (!markupBox) return; LayoutPoint adjustedPaintOffset = paintOffset; adjustedPaintOffset.move(x() + m_logicalWidth - markupBox->x(), y() + style.fontMetrics().ascent() - (markupBox->y() + markupBox->lineStyle().fontMetrics().ascent())); markupBox->paint(paintInfo, adjustedPaintOffset, lineTop, lineBottom); }
static LayoutUnit axisHeight(const RenderStyle& style) { // If we have a MATH table we just return the AxisHeight constant. const auto& primaryFont = style.fontCascade().primaryFont(); if (auto* mathData = primaryFont.mathData()) return mathData->getMathConstant(primaryFont, OpenTypeMathData::AxisHeight); // Otherwise, the idea is to try and use the middle of operators as the math axis which we thus approximate by "half of the x-height". // Note that Gecko has a slower but more accurate version that measures half of the height of U+2212 MINUS SIGN. return style.fontMetrics().xHeight() / 2; }
float SVGLengthContext::convertValueFromEXSToUserUnits(float value, ExceptionState& es) const { RenderStyle* style = renderStyleForLengthResolving(m_context); if (!style) { es.throwDOMException(NotSupportedError); return 0; } // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg // if this causes problems in real world cases maybe it would be best to remove this return value * ceilf(style->fontMetrics().xHeight()); }
float SVGLength::convertValueFromEXSToUserUnits(float value, const SVGElement* context, ExceptionCode& ec) const { if (!context || !context->renderer() || !context->renderer()->style()) { ec = NOT_SUPPORTED_ERR; return 0; } RenderStyle* style = context->renderer()->style(); // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg // if this causes problems in real world cases maybe it would be best to remove this return value * ceilf(style->fontMetrics().xHeight()); }
LayoutUnit RenderMathMLFraction::baselinePosition(FontBaseline, bool firstLine, LineDirectionMode lineDirection, LinePositionMode linePositionMode) const { if (firstChild() && firstChild()->isRenderMathMLBlock()) { RenderMathMLBlock* numerator = toRenderMathMLBlock(firstChild()); RenderStyle* refStyle = style(); if (previousSibling()) refStyle = previousSibling()->style(); else if (nextSibling()) refStyle = nextSibling()->style(); int shift = int(ceil((refStyle->fontMetrics().xHeight() + 1) / 2)); return numerator->offsetHeight() + shift; } return RenderBlock::baselinePosition(AlphabeticBaseline, firstLine, lineDirection, linePositionMode); }
float SVGLengthContext::convertValueFromUserUnitsToEXS(float value, ExceptionState& exceptionState) const { RenderStyle* style = renderStyleForLengthResolving(m_context); if (!style) { exceptionState.throwDOMException(NotSupportedError, "No context could be found."); return 0; } // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg // if this causes problems in real world cases maybe it would be best to remove this float xHeight = ceilf(style->fontMetrics().xHeight()); if (!xHeight) { exceptionState.throwDOMException(NotSupportedError, "No x-height could be determined."); return 0; } return value / xHeight; }
float SVGLengthContext::convertValueFromUserUnitsToEXS(float value, ExceptionCode& ec) const { RenderStyle* style = renderStyleForLengthResolving(m_context); if (!style) { ec = NOT_SUPPORTED_ERR; return 0; } // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg // if this causes problems in real world cases maybe it would be best to remove this float xHeight = ceilf(style->fontMetrics().xHeight()); if (!xHeight) { ec = NOT_SUPPORTED_ERR; return 0; } return value / xHeight; }
short RenderFlow::lineHeight(bool firstLine, bool isRootLineBox) const { if (firstLine) { RenderStyle* s = style(firstLine); Length lh = s->lineHeight(); if (lh.value < 0) { if (s == style()) { if (m_lineHeight == -1) m_lineHeight = RenderObject::lineHeight(false); return m_lineHeight; } return s->fontMetrics().lineSpacing(); } if (lh.isPercent()) return lh.minWidth(s->font().pixelSize()); return lh.value; } if (m_lineHeight == -1) m_lineHeight = RenderObject::lineHeight(false); return m_lineHeight; }
void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { GraphicsContext* context = paintInfo.context; RenderStyle* style = renderer().style(isFirstLineStyle()); Color textColor = style->visitedDependentColor(CSSPropertyWebkitTextFillColor); if (textColor != context->fillColor()) context->setFillColor(textColor, style->colorSpace()); bool setShadow = false; if (style->textShadow()) { context->setShadow(LayoutSize(style->textShadow()->x(), style->textShadow()->y()), style->textShadow()->radius(), style->textShadow()->color(), style->colorSpace()); setShadow = true; } const Font& font = style->font(); if (selectionState() != RenderObject::SelectionNone) { paintSelection(context, paintOffset, style, font); // Select the correct color for painting the text. Color foreground = paintInfo.forceBlackText() ? Color::black : renderer().selectionForegroundColor(); if (foreground.isValid() && foreground != textColor) context->setFillColor(foreground, style->colorSpace()); } // FIXME: Why is this always LTR? Fix by passing correct text run flags below. context->drawText(font, RenderBlock::constructTextRun(&renderer(), font, m_str, style, TextRun::AllowTrailingExpansion), LayoutPoint(x() + paintOffset.x(), y() + paintOffset.y() + style->fontMetrics().ascent())); // Restore the regular fill color. if (textColor != context->fillColor()) context->setFillColor(textColor, style->colorSpace()); if (setShadow) context->clearShadow(); paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, style); }
void RenderInline::caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height) { _x = -1; RenderBlock *cb = containingBlock(); bool rtl = cb->style()->direction() == RTL; bool outsideEnd = flags & CFOutsideEnd; // I need to explain that: outsideEnd contains a meaningful value if // and only if flags & CFOutside is set. If it is not, then randomly // either the first or the last line box is returned. // This doesn't matter because the only case this can happen is on an // empty inline element, whose first and last line boxes are actually // the same. InlineFlowBox *line = !outsideEnd ^ rtl ? firstLineBox() : lastLineBox(); if(!line) { // umpf, handle "gracefully" RenderFlow::caretPos(offset, flags, _x, _y, width, height); return; } _x = line->xPos(); width = 1; // ### regard CFOverride // Place caret outside the border if(flags & CFOutside) { RenderStyle *s = element() && element()->parent() && element()->parent()->renderer() ? element()->parent()->renderer()->style() : style(); const QFontMetrics &fm = s->fontMetrics(); _y = line->yPos() + line->baseline() - fm.ascent(); height = fm.height(); if(!outsideEnd ^ rtl) { _x -= line->marginBorderPaddingLeft(); } else { _x += line->width() + line->marginBorderPaddingRight(); } } else { const QFontMetrics &fm = style()->fontMetrics(); _y = line->yPos() + line->baseline() - fm.ascent(); height = fm.height(); } int absx, absy; if(cb && cb->absolutePosition(absx, absy)) { // kdDebug(6040) << "absx=" << absx << " absy=" << absy << endl; _x += absx; _y += absy; } else { // we don't know our absolute position, and there is no point returning // just a relative one _x = _y = -1; } }