text: /*
   * Copyright (C) 2006 Apple Inc.  All rights reserved.
   * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
   * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com>
   * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
   * Copyright (C) 2010 Holger Hans Peter Freyther
   * Copyright (C) 2014 Igalia S.L.
   *
   * 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 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 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 "config.h"
  #include "FontCascade.h"
  
  #if USE(CAIRO)
  
  #include "AffineTransform.h"
  #include "CairoUtilities.h"
  #include "Font.h"
  #include "GlyphBuffer.h"
  #include "Gradient.h"
  #include "GraphicsContext.h"
  #include "ImageBuffer.h"
  #include "Pattern.h"
  #include "PlatformContextCairo.h"
  #include "PlatformPathCairo.h"
  #include "ShadowBlur.h"
  
  namespace WebCore {
  
  void FontCascade::drawGlyphs(GraphicsContext& context, const Font& font, const GlyphBuffer& glyphBuffer,
      unsigned from, unsigned numGlyphs, const FloatPoint& point, FontSmoothingMode)
  {
      if (!font.platformData().size())
          return;
  
      auto xOffset = point.x();
      Vector<cairo_glyph_t> glyphs(numGlyphs);
      {
          ASSERT(from + numGlyphs <= glyphBuffer.size());
          auto* glyphsData = glyphBuffer.glyphs(from);
          auto* advances = glyphBuffer.advances(from);
  
          auto yOffset = point.y();
          for (size_t i = 0; i < numGlyphs; ++i) {
              glyphs[i] = { glyphsData[i], xOffset, yOffset };
              xOffset += advances[i].width();
          }
      }
  
      cairo_scaled_font_t* scaledFont = font.platformData().scaledFont();
      double syntheticBoldOffset = font.syntheticBoldOffset();
  
      ASSERT(context.hasPlatformContext());
      Cairo::drawGlyphs(*context.platformContext(), context.state(), point, scaledFont, syntheticBoldOffset, glyphs, xOffset, context);
  }
  
  #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
  struct GlyphIterationState {
      GlyphIterationState(FloatPoint startingPoint, FloatPoint currentPoint, float centerOfLine, float minX, float maxX)
          : startingPoint(startingPoint)
          , currentPoint(currentPoint)
          , centerOfLine(centerOfLine)
          , minX(minX)
          , maxX(maxX)
      {
      }
      FloatPoint startingPoint;
      FloatPoint currentPoint;
      float centerOfLine;
      float minX;
      float maxX;
  };
  
  static bool findIntersectionPoint(float y, FloatPoint p1, FloatPoint p2, float& x)
  {
      x = p1.x() + (y - p1.y()) * (p2.x() - p1.x()) / (p2.y() - p1.y());
      return (p1.y() < y && p2.y() > y) || (p1.y() > y && p2.y() < y);
  }
  
  static void updateX(GlyphIterationState& state, float x)
  {
      state.minX = std::min(state.minX, x);
      state.maxX = std::max(state.maxX, x);
  }
  
  // This function is called by Path::apply and is therefore invoked for each contour in a glyph. This
  // function models each contours as a straight line and calculates the intersections between each
  // pseudo-contour and the vertical center of the underline found in GlyphIterationState::centerOfLine.
  // It keeps track of the leftmost and rightmost intersection in  GlyphIterationState::minX and 
  // GlyphIterationState::maxX.
  static void findPathIntersections(GlyphIterationState& state, const PathElement& element)
  {
      bool doIntersection = false;
      FloatPoint point = FloatPoint();
      switch (element.type) {
      case PathElementMoveToPoint:
          state.startingPoint = element.points[0];
          state.currentPoint = element.points[0];
          break;
      case PathElementAddLineToPoint:
          doIntersection = true;
          point = element.points[0];
          break;
      case PathElementAddQuadCurveToPoint:
          doIntersection = true;
          point = element.points[1];
          break;
      case PathElementAddCurveToPoint:
          doIntersection = true;
          point = element.points[2];
          break;
      case PathElementCloseSubpath:
          doIntersection = true;
          point = state.startingPoint;
          break;
      }
  
      if (!doIntersection)
          return;
  
      float x;
      if (findIntersectionPoint(state.centerOfLine, state.currentPoint, point, x))
          updateX(state, x);
  
      state.currentPoint = point;
  }
  
  class CairoGlyphToPathTranslator final : public GlyphToPathTranslator {
  public:
      CairoGlyphToPathTranslator(const TextRun& textRun, const GlyphBuffer& glyphBuffer, const FloatPoint& textOrigin)
          : m_index(0)
          , m_textRun(textRun)
          , m_glyphBuffer(glyphBuffer)
          , m_fontData(glyphBuffer.fontAt(m_index))
          , m_translation(AffineTransform().translate(textOrigin.x(), textOrigin.y()))
      {
      }
  
      bool containsMorePaths() final { return m_index != m_glyphBuffer.size(); }
      Path path() final;
      std::pair<float, float> extents() final;
      GlyphUnderlineType underlineType() final;
      void advance() final;
  
  private:
      unsigned m_index;
      const TextRun& m_textRun;
      const GlyphBuffer& m_glyphBuffer;
      const Font* m_fontData;
      AffineTransform m_translation;
  };
  
  Path CairoGlyphToPathTranslator::path()
  {
      Path path;
      path.ensurePlatformPath();
  
      cairo_glyph_t cairoGlyph = { m_glyphBuffer.glyphAt(m_index), 0, 0 };
      cairo_set_scaled_font(path.platformPath()->context(), m_fontData->platformData().scaledFont());
      cairo_glyph_path(path.platformPath()->context(), &cairoGlyph, 1);
  
      float syntheticBoldOffset = m_fontData->syntheticBoldOffset();
      if (syntheticBoldOffset) {
          cairo_translate(path.platformPath()->context(), syntheticBoldOffset, 0);
          cairo_glyph_path(path.platformPath()->context(), &cairoGlyph, 1);
      }
  
      path.transform(m_translation);
      return path;
  }
  
  std::pair<float, float> CairoGlyphToPathTranslator::extents()
  {
      FloatPoint beginning = m_translation.mapPoint(FloatPoint());
      FloatSize end = m_translation.mapSize(m_glyphBuffer.advanceAt(m_index));
      return std::make_pair(static_cast<float>(beginning.x()), static_cast<float>(beginning.x() + end.width()));
  }
  
  GlyphToPathTranslator::GlyphUnderlineType CairoGlyphToPathTranslator::underlineType()
  {
      return computeUnderlineType(m_textRun, m_glyphBuffer, m_index);
  }
  
  void CairoGlyphToPathTranslator::advance()
  {
      GlyphBufferAdvance advance = m_glyphBuffer.advanceAt(m_index);
      m_translation = m_translation.translate(advance.width(), advance.height());
      ++m_index;
      if (m_index < m_glyphBuffer.size())
          m_fontData = m_glyphBuffer.fontAt(m_index);
  }
  
  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());
      CairoGlyphToPathTranslator translator(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;
  }
  #endif // ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
  
  } // namespace WebCore
  
  #endif // USE(CAIRO)
  