2 * (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 2000 Dirk Mueller (mueller@kde.org)
4 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
24 #include "TextPainter.h"
26 #include "GraphicsContext.h"
27 #include "InlineTextBox.h"
28 #include "RenderCombineText.h"
29 #include "TextPaintStyle.h"
33 TextPainter::TextPainter(GraphicsContext& context, bool paintSelectedTextOnly, bool paintSelectedTextSeparately, const Font& font,
34 int startPositionInTextRun, int endPositionInTextBoxString, int length, const AtomicString& emphasisMark, RenderCombineText* combinedText, TextRun& textRun,
35 FloatRect& boxRect, FloatPoint& textOrigin, int emphasisMarkOffset, const ShadowData* textShadow, const ShadowData* selectionShadow,
36 bool textBoxIsHorizontal, TextPaintStyle& textPaintStyle, TextPaintStyle& selectionPaintStyle)
37 : m_paintSelectedTextOnly(paintSelectedTextOnly)
38 , m_paintSelectedTextSeparately(paintSelectedTextSeparately)
40 , m_startPositionInTextRun(startPositionInTextRun)
41 , m_endPositionInTextRun(endPositionInTextBoxString)
43 , m_emphasisMark(emphasisMark)
44 , m_combinedText(combinedText)
47 , m_textOrigin(textOrigin)
48 , m_emphasisMarkOffset(emphasisMarkOffset)
49 , m_textBoxIsHorizontal(textBoxIsHorizontal)
50 , m_savedDrawingStateForMask(&context, &textPaintStyle, &selectionPaintStyle, textShadow, selectionShadow)
54 static void drawTextOrEmphasisMarks(GraphicsContext& context, const Font& font, const TextRun& textRun, const AtomicString& emphasisMark,
55 int emphasisMarkOffset, const FloatPoint& point, const int from, const int to)
57 if (emphasisMark.isEmpty())
58 context.drawText(font, textRun, point, from, to);
60 context.drawEmphasisMarks(font, textRun, emphasisMark, point + IntSize(0, emphasisMarkOffset), from, to);
63 static bool isEmptyShadow(const ShadowData* shadow)
67 return shadow->location() == IntPoint() && !shadow->radius();
70 static void paintTextWithShadows(GraphicsContext* context, const Font& font, const TextRun& textRun, const AtomicString& emphasisMark,
71 int emphasisMarkOffset, int startOffset, int endOffset, int truncationPoint, const FloatPoint& textOrigin, const FloatRect& boxRect,
72 const ShadowData* shadow, bool stroked, bool horizontal)
74 Color fillColor = context->fillColor();
75 ColorSpace fillColorSpace = context->fillColorSpace();
76 bool opaque = !fillColor.hasAlpha();
78 context->setFillColor(Color::black, fillColorSpace);
82 bool shadowIsEmpty = isEmptyShadow(shadow);
84 extraOffset = roundedIntSize(InlineTextBox::applyShadowToGraphicsContext(context, shadow, boxRect, stroked, opaque, horizontal));
86 context->setFillColor(fillColor, fillColorSpace);
88 if (startOffset <= endOffset)
89 drawTextOrEmphasisMarks(*context, font, textRun, emphasisMark, emphasisMarkOffset, textOrigin + extraOffset, startOffset, endOffset);
92 drawTextOrEmphasisMarks(*context, font, textRun, emphasisMark, emphasisMarkOffset, textOrigin + extraOffset, 0, endOffset);
93 if (startOffset < truncationPoint)
94 drawTextOrEmphasisMarks(*context, font, textRun, emphasisMark, emphasisMarkOffset, textOrigin + extraOffset, startOffset, truncationPoint);
100 if (shadow->next() || stroked || !opaque)
102 else if (!shadowIsEmpty)
103 context->clearShadow();
105 shadow = shadow->next();
106 } while (shadow || stroked || !opaque);
109 void TextPainter::paintText()
111 ASSERT(m_savedDrawingStateForMask.m_textPaintStyle);
112 ASSERT(m_savedDrawingStateForMask.m_selectionPaintStyle);
114 FloatPoint boxOrigin = m_boxRect.location();
116 if (!m_paintSelectedTextOnly) {
117 // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side
118 // effect, so only when we know we're stroking, do a save/restore.
119 GraphicsContextStateSaver stateSaver(*m_savedDrawingStateForMask.m_context, m_savedDrawingStateForMask.m_textPaintStyle->strokeWidth > 0);
121 updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_textPaintStyle);
122 if (!m_paintSelectedTextSeparately || m_endPositionInTextRun <= m_startPositionInTextRun) {
123 // FIXME: Truncate right-to-left text correctly.
124 paintTextWithShadows(m_savedDrawingStateForMask.m_context, m_font, m_textRun, nullAtom, 0, 0, m_length, m_length, m_textOrigin, m_boxRect, m_savedDrawingStateForMask.m_textShadow, m_savedDrawingStateForMask.m_textPaintStyle->strokeWidth > 0, m_textBoxIsHorizontal);
126 paintTextWithShadows(m_savedDrawingStateForMask.m_context, m_font, m_textRun, nullAtom, 0, m_endPositionInTextRun, m_startPositionInTextRun, m_length, m_textOrigin, m_boxRect, m_savedDrawingStateForMask.m_textShadow, m_savedDrawingStateForMask.m_textPaintStyle->strokeWidth > 0, m_textBoxIsHorizontal);
128 if (!m_emphasisMark.isEmpty()) {
129 updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_textPaintStyle, UseEmphasisMarkColor);
131 DEPRECATED_DEFINE_STATIC_LOCAL(TextRun, objectReplacementCharacterTextRun, (&objectReplacementCharacter, 1));
132 TextRun& emphasisMarkTextRun = m_combinedText ? objectReplacementCharacterTextRun : m_textRun;
133 FloatPoint emphasisMarkTextOrigin = m_combinedText ? FloatPoint(boxOrigin.x() + m_boxRect.width() / 2, boxOrigin.y() + m_font.fontMetrics().ascent()) : m_textOrigin;
135 m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Clockwise));
137 if (!m_paintSelectedTextSeparately || m_endPositionInTextRun <= m_startPositionInTextRun) {
138 // FIXME: Truncate right-to-left text correctly.
139 paintTextWithShadows(m_savedDrawingStateForMask.m_context, m_combinedText ? m_combinedText->originalFont() : m_font, emphasisMarkTextRun, m_emphasisMark, m_emphasisMarkOffset, 0, m_length, m_length, emphasisMarkTextOrigin, m_boxRect, m_savedDrawingStateForMask.m_textShadow, m_savedDrawingStateForMask.m_textPaintStyle->strokeWidth > 0, m_textBoxIsHorizontal);
141 paintTextWithShadows(m_savedDrawingStateForMask.m_context, m_combinedText ? m_combinedText->originalFont() : m_font, emphasisMarkTextRun, m_emphasisMark, m_emphasisMarkOffset, m_endPositionInTextRun, m_startPositionInTextRun, m_length, emphasisMarkTextOrigin, m_boxRect, m_savedDrawingStateForMask.m_textShadow, m_savedDrawingStateForMask.m_textPaintStyle->strokeWidth > 0, m_textBoxIsHorizontal);
144 m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Counterclockwise));
148 if ((m_paintSelectedTextOnly || m_paintSelectedTextSeparately) && m_startPositionInTextRun < m_endPositionInTextRun) {
149 // paint only the text that is selected
150 GraphicsContextStateSaver stateSaver(*m_savedDrawingStateForMask.m_context, m_savedDrawingStateForMask.m_selectionPaintStyle->strokeWidth > 0);
152 updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_selectionPaintStyle);
153 paintTextWithShadows(m_savedDrawingStateForMask.m_context, m_font, m_textRun, nullAtom, 0, m_startPositionInTextRun, m_endPositionInTextRun, m_length, m_textOrigin, m_boxRect, m_savedDrawingStateForMask.m_selectionShadow, m_savedDrawingStateForMask.m_selectionPaintStyle->strokeWidth > 0, m_textBoxIsHorizontal);
154 if (!m_emphasisMark.isEmpty()) {
155 updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_selectionPaintStyle, UseEmphasisMarkColor);
157 DEPRECATED_DEFINE_STATIC_LOCAL(TextRun, objectReplacementCharacterTextRun, (&objectReplacementCharacter, 1));
158 TextRun& emphasisMarkTextRun = m_combinedText ? objectReplacementCharacterTextRun : m_textRun;
159 FloatPoint emphasisMarkTextOrigin = m_combinedText ? FloatPoint(boxOrigin.x() + m_boxRect.width() / 2, boxOrigin.y() + m_font.fontMetrics().ascent()) : m_textOrigin;
161 m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Clockwise));
163 paintTextWithShadows(m_savedDrawingStateForMask.m_context, m_combinedText ? m_combinedText->originalFont() : m_font, emphasisMarkTextRun, m_emphasisMark, m_emphasisMarkOffset, m_startPositionInTextRun, m_endPositionInTextRun, m_length, emphasisMarkTextOrigin, m_boxRect, m_savedDrawingStateForMask.m_selectionShadow, m_savedDrawingStateForMask.m_selectionPaintStyle->strokeWidth > 0, m_textBoxIsHorizontal);
166 m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Counterclockwise));
171 void TextPainter::paintTextInContext(GraphicsContext& context, float amountToIncreaseStrokeWidthBy)
173 SavedDrawingStateForMask savedDrawingStateForMask = m_savedDrawingStateForMask;
175 ASSERT(m_savedDrawingStateForMask.m_textPaintStyle);
176 ASSERT(m_savedDrawingStateForMask.m_selectionPaintStyle);
177 m_savedDrawingStateForMask.m_context = &context;
178 m_savedDrawingStateForMask.m_textPaintStyle->strokeWidth += amountToIncreaseStrokeWidthBy;
179 m_savedDrawingStateForMask.m_selectionPaintStyle->strokeWidth += amountToIncreaseStrokeWidthBy;
180 m_savedDrawingStateForMask.m_textShadow = nullptr;
181 m_savedDrawingStateForMask.m_selectionShadow = nullptr;
184 m_savedDrawingStateForMask = savedDrawingStateForMask;
187 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
188 DashArray TextPainter::dashesForIntersectionsWithRect(const FloatRect& lineExtents)
190 return m_font.dashesForIntersectionsWithRect(m_textRun, m_textOrigin, lineExtents);
194 } // namespace WebCore