blob: 5319bbbf3942895afd31ed3992d521bd4b18bf2c [file] [log] [blame]
/*
* Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
* Copyright (C) 2006 Apple Computer Inc.
* Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
* Copyright (C) Research In Motion Limited 2010. All rights reserved.
* Copyright (C) 2011 Torch Mobile (Beijing) CO. Ltd. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h"
#include "third_party/blink/renderer/core/layout/api/line_layout_api_shim.h"
#include "third_party/blink/renderer/core/layout/api/line_layout_block_flow.h"
#include "third_party/blink/renderer/core/layout/api/line_layout_svg_inline_text.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h"
#include "third_party/blink/renderer/core/layout/svg/line/svg_inline_flow_box.h"
#include "third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.h"
#include "third_party/blink/renderer/core/layout/svg/svg_text_layout_engine.h"
#include "third_party/blink/renderer/core/paint/svg_root_inline_box_painter.h"
namespace blink {
void SVGRootInlineBox::Paint(const PaintInfo& paint_info,
const LayoutPoint& paint_offset,
LayoutUnit,
LayoutUnit) const {
SVGRootInlineBoxPainter(*this).Paint(paint_info, paint_offset);
}
void SVGRootInlineBox::MarkDirty() {
for (InlineBox* child = FirstChild(); child; child = child->NextOnLine())
child->MarkDirty();
RootInlineBox::MarkDirty();
}
void SVGRootInlineBox::ComputePerCharacterLayoutInformation() {
LayoutSVGText& text_root =
ToLayoutSVGText(*LineLayoutAPIShim::LayoutObjectFrom(Block()));
const Vector<LayoutSVGInlineText*>& descendant_text_nodes =
text_root.DescendantTextNodes();
if (descendant_text_nodes.IsEmpty())
return;
if (text_root.NeedsReordering())
ReorderValueLists();
// Perform SVG text layout phase two (see SVGTextLayoutEngine for details).
SVGTextLayoutEngine character_layout(descendant_text_nodes);
character_layout.LayoutCharactersInTextBoxes(this);
// Perform SVG text layout phase three (see SVGTextChunkBuilder for details).
character_layout.FinishLayout();
// Perform SVG text layout phase four
// Position & resize all SVGInlineText/FlowBoxes in the inline box tree,
// resize the root box as well as the LayoutSVGText parent block.
LayoutInlineBoxes(*this);
// Let the HTML block space originate from the local SVG coordinate space.
LineLayoutBlockFlow parent_block = Block();
parent_block.SetLocation(LayoutPoint());
// The width could be any value, but set it so that a line box will mirror
// within the childRect when its coordinates are converted between physical
// block direction and flipped block direction, for ease of understanding of
// flipped coordinates. The height doesn't matter.
parent_block.SetSize(LayoutSize(X() * 2 + Width(), LayoutUnit()));
SetLineTopBottomPositions(LogicalTop(), LogicalBottom(), LogicalTop(),
LogicalBottom());
}
FloatRect SVGRootInlineBox::LayoutInlineBoxes(InlineBox& box) {
FloatRect rect;
if (box.IsSVGInlineTextBox()) {
rect = ToSVGInlineTextBox(box).CalculateBoundaries();
} else {
for (InlineBox* child = ToInlineFlowBox(box).FirstChild(); child;
child = child->NextOnLine())
rect.Unite(LayoutInlineBoxes(*child));
}
LayoutRect logical_rect(rect);
if (!box.IsHorizontal())
logical_rect.SetSize(logical_rect.Size().TransposedSize());
box.SetX(logical_rect.X());
box.SetY(logical_rect.Y());
box.SetLogicalWidth(logical_rect.Width());
if (box.IsSVGInlineTextBox())
ToSVGInlineTextBox(box).SetLogicalHeight(logical_rect.Height());
else if (box.IsSVGInlineFlowBox())
ToSVGInlineFlowBox(box).SetLogicalHeight(logical_rect.Height());
else
ToSVGRootInlineBox(box).SetLogicalHeight(logical_rect.Height());
return rect;
}
InlineBox* SVGRootInlineBox::ClosestLeafChildForPosition(
const LayoutPoint& point) {
InlineBox* first_leaf = FirstLeafChild();
InlineBox* last_leaf = LastLeafChild();
if (first_leaf == last_leaf)
return first_leaf;
// FIXME: Check for vertical text!
InlineBox* closest_leaf = nullptr;
for (InlineBox* leaf = first_leaf; leaf; leaf = leaf->NextLeafChild()) {
if (!leaf->IsSVGInlineTextBox())
continue;
if (point.Y() < leaf->Y())
continue;
if (point.Y() > leaf->Y() + leaf->VirtualLogicalHeight())
continue;
closest_leaf = leaf;
if (point.X() < leaf->X() + leaf->LogicalWidth())
return leaf;
}
return closest_leaf ? closest_leaf : last_leaf;
}
static inline void SwapPositioningValuesInTextBoxes(
SVGInlineTextBox* first_text_box,
SVGInlineTextBox* last_text_box) {
LineLayoutSVGInlineText first_text_node =
LineLayoutSVGInlineText(first_text_box->GetLineLayoutItem());
SVGCharacterDataMap& first_character_data_map =
first_text_node.CharacterDataMap();
SVGCharacterDataMap::iterator it_first =
first_character_data_map.find(first_text_box->Start() + 1);
if (it_first == first_character_data_map.end())
return;
LineLayoutSVGInlineText last_text_node =
LineLayoutSVGInlineText(last_text_box->GetLineLayoutItem());
SVGCharacterDataMap& last_character_data_map =
last_text_node.CharacterDataMap();
SVGCharacterDataMap::iterator it_last =
last_character_data_map.find(last_text_box->Start() + 1);
if (it_last == last_character_data_map.end())
return;
// We only want to perform the swap if both inline boxes are absolutely
// positioned.
std::swap(it_first->value, it_last->value);
}
static inline void ReverseInlineBoxRangeAndValueListsIfNeeded(
Vector<InlineBox*>::iterator first,
Vector<InlineBox*>::iterator last) {
// This is a copy of std::reverse(first, last). It additionally assures
// that the metrics map within the layoutObjects belonging to the
// InlineBoxes are reordered as well.
while (true) {
if (first == last || first == --last)
return;
if ((*last)->IsSVGInlineTextBox() && (*first)->IsSVGInlineTextBox()) {
SVGInlineTextBox* first_text_box = ToSVGInlineTextBox(*first);
SVGInlineTextBox* last_text_box = ToSVGInlineTextBox(*last);
// Reordering is only necessary for BiDi text that is _absolutely_
// positioned.
if (first_text_box->Len() == 1 &&
first_text_box->Len() == last_text_box->Len())
SwapPositioningValuesInTextBoxes(first_text_box, last_text_box);
}
InlineBox* temp = *first;
*first = *last;
*last = temp;
++first;
}
}
void SVGRootInlineBox::ReorderValueLists() {
Vector<InlineBox*> leaf_boxes_in_logical_order;
CollectLeafBoxesInLogicalOrder(leaf_boxes_in_logical_order,
ReverseInlineBoxRangeAndValueListsIfNeeded);
}
bool SVGRootInlineBox::NodeAtPoint(HitTestResult& result,
const HitTestLocation& location_in_container,
const LayoutPoint& accumulated_offset,
LayoutUnit line_top,
LayoutUnit line_bottom) {
// Iterate the text boxes in reverse so that the top-most node will be considered first.
for (InlineBox* leaf = LastLeafChild(); leaf; leaf = leaf->PrevLeafChild()) {
if (!leaf->IsSVGInlineTextBox())
continue;
if (leaf->NodeAtPoint(result, location_in_container, accumulated_offset,
line_top, line_bottom))
return true;
}
return false;
}
} // namespace blink