blob: 84b56bff4b529540aa47302d4125d1995ada3d5b [file] [log] [blame]
/*
* Copyright (C) 2004, 2005, 2007, 2009 Apple Inc. All rights reserved.
* (C) 2005 Rob Buis <buis@kde.org>
* (C) 2006 Alexander Kellett <lypanov@kde.org>
* Copyright (C) Research In Motion Limited 2010. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.h"
#include "third_party/blink/renderer/core/layout/api/line_layout_svg_inline_text.h"
#include "third_party/blink/renderer/core/layout/layout_tree_as_text.h"
#include "third_party/blink/renderer/core/layout/line/inline_text_box.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_image.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_inline.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_linear_gradient.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_radial_gradient.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_shape.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h"
#include "third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.h"
#include "third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h"
#include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
#include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/style/reference_clip_path_operation.h"
#include "third_party/blink/renderer/core/style/style_svg_resource.h"
#include "third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.h"
#include "third_party/blink/renderer/core/svg/linear_gradient_attributes.h"
#include "third_party/blink/renderer/core/svg/pattern_attributes.h"
#include "third_party/blink/renderer/core/svg/radial_gradient_attributes.h"
#include "third_party/blink/renderer/core/svg/svg_animated_angle.h"
#include "third_party/blink/renderer/core/svg/svg_animated_length.h"
#include "third_party/blink/renderer/core/svg/svg_animated_point_list.h"
#include "third_party/blink/renderer/core/svg/svg_circle_element.h"
#include "third_party/blink/renderer/core/svg/svg_ellipse_element.h"
#include "third_party/blink/renderer/core/svg/svg_enumeration_map.h"
#include "third_party/blink/renderer/core/svg/svg_filter_element.h"
#include "third_party/blink/renderer/core/svg/svg_line_element.h"
#include "third_party/blink/renderer/core/svg/svg_linear_gradient_element.h"
#include "third_party/blink/renderer/core/svg/svg_path_element.h"
#include "third_party/blink/renderer/core/svg/svg_path_utilities.h"
#include "third_party/blink/renderer/core/svg/svg_pattern_element.h"
#include "third_party/blink/renderer/core/svg/svg_point_list.h"
#include "third_party/blink/renderer/core/svg/svg_poly_element.h"
#include "third_party/blink/renderer/core/svg/svg_radial_gradient_element.h"
#include "third_party/blink/renderer/core/svg/svg_rect_element.h"
#include "third_party/blink/renderer/platform/graphics/dash_array.h"
#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
#include "third_party/blink/renderer/platform/graphics/filters/source_graphic.h"
#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
namespace blink {
/** class + iomanip to help streaming list separators, i.e. ", " in string "a,
* b, c, d"
* Can be used in cases where you don't know which item in the list is the first
* one to be printed, but still want to avoid strings like ", b, c".
*/
class TextStreamSeparator {
public:
TextStreamSeparator(const String& s)
: separator_(s), need_to_separate_(false) {}
private:
friend WTF::TextStream& operator<<(WTF::TextStream&, TextStreamSeparator&);
String separator_;
bool need_to_separate_;
};
WTF::TextStream& operator<<(WTF::TextStream& ts, TextStreamSeparator& sep) {
if (sep.need_to_separate_)
ts << sep.separator_;
else
sep.need_to_separate_ = true;
return ts;
}
template <typename ValueType>
static void WriteNameValuePair(WTF::TextStream& ts,
const char* name,
ValueType value) {
ts << " [" << name << "=" << value << "]";
}
static void WriteSVGResourceIfNotNull(WTF::TextStream& ts,
const char* name,
const StyleSVGResource* value,
TreeScope& tree_scope) {
if (!value)
return;
AtomicString id = SVGURIReference::FragmentIdentifierFromIRIString(
value->Url(), tree_scope);
WriteNameValuePair(ts, name, id);
}
template <typename ValueType>
static void WriteNameAndQuotedValue(WTF::TextStream& ts,
const char* name,
ValueType value) {
ts << " [" << name << "=\"" << value << "\"]";
}
template <typename ValueType>
static void WriteIfNotDefault(WTF::TextStream& ts,
const char* name,
ValueType value,
ValueType default_value) {
if (value != default_value)
WriteNameValuePair(ts, name, value);
}
WTF::TextStream& operator<<(WTF::TextStream& ts,
const AffineTransform& transform) {
if (transform.IsIdentity()) {
ts << "identity";
} else {
ts << "{m=((" << transform.A() << "," << transform.B() << ")("
<< transform.C() << "," << transform.D() << ")) t=(" << transform.E()
<< "," << transform.F() << ")}";
}
return ts;
}
static WTF::TextStream& operator<<(WTF::TextStream& ts, const WindRule rule) {
switch (rule) {
case RULE_NONZERO:
ts << "NON-ZERO";
break;
case RULE_EVENODD:
ts << "EVEN-ODD";
break;
}
return ts;
}
static WTF::TextStream& operator<<(WTF::TextStream& ts,
const SVGUnitTypes::SVGUnitType& unit_type) {
ts << GetEnumerationMap<SVGUnitTypes::SVGUnitType>().NameFromValue(unit_type);
return ts;
}
static WTF::TextStream& operator<<(WTF::TextStream& ts,
const SVGMarkerUnitsType& marker_unit) {
ts << GetEnumerationMap<SVGMarkerUnitsType>().NameFromValue(marker_unit);
return ts;
}
static WTF::TextStream& operator<<(WTF::TextStream& ts,
const SVGMarkerOrientType& orient_type) {
ts << GetEnumerationMap<SVGMarkerOrientType>().NameFromValue(orient_type);
return ts;
}
// FIXME: Maybe this should be in GraphicsTypes.cpp
static WTF::TextStream& operator<<(WTF::TextStream& ts, LineCap style) {
switch (style) {
case kButtCap:
ts << "BUTT";
break;
case kRoundCap:
ts << "ROUND";
break;
case kSquareCap:
ts << "SQUARE";
break;
}
return ts;
}
// FIXME: Maybe this should be in GraphicsTypes.cpp
static WTF::TextStream& operator<<(WTF::TextStream& ts, LineJoin style) {
switch (style) {
case kMiterJoin:
ts << "MITER";
break;
case kRoundJoin:
ts << "ROUND";
break;
case kBevelJoin:
ts << "BEVEL";
break;
}
return ts;
}
static WTF::TextStream& operator<<(WTF::TextStream& ts,
const SVGSpreadMethodType& type) {
auto* name = GetEnumerationMap<SVGSpreadMethodType>().NameFromValue(type);
ts << String(name).UpperASCII();
return ts;
}
static void WriteSVGPaintingResource(WTF::TextStream& ts,
const SVGResource& resource) {
const LayoutSVGResourceContainer* container =
resource.ResourceContainerNoCycleCheck();
DCHECK(container);
switch (container->ResourceType()) {
case kPatternResourceType:
ts << "[type=PATTERN]";
break;
case kLinearGradientResourceType:
ts << "[type=LINEAR-GRADIENT]";
break;
case kRadialGradientResourceType:
ts << "[type=RADIAL-GRADIENT]";
break;
default:
NOTREACHED();
break;
}
ts << " [id=\"" << resource.Target()->GetIdAttribute() << "\"]";
}
static bool WriteSVGPaint(WTF::TextStream& ts,
const LayoutObject& object,
const SVGPaint& paint,
const CSSProperty& property,
const char* paint_name) {
TextStreamSeparator s(" ");
const ComputedStyle& style = object.StyleRef();
if (const StyleSVGResource* resource = paint.Resource()) {
const SVGResource* paint_resource = resource->Resource();
SVGResourceClient* client = SVGResources::GetClient(object);
if (GetSVGResourceAsType<LayoutSVGResourcePaintServer>(*client,
paint_resource)) {
ts << " [" << paint_name << "={" << s;
WriteSVGPaintingResource(ts, *paint_resource);
return true;
}
}
if (paint.HasColor()) {
Color color = style.VisitedDependentColor(property);
ts << " [" << paint_name << "={" << s;
ts << "[type=SOLID] [color=" << color << "]";
return true;
}
return false;
}
static void WriteStyle(WTF::TextStream& ts, const LayoutObject& object) {
const ComputedStyle& style = object.StyleRef();
if (!object.LocalSVGTransform().IsIdentity())
WriteNameValuePair(ts, "transform", object.LocalSVGTransform());
WriteIfNotDefault(
ts, "image rendering", static_cast<int>(style.ImageRendering()),
static_cast<int>(ComputedStyleInitialValues::InitialImageRendering()));
WriteIfNotDefault(ts, "opacity", style.Opacity(),
ComputedStyleInitialValues::InitialOpacity());
if (object.IsSVGShape()) {
if (WriteSVGPaint(ts, object, style.StrokePaint(), GetCSSPropertyStroke(),
"stroke")) {
const LayoutSVGShape& shape = static_cast<const LayoutSVGShape&>(object);
DCHECK(shape.GetElement());
SVGLengthContext length_context(shape.GetElement());
double dash_offset =
length_context.ValueForLength(style.StrokeDashOffset(), style);
double stroke_width = length_context.ValueForLength(style.StrokeWidth());
DashArray dash_array = SVGLayoutSupport::ResolveSVGDashArray(
*style.StrokeDashArray(), style, length_context);
WriteIfNotDefault(ts, "opacity", style.StrokeOpacity(), 1.0f);
WriteIfNotDefault(ts, "stroke width", stroke_width, 1.0);
WriteIfNotDefault(ts, "miter limit", style.StrokeMiterLimit(), 4.0f);
WriteIfNotDefault(ts, "line cap", style.CapStyle(), kButtCap);
WriteIfNotDefault(ts, "line join", style.JoinStyle(), kMiterJoin);
WriteIfNotDefault(ts, "dash offset", dash_offset, 0.0);
if (!dash_array.IsEmpty())
WriteNameValuePair(ts, "dash array", dash_array);
ts << "}]";
}
if (WriteSVGPaint(ts, object, style.FillPaint(), GetCSSPropertyFill(),
"fill")) {
WriteIfNotDefault(ts, "opacity", style.FillOpacity(), 1.0f);
WriteIfNotDefault(ts, "fill rule", style.FillRule(), RULE_NONZERO);
ts << "}]";
}
WriteIfNotDefault(ts, "clip rule", style.ClipRule(), RULE_NONZERO);
}
TreeScope& tree_scope = object.GetDocument();
WriteSVGResourceIfNotNull(ts, "start marker", style.MarkerStartResource(),
tree_scope);
WriteSVGResourceIfNotNull(ts, "middle marker", style.MarkerMidResource(),
tree_scope);
WriteSVGResourceIfNotNull(ts, "end marker", style.MarkerEndResource(),
tree_scope);
}
static WTF::TextStream& WritePositionAndStyle(WTF::TextStream& ts,
const LayoutObject& object) {
ts << " " << object.ObjectBoundingBox();
WriteStyle(ts, object);
return ts;
}
static WTF::TextStream& operator<<(WTF::TextStream& ts,
const LayoutSVGShape& shape) {
WritePositionAndStyle(ts, shape);
SVGElement* svg_element = shape.GetElement();
DCHECK(svg_element);
SVGLengthContext length_context(svg_element);
const ComputedStyle& style = shape.StyleRef();
if (IsA<SVGRectElement>(*svg_element)) {
WriteNameValuePair(
ts, "x",
length_context.ValueForLength(style.X(), style, SVGLengthMode::kWidth));
WriteNameValuePair(ts, "y",
length_context.ValueForLength(style.Y(), style,
SVGLengthMode::kHeight));
WriteNameValuePair(ts, "width",
length_context.ValueForLength(style.Width(), style,
SVGLengthMode::kWidth));
WriteNameValuePair(ts, "height",
length_context.ValueForLength(style.Height(), style,
SVGLengthMode::kHeight));
} else if (auto* element = DynamicTo<SVGLineElement>(*svg_element)) {
WriteNameValuePair(ts, "x1",
element->x1()->CurrentValue()->Value(length_context));
WriteNameValuePair(ts, "y1",
element->y1()->CurrentValue()->Value(length_context));
WriteNameValuePair(ts, "x2",
element->x2()->CurrentValue()->Value(length_context));
WriteNameValuePair(ts, "y2",
element->y2()->CurrentValue()->Value(length_context));
} else if (IsA<SVGEllipseElement>(*svg_element)) {
WriteNameValuePair(ts, "cx",
length_context.ValueForLength(style.Cx(), style,
SVGLengthMode::kWidth));
WriteNameValuePair(ts, "cy",
length_context.ValueForLength(style.Cy(), style,
SVGLengthMode::kHeight));
WriteNameValuePair(ts, "rx",
length_context.ValueForLength(style.Rx(), style,
SVGLengthMode::kWidth));
WriteNameValuePair(ts, "ry",
length_context.ValueForLength(style.Ry(), style,
SVGLengthMode::kHeight));
} else if (IsA<SVGCircleElement>(*svg_element)) {
WriteNameValuePair(ts, "cx",
length_context.ValueForLength(style.Cx(), style,
SVGLengthMode::kWidth));
WriteNameValuePair(ts, "cy",
length_context.ValueForLength(style.Cy(), style,
SVGLengthMode::kHeight));
WriteNameValuePair(
ts, "r",
length_context.ValueForLength(style.R(), style, SVGLengthMode::kOther));
} else if (auto* svg_poly_element = DynamicTo<SVGPolyElement>(svg_element)) {
WriteNameAndQuotedValue(
ts, "points",
svg_poly_element->Points()->CurrentValue()->ValueAsString());
} else if (IsA<SVGPathElement>(*svg_element)) {
const StylePath& path = style.D() ? *style.D() : *StylePath::EmptyPath();
WriteNameAndQuotedValue(
ts, "data",
BuildStringFromByteStream(path.ByteStream(), kNoTransformation));
} else {
NOTREACHED();
}
return ts;
}
static WTF::TextStream& operator<<(WTF::TextStream& ts,
const LayoutSVGRoot& root) {
ts << " " << root.FrameRect();
WriteStyle(ts, root);
return ts;
}
static void WriteLayoutSVGTextBox(WTF::TextStream& ts,
const LayoutSVGText& text) {
auto* box = To<SVGRootInlineBox>(text.FirstRootBox());
if (!box)
return;
// FIXME: Remove this hack, once the new text layout engine is completly
// landed. We want to preserve the old web test results for now.
ts << " contains 1 chunk(s)";
if (text.Parent() && (text.Parent()->ResolveColor(GetCSSPropertyColor()) !=
text.ResolveColor(GetCSSPropertyColor()))) {
WriteNameValuePair(
ts, "color",
text.ResolveColor(GetCSSPropertyColor()).NameForLayoutTreeAsText());
}
}
static inline void WriteSVGInlineTextBox(WTF::TextStream& ts,
SVGInlineTextBox* text_box,
int indent) {
Vector<SVGTextFragment>& fragments = text_box->TextFragments();
if (fragments.IsEmpty())
return;
LineLayoutSVGInlineText text_line_layout =
LineLayoutSVGInlineText(text_box->GetLineLayoutItem());
const ComputedStyle& style = text_line_layout.StyleRef();
String text = text_box->GetLineLayoutItem().GetText();
unsigned fragments_size = fragments.size();
for (unsigned i = 0; i < fragments_size; ++i) {
SVGTextFragment& fragment = fragments.at(i);
WriteIndent(ts, indent + 1);
unsigned start_offset = fragment.character_offset;
unsigned end_offset = fragment.character_offset + fragment.length;
// FIXME: Remove this hack, once the new text layout engine is completly
// landed. We want to preserve the old web test results for now.
ts << "chunk 1 ";
ETextAnchor anchor = style.TextAnchor();
bool is_vertical_text = !style.IsHorizontalWritingMode();
if (anchor == ETextAnchor::kMiddle) {
ts << "(middle anchor";
if (is_vertical_text)
ts << ", vertical";
ts << ") ";
} else if (anchor == ETextAnchor::kEnd) {
ts << "(end anchor";
if (is_vertical_text)
ts << ", vertical";
ts << ") ";
} else if (is_vertical_text) {
ts << "(vertical) ";
}
start_offset -= text_box->Start();
end_offset -= text_box->Start();
// </hack>
ts << "text run " << i + 1 << " at (" << fragment.x << "," << fragment.y
<< ")";
ts << " startOffset " << start_offset << " endOffset " << end_offset;
if (is_vertical_text)
ts << " height " << fragment.height;
else
ts << " width " << fragment.width;
if (!text_box->IsLeftToRightDirection() || text_box->DirOverride()) {
ts << (text_box->IsLeftToRightDirection() ? " LTR" : " RTL");
if (text_box->DirOverride())
ts << " override";
}
ts << ": "
<< QuoteAndEscapeNonPrintables(
text.Substring(fragment.character_offset, fragment.length))
<< "\n";
}
}
static inline void WriteSVGInlineTextBoxes(WTF::TextStream& ts,
const LayoutText& text,
int indent) {
for (InlineTextBox* box : text.TextBoxes()) {
auto* svg_inline_text_box = DynamicTo<SVGInlineTextBox>(box);
if (!svg_inline_text_box)
continue;
WriteSVGInlineTextBox(ts, svg_inline_text_box, indent);
}
}
static void WriteStandardPrefix(WTF::TextStream& ts,
const LayoutObject& object,
int indent) {
WriteIndent(ts, indent);
ts << object.DecoratedName();
if (object.GetNode())
ts << " {" << object.GetNode()->nodeName() << "}";
}
static void WriteChildren(WTF::TextStream& ts,
const LayoutObject& object,
int indent) {
for (LayoutObject* child = object.SlowFirstChild(); child;
child = child->NextSibling())
Write(ts, *child, indent + 1);
}
static inline void WriteCommonGradientProperties(
WTF::TextStream& ts,
const GradientAttributes& attrs) {
WriteNameValuePair(ts, "gradientUnits", attrs.GradientUnits());
if (attrs.SpreadMethod() != kSVGSpreadMethodPad)
ts << " [spreadMethod=" << attrs.SpreadMethod() << "]";
if (!attrs.GradientTransform().IsIdentity())
ts << " [gradientTransform=" << attrs.GradientTransform() << "]";
if (attrs.HasStops()) {
ts << " [stops=( ";
for (const auto& stop : attrs.Stops())
ts << stop.color << "@" << stop.stop << " ";
ts << ")]";
}
}
void WriteSVGResourceContainer(WTF::TextStream& ts,
const LayoutObject& object,
int indent) {
WriteStandardPrefix(ts, object, indent);
auto* element = To<Element>(object.GetNode());
const AtomicString& id = element->GetIdAttribute();
WriteNameAndQuotedValue(ts, "id", id);
auto* resource =
To<LayoutSVGResourceContainer>(const_cast<LayoutObject*>(&object));
DCHECK(resource);
if (resource->ResourceType() == kMaskerResourceType) {
auto* masker = To<LayoutSVGResourceMasker>(resource);
WriteNameValuePair(ts, "maskUnits", masker->MaskUnits());
WriteNameValuePair(ts, "maskContentUnits", masker->MaskContentUnits());
ts << "\n";
} else if (resource->ResourceType() == kFilterResourceType) {
auto* filter = To<LayoutSVGResourceFilter>(resource);
WriteNameValuePair(ts, "filterUnits", filter->FilterUnits());
WriteNameValuePair(ts, "primitiveUnits", filter->PrimitiveUnits());
ts << "\n";
// Creating a placeholder filter which is passed to the builder.
FloatRect dummy_rect;
auto* dummy_filter = MakeGarbageCollected<Filter>(dummy_rect, dummy_rect, 1,
Filter::kBoundingBox);
SVGFilterBuilder builder(dummy_filter->GetSourceGraphic());
builder.BuildGraph(dummy_filter,
To<SVGFilterElement>(*filter->GetElement()), dummy_rect);
if (FilterEffect* last_effect = builder.LastEffect())
last_effect->ExternalRepresentation(ts, indent + 1);
} else if (resource->ResourceType() == kClipperResourceType) {
WriteNameValuePair(ts, "clipPathUnits",
To<LayoutSVGResourceClipper>(resource)->ClipPathUnits());
ts << "\n";
} else if (resource->ResourceType() == kMarkerResourceType) {
auto* marker = To<LayoutSVGResourceMarker>(resource);
WriteNameValuePair(ts, "markerUnits", marker->MarkerUnits());
ts << " [ref at " << marker->ReferencePoint() << "]";
ts << " [angle=";
if (marker->OrientType() != kSVGMarkerOrientAngle)
ts << marker->OrientType() << "]\n";
else
ts << marker->Angle() << "]\n";
} else if (resource->ResourceType() == kPatternResourceType) {
LayoutSVGResourcePattern* pattern =
static_cast<LayoutSVGResourcePattern*>(resource);
// Dump final results that are used for layout. No use in asking
// SVGPatternElement for its patternUnits(), as it may link to other
// patterns using xlink:href, we need to build the full inheritance chain,
// aka. collectPatternProperties()
PatternAttributes attributes;
To<SVGPatternElement>(pattern->GetElement())
->CollectPatternAttributes(attributes);
WriteNameValuePair(ts, "patternUnits", attributes.PatternUnits());
WriteNameValuePair(ts, "patternContentUnits",
attributes.PatternContentUnits());
AffineTransform transform = attributes.PatternTransform();
if (!transform.IsIdentity())
ts << " [patternTransform=" << transform << "]";
ts << "\n";
} else if (resource->ResourceType() == kLinearGradientResourceType) {
LayoutSVGResourceLinearGradient* gradient =
static_cast<LayoutSVGResourceLinearGradient*>(resource);
// Dump final results that are used for layout. No use in asking
// SVGGradientElement for its gradientUnits(), as it may link to other
// gradients using xlink:href, we need to build the full inheritance chain,
// aka. collectGradientProperties()
LinearGradientAttributes attributes;
To<SVGLinearGradientElement>(gradient->GetElement())
->CollectGradientAttributes(attributes);
WriteCommonGradientProperties(ts, attributes);
ts << " [start=" << gradient->StartPoint(attributes)
<< "] [end=" << gradient->EndPoint(attributes) << "]\n";
} else if (resource->ResourceType() == kRadialGradientResourceType) {
auto* gradient = To<LayoutSVGResourceRadialGradient>(resource);
// Dump final results that are used for layout. No use in asking
// SVGGradientElement for its gradientUnits(), as it may link to other
// gradients using xlink:href, we need to build the full inheritance chain,
// aka. collectGradientProperties()
RadialGradientAttributes attributes;
To<SVGRadialGradientElement>(gradient->GetElement())
->CollectGradientAttributes(attributes);
WriteCommonGradientProperties(ts, attributes);
FloatPoint focal_point = gradient->FocalPoint(attributes);
FloatPoint center_point = gradient->CenterPoint(attributes);
float radius = gradient->Radius(attributes);
float focal_radius = gradient->FocalRadius(attributes);
ts << " [center=" << center_point << "] [focal=" << focal_point
<< "] [radius=" << radius << "] [focalRadius=" << focal_radius << "]\n";
} else {
ts << "\n";
}
WriteChildren(ts, object, indent);
}
void WriteSVGContainer(WTF::TextStream& ts,
const LayoutObject& container,
int indent) {
WriteStandardPrefix(ts, container, indent);
WritePositionAndStyle(ts, container);
ts << "\n";
WriteResources(ts, container, indent);
WriteChildren(ts, container, indent);
}
void Write(WTF::TextStream& ts, const LayoutSVGRoot& root, int indent) {
WriteStandardPrefix(ts, root, indent);
ts << root << "\n";
WriteChildren(ts, root, indent);
}
void WriteSVGText(WTF::TextStream& ts, const LayoutSVGText& text, int indent) {
WriteStandardPrefix(ts, text, indent);
WritePositionAndStyle(ts, text);
WriteLayoutSVGTextBox(ts, text);
ts << "\n";
WriteResources(ts, text, indent);
WriteChildren(ts, text, indent);
}
void WriteSVGInline(WTF::TextStream& ts,
const LayoutSVGInline& text,
int indent) {
WriteStandardPrefix(ts, text, indent);
WritePositionAndStyle(ts, text);
ts << "\n";
WriteResources(ts, text, indent);
WriteChildren(ts, text, indent);
}
void WriteSVGInlineText(WTF::TextStream& ts,
const LayoutSVGInlineText& text,
int indent) {
WriteStandardPrefix(ts, text, indent);
WritePositionAndStyle(ts, text);
ts << "\n";
WriteSVGInlineTextBoxes(ts, text, indent);
}
void WriteSVGImage(WTF::TextStream& ts,
const LayoutSVGImage& image,
int indent) {
WriteStandardPrefix(ts, image, indent);
WritePositionAndStyle(ts, image);
ts << "\n";
WriteResources(ts, image, indent);
}
void Write(WTF::TextStream& ts, const LayoutSVGShape& shape, int indent) {
WriteStandardPrefix(ts, shape, indent);
ts << shape << "\n";
WriteResources(ts, shape, indent);
}
// Get the LayoutSVGResourceFilter from the 'filter' property iff the 'filter'
// is a single url(...) reference.
static LayoutSVGResourceFilter* GetFilterResourceForSVG(
SVGResourceClient& client,
const ComputedStyle& style) {
if (!style.HasFilter())
return nullptr;
const FilterOperations& operations = style.Filter();
if (operations.size() != 1)
return nullptr;
const auto* reference_filter =
DynamicTo<ReferenceFilterOperation>(*operations.at(0));
if (!reference_filter)
return nullptr;
return GetSVGResourceAsType<LayoutSVGResourceFilter>(
client, reference_filter->Resource());
}
static void WriteSVGResourceReferencePrefix(
WTF::TextStream& ts,
const char* resource_name,
const LayoutSVGResourceContainer* resource_object,
const AtomicString& url,
const TreeScope& tree_scope,
int indent) {
AtomicString id =
SVGURIReference::FragmentIdentifierFromIRIString(url, tree_scope);
WriteIndent(ts, indent);
ts << " ";
WriteNameAndQuotedValue(ts, resource_name, id);
ts << " ";
WriteStandardPrefix(ts, *resource_object, 0);
}
void WriteResources(WTF::TextStream& ts,
const LayoutObject& object,
int indent) {
const FloatRect reference_box = object.ObjectBoundingBox();
const ComputedStyle& style = object.StyleRef();
TreeScope& tree_scope = object.GetDocument();
SVGResourceClient* client = SVGResources::GetClient(object);
if (!client)
return;
if (auto* masker = GetSVGResourceAsType<LayoutSVGResourceMasker>(
*client, style.MaskerResource())) {
WriteSVGResourceReferencePrefix(ts, "masker", masker,
style.MaskerResource()->Url(), tree_scope,
indent);
ts << " " << masker->ResourceBoundingBox(reference_box, 1) << "\n";
}
if (const ClipPathOperation* clip_path = style.ClipPath()) {
if (LayoutSVGResourceClipper* clipper =
GetSVGResourceAsType(*client, clip_path)) {
DCHECK_EQ(clip_path->GetType(), ClipPathOperation::REFERENCE);
const auto& clip_path_reference =
To<ReferenceClipPathOperation>(*clip_path);
WriteSVGResourceReferencePrefix(ts, "clipPath", clipper,
clip_path_reference.Url(), tree_scope,
indent);
ts << " " << clipper->ResourceBoundingBox(reference_box) << "\n";
}
}
// TODO(fs): Only handles the single url(...) case. Do we care?
if (LayoutSVGResourceFilter* filter =
GetFilterResourceForSVG(*client, style)) {
DCHECK(style.HasFilter());
DCHECK_EQ(style.Filter().size(), 1u);
const FilterOperation& filter_operation = *style.Filter().at(0);
DCHECK_EQ(filter_operation.GetType(), FilterOperation::REFERENCE);
const auto& reference_filter_operation =
To<ReferenceFilterOperation>(filter_operation);
WriteSVGResourceReferencePrefix(ts, "filter", filter,
reference_filter_operation.Url(),
tree_scope, indent);
ts << " " << filter->ResourceBoundingBox(reference_box) << "\n";
}
}
} // namespace blink