WebCore/rendering/SVGTextChunkLayoutInfo.h
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 04 Oct 2010 01:32:07 +0300
changeset 2 303757a437d3
parent 0 4f2f89ce4247
permissions -rw-r--r--
Revision: 201037 Kit: 201039

/*
 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
 * Copyright (C) Research In Motion Limited 2010. 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.
 *
 */

#ifndef SVGTextChunkLayoutInfo_h
#define SVGTextChunkLayoutInfo_h

#if ENABLE(SVG)
#include "AffineTransform.h"
#include "SVGCharacterData.h"
#include "SVGRenderStyle.h"
#include "SVGTextContentElement.h"

#include <wtf/Assertions.h>
#include <wtf/Vector.h>

namespace WebCore {

class InlineBox;
class InlineFlowBox;
class SVGInlineTextBox;

// A SVGTextChunk directly corresponds to the definition of a "text chunk" per SVG 1.1 specification
// For example, each absolute positioned character starts a text chunk (much more to respect, see spec).
// Each SVGTextChunk contains a Vector of SVGInlineBoxCharacterRange, describing how many boxes are spanned
// by this chunk. Following two examples should clarify the code a bit:
//
// 1. <text x="10 20 30">ABC</text> - one InlineTextBox is created, three SVGTextChunks each with one SVGInlineBoxCharaterRange
// [SVGTextChunk 1]
//    [SVGInlineBoxCharacterRange 1, startOffset=0, endOffset=1, box=0x1]
// [SVGTextChunk 2]
//    [SVGInlineBoxCharacterRange 1, startOffset=1, endOffset=2, box=0x1]
// [SVGTextChunk 3]
//    [SVGInlineBoxCharacterRange 1, startOffset=2, endOffset=3, box=0x1]
//
// 2. <text x="10">A<tspan>B</tspan>C</text> - three InlineTextBoxs are created, one SVGTextChunk, with three SVGInlineBoxCharacterRanges
// [SVGTextChunk 1]
//    [SVGInlineBoxCharacterRange 1, startOffset=0, endOffset=1, box=0x1]
//    [SVGInlineBoxCharacterRange 2, startOffset=0, endOffset=1, box=0x2]
//    [SVGInlineBoxCharacterRange 3, startOffset=0, endOffset=1, box=0x3]
//
// High level overview of the SVG text layout code:
// Step #1) - Build Vector of SVGChar objects starting from root <text> diving into children
// Step #2) - Build Vector of SVGTextChunk objects, containing offsets into the InlineTextBoxes and SVGChar vectors
// Step #3) - Apply chunk post processing (text-anchor / textLength support, which operate on text chunks!)
// Step #4) - Propagate information, how many chunk "parts" are associated with each SVGInlineTextBox (see below)
// Step #5) - Layout all InlineBoxes, only by measuring their context rect (x/y/width/height defined through SVGChars and transformations)
// Step #6) - Layout SVGRootInlineBox, it's parent RenderSVGText block and fixup child positions, to be relative to the root box
//
// When painting a range of characters, we have to determine how many can be drawn in a row. Each absolute postioned
// character is drawn individually. After step #2) we know all text chunks, and how they span across the SVGInlineTextBoxes.
// In step #4) we build a list of text chunk "parts" and store it in each SVGInlineTextBox. A chunk "part" is a part of a
// text chunk that lives in a SVGInlineTextBox (consists of a length, width, height and a monotonic offset from the chunk begin)
// The SVGTextChunkPart object describes this information.
// When painting we can follow the regular InlineBox flow, we start painting the SVGRootInlineBox, which just asks its children
// to paint. They can paint on their own because all position information are known. Previously we used to draw _all_ characters
// from the SVGRootInlineBox, which violates the whole concept of the multiple InlineBoxes, and made text selection very hard to
// implement.

struct SVGTextChunkPart {
    SVGTextChunkPart()
        : offset(-1)
        , length(-1)
        , width(0)
        , height(0)
    {
    }

    bool isValid() const
    {
        return offset != -1
            && length != -1
            && width
            && height;
    }

    // First character of this text chunk part, defining the origin to be drawn
    Vector<SVGChar>::const_iterator firstCharacter;

    // Start offset in textRenderer()->characters() buffer.
    int offset;

    // length/width/height of chunk part
    int length;
    float width;
    float height;
};

struct SVGInlineBoxCharacterRange {
    SVGInlineBoxCharacterRange()
        : startOffset(INT_MIN)
        , endOffset(INT_MIN)
        , box(0)
    {
    }

    bool isOpen() const { return (startOffset == endOffset) && (endOffset == INT_MIN); }
    bool isClosed() const { return startOffset != INT_MIN && endOffset != INT_MIN; }

    int startOffset;
    int endOffset;

    InlineBox* box;
};

struct SVGChar;

// Convenience typedef
typedef SVGTextContentElement::SVGLengthAdjustType ELengthAdjust;

struct SVGTextChunk {
    SVGTextChunk(Vector<SVGChar>::iterator it)
        : anchor(TA_START)
        , textLength(0.0f)
        , lengthAdjust(SVGTextContentElement::LENGTHADJUST_SPACING)
        , isVerticalText(false)
        , isTextPath(false)
        , start(it)
        , end(it)
    {
    }

    // text-anchor support
    ETextAnchor anchor;

    // textLength & lengthAdjust support
    float textLength;
    ELengthAdjust lengthAdjust;
    AffineTransform ctm;

    // status flags
    bool isVerticalText : 1;
    bool isTextPath : 1;

    // main chunk data
    Vector<SVGChar>::iterator start;
    Vector<SVGChar>::iterator end;

    Vector<SVGInlineBoxCharacterRange> boxes;
};

struct SVGTextChunkLayoutInfo {
    SVGTextChunkLayoutInfo()
        : m_assignChunkProperties(true)
        , m_handlingTextPath(false)
        , m_charsIt(0)
        , m_charsBegin(0)
        , m_charsEnd(0)
        , m_chunk(0)
    {
    }

    const Vector<SVGTextChunk>& textChunks() const { return m_svgTextChunks; }

    void buildTextChunks(Vector<SVGChar>::iterator charsBegin, Vector<SVGChar>::iterator charsEnd, InlineFlowBox* start);
    void layoutTextChunks();

private:
    void startTextChunk();
    void closeTextChunk();
    void recursiveBuildTextChunks(InlineFlowBox* start);

    bool m_assignChunkProperties : 1;
    bool m_handlingTextPath : 1;

    Vector<SVGChar>::iterator m_charsIt;
    Vector<SVGChar>::iterator m_charsBegin;
    Vector<SVGChar>::iterator m_charsEnd;

    Vector<SVGTextChunk> m_svgTextChunks;
    SVGTextChunk m_chunk;
};

// Helper functions
float calculateTextAnchorShiftForTextChunk(SVGTextChunk&, ETextAnchor);
float calculateTextLengthCorrectionForTextChunk(SVGTextChunk&, ELengthAdjust, float& computedLength);

} // namespace WebCore

#endif // ENABLE(SVG)
#endif // SVGTextChunkLayoutInfo_h