void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool printing, bool doubleUnderlines) { DashArray widths; widths.append(width); widths.append(0); drawLinesForText(origin, widths, printing, doubleUnderlines); }
DashArray FontCascade::dashesForIntersectionsWithRect(const TextRun& run, const FloatPoint& textOrigin, const FloatRect& lineExtents) const { if (isLoadingCustomFonts()) return DashArray(); GlyphBuffer glyphBuffer; glyphBuffer.saveOffsetsInString(); float deltaX; if (codePath(run) != FontCascade::Complex) deltaX = getGlyphsAndAdvancesForSimpleText(run, 0, run.length(), glyphBuffer); else deltaX = getGlyphsAndAdvancesForComplexText(run, 0, run.length(), glyphBuffer); if (!glyphBuffer.size()) return DashArray(); // FIXME: Handle SVG + non-SVG interleaved runs. https://bugs.webkit.org/show_bug.cgi?id=133778 FloatPoint origin = FloatPoint(textOrigin.x() + deltaX, textOrigin.y()); std::unique_ptr<GlyphToPathTranslator> translator = std::make_unique<CairoGlyphToPathTranslator>(run, glyphBuffer, origin); DashArray result; for (int index = 0; translator->containsMorePaths(); ++index, translator->advance()) { float centerOfLine = lineExtents.y() + (lineExtents.height() / 2); GlyphIterationState info = GlyphIterationState(FloatPoint(), FloatPoint(), centerOfLine, lineExtents.x() + lineExtents.width(), lineExtents.x()); const Font* localFontData = glyphBuffer.fontAt(index); if (!localFontData) { // The advances will get all messed up if we do anything other than bail here. result.clear(); break; } switch (translator->underlineType()) { case GlyphToPathTranslator::GlyphUnderlineType::SkipDescenders: { Path path = translator->path(); path.apply([&info](const PathElement& pathElement) { findPathIntersections(info, pathElement); }); if (info.minX < info.maxX) { result.append(info.minX - lineExtents.x()); result.append(info.maxX - lineExtents.x()); } break; } case GlyphToPathTranslator::GlyphUnderlineType::SkipGlyph: { std::pair<float, float> extents = translator->extents(); result.append(extents.first - lineExtents.x()); result.append(extents.second - lineExtents.x()); break; } case GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph: // Nothing to do break; } } return result; }
void SVGRenderSupport::applyStrokeStyleToStrokeData(StrokeData* strokeData, const RenderStyle* style, const RenderObject* object) { ASSERT(strokeData); ASSERT(style); ASSERT(object); ASSERT(object->node()); ASSERT(object->node()->isSVGElement()); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); SVGLengthContext lengthContext(toSVGElement(object->node())); strokeData->setThickness(svgStyle->strokeWidth()->value(lengthContext)); strokeData->setLineCap(svgStyle->capStyle()); strokeData->setLineJoin(svgStyle->joinStyle()); strokeData->setMiterLimit(svgStyle->strokeMiterLimit()); RefPtr<SVGLengthList> dashes = svgStyle->strokeDashArray(); if (dashes->isEmpty()) return; DashArray dashArray; size_t length = dashes->length(); for (size_t i = 0; i < length; ++i) dashArray.append(dashes->at(i)->value(lengthContext)); strokeData->setLineDash(dashArray, svgStyle->strokeDashOffset()->value(lengthContext)); }
void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object) { ASSERT(context); ASSERT(style); ASSERT(object); ASSERT(object->node()); ASSERT(object->node()->isSVGElement()); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); SVGLengthContext lengthContext(toSVGElement(object->node())); context->setStrokeThickness(svgStyle->strokeWidth()->value(lengthContext)); context->setLineCap(svgStyle->capStyle()); context->setLineJoin(svgStyle->joinStyle()); context->setMiterLimit(svgStyle->strokeMiterLimit()); RefPtr<SVGLengthList> dashes = svgStyle->strokeDashArray(); if (dashes->isEmpty()) return; DashArray dashArray; SVGLengthList::ConstIterator it = dashes->begin(); SVGLengthList::ConstIterator itEnd = dashes->end(); for (; it != itEnd; ++it) dashArray.append(it->value(lengthContext)); context->setLineDash(dashArray, svgStyle->strokeDashOffset()->value(lengthContext)); }
void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object) { ASSERT(context); ASSERT(style); ASSERT(object); ASSERT(object->node()); ASSERT(object->node()->isSVGElement()); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); SVGLengthContext lengthContext(static_cast<SVGElement*>(object->node())); context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext)); context->setLineCap(svgStyle->capStyle()); context->setLineJoin(svgStyle->joinStyle()); if (svgStyle->joinStyle() == MiterJoin) context->setMiterLimit(svgStyle->strokeMiterLimit()); const Vector<SVGLength>& dashes = svgStyle->strokeDashArray(); if (dashes.isEmpty()) context->setStrokeStyle(SolidStroke); else { DashArray dashArray; const Vector<SVGLength>::const_iterator end = dashes.end(); for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it) dashArray.append((*it).value(lengthContext)); context->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext)); } }
void SVGRenderSupport::applyStrokeStyleToStrokeData(StrokeData* strokeData, const RenderStyle* style, const RenderObject* object) { ASSERT(strokeData); ASSERT(style); ASSERT(object); ASSERT(object->node()); ASSERT(object->node()->isSVGElement()); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); SVGLengthContext lengthContext(toSVGElement(object->node())); strokeData->setThickness(svgStyle->strokeWidth().value(lengthContext)); strokeData->setLineCap(svgStyle->capStyle()); strokeData->setLineJoin(svgStyle->joinStyle()); strokeData->setMiterLimit(svgStyle->strokeMiterLimit()); const Vector<SVGLength>& dashes = svgStyle->strokeDashArray(); if (dashes.isEmpty()) return; DashArray dashArray; const Vector<SVGLength>::const_iterator end = dashes.end(); for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it) dashArray.append((*it).value(lengthContext)); strokeData->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext)); }
DashArray SVGLayoutSupport::resolveSVGDashArray(const SVGDashArray& svgDashArray, const ComputedStyle& style, const SVGLengthContext& lengthContext) { DashArray dashArray; for (const Length& dashLength : svgDashArray.vector()) dashArray.append(lengthContext.valueForLength(dashLength, style)); return dashArray; }
static void writeStyle(TextStream& ts, const RenderObject& object) { const RenderStyle* style = object.style(); const SVGRenderStyle* svgStyle = style->svgStyle(); if (!object.localTransform().isIdentity()) writeNameValuePair(ts, "transform", object.localTransform()); writeIfNotDefault(ts, "image rendering", style->imageRendering(), RenderStyle::initialImageRendering()); writeIfNotDefault(ts, "opacity", style->opacity(), RenderStyle::initialOpacity()); if (object.isSVGShape()) { const RenderSVGShape& shape = static_cast<const RenderSVGShape&>(object); ASSERT(shape.node()); ASSERT(shape.node()->isSVGElement()); Color fallbackColor; if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(const_cast<RenderSVGShape*>(&shape), shape.style(), fallbackColor)) { TextStreamSeparator s(" "); ts << " [stroke={" << s; writeSVGPaintingResource(ts, strokePaintingResource); SVGLengthContext lengthContext(toSVGElement(shape.node())); double dashOffset = svgStyle->strokeDashOffset().value(lengthContext); double strokeWidth = svgStyle->strokeWidth().value(lengthContext); const Vector<SVGLength>& dashes = svgStyle->strokeDashArray(); DashArray dashArray; const Vector<SVGLength>::const_iterator end = dashes.end(); for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it) dashArray.append((*it).value(lengthContext)); writeIfNotDefault(ts, "opacity", svgStyle->strokeOpacity(), 1.0f); writeIfNotDefault(ts, "stroke width", strokeWidth, 1.0); writeIfNotDefault(ts, "miter limit", svgStyle->strokeMiterLimit(), 4.0f); writeIfNotDefault(ts, "line cap", svgStyle->capStyle(), ButtCap); writeIfNotDefault(ts, "line join", svgStyle->joinStyle(), MiterJoin); writeIfNotDefault(ts, "dash offset", dashOffset, 0.0); if (!dashArray.isEmpty()) writeNameValuePair(ts, "dash array", dashArray); ts << "}]"; } if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(const_cast<RenderSVGShape*>(&shape), shape.style(), fallbackColor)) { TextStreamSeparator s(" "); ts << " [fill={" << s; writeSVGPaintingResource(ts, fillPaintingResource); writeIfNotDefault(ts, "opacity", svgStyle->fillOpacity(), 1.0f); writeIfNotDefault(ts, "fill rule", svgStyle->fillRule(), RULE_NONZERO); ts << "}]"; } writeIfNotDefault(ts, "clip rule", svgStyle->clipRule(), RULE_NONZERO); } writeIfNotEmpty(ts, "start marker", svgStyle->markerStartResource()); writeIfNotEmpty(ts, "middle marker", svgStyle->markerMidResource()); writeIfNotEmpty(ts, "end marker", svgStyle->markerEndResource()); }
static DashArray translateIntersectionPointsToSkipInkBoundaries(const DashArray& intersections, float dilationAmount, float totalWidth) { ASSERT(!(intersections.size() % 2)); // Step 1: Make pairs so we can sort based on range starting-point. We dilate the ranges in this step as well. Vector<std::pair<float, float>> tuples; for (auto i = intersections.begin(); i != intersections.end(); i++, i++) tuples.append(std::make_pair(*i - dilationAmount, *(i + 1) + dilationAmount)); std::sort(tuples.begin(), tuples.end(), &compareTuples); // Step 2: Deal with intersecting ranges. Vector<std::pair<float, float>> intermediateTuples; if (tuples.size() >= 2) { intermediateTuples.append(*tuples.begin()); for (auto i = tuples.begin() + 1; i != tuples.end(); i++) { float& firstEnd = intermediateTuples.last().second; float secondStart = i->first; float secondEnd = i->second; if (secondStart <= firstEnd && secondEnd <= firstEnd) { // Ignore this range completely } else if (secondStart <= firstEnd) firstEnd = secondEnd; else intermediateTuples.append(*i); } } else intermediateTuples = tuples; // Step 3: Output the space between the ranges, but only if the space warrants an underline. float previous = 0; DashArray result; for (const auto& tuple : intermediateTuples) { if (tuple.first - previous > dilationAmount) { result.append(previous); result.append(tuple.first); } previous = tuple.second; } if (totalWidth - previous > dilationAmount) { result.append(previous); result.append(totalWidth); } return result; }
DashArray dashArrayFromRenderingStyle(const RenderStyle* style, RenderStyle* rootStyle) { DashArray array; CSSValueList* dashes = style->svgStyle()->strokeDashArray(); if (dashes) { CSSPrimitiveValue* dash = 0; unsigned long len = dashes->length(); for (unsigned long i = 0; i < len; i++) { dash = static_cast<CSSPrimitiveValue*>(dashes->itemWithoutBoundsCheck(i)); if (!dash) continue; array.append((float) dash->computeLengthFloat(const_cast<RenderStyle*>(style), rootStyle)); } } return array; }