blob: 2cd2bea2ed9c68f6a0b7930016bca6c3a1d0445b [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/layout/svg/svg_resources_cache.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_circle_element.h"
#include "third_party/blink/renderer/core/svg/svg_ellipse_element.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 <math.h>
#include <memory>
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 << "\"]";
}
static void WriteQuotedSVGResource(WTF::TextStream& ts,
const char* name,
const StyleSVGResource* value,
TreeScope& tree_scope) {
DCHECK(value);
AtomicString id = SVGURIReference::FragmentIdentifierFromIRIString(
value->Url(), tree_scope);
WriteNameAndQuotedValue(ts, name, id);
}
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;
}
namespace {
template <typename Enum>
String SVGEnumerationToString(Enum value) {
const SVGEnumerationStringEntries& entries = GetStaticStringEntries<Enum>();
SVGEnumerationStringEntries::const_iterator it = entries.begin();
SVGEnumerationStringEntries::const_iterator it_end = entries.end();
for (; it != it_end; ++it) {
if (value == it->first)
return it->second;
}
NOTREACHED();
return String();
}
} // namespace
static WTF::TextStream& operator<<(WTF::TextStream& ts,
const SVGUnitTypes::SVGUnitType& unit_type) {
ts << SVGEnumerationToString<SVGUnitTypes::SVGUnitType>(unit_type);
return ts;
}
static WTF::TextStream& operator<<(WTF::TextStream& ts,
const SVGMarkerUnitsType& marker_unit) {
ts << SVGEnumerationToString<SVGMarkerUnitsType>(marker_unit);
return ts;
}
static WTF::TextStream& operator<<(WTF::TextStream& ts,
const SVGMarkerOrientType& orient_type) {
ts << SVGEnumerationToString<SVGMarkerOrientType>(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) {
ts << SVGEnumerationToString<SVGSpreadMethodType>(type).UpperASCII();
return ts;
}
static void WriteSVGPaintingResource(
WTF::TextStream& ts,
const SVGPaintDescription& paint_description) {
DCHECK(paint_description.is_valid);
if (!paint_description.resource) {
ts << "[type=SOLID] [color=" << paint_description.color << "]";
return;
}
LayoutSVGResourcePaintServer* paint_server_container =
paint_description.resource;
SVGElement* element = paint_server_container->GetElement();
DCHECK(element);
if (paint_server_container->ResourceType() == kPatternResourceType)
ts << "[type=PATTERN]";
else if (paint_server_container->ResourceType() ==
kLinearGradientResourceType)
ts << "[type=LINEAR-GRADIENT]";
else if (paint_server_container->ResourceType() ==
kRadialGradientResourceType)
ts << "[type=RADIAL-GRADIENT]";
ts << " [id=\"" << element->GetIdAttribute() << "\"]";
}
static void WriteStyle(WTF::TextStream& ts, const LayoutObject& object) {
const ComputedStyle& style = object.StyleRef();
const SVGComputedStyle& svg_style = style.SvgStyle();
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()) {
const LayoutSVGShape& shape = static_cast<const LayoutSVGShape&>(object);
DCHECK(shape.GetElement());
SVGPaintDescription stroke_paint_description =
LayoutSVGResourcePaintServer::RequestPaintDescription(
shape, shape.StyleRef(), kApplyToStrokeMode);
if (stroke_paint_description.is_valid) {
TextStreamSeparator s(" ");
ts << " [stroke={" << s;
WriteSVGPaintingResource(ts, stroke_paint_description);
SVGLengthContext length_context(shape.GetElement());
double dash_offset =
length_context.ValueForLength(svg_style.StrokeDashOffset(), style);
double stroke_width =
length_context.ValueForLength(svg_style.StrokeWidth());
DashArray dash_array = SVGLayoutSupport::ResolveSVGDashArray(
*svg_style.StrokeDashArray(), style, length_context);
WriteIfNotDefault(ts, "opacity", svg_style.StrokeOpacity(), 1.0f);
WriteIfNotDefault(ts, "stroke width", stroke_width, 1.0);
WriteIfNotDefault(ts, "miter limit", svg_style.StrokeMiterLimit(), 4.0f);
WriteIfNotDefault(ts, "line cap", svg_style.CapStyle(), kButtCap);
WriteIfNotDefault(ts, "line join", svg_style.JoinStyle(), kMiterJoin);
WriteIfNotDefault(ts, "dash offset", dash_offset, 0.0);
if (!dash_array.IsEmpty())
WriteNameValuePair(ts, "dash array", dash_array);
ts << "}]";
}
SVGPaintDescription fill_paint_description =
LayoutSVGResourcePaintServer::RequestPaintDescription(
shape, shape.StyleRef(), kApplyToFillMode);
if (fill_paint_description.is_valid) {
TextStreamSeparator s(" ");
ts << " [fill={" << s;
WriteSVGPaintingResource(ts, fill_paint_description);
WriteIfNotDefault(ts, "opacity", svg_style.FillOpacity(), 1.0f);
WriteIfNotDefault(ts, "fill rule", svg_style.FillRule(), RULE_NONZERO);
ts << "}]";
}
WriteIfNotDefault(ts, "clip rule", svg_style.ClipRule(), RULE_NONZERO);
}
TreeScope& tree_scope = object.GetDocument();
WriteSVGResourceIfNotNull(ts, "start marker", svg_style.MarkerStartResource(),
tree_scope);
WriteSVGResourceIfNotNull(ts, "middle marker", svg_style.MarkerMidResource(),
tree_scope);
WriteSVGResourceIfNotNull(ts, "end marker", svg_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();
const SVGComputedStyle& svg_style = style.SvgStyle();
if (IsSVGRectElement(*svg_element)) {
WriteNameValuePair(ts, "x",
length_context.ValueForLength(svg_style.X(), style,
SVGLengthMode::kWidth));
WriteNameValuePair(ts, "y",
length_context.ValueForLength(svg_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 (IsSVGLineElement(*svg_element)) {
SVGLineElement& element = ToSVGLineElement(*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 (IsSVGEllipseElement(*svg_element)) {
WriteNameValuePair(ts, "cx",
length_context.ValueForLength(svg_style.Cx(), style,
SVGLengthMode::kWidth));
WriteNameValuePair(ts, "cy",
length_context.ValueForLength(svg_style.Cy(), style,
SVGLengthMode::kHeight));
WriteNameValuePair(ts, "rx",
length_context.ValueForLength(svg_style.Rx(), style,
SVGLengthMode::kWidth));
WriteNameValuePair(ts, "ry",
length_context.ValueForLength(svg_style.Ry(), style,
SVGLengthMode::kHeight));
} else if (IsSVGCircleElement(*svg_element)) {
WriteNameValuePair(ts, "cx",
length_context.ValueForLength(svg_style.Cx(), style,
SVGLengthMode::kWidth));
WriteNameValuePair(ts, "cy",
length_context.ValueForLength(svg_style.Cy(), style,
SVGLengthMode::kHeight));
WriteNameValuePair(ts, "r",
length_context.ValueForLength(svg_style.R(), style,
SVGLengthMode::kOther));
} else if (IsSVGPolyElement(*svg_element)) {
WriteNameAndQuotedValue(ts, "points",
ToSVGPolyElement(*svg_element)
.Points()
->CurrentValue()
->ValueAsString());
} else if (IsSVGPathElement(*svg_element)) {
const StylePath& path =
svg_style.D() ? *svg_style.D() : *StylePath::EmptyPath();
WriteNameAndQuotedValue(ts, "data",
BuildStringFromByteStream(path.ByteStream()));
} 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) {
SVGRootInlineBox* box = ToSVGRootInlineBox(text.FirstRootBox());
if (!box)
return;
// FIXME: Remove this hack, once the new text layout engine is completly
// landed. We want to preserve the old layout 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 SVGComputedStyle& svg_style = text_line_layout.StyleRef().SvgStyle();
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 layout test results for now.
ts << "chunk 1 ";
ETextAnchor anchor = svg_style.TextAnchor();
bool is_vertical_text =
!text_line_layout.StyleRef().IsHorizontalWritingMode();
if (anchor == TA_MIDDLE) {
ts << "(middle anchor";
if (is_vertical_text)
ts << ", vertical";
ts << ") ";
} else if (anchor == TA_END) {
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()) {
if (!box->IsSVGInlineTextBox())
continue;
WriteSVGInlineTextBox(ts, ToSVGInlineTextBox(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);
Element* element = ToElement(object.GetNode());
const AtomicString& id = element->GetIdAttribute();
WriteNameAndQuotedValue(ts, "id", id);
LayoutSVGResourceContainer* resource =
ToLayoutSVGResourceContainer(const_cast<LayoutObject*>(&object));
DCHECK(resource);
if (resource->ResourceType() == kMaskerResourceType) {
LayoutSVGResourceMasker* masker = ToLayoutSVGResourceMasker(resource);
WriteNameValuePair(ts, "maskUnits", masker->MaskUnits());
WriteNameValuePair(ts, "maskContentUnits", masker->MaskContentUnits());
ts << "\n";
} else if (resource->ResourceType() == kFilterResourceType) {
LayoutSVGResourceFilter* filter = ToLayoutSVGResourceFilter(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;
Filter* dummy_filter =
Filter::Create(dummy_rect, dummy_rect, 1, Filter::kBoundingBox);
SVGFilterBuilder builder(dummy_filter->GetSourceGraphic());
builder.BuildGraph(dummy_filter, ToSVGFilterElement(*filter->GetElement()),
dummy_rect);
if (FilterEffect* last_effect = builder.LastEffect())
last_effect->ExternalRepresentation(ts, indent + 1);
} else if (resource->ResourceType() == kClipperResourceType) {
WriteNameValuePair(ts, "clipPathUnits",
ToLayoutSVGResourceClipper(resource)->ClipPathUnits());
ts << "\n";
} else if (resource->ResourceType() == kMarkerResourceType) {
LayoutSVGResourceMarker* marker = ToLayoutSVGResourceMarker(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;
ToSVGPatternElement(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;
ToSVGLinearGradientElement(gradient->GetElement())
->CollectGradientAttributes(attributes);
WriteCommonGradientProperties(ts, attributes);
ts << " [start=" << gradient->StartPoint(attributes)
<< "] [end=" << gradient->EndPoint(attributes) << "]\n";
} else if (resource->ResourceType() == kRadialGradientResourceType) {
LayoutSVGResourceRadialGradient* gradient =
ToLayoutSVGResourceRadialGradient(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;
ToSVGRadialGradientElement(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) {
// Currently LayoutSVGResourceFilterPrimitive has no meaningful output.
if (container.IsSVGResourceFilterPrimitive())
return;
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";
WriteResources(ts, text, indent);
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);
}
void WriteResources(WTF::TextStream& ts,
const LayoutObject& object,
int indent) {
SVGResources* resources =
SVGResourcesCache::CachedResourcesForLayoutObject(object);
if (!resources)
return;
const ComputedStyle& style = object.StyleRef();
TreeScope& tree_scope = object.GetDocument();
if (LayoutSVGResourceMasker* masker = resources->Masker()) {
WriteIndent(ts, indent);
ts << " ";
WriteQuotedSVGResource(ts, "masker", style.SvgStyle().MaskerResource(),
tree_scope);
ts << " ";
WriteStandardPrefix(ts, *masker, 0);
ts << " " << masker->ResourceBoundingBox(&object) << "\n";
}
if (LayoutSVGResourceClipper* clipper = resources->Clipper()) {
DCHECK(style.ClipPath());
DCHECK_EQ(style.ClipPath()->GetType(), ClipPathOperation::REFERENCE);
const ReferenceClipPathOperation& clip_path_reference =
ToReferenceClipPathOperation(*style.ClipPath());
AtomicString id = SVGURIReference::FragmentIdentifierFromIRIString(
clip_path_reference.Url(), tree_scope);
WriteIndent(ts, indent);
ts << " ";
WriteNameAndQuotedValue(ts, "clipPath", id);
ts << " ";
WriteStandardPrefix(ts, *clipper, 0);
ts << " " << clipper->ResourceBoundingBox(object.ObjectBoundingBox())
<< "\n";
}
if (LayoutSVGResourceFilter* filter = resources->Filter()) {
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 =
ToReferenceFilterOperation(filter_operation);
AtomicString id = SVGURIReference::FragmentIdentifierFromIRIString(
reference_filter_operation.Url(), tree_scope);
WriteIndent(ts, indent);
ts << " ";
WriteNameAndQuotedValue(ts, "filter", id);
ts << " ";
WriteStandardPrefix(ts, *filter, 0);
ts << " " << filter->ResourceBoundingBox(&object) << "\n";
}
}
} // namespace blink