| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1 | /* | 
| vandebo@chromium.org | 2a22e10 | 2011-01-25 21:01:34 +0000 | [diff] [blame] | 2 | * Copyright (C) 2011 Google Inc. | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 3 | * | 
|  | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 5 | * you may not use this file except in compliance with the License. | 
|  | 6 | * You may obtain a copy of the License at | 
|  | 7 | * | 
|  | 8 | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | * | 
|  | 10 | * Unless required by applicable law or agreed to in writing, software | 
|  | 11 | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 13 | * See the License for the specific language governing permissions and | 
|  | 14 | * limitations under the License. | 
|  | 15 | */ | 
|  | 16 |  | 
|  | 17 | #include "SkPDFDevice.h" | 
|  | 18 |  | 
|  | 19 | #include "SkColor.h" | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 20 | #include "SkClipStack.h" | 
| reed@google.com | 8a85d0c | 2011-06-24 19:12:12 +0000 | [diff] [blame] | 21 | #include "SkData.h" | 
| vandebo@chromium.org | fb0b0ed | 2011-04-15 20:01:17 +0000 | [diff] [blame] | 22 | #include "SkDraw.h" | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 23 | #include "SkGlyphCache.h" | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 24 | #include "SkPaint.h" | 
| vandebo@chromium.org | a518086 | 2010-10-26 19:48:49 +0000 | [diff] [blame] | 25 | #include "SkPath.h" | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 26 | #include "SkPDFFont.h" | 
| vandebo@chromium.org | eb6c759 | 2010-10-26 19:54:45 +0000 | [diff] [blame] | 27 | #include "SkPDFFormXObject.h" | 
| vandebo@chromium.org | 77bcaa3 | 2011-04-15 20:57:37 +0000 | [diff] [blame] | 28 | #include "SkPDFGraphicState.h" | 
|  | 29 | #include "SkPDFImage.h" | 
| vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 30 | #include "SkPDFShader.h" | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 31 | #include "SkPDFStream.h" | 
| vandebo@chromium.org | 77bcaa3 | 2011-04-15 20:57:37 +0000 | [diff] [blame] | 32 | #include "SkPDFTypes.h" | 
| ctguil@chromium.org | 9db86bb | 2011-03-04 21:43:27 +0000 | [diff] [blame] | 33 | #include "SkPDFUtils.h" | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 34 | #include "SkRect.h" | 
|  | 35 | #include "SkString.h" | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 36 | #include "SkTextFormatParams.h" | 
|  | 37 | #include "SkTypeface.h" | 
|  | 38 | #include "SkTypes.h" | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 39 |  | 
|  | 40 | // Utility functions | 
|  | 41 |  | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 42 | static void emit_pdf_color(SkColor color, SkWStream* result) { | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 43 | SkASSERT(SkColorGetA(color) == 0xFF);  // We handle alpha elsewhere. | 
|  | 44 | SkScalar colorMax = SkIntToScalar(0xFF); | 
| vandebo@chromium.org | 094316b | 2011-03-04 03:15:13 +0000 | [diff] [blame] | 45 | SkPDFScalar::Append( | 
| vandebo@chromium.org | cae5fba | 2011-03-28 19:03:50 +0000 | [diff] [blame] | 46 | SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), result); | 
|  | 47 | result->writeText(" "); | 
| vandebo@chromium.org | 094316b | 2011-03-04 03:15:13 +0000 | [diff] [blame] | 48 | SkPDFScalar::Append( | 
| vandebo@chromium.org | cae5fba | 2011-03-28 19:03:50 +0000 | [diff] [blame] | 49 | SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), result); | 
|  | 50 | result->writeText(" "); | 
| vandebo@chromium.org | 094316b | 2011-03-04 03:15:13 +0000 | [diff] [blame] | 51 | SkPDFScalar::Append( | 
| vandebo@chromium.org | cae5fba | 2011-03-28 19:03:50 +0000 | [diff] [blame] | 52 | SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), result); | 
|  | 53 | result->writeText(" "); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 54 | } | 
|  | 55 |  | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 56 | static SkPaint calculate_text_paint(const SkPaint& paint) { | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 57 | SkPaint result = paint; | 
|  | 58 | if (result.isFakeBoldText()) { | 
|  | 59 | SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(), | 
|  | 60 | kStdFakeBoldInterpKeys, | 
|  | 61 | kStdFakeBoldInterpValues, | 
|  | 62 | kStdFakeBoldInterpLength); | 
|  | 63 | SkScalar width = SkScalarMul(result.getTextSize(), fakeBoldScale); | 
|  | 64 | if (result.getStyle() == SkPaint::kFill_Style) | 
|  | 65 | result.setStyle(SkPaint::kStrokeAndFill_Style); | 
|  | 66 | else | 
|  | 67 | width += result.getStrokeWidth(); | 
|  | 68 | result.setStrokeWidth(width); | 
|  | 69 | } | 
|  | 70 | return result; | 
|  | 71 | } | 
|  | 72 |  | 
|  | 73 | // Stolen from measure_text in SkDraw.cpp and then tweaked. | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 74 | static void align_text(SkDrawCacheProc glyphCacheProc, const SkPaint& paint, | 
|  | 75 | const uint16_t* glyphs, size_t len, SkScalar* x, | 
|  | 76 | SkScalar* y, SkScalar* width) { | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 77 | if (paint.getTextAlign() == SkPaint::kLeft_Align && width == NULL) | 
|  | 78 | return; | 
|  | 79 |  | 
|  | 80 | SkMatrix ident; | 
|  | 81 | ident.reset(); | 
|  | 82 | SkAutoGlyphCache autoCache(paint, &ident); | 
|  | 83 | SkGlyphCache* cache = autoCache.getCache(); | 
|  | 84 |  | 
|  | 85 | const char* start = (char*)glyphs; | 
|  | 86 | const char* stop = (char*)(glyphs + len); | 
|  | 87 | SkFixed xAdv = 0, yAdv = 0; | 
|  | 88 |  | 
|  | 89 | // TODO(vandebo) This probably needs to take kerning into account. | 
|  | 90 | while (start < stop) { | 
|  | 91 | const SkGlyph& glyph = glyphCacheProc(cache, &start, 0, 0); | 
|  | 92 | xAdv += glyph.fAdvanceX; | 
|  | 93 | yAdv += glyph.fAdvanceY; | 
|  | 94 | }; | 
|  | 95 | if (width) | 
|  | 96 | *width = SkFixedToScalar(xAdv); | 
|  | 97 | if (paint.getTextAlign() == SkPaint::kLeft_Align) | 
|  | 98 | return; | 
|  | 99 |  | 
|  | 100 | SkScalar xAdj = SkFixedToScalar(xAdv); | 
|  | 101 | SkScalar yAdj = SkFixedToScalar(yAdv); | 
|  | 102 | if (paint.getTextAlign() == SkPaint::kCenter_Align) { | 
|  | 103 | xAdj = SkScalarHalf(xAdj); | 
|  | 104 | yAdj = SkScalarHalf(yAdj); | 
|  | 105 | } | 
|  | 106 | *x = *x - xAdj; | 
|  | 107 | *y = *y - yAdj; | 
|  | 108 | } | 
|  | 109 |  | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 110 | static void set_text_transform(SkScalar x, SkScalar y, SkScalar textSkewX, | 
|  | 111 | SkWStream* content) { | 
|  | 112 | // Flip the text about the x-axis to account for origin swap and include | 
|  | 113 | // the passed parameters. | 
|  | 114 | content->writeText("1 0 "); | 
|  | 115 | SkPDFScalar::Append(0 - textSkewX, content); | 
|  | 116 | content->writeText(" -1 "); | 
|  | 117 | SkPDFScalar::Append(x, content); | 
|  | 118 | content->writeText(" "); | 
|  | 119 | SkPDFScalar::Append(y, content); | 
|  | 120 | content->writeText(" Tm\n"); | 
|  | 121 | } | 
|  | 122 |  | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 123 | // It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the | 
|  | 124 | // later being our representation of an object in the PDF file. | 
|  | 125 | struct GraphicStateEntry { | 
|  | 126 | GraphicStateEntry(); | 
|  | 127 |  | 
|  | 128 | // Compare the fields we care about when setting up a new content entry. | 
|  | 129 | bool compareInitialState(const GraphicStateEntry& b); | 
|  | 130 |  | 
|  | 131 | SkMatrix fMatrix; | 
|  | 132 | // We can't do set operations on Paths, though PDF natively supports | 
|  | 133 | // intersect.  If the clip stack does anything other than intersect, | 
|  | 134 | // we have to fall back to the region.  Treat fClipStack as authoritative. | 
|  | 135 | // See http://code.google.com/p/skia/issues/detail?id=221 | 
|  | 136 | SkClipStack fClipStack; | 
|  | 137 | SkRegion fClipRegion; | 
|  | 138 |  | 
|  | 139 | // When emitting the content entry, we will ensure the graphic state | 
|  | 140 | // is set to these values first. | 
|  | 141 | SkColor fColor; | 
|  | 142 | SkScalar fTextScaleX;  // Zero means we don't care what the value is. | 
|  | 143 | SkPaint::Style fTextFill;  // Only if TextScaleX is non-zero. | 
|  | 144 | int fShaderIndex; | 
|  | 145 | int fGraphicStateIndex; | 
|  | 146 |  | 
|  | 147 | // We may change the font (i.e. for Type1 support) within a | 
|  | 148 | // ContentEntry.  This is the one currently in effect, or NULL if none. | 
|  | 149 | SkPDFFont* fFont; | 
|  | 150 | // In PDF, text size has no default value. It is only valid if fFont is | 
|  | 151 | // not NULL. | 
|  | 152 | SkScalar fTextSize; | 
|  | 153 | }; | 
|  | 154 |  | 
|  | 155 | GraphicStateEntry::GraphicStateEntry() : fColor(SK_ColorBLACK), | 
|  | 156 | fTextScaleX(SK_Scalar1), | 
|  | 157 | fTextFill(SkPaint::kFill_Style), | 
|  | 158 | fShaderIndex(-1), | 
|  | 159 | fGraphicStateIndex(-1), | 
|  | 160 | fFont(NULL), | 
|  | 161 | fTextSize(SK_ScalarNaN) { | 
|  | 162 | fMatrix.reset(); | 
|  | 163 | } | 
|  | 164 |  | 
|  | 165 | bool GraphicStateEntry::compareInitialState(const GraphicStateEntry& b) { | 
|  | 166 | return fColor == b.fColor && | 
|  | 167 | fShaderIndex == b.fShaderIndex && | 
|  | 168 | fGraphicStateIndex == b.fGraphicStateIndex && | 
|  | 169 | fMatrix == b.fMatrix && | 
|  | 170 | fClipStack == b.fClipStack && | 
|  | 171 | (fTextScaleX == 0 || | 
|  | 172 | b.fTextScaleX == 0 || | 
|  | 173 | (fTextScaleX == b.fTextScaleX && fTextFill == b.fTextFill)); | 
|  | 174 | } | 
|  | 175 |  | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 176 | class GraphicStackState { | 
|  | 177 | public: | 
|  | 178 | GraphicStackState(const SkClipStack& existingClipStack, | 
|  | 179 | const SkRegion& existingClipRegion, | 
|  | 180 | SkWStream* contentStream) | 
|  | 181 | : fStackDepth(0), | 
|  | 182 | fContentStream(contentStream) { | 
|  | 183 | fEntries[0].fClipStack = existingClipStack; | 
|  | 184 | fEntries[0].fClipRegion = existingClipRegion; | 
|  | 185 | } | 
|  | 186 |  | 
|  | 187 | void updateClip(const SkClipStack& clipStack, const SkRegion& clipRegion, | 
|  | 188 | const SkIPoint& translation); | 
|  | 189 | void updateMatrix(const SkMatrix& matrix); | 
|  | 190 | void updateDrawingState(const GraphicStateEntry& state); | 
|  | 191 |  | 
|  | 192 | void drainStack(); | 
|  | 193 |  | 
|  | 194 | private: | 
|  | 195 | void push(); | 
|  | 196 | void pop(); | 
|  | 197 | GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; } | 
|  | 198 |  | 
|  | 199 | // Conservative limit on save depth, see impl. notes in PDF 1.4 spec. | 
|  | 200 | static const int kMaxStackDepth = 12; | 
|  | 201 | GraphicStateEntry fEntries[kMaxStackDepth + 1]; | 
|  | 202 | int fStackDepth; | 
|  | 203 | SkWStream* fContentStream; | 
|  | 204 | }; | 
|  | 205 |  | 
|  | 206 | void GraphicStackState::drainStack() { | 
|  | 207 | while (fStackDepth) { | 
|  | 208 | pop(); | 
|  | 209 | } | 
|  | 210 | } | 
|  | 211 |  | 
|  | 212 | void GraphicStackState::push() { | 
|  | 213 | SkASSERT(fStackDepth < kMaxStackDepth); | 
|  | 214 | fContentStream->writeText("q\n"); | 
|  | 215 | fStackDepth++; | 
|  | 216 | fEntries[fStackDepth] = fEntries[fStackDepth - 1]; | 
|  | 217 | } | 
|  | 218 |  | 
|  | 219 | void GraphicStackState::pop() { | 
|  | 220 | SkASSERT(fStackDepth > 0); | 
|  | 221 | fContentStream->writeText("Q\n"); | 
|  | 222 | fStackDepth--; | 
|  | 223 | } | 
|  | 224 |  | 
|  | 225 | // This function initializes iter to be an interator on the "stack" argument | 
|  | 226 | // and then skips over the leading entries as specified in prefix.  It requires | 
|  | 227 | // and asserts that "prefix" will be a prefix to "stack." | 
|  | 228 | static void skip_clip_stack_prefix(const SkClipStack& prefix, | 
|  | 229 | const SkClipStack& stack, | 
|  | 230 | SkClipStack::B2FIter* iter) { | 
|  | 231 | SkClipStack::B2FIter prefixIter(prefix); | 
|  | 232 | iter->reset(stack); | 
|  | 233 |  | 
|  | 234 | const SkClipStack::B2FIter::Clip* prefixEntry; | 
|  | 235 | const SkClipStack::B2FIter::Clip* iterEntry; | 
|  | 236 |  | 
| vandebo@chromium.org | 8887ede | 2011-05-25 01:27:52 +0000 | [diff] [blame] | 237 | int count = 0; | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 238 | for (prefixEntry = prefixIter.next(); prefixEntry; | 
| vandebo@chromium.org | 8887ede | 2011-05-25 01:27:52 +0000 | [diff] [blame] | 239 | prefixEntry = prefixIter.next(), count++) { | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 240 | iterEntry = iter->next(); | 
|  | 241 | SkASSERT(iterEntry); | 
| vandebo@chromium.org | 8887ede | 2011-05-25 01:27:52 +0000 | [diff] [blame] | 242 | // Because of SkClipStack does internal intersection, the last clip | 
|  | 243 | // entry may differ. | 
|  | 244 | if(*prefixEntry != *iterEntry) { | 
|  | 245 | SkASSERT(prefixEntry->fOp == SkRegion::kIntersect_Op); | 
|  | 246 | SkASSERT(iterEntry->fOp == SkRegion::kIntersect_Op); | 
|  | 247 | SkASSERT((iterEntry->fRect == NULL) == | 
|  | 248 | (prefixEntry->fRect == NULL)); | 
|  | 249 | SkASSERT((iterEntry->fPath == NULL) == | 
|  | 250 | (prefixEntry->fPath == NULL)); | 
|  | 251 | // We need to back up the iterator by one but don't have that | 
|  | 252 | // function, so reset and go forward by one less. | 
|  | 253 | iter->reset(stack); | 
|  | 254 | for (int i = 0; i < count; i++) { | 
|  | 255 | iter->next(); | 
|  | 256 | } | 
|  | 257 | prefixEntry = prefixIter.next(); | 
|  | 258 | break; | 
|  | 259 | } | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 260 | } | 
|  | 261 |  | 
|  | 262 | SkASSERT(prefixEntry == NULL); | 
|  | 263 | } | 
|  | 264 |  | 
|  | 265 | static void emit_clip(SkPath* clipPath, SkRect* clipRect, | 
|  | 266 | SkWStream* contentStream) { | 
|  | 267 | SkASSERT(clipPath || clipRect); | 
|  | 268 |  | 
|  | 269 | SkPath::FillType clipFill; | 
|  | 270 | if (clipPath) { | 
|  | 271 | SkPDFUtils::EmitPath(*clipPath, contentStream); | 
|  | 272 | clipFill = clipPath->getFillType(); | 
| vandebo@chromium.org | 3e7b280 | 2011-06-27 18:12:31 +0000 | [diff] [blame] | 273 | } else { | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 274 | SkPDFUtils::AppendRectangle(*clipRect, contentStream); | 
|  | 275 | clipFill = SkPath::kWinding_FillType; | 
|  | 276 | } | 
|  | 277 |  | 
|  | 278 | NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false); | 
|  | 279 | NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false); | 
|  | 280 | if (clipFill == SkPath::kEvenOdd_FillType) { | 
|  | 281 | contentStream->writeText("W* n\n"); | 
|  | 282 | } else { | 
|  | 283 | contentStream->writeText("W n\n"); | 
|  | 284 | } | 
|  | 285 | } | 
|  | 286 |  | 
|  | 287 | // TODO(vandebo) Take advantage of SkClipStack::getSaveCount(), the PDF | 
|  | 288 | // graphic state stack, and the fact that we can know all the clips used | 
|  | 289 | // on the page to optimize this. | 
|  | 290 | void GraphicStackState::updateClip(const SkClipStack& clipStack, | 
|  | 291 | const SkRegion& clipRegion, | 
|  | 292 | const SkIPoint& translation) { | 
|  | 293 | if (clipStack == currentEntry()->fClipStack) { | 
|  | 294 | return; | 
|  | 295 | } | 
|  | 296 |  | 
|  | 297 | while (fStackDepth > 0) { | 
|  | 298 | pop(); | 
|  | 299 | if (clipStack == currentEntry()->fClipStack) { | 
|  | 300 | return; | 
|  | 301 | } | 
|  | 302 | } | 
|  | 303 | push(); | 
|  | 304 |  | 
|  | 305 | // gsState->initialEntry()->fClipStack/Region specifies the clip that has | 
|  | 306 | // already been applied.  (If this is a top level device, then it specifies | 
|  | 307 | // a clip to the content area.  If this is a layer, then it specifies | 
|  | 308 | // the clip in effect when the layer was created.)  There's no need to | 
|  | 309 | // reapply that clip; SKCanvas's SkDrawIter will draw anything outside the | 
|  | 310 | // initial clip on the parent layer.  (This means there's a bug if the user | 
|  | 311 | // expands the clip and then uses any xfer mode that uses dst: | 
|  | 312 | // http://code.google.com/p/skia/issues/detail?id=228 ) | 
|  | 313 | SkClipStack::B2FIter iter; | 
|  | 314 | skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); | 
|  | 315 |  | 
|  | 316 | // If the clip stack does anything other than intersect or if it uses | 
|  | 317 | // an inverse fill type, we have to fall back to the clip region. | 
|  | 318 | bool needRegion = false; | 
|  | 319 | const SkClipStack::B2FIter::Clip* clipEntry; | 
|  | 320 | for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { | 
|  | 321 | if (clipEntry->fOp != SkRegion::kIntersect_Op || | 
|  | 322 | (clipEntry->fPath && clipEntry->fPath->isInverseFillType())) { | 
|  | 323 | needRegion = true; | 
|  | 324 | break; | 
|  | 325 | } | 
|  | 326 | } | 
|  | 327 |  | 
|  | 328 | if (needRegion) { | 
|  | 329 | SkPath clipPath; | 
|  | 330 | SkAssertResult(clipRegion.getBoundaryPath(&clipPath)); | 
|  | 331 | emit_clip(&clipPath, NULL, fContentStream); | 
|  | 332 | } else { | 
|  | 333 | skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); | 
|  | 334 | SkMatrix transform; | 
|  | 335 | transform.setTranslate(translation.fX, translation.fY); | 
|  | 336 | const SkClipStack::B2FIter::Clip* clipEntry; | 
|  | 337 | for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { | 
|  | 338 | SkASSERT(clipEntry->fOp == SkRegion::kIntersect_Op); | 
|  | 339 | if (clipEntry->fRect) { | 
|  | 340 | SkRect translatedClip; | 
|  | 341 | transform.mapRect(&translatedClip, *clipEntry->fRect); | 
|  | 342 | emit_clip(NULL, &translatedClip, fContentStream); | 
|  | 343 | } else if (clipEntry->fPath) { | 
|  | 344 | SkPath translatedPath; | 
|  | 345 | clipEntry->fPath->transform(transform, &translatedPath); | 
|  | 346 | emit_clip(&translatedPath, NULL, fContentStream); | 
|  | 347 | } else { | 
|  | 348 | SkASSERT(false); | 
|  | 349 | } | 
|  | 350 | } | 
|  | 351 | } | 
|  | 352 | currentEntry()->fClipStack = clipStack; | 
|  | 353 | currentEntry()->fClipRegion = clipRegion; | 
|  | 354 | } | 
|  | 355 |  | 
|  | 356 | void GraphicStackState::updateMatrix(const SkMatrix& matrix) { | 
|  | 357 | if (matrix == currentEntry()->fMatrix) { | 
|  | 358 | return; | 
|  | 359 | } | 
|  | 360 |  | 
|  | 361 | if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) { | 
|  | 362 | SkASSERT(fStackDepth > 0); | 
|  | 363 | SkASSERT(fEntries[fStackDepth].fClipStack == | 
|  | 364 | fEntries[fStackDepth -1].fClipStack); | 
|  | 365 | pop(); | 
|  | 366 |  | 
|  | 367 | SkASSERT(currentEntry()->fMatrix.getType() == SkMatrix::kIdentity_Mask); | 
|  | 368 | } | 
|  | 369 | if (matrix.getType() == SkMatrix::kIdentity_Mask) { | 
|  | 370 | return; | 
|  | 371 | } | 
|  | 372 |  | 
|  | 373 | push(); | 
|  | 374 | SkPDFUtils::AppendTransform(matrix, fContentStream); | 
|  | 375 | currentEntry()->fMatrix = matrix; | 
|  | 376 | } | 
|  | 377 |  | 
|  | 378 | void GraphicStackState::updateDrawingState(const GraphicStateEntry& state) { | 
|  | 379 | // PDF treats a shader as a color, so we only set one or the other. | 
|  | 380 | if (state.fShaderIndex >= 0) { | 
|  | 381 | if (state.fShaderIndex != currentEntry()->fShaderIndex) { | 
|  | 382 | fContentStream->writeText("/Pattern CS /Pattern cs /P"); | 
|  | 383 | fContentStream->writeDecAsText(state.fShaderIndex); | 
|  | 384 | fContentStream->writeText(" SCN /P"); | 
|  | 385 | fContentStream->writeDecAsText(state.fShaderIndex); | 
|  | 386 | fContentStream->writeText(" scn\n"); | 
|  | 387 | currentEntry()->fShaderIndex = state.fShaderIndex; | 
|  | 388 | } | 
|  | 389 | } else { | 
|  | 390 | if (state.fColor != currentEntry()->fColor || | 
|  | 391 | currentEntry()->fShaderIndex >= 0) { | 
|  | 392 | emit_pdf_color(state.fColor, fContentStream); | 
|  | 393 | fContentStream->writeText("RG "); | 
|  | 394 | emit_pdf_color(state.fColor, fContentStream); | 
|  | 395 | fContentStream->writeText("rg\n"); | 
|  | 396 | currentEntry()->fColor = state.fColor; | 
|  | 397 | currentEntry()->fShaderIndex = -1; | 
|  | 398 | } | 
|  | 399 | } | 
|  | 400 |  | 
|  | 401 | if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) { | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 402 | SkPDFUtils::ApplyGraphicState(state.fGraphicStateIndex, fContentStream); | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 403 | currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex; | 
|  | 404 | } | 
|  | 405 |  | 
|  | 406 | if (state.fTextScaleX) { | 
|  | 407 | if (state.fTextScaleX != currentEntry()->fTextScaleX) { | 
|  | 408 | SkScalar pdfScale = SkScalarMul(state.fTextScaleX, | 
|  | 409 | SkIntToScalar(100)); | 
|  | 410 | SkPDFScalar::Append(pdfScale, fContentStream); | 
|  | 411 | fContentStream->writeText(" Tz\n"); | 
|  | 412 | currentEntry()->fTextScaleX = state.fTextScaleX; | 
|  | 413 | } | 
|  | 414 | if (state.fTextFill != currentEntry()->fTextFill) { | 
|  | 415 | SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value); | 
|  | 416 | SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1, | 
|  | 417 | enum_must_match_value); | 
|  | 418 | SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2, | 
|  | 419 | enum_must_match_value); | 
|  | 420 | fContentStream->writeDecAsText(state.fTextFill); | 
|  | 421 | fContentStream->writeText(" Tr\n"); | 
|  | 422 | currentEntry()->fTextFill = state.fTextFill; | 
|  | 423 | } | 
|  | 424 | } | 
|  | 425 | } | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 426 |  | 
| vandebo@chromium.org | 9859428 | 2011-07-25 22:34:12 +0000 | [diff] [blame^] | 427 | SkDevice* SkPDFDevice::onCreateCompatibleDevice(SkBitmap::Config config, | 
|  | 428 | int width, int height, | 
| bsalomon@google.com | e97f085 | 2011-06-17 13:10:25 +0000 | [diff] [blame] | 429 | bool isOpaque, | 
|  | 430 | Usage usage) { | 
|  | 431 | SkMatrix initialTransform; | 
|  | 432 | initialTransform.reset(); | 
|  | 433 | SkISize size = SkISize::Make(width, height); | 
|  | 434 | return SkNEW_ARGS(SkPDFDevice, (size, size, initialTransform)); | 
|  | 435 | } | 
|  | 436 |  | 
|  | 437 |  | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 438 | struct ContentEntry { | 
|  | 439 | GraphicStateEntry fState; | 
|  | 440 | SkDynamicMemoryWStream fContent; | 
|  | 441 | SkTScopedPtr<ContentEntry> fNext; | 
|  | 442 | }; | 
|  | 443 |  | 
|  | 444 | // A helper class to automatically finish a ContentEntry at the end of a | 
|  | 445 | // drawing method and maintain the state needed between set up and finish. | 
| vandebo@chromium.org | 13d14a9 | 2011-05-24 23:12:41 +0000 | [diff] [blame] | 446 | class ScopedContentEntry { | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 447 | public: | 
| vandebo@chromium.org | 13d14a9 | 2011-05-24 23:12:41 +0000 | [diff] [blame] | 448 | ScopedContentEntry(SkPDFDevice* device, const SkDraw& draw, | 
|  | 449 | const SkPaint& paint, bool hasText = false) | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 450 | : fDevice(device), | 
|  | 451 | fContentEntry(NULL), | 
|  | 452 | fXfermode(SkXfermode::kSrcOver_Mode) { | 
|  | 453 | init(draw.fClipStack, *draw.fClip, *draw.fMatrix, paint, hasText); | 
|  | 454 | } | 
| vandebo@chromium.org | 13d14a9 | 2011-05-24 23:12:41 +0000 | [diff] [blame] | 455 | ScopedContentEntry(SkPDFDevice* device, const SkClipStack* clipStack, | 
|  | 456 | const SkRegion& clipRegion, const SkMatrix& matrix, | 
|  | 457 | const SkPaint& paint, bool hasText = false) | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 458 | : fDevice(device), | 
|  | 459 | fContentEntry(NULL), | 
|  | 460 | fXfermode(SkXfermode::kSrcOver_Mode) { | 
|  | 461 | init(clipStack, clipRegion, matrix, paint, hasText); | 
|  | 462 | } | 
|  | 463 |  | 
| vandebo@chromium.org | 13d14a9 | 2011-05-24 23:12:41 +0000 | [diff] [blame] | 464 | ~ScopedContentEntry() { | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 465 | if (fContentEntry) { | 
|  | 466 | fDevice->finishContentEntry(fXfermode, fDstFormXObject.get()); | 
|  | 467 | } | 
|  | 468 | } | 
|  | 469 |  | 
|  | 470 | ContentEntry* entry() { return fContentEntry; } | 
|  | 471 | private: | 
|  | 472 | SkPDFDevice* fDevice; | 
|  | 473 | ContentEntry* fContentEntry; | 
|  | 474 | SkXfermode::Mode fXfermode; | 
|  | 475 | SkRefPtr<SkPDFFormXObject> fDstFormXObject; | 
|  | 476 |  | 
|  | 477 | void init(const SkClipStack* clipStack, const SkRegion& clipRegion, | 
|  | 478 | const SkMatrix& matrix, const SkPaint& paint, bool hasText) { | 
|  | 479 | if (paint.getXfermode()) { | 
|  | 480 | paint.getXfermode()->asMode(&fXfermode); | 
|  | 481 | } | 
|  | 482 | fContentEntry = fDevice->setUpContentEntry(clipStack, clipRegion, | 
|  | 483 | matrix, paint, hasText, | 
|  | 484 | &fDstFormXObject); | 
|  | 485 | } | 
|  | 486 | }; | 
|  | 487 |  | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 488 | //////////////////////////////////////////////////////////////////////////////// | 
|  | 489 |  | 
| ctguil@chromium.org | 1526129 | 2011-04-29 17:54:16 +0000 | [diff] [blame] | 490 | static inline SkBitmap makeContentBitmap(const SkISize& contentSize, | 
| vandebo@chromium.org | a0c7edb | 2011-05-09 07:58:08 +0000 | [diff] [blame] | 491 | const SkMatrix* initialTransform) { | 
| reed@android.com | f2b98d6 | 2010-12-20 18:26:13 +0000 | [diff] [blame] | 492 | SkBitmap bitmap; | 
| vandebo@chromium.org | a0c7edb | 2011-05-09 07:58:08 +0000 | [diff] [blame] | 493 | if (initialTransform) { | 
|  | 494 | // Compute the size of the drawing area. | 
|  | 495 | SkVector drawingSize; | 
|  | 496 | SkMatrix inverse; | 
|  | 497 | drawingSize.set(contentSize.fWidth, contentSize.fHeight); | 
|  | 498 | initialTransform->invert(&inverse); | 
|  | 499 | inverse.mapVectors(&drawingSize, 1); | 
|  | 500 | SkISize size = SkSize::Make(drawingSize.fX, drawingSize.fY).toRound(); | 
|  | 501 | bitmap.setConfig(SkBitmap::kNo_Config, abs(size.fWidth), | 
|  | 502 | abs(size.fHeight)); | 
|  | 503 | } else { | 
|  | 504 | bitmap.setConfig(SkBitmap::kNo_Config, abs(contentSize.fWidth), | 
|  | 505 | abs(contentSize.fHeight)); | 
|  | 506 | } | 
|  | 507 |  | 
| reed@android.com | f2b98d6 | 2010-12-20 18:26:13 +0000 | [diff] [blame] | 508 | return bitmap; | 
|  | 509 | } | 
|  | 510 |  | 
| ctguil@chromium.org | 1526129 | 2011-04-29 17:54:16 +0000 | [diff] [blame] | 511 | SkPDFDevice::SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize, | 
| vandebo@chromium.org | 75f97e4 | 2011-04-11 23:24:18 +0000 | [diff] [blame] | 512 | const SkMatrix& initialTransform) | 
| reed@google.com | af951c9 | 2011-06-16 19:10:39 +0000 | [diff] [blame] | 513 | : SkDevice(makeContentBitmap(contentSize, &initialTransform)), | 
| ctguil@chromium.org | 1526129 | 2011-04-29 17:54:16 +0000 | [diff] [blame] | 514 | fPageSize(pageSize), | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 515 | fContentSize(contentSize), | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 516 | fLastContentEntry(NULL), | 
|  | 517 | fLastMarginContentEntry(NULL) { | 
| vandebo@chromium.org | 77bcaa3 | 2011-04-15 20:57:37 +0000 | [diff] [blame] | 518 | // Skia generally uses the top left as the origin but PDF natively has the | 
| vandebo@chromium.org | a0c7edb | 2011-05-09 07:58:08 +0000 | [diff] [blame] | 519 | // origin at the bottom left. This matrix corrects for that.  But that only | 
|  | 520 | // needs to be done once, we don't do it when layering. | 
| ctguil@chromium.org | 1526129 | 2011-04-29 17:54:16 +0000 | [diff] [blame] | 521 | fInitialTransform.setTranslate(0, pageSize.fHeight); | 
| vandebo@chromium.org | 77bcaa3 | 2011-04-15 20:57:37 +0000 | [diff] [blame] | 522 | fInitialTransform.preScale(1, -1); | 
|  | 523 | fInitialTransform.preConcat(initialTransform); | 
|  | 524 |  | 
| vandebo@chromium.org | a0c7edb | 2011-05-09 07:58:08 +0000 | [diff] [blame] | 525 | SkIRect existingClip = SkIRect::MakeWH(this->width(), this->height()); | 
|  | 526 | fExistingClipStack.clipDevRect(existingClip); | 
|  | 527 | fExistingClipRegion.setRect(existingClip); | 
|  | 528 |  | 
|  | 529 | this->init(); | 
|  | 530 | } | 
|  | 531 |  | 
|  | 532 | SkPDFDevice::SkPDFDevice(const SkISize& layerSize, | 
|  | 533 | const SkClipStack& existingClipStack, | 
|  | 534 | const SkRegion& existingClipRegion) | 
| reed@google.com | af951c9 | 2011-06-16 19:10:39 +0000 | [diff] [blame] | 535 | : SkDevice(makeContentBitmap(layerSize, NULL)), | 
| vandebo@chromium.org | a0c7edb | 2011-05-09 07:58:08 +0000 | [diff] [blame] | 536 | fPageSize(layerSize), | 
|  | 537 | fContentSize(layerSize), | 
|  | 538 | fExistingClipStack(existingClipStack), | 
|  | 539 | fExistingClipRegion(existingClipRegion), | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 540 | fLastContentEntry(NULL), | 
|  | 541 | fLastMarginContentEntry(NULL) { | 
| vandebo@chromium.org | a0c7edb | 2011-05-09 07:58:08 +0000 | [diff] [blame] | 542 | fInitialTransform.reset(); | 
| vandebo@chromium.org | 77bcaa3 | 2011-04-15 20:57:37 +0000 | [diff] [blame] | 543 | this->init(); | 
|  | 544 | } | 
|  | 545 |  | 
|  | 546 | SkPDFDevice::~SkPDFDevice() { | 
| vandebo@chromium.org | 9859428 | 2011-07-25 22:34:12 +0000 | [diff] [blame^] | 547 | this->cleanUp(true); | 
| vandebo@chromium.org | 77bcaa3 | 2011-04-15 20:57:37 +0000 | [diff] [blame] | 548 | } | 
|  | 549 |  | 
|  | 550 | void SkPDFDevice::init() { | 
| vandebo@chromium.org | 77bcaa3 | 2011-04-15 20:57:37 +0000 | [diff] [blame] | 551 | fResourceDict = NULL; | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 552 | fContentEntries.reset(); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 553 | fLastContentEntry = NULL; | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 554 | fMarginContentEntries.reset(); | 
|  | 555 | fLastMarginContentEntry = NULL; | 
| vandebo@chromium.org | 9859428 | 2011-07-25 22:34:12 +0000 | [diff] [blame^] | 556 | fDrawingArea = kContent_DrawingArea; | 
|  | 557 | if (fFontGlyphUsage == NULL) { | 
|  | 558 | fFontGlyphUsage.reset(new SkPDFGlyphSetMap()); | 
|  | 559 | } | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 560 | } | 
|  | 561 |  | 
| vandebo@chromium.org | 9859428 | 2011-07-25 22:34:12 +0000 | [diff] [blame^] | 562 | void SkPDFDevice::cleanUp(bool clearFontUsage) { | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 563 | fGraphicStateResources.unrefAll(); | 
|  | 564 | fXObjectResources.unrefAll(); | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 565 | fFontResources.unrefAll(); | 
| vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 566 | fShaderResources.unrefAll(); | 
| vandebo@chromium.org | 9859428 | 2011-07-25 22:34:12 +0000 | [diff] [blame^] | 567 | if (clearFontUsage) { | 
|  | 568 | fFontGlyphUsage->reset(); | 
|  | 569 | } | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 570 | } | 
|  | 571 |  | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 572 | void SkPDFDevice::clear(SkColor color) { | 
| vandebo@chromium.org | 9859428 | 2011-07-25 22:34:12 +0000 | [diff] [blame^] | 573 | this->cleanUp(true); | 
| vandebo@chromium.org | 77bcaa3 | 2011-04-15 20:57:37 +0000 | [diff] [blame] | 574 | this->init(); | 
|  | 575 |  | 
|  | 576 | SkPaint paint; | 
|  | 577 | paint.setColor(color); | 
|  | 578 | paint.setStyle(SkPaint::kFill_Style); | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 579 | SkMatrix identity; | 
|  | 580 | identity.reset(); | 
| vandebo@chromium.org | 13d14a9 | 2011-05-24 23:12:41 +0000 | [diff] [blame] | 581 | ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion, | 
|  | 582 | identity, paint); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 583 | internalDrawPaint(paint, content.entry()); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 584 | } | 
|  | 585 |  | 
|  | 586 | void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) { | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 587 | SkPaint newPaint = paint; | 
|  | 588 | newPaint.setStyle(SkPaint::kFill_Style); | 
| vandebo@chromium.org | 13d14a9 | 2011-05-24 23:12:41 +0000 | [diff] [blame] | 589 | ScopedContentEntry content(this, d, newPaint); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 590 | internalDrawPaint(newPaint, content.entry()); | 
| vandebo@chromium.org | 77bcaa3 | 2011-04-15 20:57:37 +0000 | [diff] [blame] | 591 | } | 
|  | 592 |  | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 593 | void SkPDFDevice::internalDrawPaint(const SkPaint& paint, | 
|  | 594 | ContentEntry* contentEntry) { | 
|  | 595 | if (!contentEntry) { | 
|  | 596 | return; | 
|  | 597 | } | 
| vandebo@chromium.org | 77bcaa3 | 2011-04-15 20:57:37 +0000 | [diff] [blame] | 598 | SkRect bbox = SkRect::MakeWH(SkIntToScalar(this->width()), | 
|  | 599 | SkIntToScalar(this->height())); | 
|  | 600 | SkMatrix totalTransform = fInitialTransform; | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 601 | totalTransform.preConcat(contentEntry->fState.fMatrix); | 
| vandebo@chromium.org | 77bcaa3 | 2011-04-15 20:57:37 +0000 | [diff] [blame] | 602 | SkMatrix inverse; | 
|  | 603 | inverse.reset(); | 
|  | 604 | totalTransform.invert(&inverse); | 
|  | 605 | inverse.mapRect(&bbox); | 
|  | 606 |  | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 607 | SkPDFUtils::AppendRectangle(bbox, &contentEntry->fContent); | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 608 | SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType, | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 609 | &contentEntry->fContent); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 610 | } | 
|  | 611 |  | 
|  | 612 | void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode, | 
|  | 613 | size_t count, const SkPoint* points, | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 614 | const SkPaint& passedPaint) { | 
|  | 615 | if (count == 0) { | 
|  | 616 | return; | 
|  | 617 | } | 
|  | 618 |  | 
| vandebo@chromium.org | ff39032 | 2011-05-17 18:58:44 +0000 | [diff] [blame] | 619 | // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath. | 
|  | 620 | // We only use this when there's a path effect because of the overhead | 
|  | 621 | // of multiple calls to setUpContentEntry it causes. | 
|  | 622 | if (passedPaint.getPathEffect()) { | 
|  | 623 | if (d.fClip->isEmpty()) { | 
|  | 624 | return; | 
|  | 625 | } | 
|  | 626 | SkDraw pointDraw(d); | 
|  | 627 | pointDraw.fDevice = this; | 
|  | 628 | pointDraw.drawPoints(mode, count, points, passedPaint, true); | 
|  | 629 | return; | 
|  | 630 | } | 
|  | 631 |  | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 632 | const SkPaint* paint = &passedPaint; | 
|  | 633 | SkPaint modifiedPaint; | 
|  | 634 |  | 
|  | 635 | if (mode == SkCanvas::kPoints_PointMode && | 
|  | 636 | paint->getStrokeCap() != SkPaint::kRound_Cap) { | 
|  | 637 | modifiedPaint = *paint; | 
|  | 638 | paint = &modifiedPaint; | 
|  | 639 | if (paint->getStrokeWidth()) { | 
|  | 640 | // PDF won't draw a single point with square/butt caps because the | 
|  | 641 | // orientation is ambiguous.  Draw a rectangle instead. | 
|  | 642 | modifiedPaint.setStyle(SkPaint::kFill_Style); | 
|  | 643 | SkScalar strokeWidth = paint->getStrokeWidth(); | 
|  | 644 | SkScalar halfStroke = SkScalarHalf(strokeWidth); | 
|  | 645 | for (size_t i = 0; i < count; i++) { | 
|  | 646 | SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY, 0, 0); | 
|  | 647 | r.inset(-halfStroke, -halfStroke); | 
|  | 648 | drawRect(d, r, modifiedPaint); | 
|  | 649 | } | 
|  | 650 | return; | 
|  | 651 | } else { | 
|  | 652 | modifiedPaint.setStrokeCap(SkPaint::kRound_Cap); | 
|  | 653 | } | 
|  | 654 | } | 
|  | 655 |  | 
| vandebo@chromium.org | 13d14a9 | 2011-05-24 23:12:41 +0000 | [diff] [blame] | 656 | ScopedContentEntry content(this, d, *paint); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 657 | if (!content.entry()) { | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 658 | return; | 
| vandebo@chromium.org | fb0b0ed | 2011-04-15 20:01:17 +0000 | [diff] [blame] | 659 | } | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 660 |  | 
|  | 661 | switch (mode) { | 
|  | 662 | case SkCanvas::kPolygon_PointMode: | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 663 | SkPDFUtils::MoveTo(points[0].fX, points[0].fY, | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 664 | &content.entry()->fContent); | 
| ctguil@chromium.org | 9db86bb | 2011-03-04 21:43:27 +0000 | [diff] [blame] | 665 | for (size_t i = 1; i < count; i++) { | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 666 | SkPDFUtils::AppendLine(points[i].fX, points[i].fY, | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 667 | &content.entry()->fContent); | 
| ctguil@chromium.org | 9db86bb | 2011-03-04 21:43:27 +0000 | [diff] [blame] | 668 | } | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 669 | SkPDFUtils::StrokePath(&content.entry()->fContent); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 670 | break; | 
|  | 671 | case SkCanvas::kLines_PointMode: | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 672 | for (size_t i = 0; i < count/2; i++) { | 
| ctguil@chromium.org | 9db86bb | 2011-03-04 21:43:27 +0000 | [diff] [blame] | 673 | SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY, | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 674 | &content.entry()->fContent); | 
| ctguil@chromium.org | 9db86bb | 2011-03-04 21:43:27 +0000 | [diff] [blame] | 675 | SkPDFUtils::AppendLine(points[i * 2 + 1].fX, | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 676 | points[i * 2 + 1].fY, | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 677 | &content.entry()->fContent); | 
|  | 678 | SkPDFUtils::StrokePath(&content.entry()->fContent); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 679 | } | 
|  | 680 | break; | 
|  | 681 | case SkCanvas::kPoints_PointMode: | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 682 | SkASSERT(paint->getStrokeCap() == SkPaint::kRound_Cap); | 
|  | 683 | for (size_t i = 0; i < count; i++) { | 
|  | 684 | SkPDFUtils::MoveTo(points[i].fX, points[i].fY, | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 685 | &content.entry()->fContent); | 
|  | 686 | SkPDFUtils::ClosePath(&content.entry()->fContent); | 
|  | 687 | SkPDFUtils::StrokePath(&content.entry()->fContent); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 688 | } | 
|  | 689 | break; | 
|  | 690 | default: | 
|  | 691 | SkASSERT(false); | 
|  | 692 | } | 
|  | 693 | } | 
|  | 694 |  | 
|  | 695 | void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r, | 
|  | 696 | const SkPaint& paint) { | 
|  | 697 | if (paint.getPathEffect()) { | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 698 | if (d.fClip->isEmpty()) { | 
|  | 699 | return; | 
|  | 700 | } | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 701 | SkPath path; | 
|  | 702 | path.addRect(r); | 
| vandebo@chromium.org | ff39032 | 2011-05-17 18:58:44 +0000 | [diff] [blame] | 703 | drawPath(d, path, paint, NULL, true); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 704 | return; | 
|  | 705 | } | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 706 |  | 
| vandebo@chromium.org | 13d14a9 | 2011-05-24 23:12:41 +0000 | [diff] [blame] | 707 | ScopedContentEntry content(this, d, paint); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 708 | if (!content.entry()) { | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 709 | return; | 
|  | 710 | } | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 711 | SkPDFUtils::AppendRectangle(r, &content.entry()->fContent); | 
| ctguil@chromium.org | 9db86bb | 2011-03-04 21:43:27 +0000 | [diff] [blame] | 712 | SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType, | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 713 | &content.entry()->fContent); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 714 | } | 
|  | 715 |  | 
| vandebo@chromium.org | ff39032 | 2011-05-17 18:58:44 +0000 | [diff] [blame] | 716 | void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& origPath, | 
| vandebo@chromium.org | 02cc5aa | 2011-01-25 22:06:29 +0000 | [diff] [blame] | 717 | const SkPaint& paint, const SkMatrix* prePathMatrix, | 
|  | 718 | bool pathIsMutable) { | 
| vandebo@chromium.org | ff39032 | 2011-05-17 18:58:44 +0000 | [diff] [blame] | 719 | SkPath modifiedPath; | 
|  | 720 | SkPath* pathPtr = const_cast<SkPath*>(&origPath); | 
|  | 721 |  | 
|  | 722 | SkMatrix matrix = *d.fMatrix; | 
|  | 723 | if (prePathMatrix) { | 
|  | 724 | if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) { | 
|  | 725 | if (!pathIsMutable) { | 
|  | 726 | pathPtr = &modifiedPath; | 
|  | 727 | pathIsMutable = true; | 
|  | 728 | } | 
|  | 729 | origPath.transform(*prePathMatrix, pathPtr); | 
|  | 730 | } else { | 
|  | 731 | if (!matrix.preConcat(*prePathMatrix)) { | 
|  | 732 | return; | 
|  | 733 | } | 
|  | 734 | } | 
|  | 735 | } | 
| vandebo@chromium.org | 02cc5aa | 2011-01-25 22:06:29 +0000 | [diff] [blame] | 736 |  | 
| vandebo@chromium.org | 7d71f7f | 2010-10-26 19:51:44 +0000 | [diff] [blame] | 737 | if (paint.getPathEffect()) { | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 738 | if (d.fClip->isEmpty()) { | 
|  | 739 | return; | 
|  | 740 | } | 
| vandebo@chromium.org | ff39032 | 2011-05-17 18:58:44 +0000 | [diff] [blame] | 741 | if (!pathIsMutable) { | 
|  | 742 | pathPtr = &modifiedPath; | 
|  | 743 | pathIsMutable = true; | 
|  | 744 | } | 
|  | 745 | bool fill = paint.getFillPath(origPath, pathPtr); | 
| vandebo@chromium.org | 7d71f7f | 2010-10-26 19:51:44 +0000 | [diff] [blame] | 746 |  | 
| vandebo@chromium.org | 7e2ff7c | 2010-11-03 23:55:28 +0000 | [diff] [blame] | 747 | SkPaint noEffectPaint(paint); | 
| vandebo@chromium.org | ff39032 | 2011-05-17 18:58:44 +0000 | [diff] [blame] | 748 | noEffectPaint.setPathEffect(NULL); | 
|  | 749 | if (fill) { | 
|  | 750 | noEffectPaint.setStyle(SkPaint::kFill_Style); | 
|  | 751 | } else { | 
|  | 752 | noEffectPaint.setStyle(SkPaint::kStroke_Style); | 
|  | 753 | noEffectPaint.setStrokeWidth(0); | 
|  | 754 | } | 
|  | 755 | drawPath(d, *pathPtr, noEffectPaint, NULL, true); | 
| vandebo@chromium.org | 7d71f7f | 2010-10-26 19:51:44 +0000 | [diff] [blame] | 756 | return; | 
|  | 757 | } | 
| vandebo@chromium.org | ff39032 | 2011-05-17 18:58:44 +0000 | [diff] [blame] | 758 |  | 
| vandebo@chromium.org | 13d14a9 | 2011-05-24 23:12:41 +0000 | [diff] [blame] | 759 | ScopedContentEntry content(this, d, paint); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 760 | if (!content.entry()) { | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 761 | return; | 
|  | 762 | } | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 763 | SkPDFUtils::EmitPath(*pathPtr, &content.entry()->fContent); | 
| vandebo@chromium.org | ff39032 | 2011-05-17 18:58:44 +0000 | [diff] [blame] | 764 | SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(), | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 765 | &content.entry()->fContent); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 766 | } | 
|  | 767 |  | 
| vandebo@chromium.org | fb0b0ed | 2011-04-15 20:01:17 +0000 | [diff] [blame] | 768 | void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap, | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 769 | const SkIRect* srcRect, const SkMatrix& matrix, | 
|  | 770 | const SkPaint& paint) { | 
| vandebo@chromium.org | fb0b0ed | 2011-04-15 20:01:17 +0000 | [diff] [blame] | 771 | if (d.fClip->isEmpty()) { | 
|  | 772 | return; | 
|  | 773 | } | 
|  | 774 |  | 
| vandebo@chromium.org | 7e2ff7c | 2010-11-03 23:55:28 +0000 | [diff] [blame] | 775 | SkMatrix transform = matrix; | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 776 | transform.postConcat(*d.fMatrix); | 
| vandebo@chromium.org | 78dad54 | 2011-05-11 18:46:03 +0000 | [diff] [blame] | 777 | internalDrawBitmap(transform, d.fClipStack, *d.fClip, bitmap, srcRect, | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 778 | paint); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 779 | } | 
|  | 780 |  | 
| vandebo@chromium.org | fb0b0ed | 2011-04-15 20:01:17 +0000 | [diff] [blame] | 781 | void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap, | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 782 | int x, int y, const SkPaint& paint) { | 
| vandebo@chromium.org | fb0b0ed | 2011-04-15 20:01:17 +0000 | [diff] [blame] | 783 | if (d.fClip->isEmpty()) { | 
|  | 784 | return; | 
|  | 785 | } | 
|  | 786 |  | 
| vandebo@chromium.org | 7e2ff7c | 2010-11-03 23:55:28 +0000 | [diff] [blame] | 787 | SkMatrix matrix; | 
| reed@google.com | a6d59f6 | 2011-03-07 21:29:21 +0000 | [diff] [blame] | 788 | matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y)); | 
| vandebo@chromium.org | 78dad54 | 2011-05-11 18:46:03 +0000 | [diff] [blame] | 789 | internalDrawBitmap(matrix, d.fClipStack, *d.fClip, bitmap, NULL, paint); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 790 | } | 
|  | 791 |  | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 792 | void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 793 | SkScalar x, SkScalar y, const SkPaint& paint) { | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 794 | SkPaint textPaint = calculate_text_paint(paint); | 
| vandebo@chromium.org | 13d14a9 | 2011-05-24 23:12:41 +0000 | [diff] [blame] | 795 | ScopedContentEntry content(this, d, textPaint, true); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 796 | if (!content.entry()) { | 
| vandebo@chromium.org | fb0b0ed | 2011-04-15 20:01:17 +0000 | [diff] [blame] | 797 | return; | 
|  | 798 | } | 
|  | 799 |  | 
| vandebo@chromium.org | 0129410 | 2011-02-28 19:52:18 +0000 | [diff] [blame] | 800 | // We want the text in glyph id encoding and a writable buffer, so we end | 
|  | 801 | // up making a copy either way. | 
|  | 802 | size_t numGlyphs = paint.textToGlyphs(text, len, NULL); | 
|  | 803 | uint16_t* glyphIDs = | 
|  | 804 | (uint16_t*)sk_malloc_flags(numGlyphs * 2, | 
|  | 805 | SK_MALLOC_TEMP | SK_MALLOC_THROW); | 
|  | 806 | SkAutoFree autoFreeGlyphIDs(glyphIDs); | 
| vandebo@chromium.org | 2a22e10 | 2011-01-25 21:01:34 +0000 | [diff] [blame] | 807 | if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) { | 
| vandebo@chromium.org | 2a22e10 | 2011-01-25 21:01:34 +0000 | [diff] [blame] | 808 | paint.textToGlyphs(text, len, glyphIDs); | 
|  | 809 | textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); | 
|  | 810 | } else { | 
|  | 811 | SkASSERT((len & 1) == 0); | 
| vandebo@chromium.org | 0129410 | 2011-02-28 19:52:18 +0000 | [diff] [blame] | 812 | SkASSERT(len / 2 == numGlyphs); | 
|  | 813 | memcpy(glyphIDs, text, len); | 
| vandebo@chromium.org | 2a22e10 | 2011-01-25 21:01:34 +0000 | [diff] [blame] | 814 | } | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 815 |  | 
|  | 816 | SkScalar width; | 
|  | 817 | SkScalar* widthPtr = NULL; | 
|  | 818 | if (textPaint.isUnderlineText() || textPaint.isStrikeThruText()) | 
|  | 819 | widthPtr = &width; | 
|  | 820 |  | 
|  | 821 | SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc(); | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 822 | align_text(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, | 
|  | 823 | widthPtr); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 824 | content.entry()->fContent.writeText("BT\n"); | 
|  | 825 | set_text_transform(x, y, textPaint.getTextSkewX(), | 
|  | 826 | &content.entry()->fContent); | 
| vandebo@chromium.org | 2a22e10 | 2011-01-25 21:01:34 +0000 | [diff] [blame] | 827 | size_t consumedGlyphCount = 0; | 
|  | 828 | while (numGlyphs > consumedGlyphCount) { | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 829 | updateFont(textPaint, glyphIDs[consumedGlyphCount], content.entry()); | 
|  | 830 | SkPDFFont* font = content.entry()->fState.fFont; | 
| vandebo@chromium.org | 0129410 | 2011-02-28 19:52:18 +0000 | [diff] [blame] | 831 | size_t availableGlyphs = | 
|  | 832 | font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount, | 
|  | 833 | numGlyphs - consumedGlyphCount); | 
| vandebo@chromium.org | 9859428 | 2011-07-25 22:34:12 +0000 | [diff] [blame^] | 834 | fFontGlyphUsage->noteGlyphUsage(font, glyphIDs + consumedGlyphCount, | 
|  | 835 | availableGlyphs); | 
| vandebo@chromium.org | cae5fba | 2011-03-28 19:03:50 +0000 | [diff] [blame] | 836 | SkString encodedString = | 
| reed@google.com | f6c3ebd | 2011-07-20 17:20:28 +0000 | [diff] [blame] | 837 | SkPDFString::FormatString(glyphIDs + consumedGlyphCount, | 
| vandebo@chromium.org | cae5fba | 2011-03-28 19:03:50 +0000 | [diff] [blame] | 838 | availableGlyphs, font->multiByteGlyphs()); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 839 | content.entry()->fContent.writeText(encodedString.c_str()); | 
| vandebo@chromium.org | 0129410 | 2011-02-28 19:52:18 +0000 | [diff] [blame] | 840 | consumedGlyphCount += availableGlyphs; | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 841 | content.entry()->fContent.writeText(" Tj\n"); | 
| vandebo@chromium.org | 2a22e10 | 2011-01-25 21:01:34 +0000 | [diff] [blame] | 842 | } | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 843 | content.entry()->fContent.writeText("ET\n"); | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 844 |  | 
|  | 845 | // Draw underline and/or strikethrough if the paint has them. | 
|  | 846 | // drawPosText() and drawTextOnPath() don't draw underline or strikethrough | 
|  | 847 | // because the raster versions don't.  Use paint instead of textPaint | 
|  | 848 | // because we may have changed strokeWidth to do fakeBold text. | 
|  | 849 | if (paint.isUnderlineText() || paint.isStrikeThruText()) { | 
|  | 850 | SkScalar textSize = paint.getTextSize(); | 
|  | 851 | SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness); | 
|  | 852 |  | 
|  | 853 | if (paint.isUnderlineText()) { | 
|  | 854 | SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y); | 
|  | 855 | SkRect r = SkRect::MakeXYWH(x, top - height, width, height); | 
|  | 856 | drawRect(d, r, paint); | 
|  | 857 | } | 
|  | 858 | if (paint.isStrikeThruText()) { | 
|  | 859 | SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y); | 
|  | 860 | SkRect r = SkRect::MakeXYWH(x, top - height, width, height); | 
|  | 861 | drawRect(d, r, paint); | 
|  | 862 | } | 
|  | 863 | } | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 864 | } | 
|  | 865 |  | 
| vandebo@chromium.org | fb0b0ed | 2011-04-15 20:01:17 +0000 | [diff] [blame] | 866 | void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len, | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 867 | const SkScalar pos[], SkScalar constY, | 
|  | 868 | int scalarsPerPos, const SkPaint& paint) { | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 869 | SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos); | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 870 | SkPaint textPaint = calculate_text_paint(paint); | 
| vandebo@chromium.org | 13d14a9 | 2011-05-24 23:12:41 +0000 | [diff] [blame] | 871 | ScopedContentEntry content(this, d, textPaint, true); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 872 | if (!content.entry()) { | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 873 | return; | 
|  | 874 | } | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 875 |  | 
| vandebo@chromium.org | 2a22e10 | 2011-01-25 21:01:34 +0000 | [diff] [blame] | 876 | // Make sure we have a glyph id encoding. | 
|  | 877 | SkAutoFree glyphStorage; | 
|  | 878 | uint16_t* glyphIDs; | 
|  | 879 | size_t numGlyphs; | 
|  | 880 | if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) { | 
|  | 881 | numGlyphs = paint.textToGlyphs(text, len, NULL); | 
|  | 882 | glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2, | 
|  | 883 | SK_MALLOC_TEMP | SK_MALLOC_THROW); | 
|  | 884 | glyphStorage.set(glyphIDs); | 
|  | 885 | paint.textToGlyphs(text, len, glyphIDs); | 
|  | 886 | textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); | 
|  | 887 | } else { | 
|  | 888 | SkASSERT((len & 1) == 0); | 
|  | 889 | numGlyphs = len / 2; | 
|  | 890 | glyphIDs = (uint16_t*)text; | 
|  | 891 | } | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 892 |  | 
|  | 893 | SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc(); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 894 | content.entry()->fContent.writeText("BT\n"); | 
|  | 895 | updateFont(textPaint, glyphIDs[0], content.entry()); | 
| vandebo@chromium.org | 2a22e10 | 2011-01-25 21:01:34 +0000 | [diff] [blame] | 896 | for (size_t i = 0; i < numGlyphs; i++) { | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 897 | SkPDFFont* font = content.entry()->fState.fFont; | 
| vandebo@chromium.org | 0129410 | 2011-02-28 19:52:18 +0000 | [diff] [blame] | 898 | uint16_t encodedValue = glyphIDs[i]; | 
|  | 899 | if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) { | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 900 | updateFont(textPaint, glyphIDs[i], content.entry()); | 
| vandebo@chromium.org | 2a22e10 | 2011-01-25 21:01:34 +0000 | [diff] [blame] | 901 | i--; | 
|  | 902 | continue; | 
|  | 903 | } | 
| vandebo@chromium.org | 9859428 | 2011-07-25 22:34:12 +0000 | [diff] [blame^] | 904 | fFontGlyphUsage->noteGlyphUsage(font, &encodedValue, 1); | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 905 | SkScalar x = pos[i * scalarsPerPos]; | 
|  | 906 | SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1]; | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 907 | align_text(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 908 | set_text_transform(x, y, textPaint.getTextSkewX(), | 
|  | 909 | &content.entry()->fContent); | 
| vandebo@chromium.org | cae5fba | 2011-03-28 19:03:50 +0000 | [diff] [blame] | 910 | SkString encodedString = | 
| reed@google.com | f6c3ebd | 2011-07-20 17:20:28 +0000 | [diff] [blame] | 911 | SkPDFString::FormatString(&encodedValue, 1, | 
| vandebo@chromium.org | cae5fba | 2011-03-28 19:03:50 +0000 | [diff] [blame] | 912 | font->multiByteGlyphs()); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 913 | content.entry()->fContent.writeText(encodedString.c_str()); | 
|  | 914 | content.entry()->fContent.writeText(" Tj\n"); | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 915 | } | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 916 | content.entry()->fContent.writeText("ET\n"); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 917 | } | 
|  | 918 |  | 
| vandebo@chromium.org | fb0b0ed | 2011-04-15 20:01:17 +0000 | [diff] [blame] | 919 | void SkPDFDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len, | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 920 | const SkPath& path, const SkMatrix* matrix, | 
|  | 921 | const SkPaint& paint) { | 
| vandebo@chromium.org | fb0b0ed | 2011-04-15 20:01:17 +0000 | [diff] [blame] | 922 | if (d.fClip->isEmpty()) { | 
|  | 923 | return; | 
|  | 924 | } | 
| vandebo@chromium.org | 0f1c95c | 2011-06-24 23:13:47 +0000 | [diff] [blame] | 925 | NOT_IMPLEMENTED("drawTextOnPath", false); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 926 | } | 
|  | 927 |  | 
| vandebo@chromium.org | fb0b0ed | 2011-04-15 20:01:17 +0000 | [diff] [blame] | 928 | void SkPDFDevice::drawVertices(const SkDraw& d, SkCanvas::VertexMode, | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 929 | int vertexCount, const SkPoint verts[], | 
|  | 930 | const SkPoint texs[], const SkColor colors[], | 
|  | 931 | SkXfermode* xmode, const uint16_t indices[], | 
|  | 932 | int indexCount, const SkPaint& paint) { | 
| vandebo@chromium.org | fb0b0ed | 2011-04-15 20:01:17 +0000 | [diff] [blame] | 933 | if (d.fClip->isEmpty()) { | 
|  | 934 | return; | 
|  | 935 | } | 
| vandebo@chromium.org | a518086 | 2010-10-26 19:48:49 +0000 | [diff] [blame] | 936 | NOT_IMPLEMENTED("drawVerticies", true); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 937 | } | 
|  | 938 |  | 
| vandebo@chromium.org | eb6c759 | 2010-10-26 19:54:45 +0000 | [diff] [blame] | 939 | void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y, | 
|  | 940 | const SkPaint& paint) { | 
|  | 941 | if ((device->getDeviceCapabilities() & kVector_Capability) == 0) { | 
|  | 942 | // If we somehow get a raster device, do what our parent would do. | 
|  | 943 | SkDevice::drawDevice(d, device, x, y, paint); | 
|  | 944 | return; | 
|  | 945 | } | 
| vandebo@chromium.org | eb6c759 | 2010-10-26 19:54:45 +0000 | [diff] [blame] | 946 |  | 
| vandebo@chromium.org | ee7a956 | 2011-05-24 17:38:01 +0000 | [diff] [blame] | 947 | // Assume that a vector capable device means that it's a PDF Device. | 
|  | 948 | SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device); | 
| ctguil@chromium.org | f4ff39c | 2011-05-24 19:55:05 +0000 | [diff] [blame] | 949 | if (pdfDevice->isContentEmpty()) { | 
| vandebo@chromium.org | ee7a956 | 2011-05-24 17:38:01 +0000 | [diff] [blame] | 950 | return; | 
|  | 951 | } | 
|  | 952 |  | 
| vandebo@chromium.org | 1aef2ed | 2011-02-03 21:46:10 +0000 | [diff] [blame] | 953 | SkMatrix matrix; | 
| reed@google.com | a6d59f6 | 2011-03-07 21:29:21 +0000 | [diff] [blame] | 954 | matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y)); | 
| vandebo@chromium.org | 13d14a9 | 2011-05-24 23:12:41 +0000 | [diff] [blame] | 955 | ScopedContentEntry content(this, d.fClipStack, *d.fClip, matrix, paint); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 956 | if (!content.entry()) { | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 957 | return; | 
|  | 958 | } | 
| vandebo@chromium.org | 1aef2ed | 2011-02-03 21:46:10 +0000 | [diff] [blame] | 959 |  | 
|  | 960 | SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice); | 
| vandebo@chromium.org | eb6c759 | 2010-10-26 19:54:45 +0000 | [diff] [blame] | 961 | fXObjectResources.push(xobject);  // Transfer reference. | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 962 | SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1, | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 963 | &content.entry()->fContent); | 
| vandebo@chromium.org | 9859428 | 2011-07-25 22:34:12 +0000 | [diff] [blame^] | 964 |  | 
|  | 965 | // Merge glyph sets from the drawn device. | 
|  | 966 | fFontGlyphUsage->merge(pdfDevice->getFontGlyphUsage()); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 967 | } | 
|  | 968 |  | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 969 | ContentEntry* SkPDFDevice::getLastContentEntry() { | 
|  | 970 | if (fDrawingArea == kContent_DrawingArea) { | 
|  | 971 | return fLastContentEntry; | 
|  | 972 | } else { | 
|  | 973 | return fLastMarginContentEntry; | 
|  | 974 | } | 
|  | 975 | } | 
|  | 976 |  | 
|  | 977 | SkTScopedPtr<ContentEntry>& SkPDFDevice::getContentEntries() { | 
|  | 978 | if (fDrawingArea == kContent_DrawingArea) { | 
|  | 979 | return fContentEntries; | 
|  | 980 | } else { | 
|  | 981 | return fMarginContentEntries; | 
|  | 982 | } | 
|  | 983 | } | 
|  | 984 |  | 
|  | 985 | void SkPDFDevice::setLastContentEntry(ContentEntry* contentEntry) { | 
|  | 986 | if (fDrawingArea == kContent_DrawingArea) { | 
|  | 987 | fLastContentEntry = contentEntry; | 
|  | 988 | } else { | 
|  | 989 | fLastMarginContentEntry = contentEntry; | 
|  | 990 | } | 
|  | 991 | } | 
|  | 992 |  | 
|  | 993 | void SkPDFDevice::setDrawingArea(DrawingArea drawingArea) { | 
|  | 994 | // TODO(ctguil): Verify this isn't called when a ScopedContentEntry exists. | 
|  | 995 | fDrawingArea = drawingArea; | 
|  | 996 | } | 
|  | 997 |  | 
| reed@google.com | 1feb330 | 2011-07-20 18:43:19 +0000 | [diff] [blame] | 998 | SkPDFDict* SkPDFDevice::getResourceDict() { | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 999 | if (fResourceDict.get() == NULL) { | 
|  | 1000 | fResourceDict = new SkPDFDict; | 
|  | 1001 | fResourceDict->unref();  // SkRefPtr and new both took a reference. | 
|  | 1002 |  | 
|  | 1003 | if (fGraphicStateResources.count()) { | 
|  | 1004 | SkRefPtr<SkPDFDict> extGState = new SkPDFDict(); | 
|  | 1005 | extGState->unref();  // SkRefPtr and new both took a reference. | 
|  | 1006 | for (int i = 0; i < fGraphicStateResources.count(); i++) { | 
|  | 1007 | SkString nameString("G"); | 
|  | 1008 | nameString.appendS32(i); | 
| vandebo@chromium.org | f7c1576 | 2011-02-01 22:19:44 +0000 | [diff] [blame] | 1009 | extGState->insert( | 
|  | 1010 | nameString.c_str(), | 
|  | 1011 | new SkPDFObjRef(fGraphicStateResources[i]))->unref(); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1012 | } | 
|  | 1013 | fResourceDict->insert("ExtGState", extGState.get()); | 
|  | 1014 | } | 
|  | 1015 |  | 
|  | 1016 | if (fXObjectResources.count()) { | 
|  | 1017 | SkRefPtr<SkPDFDict> xObjects = new SkPDFDict(); | 
|  | 1018 | xObjects->unref();  // SkRefPtr and new both took a reference. | 
|  | 1019 | for (int i = 0; i < fXObjectResources.count(); i++) { | 
|  | 1020 | SkString nameString("X"); | 
|  | 1021 | nameString.appendS32(i); | 
| vandebo@chromium.org | f7c1576 | 2011-02-01 22:19:44 +0000 | [diff] [blame] | 1022 | xObjects->insert( | 
|  | 1023 | nameString.c_str(), | 
|  | 1024 | new SkPDFObjRef(fXObjectResources[i]))->unref(); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1025 | } | 
|  | 1026 | fResourceDict->insert("XObject", xObjects.get()); | 
|  | 1027 | } | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 1028 |  | 
|  | 1029 | if (fFontResources.count()) { | 
|  | 1030 | SkRefPtr<SkPDFDict> fonts = new SkPDFDict(); | 
|  | 1031 | fonts->unref();  // SkRefPtr and new both took a reference. | 
|  | 1032 | for (int i = 0; i < fFontResources.count(); i++) { | 
|  | 1033 | SkString nameString("F"); | 
|  | 1034 | nameString.appendS32(i); | 
| vandebo@chromium.org | f7c1576 | 2011-02-01 22:19:44 +0000 | [diff] [blame] | 1035 | fonts->insert(nameString.c_str(), | 
|  | 1036 | new SkPDFObjRef(fFontResources[i]))->unref(); | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 1037 | } | 
|  | 1038 | fResourceDict->insert("Font", fonts.get()); | 
|  | 1039 | } | 
|  | 1040 |  | 
| vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 1041 | if (fShaderResources.count()) { | 
|  | 1042 | SkRefPtr<SkPDFDict> patterns = new SkPDFDict(); | 
|  | 1043 | patterns->unref();  // SkRefPtr and new both took a reference. | 
|  | 1044 | for (int i = 0; i < fShaderResources.count(); i++) { | 
|  | 1045 | SkString nameString("P"); | 
|  | 1046 | nameString.appendS32(i); | 
|  | 1047 | patterns->insert(nameString.c_str(), | 
|  | 1048 | new SkPDFObjRef(fShaderResources[i]))->unref(); | 
|  | 1049 | } | 
|  | 1050 | fResourceDict->insert("Pattern", patterns.get()); | 
|  | 1051 | } | 
|  | 1052 |  | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 1053 | // For compatibility, add all proc sets (only used for output to PS | 
|  | 1054 | // devices). | 
|  | 1055 | const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"}; | 
|  | 1056 | SkRefPtr<SkPDFArray> procSets = new SkPDFArray(); | 
|  | 1057 | procSets->unref();  // SkRefPtr and new both took a reference. | 
|  | 1058 | procSets->reserve(SK_ARRAY_COUNT(procs)); | 
| vandebo@chromium.org | f7c1576 | 2011-02-01 22:19:44 +0000 | [diff] [blame] | 1059 | for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++) | 
| reed@google.com | c789cf1 | 2011-07-20 12:14:33 +0000 | [diff] [blame] | 1060 | procSets->appendName(procs[i]); | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 1061 | fResourceDict->insert("ProcSet", procSets.get()); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1062 | } | 
| reed@google.com | 1feb330 | 2011-07-20 18:43:19 +0000 | [diff] [blame] | 1063 | return fResourceDict.get(); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1064 | } | 
|  | 1065 |  | 
| vandebo@chromium.org | a518086 | 2010-10-26 19:48:49 +0000 | [diff] [blame] | 1066 | void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const { | 
|  | 1067 | resourceList->setReserve(resourceList->count() + | 
|  | 1068 | fGraphicStateResources.count() + | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 1069 | fXObjectResources.count() + | 
| vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 1070 | fFontResources.count() + | 
|  | 1071 | fShaderResources.count()); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1072 | for (int i = 0; i < fGraphicStateResources.count(); i++) { | 
| vandebo@chromium.org | a518086 | 2010-10-26 19:48:49 +0000 | [diff] [blame] | 1073 | resourceList->push(fGraphicStateResources[i]); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1074 | fGraphicStateResources[i]->ref(); | 
| vandebo@chromium.org | a518086 | 2010-10-26 19:48:49 +0000 | [diff] [blame] | 1075 | fGraphicStateResources[i]->getResources(resourceList); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1076 | } | 
|  | 1077 | for (int i = 0; i < fXObjectResources.count(); i++) { | 
| vandebo@chromium.org | a518086 | 2010-10-26 19:48:49 +0000 | [diff] [blame] | 1078 | resourceList->push(fXObjectResources[i]); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1079 | fXObjectResources[i]->ref(); | 
| vandebo@chromium.org | a518086 | 2010-10-26 19:48:49 +0000 | [diff] [blame] | 1080 | fXObjectResources[i]->getResources(resourceList); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1081 | } | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 1082 | for (int i = 0; i < fFontResources.count(); i++) { | 
|  | 1083 | resourceList->push(fFontResources[i]); | 
|  | 1084 | fFontResources[i]->ref(); | 
|  | 1085 | fFontResources[i]->getResources(resourceList); | 
|  | 1086 | } | 
| vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 1087 | for (int i = 0; i < fShaderResources.count(); i++) { | 
|  | 1088 | resourceList->push(fShaderResources[i]); | 
|  | 1089 | fShaderResources[i]->ref(); | 
|  | 1090 | fShaderResources[i]->getResources(resourceList); | 
|  | 1091 | } | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1092 | } | 
|  | 1093 |  | 
| vandebo@chromium.org | f0ec266 | 2011-05-29 05:55:42 +0000 | [diff] [blame] | 1094 | const SkTDArray<SkPDFFont*>& SkPDFDevice::getFontResources() const { | 
|  | 1095 | return fFontResources; | 
|  | 1096 | } | 
|  | 1097 |  | 
| vandebo@chromium.org | a518086 | 2010-10-26 19:48:49 +0000 | [diff] [blame] | 1098 | SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const { | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1099 | SkRefPtr<SkPDFInt> zero = new SkPDFInt(0); | 
|  | 1100 | zero->unref();  // SkRefPtr and new both took a reference. | 
| vandebo@chromium.org | f7c1576 | 2011-02-01 22:19:44 +0000 | [diff] [blame] | 1101 |  | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1102 | SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray(); | 
|  | 1103 | mediaBox->unref();  // SkRefPtr and new both took a reference. | 
|  | 1104 | mediaBox->reserve(4); | 
|  | 1105 | mediaBox->append(zero.get()); | 
|  | 1106 | mediaBox->append(zero.get()); | 
| reed@google.com | c789cf1 | 2011-07-20 12:14:33 +0000 | [diff] [blame] | 1107 | mediaBox->appendInt(fPageSize.fWidth); | 
|  | 1108 | mediaBox->appendInt(fPageSize.fHeight); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1109 | return mediaBox; | 
|  | 1110 | } | 
|  | 1111 |  | 
| vandebo@chromium.org | c2a9b7f | 2011-02-24 23:22:30 +0000 | [diff] [blame] | 1112 | SkStream* SkPDFDevice::content() const { | 
| reed@google.com | 5667afc | 2011-06-27 14:42:15 +0000 | [diff] [blame] | 1113 | SkMemoryStream* result = new SkMemoryStream; | 
|  | 1114 | result->setData(this->copyContentToData())->unref(); | 
|  | 1115 | return result; | 
|  | 1116 | } | 
|  | 1117 |  | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 1118 | void SkPDFDevice::copyContentEntriesToData(ContentEntry* entry, | 
|  | 1119 | SkWStream* data) const { | 
|  | 1120 | GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, data); | 
|  | 1121 | while(entry != NULL) { | 
|  | 1122 | SkIPoint translation = this->getOrigin(); | 
|  | 1123 | translation.negate(); | 
|  | 1124 | gsState.updateClip(entry->fState.fClipStack, entry->fState.fClipRegion, | 
|  | 1125 | translation); | 
|  | 1126 | gsState.updateMatrix(entry->fState.fMatrix); | 
|  | 1127 | gsState.updateDrawingState(entry->fState); | 
|  | 1128 |  | 
|  | 1129 | SkAutoDataUnref copy(entry->fContent.copyToData()); | 
|  | 1130 | data->write(copy.data(), copy.size()); | 
|  | 1131 | entry = entry->fNext.get(); | 
|  | 1132 | } | 
|  | 1133 | gsState.drainStack(); | 
|  | 1134 | } | 
|  | 1135 |  | 
| reed@google.com | 5667afc | 2011-06-27 14:42:15 +0000 | [diff] [blame] | 1136 | SkData* SkPDFDevice::copyContentToData() const { | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1137 | SkDynamicMemoryWStream data; | 
|  | 1138 | if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) { | 
|  | 1139 | SkPDFUtils::AppendTransform(fInitialTransform, &data); | 
| vandebo@chromium.org | c2a9b7f | 2011-02-24 23:22:30 +0000 | [diff] [blame] | 1140 | } | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 1141 |  | 
|  | 1142 | // TODO(aayushkumar): Apply clip along the margins.  Currently, webkit | 
|  | 1143 | // colors the contentArea white before it starts drawing into it and | 
|  | 1144 | // that currently acts as our clip. | 
|  | 1145 | // Also, think about adding a transform here (or assume that the values | 
|  | 1146 | // sent across account for that) | 
|  | 1147 | SkPDFDevice::copyContentEntriesToData(fMarginContentEntries.get(), &data); | 
|  | 1148 |  | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1149 | // If the content area is the entire page, then we don't need to clip | 
|  | 1150 | // the content area (PDF area clips to the page size).  Otherwise, | 
|  | 1151 | // we have to clip to the content area; we've already applied the | 
|  | 1152 | // initial transform, so just clip to the device size. | 
|  | 1153 | if (fPageSize != fContentSize) { | 
|  | 1154 | SkRect r = SkRect::MakeWH(this->width(), this->height()); | 
|  | 1155 | emit_clip(NULL, &r, &data); | 
|  | 1156 | } | 
| vandebo@chromium.org | 9859428 | 2011-07-25 22:34:12 +0000 | [diff] [blame^] | 1157 |  | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 1158 | SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), &data); | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1159 |  | 
| reed@google.com | 5667afc | 2011-06-27 14:42:15 +0000 | [diff] [blame] | 1160 | // potentially we could cache this SkData, and only rebuild it if we | 
|  | 1161 | // see that our state has changed. | 
|  | 1162 | return data.copyToData(); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1163 | } | 
|  | 1164 |  | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1165 | void SkPDFDevice::createFormXObjectFromDevice( | 
|  | 1166 | SkRefPtr<SkPDFFormXObject>* xobject) { | 
|  | 1167 | *xobject = new SkPDFFormXObject(this); | 
|  | 1168 | (*xobject)->unref();  // SkRefPtr and new both took a reference. | 
| vandebo@chromium.org | 9859428 | 2011-07-25 22:34:12 +0000 | [diff] [blame^] | 1169 | // We always draw the form xobjects that we create back into the device, so | 
|  | 1170 | // we simply preserve the font usage instead of pulling it out and merging | 
|  | 1171 | // it back in later. | 
|  | 1172 | cleanUp(false);  // Reset this device to have no content. | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1173 | init(); | 
|  | 1174 | } | 
|  | 1175 |  | 
| vandebo@chromium.org | 466f3d6 | 2011-05-18 23:06:29 +0000 | [diff] [blame] | 1176 | void SkPDFDevice::clearClipFromContent(const SkClipStack* clipStack, | 
|  | 1177 | const SkRegion& clipRegion) { | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1178 | if (clipRegion.isEmpty() || isContentEmpty()) { | 
|  | 1179 | return; | 
|  | 1180 | } | 
| vandebo@chromium.org | 466f3d6 | 2011-05-18 23:06:29 +0000 | [diff] [blame] | 1181 | SkRefPtr<SkPDFFormXObject> curContent; | 
|  | 1182 | createFormXObjectFromDevice(&curContent); | 
|  | 1183 |  | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1184 | // Redraw what we already had, but with the clip as a mask. | 
|  | 1185 | drawFormXObjectWithClip(curContent.get(), clipStack, clipRegion, true); | 
|  | 1186 | } | 
|  | 1187 |  | 
|  | 1188 | void SkPDFDevice::drawFormXObjectWithClip(SkPDFFormXObject* xobject, | 
|  | 1189 | const SkClipStack* clipStack, | 
|  | 1190 | const SkRegion& clipRegion, | 
|  | 1191 | bool invertClip) { | 
|  | 1192 | if (clipRegion.isEmpty() && !invertClip) { | 
|  | 1193 | return; | 
|  | 1194 | } | 
|  | 1195 |  | 
| vandebo@chromium.org | 466f3d6 | 2011-05-18 23:06:29 +0000 | [diff] [blame] | 1196 | // Create the mask. | 
|  | 1197 | SkMatrix identity; | 
|  | 1198 | identity.reset(); | 
|  | 1199 | SkDraw draw; | 
|  | 1200 | draw.fMatrix = &identity; | 
|  | 1201 | draw.fClip = &clipRegion; | 
|  | 1202 | draw.fClipStack = clipStack; | 
|  | 1203 | SkPaint stockPaint; | 
|  | 1204 | this->drawPaint(draw, stockPaint); | 
|  | 1205 | SkRefPtr<SkPDFFormXObject> maskFormXObject; | 
|  | 1206 | createFormXObjectFromDevice(&maskFormXObject); | 
|  | 1207 | SkRefPtr<SkPDFGraphicState> sMaskGS = | 
| reed@google.com | f6c3ebd | 2011-07-20 17:20:28 +0000 | [diff] [blame] | 1208 | SkPDFGraphicState::GetSMaskGraphicState(maskFormXObject.get(), | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1209 | invertClip); | 
| vandebo@chromium.org | 466f3d6 | 2011-05-18 23:06:29 +0000 | [diff] [blame] | 1210 | sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref. | 
|  | 1211 |  | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1212 | // Draw the xobject with the clip as a mask. | 
| vandebo@chromium.org | 13d14a9 | 2011-05-24 23:12:41 +0000 | [diff] [blame] | 1213 | ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion, | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1214 | identity, stockPaint); | 
|  | 1215 | if (!content.entry()) { | 
|  | 1216 | return; | 
|  | 1217 | } | 
| vandebo@chromium.org | 466f3d6 | 2011-05-18 23:06:29 +0000 | [diff] [blame] | 1218 | SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1219 | &content.entry()->fContent); | 
| vandebo@chromium.org | 466f3d6 | 2011-05-18 23:06:29 +0000 | [diff] [blame] | 1220 | SkPDFUtils::DrawFormXObject(fXObjectResources.count(), | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1221 | &content.entry()->fContent); | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1222 | fXObjectResources.push(xobject); | 
|  | 1223 | xobject->ref(); | 
| vandebo@chromium.org | 466f3d6 | 2011-05-18 23:06:29 +0000 | [diff] [blame] | 1224 |  | 
| reed@google.com | f6c3ebd | 2011-07-20 17:20:28 +0000 | [diff] [blame] | 1225 | sMaskGS = SkPDFGraphicState::GetNoSMaskGraphicState(); | 
| vandebo@chromium.org | 466f3d6 | 2011-05-18 23:06:29 +0000 | [diff] [blame] | 1226 | sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref. | 
|  | 1227 | SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1228 | &content.entry()->fContent); | 
| vandebo@chromium.org | 466f3d6 | 2011-05-18 23:06:29 +0000 | [diff] [blame] | 1229 | } | 
|  | 1230 |  | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1231 | ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack, | 
|  | 1232 | const SkRegion& clipRegion, | 
|  | 1233 | const SkMatrix& matrix, | 
|  | 1234 | const SkPaint& paint, | 
|  | 1235 | bool hasText, | 
|  | 1236 | SkRefPtr<SkPDFFormXObject>* dst) { | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 1237 | if (clipRegion.isEmpty()) { | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1238 | return NULL; | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 1239 | } | 
|  | 1240 |  | 
| vandebo@chromium.org | 78dad54 | 2011-05-11 18:46:03 +0000 | [diff] [blame] | 1241 | // The clip stack can come from an SkDraw where it is technically optional. | 
|  | 1242 | SkClipStack synthesizedClipStack; | 
|  | 1243 | if (clipStack == NULL) { | 
|  | 1244 | if (clipRegion == fExistingClipRegion) { | 
|  | 1245 | clipStack = &fExistingClipStack; | 
|  | 1246 | } else { | 
|  | 1247 | // GraphicStackState::updateClip expects the clip stack to have | 
|  | 1248 | // fExistingClip as a prefix, so start there, then set the clip | 
|  | 1249 | // to the passed region. | 
|  | 1250 | synthesizedClipStack = fExistingClipStack; | 
|  | 1251 | SkPath clipPath; | 
|  | 1252 | clipRegion.getBoundaryPath(&clipPath); | 
|  | 1253 | synthesizedClipStack.clipDevPath(clipPath, SkRegion::kReplace_Op); | 
|  | 1254 | clipStack = &synthesizedClipStack; | 
|  | 1255 | } | 
|  | 1256 | } | 
|  | 1257 |  | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 1258 | SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode; | 
|  | 1259 | if (paint.getXfermode()) { | 
|  | 1260 | paint.getXfermode()->asMode(&xfermode); | 
|  | 1261 | } | 
|  | 1262 |  | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 1263 | if (xfermode == SkXfermode::kClear_Mode || | 
|  | 1264 | xfermode == SkXfermode::kSrc_Mode) { | 
| vandebo@chromium.org | 466f3d6 | 2011-05-18 23:06:29 +0000 | [diff] [blame] | 1265 | this->clearClipFromContent(clipStack, clipRegion); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1266 | } else if (xfermode == SkXfermode::kSrcIn_Mode || | 
|  | 1267 | xfermode == SkXfermode::kDstIn_Mode || | 
|  | 1268 | xfermode == SkXfermode::kSrcOut_Mode || | 
|  | 1269 | xfermode == SkXfermode::kDstOut_Mode) { | 
|  | 1270 | // For the following modes, we use both source and destination, but | 
|  | 1271 | // we use one as a smask for the other, so we have to make form xobjects | 
|  | 1272 | // out of both of them: SrcIn, DstIn, SrcOut, DstOut. | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1273 | if (isContentEmpty()) { | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1274 | return NULL; | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1275 | } else { | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1276 | createFormXObjectFromDevice(dst); | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1277 | } | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1278 | } | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1279 | // TODO(vandebo) Figure out how/if we can handle the following modes: | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1280 | // SrcAtop, DestAtop, Xor, Plus. | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 1281 |  | 
|  | 1282 | // These xfer modes don't draw source at all. | 
|  | 1283 | if (xfermode == SkXfermode::kClear_Mode || | 
|  | 1284 | xfermode == SkXfermode::kDst_Mode) { | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1285 | return NULL; | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 1286 | } | 
|  | 1287 |  | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1288 | ContentEntry* entry; | 
|  | 1289 | SkTScopedPtr<ContentEntry> newEntry; | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 1290 |  | 
|  | 1291 | ContentEntry* lastContentEntry = getLastContentEntry(); | 
|  | 1292 | if (lastContentEntry && lastContentEntry->fContent.getOffset() == 0) { | 
|  | 1293 | entry = lastContentEntry; | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1294 | } else { | 
|  | 1295 | newEntry.reset(new ContentEntry); | 
|  | 1296 | entry = newEntry.get(); | 
|  | 1297 | } | 
|  | 1298 |  | 
| vandebo@chromium.org | 78dad54 | 2011-05-11 18:46:03 +0000 | [diff] [blame] | 1299 | populateGraphicStateEntryFromPaint(matrix, *clipStack, clipRegion, paint, | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1300 | hasText, &entry->fState); | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 1301 | if (lastContentEntry && xfermode != SkXfermode::kDstOver_Mode && | 
|  | 1302 | entry->fState.compareInitialState(lastContentEntry->fState)) { | 
|  | 1303 | return lastContentEntry; | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1304 | } | 
|  | 1305 |  | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 1306 | SkTScopedPtr<ContentEntry>& contentEntries = getContentEntries(); | 
|  | 1307 | if (!lastContentEntry) { | 
|  | 1308 | contentEntries.reset(entry); | 
|  | 1309 | setLastContentEntry(entry); | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 1310 | } else if (xfermode == SkXfermode::kDstOver_Mode) { | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 1311 | entry->fNext.reset(contentEntries.release()); | 
|  | 1312 | contentEntries.reset(entry); | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1313 | } else { | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 1314 | lastContentEntry->fNext.reset(entry); | 
|  | 1315 | setLastContentEntry(entry); | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1316 | } | 
|  | 1317 | newEntry.release(); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1318 | return entry; | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1319 | } | 
|  | 1320 |  | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1321 | void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode, | 
|  | 1322 | SkPDFFormXObject* dst) { | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1323 | if (xfermode != SkXfermode::kSrcIn_Mode && | 
|  | 1324 | xfermode != SkXfermode::kDstIn_Mode && | 
|  | 1325 | xfermode != SkXfermode::kSrcOut_Mode && | 
|  | 1326 | xfermode != SkXfermode::kDstOut_Mode) { | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1327 | SkASSERT(!dst); | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1328 | return; | 
|  | 1329 | } | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 1330 |  | 
|  | 1331 | SkTScopedPtr<ContentEntry>& contentEntries = getContentEntries(); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1332 | SkASSERT(dst); | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 1333 | SkASSERT(!contentEntries->fNext.get()); | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1334 | // We have to make a copy of these here because changing the current | 
|  | 1335 | // content into a form xobject will destroy them. | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 1336 | SkClipStack clipStack = contentEntries->fState.fClipStack; | 
|  | 1337 | SkRegion clipRegion = contentEntries->fState.fClipRegion; | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1338 |  | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1339 | SkRefPtr<SkPDFFormXObject> srcFormXObject; | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1340 | if (!isContentEmpty()) { | 
|  | 1341 | createFormXObjectFromDevice(&srcFormXObject); | 
|  | 1342 | } | 
|  | 1343 |  | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1344 | drawFormXObjectWithClip(dst, &clipStack, clipRegion, true); | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1345 |  | 
|  | 1346 | // We've redrawn dst minus the clip area, if there's no src, we're done. | 
|  | 1347 | if (!srcFormXObject.get()) { | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1348 | return; | 
|  | 1349 | } | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1350 |  | 
|  | 1351 | SkMatrix identity; | 
|  | 1352 | identity.reset(); | 
|  | 1353 | SkPaint stockPaint; | 
| vandebo@chromium.org | 13d14a9 | 2011-05-24 23:12:41 +0000 | [diff] [blame] | 1354 | ScopedContentEntry inClipContentEntry(this, &fExistingClipStack, | 
|  | 1355 | fExistingClipRegion, identity, | 
|  | 1356 | stockPaint); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1357 | if (!inClipContentEntry.entry()) { | 
|  | 1358 | return; | 
|  | 1359 | } | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1360 |  | 
|  | 1361 | SkRefPtr<SkPDFGraphicState> sMaskGS; | 
|  | 1362 | if (xfermode == SkXfermode::kSrcIn_Mode || | 
|  | 1363 | xfermode == SkXfermode::kSrcOut_Mode) { | 
| reed@google.com | f6c3ebd | 2011-07-20 17:20:28 +0000 | [diff] [blame] | 1364 | sMaskGS = SkPDFGraphicState::GetSMaskGraphicState( | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1365 | dst, xfermode == SkXfermode::kSrcOut_Mode); | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1366 | fXObjectResources.push(srcFormXObject.get()); | 
|  | 1367 | srcFormXObject->ref(); | 
|  | 1368 | } else { | 
| reed@google.com | f6c3ebd | 2011-07-20 17:20:28 +0000 | [diff] [blame] | 1369 | sMaskGS = SkPDFGraphicState::GetSMaskGraphicState( | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1370 | srcFormXObject.get(), xfermode == SkXfermode::kDstOut_Mode); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1371 | // dst already added to fXObjectResources in drawFormXObjectWithClip. | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1372 | } | 
|  | 1373 | sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref. | 
|  | 1374 | SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1375 | &inClipContentEntry.entry()->fContent); | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1376 |  | 
|  | 1377 | SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1, | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1378 | &inClipContentEntry.entry()->fContent); | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1379 |  | 
| reed@google.com | f6c3ebd | 2011-07-20 17:20:28 +0000 | [diff] [blame] | 1380 | sMaskGS = SkPDFGraphicState::GetNoSMaskGraphicState(); | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1381 | sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref. | 
|  | 1382 | SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1383 | &inClipContentEntry.entry()->fContent); | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1384 | } | 
|  | 1385 |  | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1386 | bool SkPDFDevice::isContentEmpty() { | 
| ctguil@chromium.org | 8dcf74f | 2011-07-12 21:56:27 +0000 | [diff] [blame] | 1387 | SkTScopedPtr<ContentEntry>& contentEntries = getContentEntries(); | 
|  | 1388 | if (!contentEntries.get() || contentEntries->fContent.getOffset() == 0) { | 
|  | 1389 | SkASSERT(!contentEntries.get() || !contentEntries->fNext.get()); | 
| vandebo@chromium.org | 481aef6 | 2011-05-24 16:39:05 +0000 | [diff] [blame] | 1390 | return true; | 
|  | 1391 | } | 
|  | 1392 | return false; | 
|  | 1393 | } | 
|  | 1394 |  | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1395 | void SkPDFDevice::populateGraphicStateEntryFromPaint( | 
|  | 1396 | const SkMatrix& matrix, | 
|  | 1397 | const SkClipStack& clipStack, | 
|  | 1398 | const SkRegion& clipRegion, | 
|  | 1399 | const SkPaint& paint, | 
|  | 1400 | bool hasText, | 
|  | 1401 | GraphicStateEntry* entry) { | 
| vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 1402 | SkASSERT(paint.getPathEffect() == NULL); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1403 |  | 
| vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 1404 | NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false); | 
|  | 1405 | NOT_IMPLEMENTED(paint.getColorFilter() != NULL, false); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1406 |  | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1407 | entry->fMatrix = matrix; | 
|  | 1408 | entry->fClipStack = clipStack; | 
|  | 1409 | entry->fClipRegion = clipRegion; | 
| vandebo@chromium.org | 4854327 | 2011-02-08 19:28:07 +0000 | [diff] [blame] | 1410 |  | 
| vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 1411 | // PDF treats a shader as a color, so we only set one or the other. | 
| vandebo@chromium.org | 421d644 | 2011-07-20 17:39:01 +0000 | [diff] [blame] | 1412 | SkRefPtr<SkPDFObject> pdfShader; | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1413 | const SkShader* shader = paint.getShader(); | 
|  | 1414 | SkColor color = paint.getColor(); | 
| vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 1415 | if (shader) { | 
|  | 1416 | // PDF positions patterns relative to the initial transform, so | 
|  | 1417 | // we need to apply the current transform to the shader parameters. | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1418 | SkMatrix transform = matrix; | 
| vandebo@chromium.org | 75f97e4 | 2011-04-11 23:24:18 +0000 | [diff] [blame] | 1419 | transform.postConcat(fInitialTransform); | 
| vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 1420 |  | 
|  | 1421 | // PDF doesn't support kClamp_TileMode, so we simulate it by making | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1422 | // a pattern the size of the current clip. | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1423 | SkIRect bounds = clipRegion.getBounds(); | 
| reed@google.com | f6c3ebd | 2011-07-20 17:20:28 +0000 | [diff] [blame] | 1424 | pdfShader = SkPDFShader::GetPDFShader(*shader, transform, bounds); | 
| vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 1425 | SkSafeUnref(pdfShader.get());  // getShader and SkRefPtr both took a ref | 
|  | 1426 |  | 
| vandebo@chromium.org | b88cfe5 | 2011-07-18 18:40:32 +0000 | [diff] [blame] | 1427 | if (pdfShader.get()) { | 
|  | 1428 | // pdfShader has been canonicalized so we can directly compare | 
|  | 1429 | // pointers. | 
|  | 1430 | int resourceIndex = fShaderResources.find(pdfShader.get()); | 
|  | 1431 | if (resourceIndex < 0) { | 
|  | 1432 | resourceIndex = fShaderResources.count(); | 
|  | 1433 | fShaderResources.push(pdfShader.get()); | 
|  | 1434 | pdfShader->ref(); | 
|  | 1435 | } | 
|  | 1436 | entry->fShaderIndex = resourceIndex; | 
|  | 1437 | } else { | 
|  | 1438 | // A color shader is treated as an invalid shader so we don't have | 
|  | 1439 | // to set a shader just for a color. | 
|  | 1440 | entry->fShaderIndex = -1; | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1441 | entry->fColor = 0; | 
|  | 1442 | color = 0; | 
| vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 1443 |  | 
|  | 1444 | // Check for a color shader. | 
|  | 1445 | SkShader::GradientInfo gradientInfo; | 
|  | 1446 | SkColor gradientColor; | 
|  | 1447 | gradientInfo.fColors = &gradientColor; | 
|  | 1448 | gradientInfo.fColorOffsets = NULL; | 
|  | 1449 | gradientInfo.fColorCount = 1; | 
|  | 1450 | if (shader->asAGradient(&gradientInfo) == | 
|  | 1451 | SkShader::kColor_GradientType) { | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1452 | entry->fColor = SkColorSetA(gradientColor, 0xFF); | 
|  | 1453 | color = gradientColor; | 
| vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 1454 | } | 
|  | 1455 | } | 
| vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 1456 | } else { | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1457 | entry->fShaderIndex = -1; | 
|  | 1458 | entry->fColor = SkColorSetA(paint.getColor(), 0xFF); | 
|  | 1459 | color = paint.getColor(); | 
| vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 1460 | } | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1461 |  | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1462 | SkRefPtr<SkPDFGraphicState> newGraphicState; | 
|  | 1463 | if (color == paint.getColor()) { | 
| reed@google.com | f6c3ebd | 2011-07-20 17:20:28 +0000 | [diff] [blame] | 1464 | newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(paint); | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1465 | } else { | 
|  | 1466 | SkPaint newPaint = paint; | 
|  | 1467 | newPaint.setColor(color); | 
| reed@google.com | f6c3ebd | 2011-07-20 17:20:28 +0000 | [diff] [blame] | 1468 | newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(newPaint); | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1469 | } | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1470 | newGraphicState->unref();  // getGraphicState and SkRefPtr both took a ref. | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1471 | int resourceIndex = addGraphicStateResource(newGraphicState.get()); | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1472 | entry->fGraphicStateIndex = resourceIndex; | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1473 |  | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1474 | if (hasText) { | 
|  | 1475 | entry->fTextScaleX = paint.getTextScaleX(); | 
|  | 1476 | entry->fTextFill = paint.getStyle(); | 
|  | 1477 | } else { | 
|  | 1478 | entry->fTextScaleX = 0; | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1479 | } | 
|  | 1480 | } | 
|  | 1481 |  | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1482 | int SkPDFDevice::addGraphicStateResource(SkPDFGraphicState* gs) { | 
|  | 1483 | // Assumes that gs has been canonicalized (so we can directly compare | 
|  | 1484 | // pointers). | 
|  | 1485 | int result = fGraphicStateResources.find(gs); | 
|  | 1486 | if (result < 0) { | 
|  | 1487 | result = fGraphicStateResources.count(); | 
|  | 1488 | fGraphicStateResources.push(gs); | 
|  | 1489 | gs->ref(); | 
|  | 1490 | } | 
|  | 1491 | return result; | 
|  | 1492 | } | 
|  | 1493 |  | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1494 | void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID, | 
|  | 1495 | ContentEntry* contentEntry) { | 
| ctguil@chromium.org | 9db86bb | 2011-03-04 21:43:27 +0000 | [diff] [blame] | 1496 | SkTypeface* typeface = paint.getTypeface(); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1497 | if (contentEntry->fState.fFont == NULL || | 
|  | 1498 | contentEntry->fState.fTextSize != paint.getTextSize() || | 
|  | 1499 | !contentEntry->fState.fFont->hasGlyph(glyphID)) { | 
| ctguil@chromium.org | 9db86bb | 2011-03-04 21:43:27 +0000 | [diff] [blame] | 1500 | int fontIndex = getFontResourceIndex(typeface, glyphID); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1501 | contentEntry->fContent.writeText("/F"); | 
|  | 1502 | contentEntry->fContent.writeDecAsText(fontIndex); | 
|  | 1503 | contentEntry->fContent.writeText(" "); | 
|  | 1504 | SkPDFScalar::Append(paint.getTextSize(), &contentEntry->fContent); | 
|  | 1505 | contentEntry->fContent.writeText(" Tf\n"); | 
|  | 1506 | contentEntry->fState.fFont = fFontResources[fontIndex]; | 
| vandebo@chromium.org | 2a22e10 | 2011-01-25 21:01:34 +0000 | [diff] [blame] | 1507 | } | 
|  | 1508 | } | 
|  | 1509 |  | 
| ctguil@chromium.org | 9db86bb | 2011-03-04 21:43:27 +0000 | [diff] [blame] | 1510 | int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) { | 
| reed@google.com | f6c3ebd | 2011-07-20 17:20:28 +0000 | [diff] [blame] | 1511 | SkRefPtr<SkPDFFont> newFont = SkPDFFont::GetFontResource(typeface, glyphID); | 
| vandebo@chromium.org | 2a22e10 | 2011-01-25 21:01:34 +0000 | [diff] [blame] | 1512 | newFont->unref();  // getFontResource and SkRefPtr both took a ref. | 
| vandebo@chromium.org | 28be72b | 2010-11-11 21:37:00 +0000 | [diff] [blame] | 1513 | int resourceIndex = fFontResources.find(newFont.get()); | 
|  | 1514 | if (resourceIndex < 0) { | 
|  | 1515 | resourceIndex = fFontResources.count(); | 
|  | 1516 | fFontResources.push(newFont.get()); | 
|  | 1517 | newFont->ref(); | 
|  | 1518 | } | 
|  | 1519 | return resourceIndex; | 
|  | 1520 | } | 
|  | 1521 |  | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1522 | void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix, | 
| vandebo@chromium.org | 78dad54 | 2011-05-11 18:46:03 +0000 | [diff] [blame] | 1523 | const SkClipStack* clipStack, | 
| vandebo@chromium.org | 9fbdf87 | 2011-05-09 07:55:58 +0000 | [diff] [blame] | 1524 | const SkRegion& clipRegion, | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1525 | const SkBitmap& bitmap, | 
| vandebo@chromium.org | befebb8 | 2011-01-29 01:38:50 +0000 | [diff] [blame] | 1526 | const SkIRect* srcRect, | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1527 | const SkPaint& paint) { | 
| vandebo@chromium.org | 7e2ff7c | 2010-11-03 23:55:28 +0000 | [diff] [blame] | 1528 | SkMatrix scaled; | 
|  | 1529 | // Adjust for origin flip. | 
|  | 1530 | scaled.setScale(1, -1); | 
|  | 1531 | scaled.postTranslate(0, 1); | 
|  | 1532 | // Scale the image up from 1x1 to WxH. | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 1533 | SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height()); | 
| reed@google.com | a6d59f6 | 2011-03-07 21:29:21 +0000 | [diff] [blame] | 1534 | scaled.postScale(SkIntToScalar(subset.width()), | 
|  | 1535 | SkIntToScalar(subset.height())); | 
| vandebo@chromium.org | 7e2ff7c | 2010-11-03 23:55:28 +0000 | [diff] [blame] | 1536 | scaled.postConcat(matrix); | 
| vandebo@chromium.org | 13d14a9 | 2011-05-24 23:12:41 +0000 | [diff] [blame] | 1537 | ScopedContentEntry content(this, clipStack, clipRegion, scaled, paint); | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1538 | if (!content.entry()) { | 
| vandebo@chromium.org | 25adce8 | 2011-05-09 08:05:01 +0000 | [diff] [blame] | 1539 | return; | 
|  | 1540 | } | 
|  | 1541 |  | 
|  | 1542 | if (srcRect && !subset.intersect(*srcRect)) { | 
|  | 1543 | return; | 
|  | 1544 | } | 
|  | 1545 |  | 
|  | 1546 | SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint); | 
|  | 1547 | if (!image) { | 
|  | 1548 | return; | 
|  | 1549 | } | 
| vandebo@chromium.org | 7e2ff7c | 2010-11-03 23:55:28 +0000 | [diff] [blame] | 1550 |  | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1551 | fXObjectResources.push(image);  // Transfer reference. | 
| vandebo@chromium.org | 6112c21 | 2011-05-13 03:50:38 +0000 | [diff] [blame] | 1552 | SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1, | 
| vandebo@chromium.org | b069c8c | 2011-05-24 17:19:38 +0000 | [diff] [blame] | 1553 | &content.entry()->fContent); | 
| vandebo@chromium.org | 9b49dc0 | 2010-10-20 22:23:29 +0000 | [diff] [blame] | 1554 | } |