1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "config.h" 6#include "core/paint/ListMarkerPainter.h" 7 8#include "core/paint/BlockPainter.h" 9#include "core/rendering/GraphicsContextAnnotator.h" 10#include "core/rendering/PaintInfo.h" 11#include "core/rendering/RenderListItem.h" 12#include "core/rendering/RenderListMarker.h" 13#include "core/rendering/TextRunConstructor.h" 14#include "platform/geometry/LayoutPoint.h" 15#include "platform/graphics/GraphicsContextStateSaver.h" 16#include "wtf/unicode/CharacterNames.h" 17 18namespace blink { 19 20void ListMarkerPainter::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 21{ 22 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, &m_renderListMarker); 23 24 if (paintInfo.phase != PaintPhaseForeground) 25 return; 26 27 if (m_renderListMarker.style()->visibility() != VISIBLE) 28 return; 29 30 LayoutPoint boxOrigin(paintOffset + m_renderListMarker.location()); 31 LayoutRect overflowRect(m_renderListMarker.visualOverflowRect()); 32 overflowRect.moveBy(boxOrigin); 33 34 if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect))) 35 return; 36 37 LayoutRect box(boxOrigin, m_renderListMarker.size()); 38 39 IntRect marker = m_renderListMarker.getRelativeMarkerRect(); 40 marker.moveBy(roundedIntPoint(boxOrigin)); 41 42 GraphicsContext* context = paintInfo.context; 43 44 if (m_renderListMarker.isImage()) { 45 context->drawImage(m_renderListMarker.image()->image(&m_renderListMarker, marker.size()).get(), marker); 46 if (m_renderListMarker.selectionState() != RenderObject::SelectionNone) { 47 LayoutRect selRect = m_renderListMarker.localSelectionRect(); 48 selRect.moveBy(boxOrigin); 49 context->fillRect(pixelSnappedIntRect(selRect), m_renderListMarker.selectionBackgroundColor()); 50 } 51 return; 52 } 53 54 if (m_renderListMarker.selectionState() != RenderObject::SelectionNone) { 55 LayoutRect selRect = m_renderListMarker.localSelectionRect(); 56 selRect.moveBy(boxOrigin); 57 context->fillRect(pixelSnappedIntRect(selRect), m_renderListMarker.selectionBackgroundColor()); 58 } 59 60 const Color color(m_renderListMarker.resolveColor(CSSPropertyColor)); 61 context->setStrokeColor(color); 62 context->setStrokeStyle(SolidStroke); 63 context->setStrokeThickness(1.0f); 64 context->setFillColor(color); 65 66 EListStyleType type = m_renderListMarker.style()->listStyleType(); 67 switch (type) { 68 case Disc: 69 context->fillEllipse(marker); 70 return; 71 case Circle: 72 context->strokeEllipse(marker); 73 return; 74 case Square: 75 context->fillRect(marker); 76 return; 77 case NoneListStyle: 78 return; 79 case Afar: 80 case Amharic: 81 case AmharicAbegede: 82 case ArabicIndic: 83 case Armenian: 84 case BinaryListStyle: 85 case Bengali: 86 case Cambodian: 87 case CJKIdeographic: 88 case CjkEarthlyBranch: 89 case CjkHeavenlyStem: 90 case DecimalLeadingZero: 91 case DecimalListStyle: 92 case Devanagari: 93 case Ethiopic: 94 case EthiopicAbegede: 95 case EthiopicAbegedeAmEt: 96 case EthiopicAbegedeGez: 97 case EthiopicAbegedeTiEr: 98 case EthiopicAbegedeTiEt: 99 case EthiopicHalehameAaEr: 100 case EthiopicHalehameAaEt: 101 case EthiopicHalehameAmEt: 102 case EthiopicHalehameGez: 103 case EthiopicHalehameOmEt: 104 case EthiopicHalehameSidEt: 105 case EthiopicHalehameSoEt: 106 case EthiopicHalehameTiEr: 107 case EthiopicHalehameTiEt: 108 case EthiopicHalehameTig: 109 case Georgian: 110 case Gujarati: 111 case Gurmukhi: 112 case Hangul: 113 case HangulConsonant: 114 case Hebrew: 115 case Hiragana: 116 case HiraganaIroha: 117 case Kannada: 118 case Katakana: 119 case KatakanaIroha: 120 case Khmer: 121 case Lao: 122 case LowerAlpha: 123 case LowerArmenian: 124 case LowerGreek: 125 case LowerHexadecimal: 126 case LowerLatin: 127 case LowerNorwegian: 128 case LowerRoman: 129 case Malayalam: 130 case Mongolian: 131 case Myanmar: 132 case Octal: 133 case Oriya: 134 case Oromo: 135 case Persian: 136 case Sidama: 137 case Somali: 138 case Telugu: 139 case Thai: 140 case Tibetan: 141 case Tigre: 142 case TigrinyaEr: 143 case TigrinyaErAbegede: 144 case TigrinyaEt: 145 case TigrinyaEtAbegede: 146 case UpperAlpha: 147 case UpperArmenian: 148 case UpperGreek: 149 case UpperHexadecimal: 150 case UpperLatin: 151 case UpperNorwegian: 152 case UpperRoman: 153 case Urdu: 154 case Asterisks: 155 case Footnotes: 156 break; 157 } 158 if (m_renderListMarker.text().isEmpty()) 159 return; 160 161 const Font& font = m_renderListMarker.style()->font(); 162 TextRun textRun = constructTextRun(&m_renderListMarker, font, m_renderListMarker.text(), m_renderListMarker.style()); 163 164 GraphicsContextStateSaver stateSaver(*context, false); 165 if (!m_renderListMarker.style()->isHorizontalWritingMode()) { 166 marker.moveBy(roundedIntPoint(-boxOrigin)); 167 marker = marker.transposedRect(); 168 marker.moveBy(IntPoint(roundToInt(box.x()), roundToInt(box.y() - m_renderListMarker.logicalHeight()))); 169 stateSaver.save(); 170 context->translate(marker.x(), marker.maxY()); 171 context->rotate(static_cast<float>(deg2rad(90.))); 172 context->translate(-marker.x(), -marker.maxY()); 173 } 174 175 TextRunPaintInfo textRunPaintInfo(textRun); 176 textRunPaintInfo.bounds = marker; 177 IntPoint textOrigin = IntPoint(marker.x(), marker.y() + m_renderListMarker.style()->fontMetrics().ascent()); 178 179 if (type == Asterisks || type == Footnotes) { 180 context->drawText(font, textRunPaintInfo, textOrigin); 181 } else { 182 // Text is not arbitrary. We can judge whether it's RTL from the first character, 183 // and we only need to handle the direction RightToLeft for now. 184 bool textNeedsReversing = WTF::Unicode::direction(m_renderListMarker.text()[0]) == WTF::Unicode::RightToLeft; 185 StringBuilder reversedText; 186 if (textNeedsReversing) { 187 int length = m_renderListMarker.text().length(); 188 reversedText.reserveCapacity(length); 189 for (int i = length - 1; i >= 0; --i) 190 reversedText.append(m_renderListMarker.text()[i]); 191 ASSERT(reversedText.length() == reversedText.capacity()); 192 textRun.setText(reversedText.toString()); 193 } 194 195 const UChar suffix = m_renderListMarker.listMarkerSuffix(type, m_renderListMarker.listItem()->value()); 196 UChar suffixStr[2] = { 197 m_renderListMarker.style()->isLeftToRightDirection() ? suffix : ' ', 198 m_renderListMarker.style()->isLeftToRightDirection() ? ' ' : suffix 199 }; 200 TextRun suffixRun = constructTextRun(&m_renderListMarker, font, suffixStr, 2, m_renderListMarker.style(), m_renderListMarker.style()->direction()); 201 TextRunPaintInfo suffixRunInfo(suffixRun); 202 suffixRunInfo.bounds = marker; 203 204 if (m_renderListMarker.style()->isLeftToRightDirection()) { 205 context->drawText(font, textRunPaintInfo, textOrigin); 206 context->drawText(font, suffixRunInfo, textOrigin + IntSize(font.width(textRun), 0)); 207 } else { 208 context->drawText(font, suffixRunInfo, textOrigin); 209 context->drawText(font, textRunPaintInfo, textOrigin + IntSize(font.width(suffixRun), 0)); 210 } 211 } 212} 213 214} // namespace blink 215