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