text: /*
   * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
   *
   * 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/layout_svg_text_path.h"
  
  #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
  #include "third_party/blink/renderer/core/svg/svg_animated_length.h"
  #include "third_party/blink/renderer/core/svg/svg_path_element.h"
  #include "third_party/blink/renderer/core/svg/svg_text_path_element.h"
  #include "third_party/blink/renderer/platform/graphics/path.h"
  
  namespace blink {
  
  PathPositionMapper::PathPositionMapper(const Path& path,
                                         float computed_path_length,
                                         float start_offset)
      : position_calculator_(path),
        path_length_(computed_path_length),
        path_start_offset_(start_offset) {}
  
  PathPositionMapper::PositionType PathPositionMapper::PointAndNormalAtLength(
      float length,
      PointAndTangent& point_and_tangent) {
    if (length < 0)
      return kBeforePath;
    if (length > path_length_)
      return kAfterPath;
    DCHECK_GE(length, 0);
    DCHECK_LE(length, path_length_);
  
    point_and_tangent = position_calculator_.PointAndNormalAtLength(length);
    return kOnPath;
  }
  
  LayoutSVGTextPath::LayoutSVGTextPath(Element* element)
      : LayoutSVGInline(element) {}
  
  bool LayoutSVGTextPath::IsChildAllowed(LayoutObject* child,
                                         const ComputedStyle&) const {
    NOT_DESTROYED();
    if (child->IsText())
      return SVGLayoutSupport::IsLayoutableTextNode(child);
  
    return child->IsSVGInline() && !child->IsSVGTextPath();
  }
  
  std::unique_ptr<PathPositionMapper> LayoutSVGTextPath::LayoutPath() const {
    NOT_DESTROYED();
    const auto& text_path_element = To<SVGTextPathElement>(*GetNode());
    Element* target_element = SVGURIReference::TargetElementFromIRIString(
        text_path_element.HrefString(), text_path_element.OriginatingTreeScope());
  
    const auto* path_element = DynamicTo<SVGPathElement>(target_element);
    if (!path_element)
      return nullptr;
  
    Path path_data = path_element->AsPath();
    if (path_data.IsEmpty())
      return nullptr;
  
    // Spec: The 'transform' attribute on the referenced 'path' ...
    // element represents a supplemental transformation relative to the current
    // user coordinate system for the current 'text' element, including any
    // adjustments to the current user coordinate system due to a possible
    // 'transform' property on the current 'text' element.
    // https://svgwg.org/svg2-draft/text.html#TextPathElement
    path_data.Transform(
        path_element->CalculateTransform(SVGElement::kIncludeMotionTransform));
  
    // Determine the length to resolve any percentage 'startOffset'
    // against - either 'pathLength' (author path length) or the
    // computed length of the path.
    float computed_path_length = path_data.length();
    float author_path_length = path_element->AuthorPathLength();
    float offset_scale = 1;
    if (!std::isnan(author_path_length)) {
      offset_scale = SVGGeometryElement::PathLengthScaleFactor(
          computed_path_length, author_path_length);
    } else {
      author_path_length = computed_path_length;
    }
  
    const SVGLength& start_offset =
        *text_path_element.startOffset()->CurrentValue();
    float path_start_offset = start_offset.ValueAsPercentage();
    if (start_offset.IsPercentage())
      path_start_offset *= author_path_length;
  
    path_start_offset *= offset_scale;
  
    return std::make_unique<PathPositionMapper>(path_data, computed_path_length,
                                                path_start_offset);
  }
  
  }  // namespace blink
  