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