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