SkPDFDevice.cpp revision 9db86bb9cd1b77be0afc504ccc07026e4282d7e7
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 "SkGlyphCache.h"
21#include "SkPaint.h"
22#include "SkPath.h"
23#include "SkPDFImage.h"
24#include "SkPDFGraphicState.h"
25#include "SkPDFFont.h"
26#include "SkPDFFormXObject.h"
27#include "SkPDFTypes.h"
28#include "SkPDFStream.h"
29#include "SkPDFUtils.h"
30#include "SkRect.h"
31#include "SkString.h"
32#include "SkTextFormatParams.h"
33#include "SkTypeface.h"
34#include "SkTypes.h"
35
36// Utility functions
37
38namespace {
39
40SkString toPDFColor(SkColor color) {
41    SkASSERT(SkColorGetA(color) == 0xFF);  // We handle alpha elsewhere.
42    SkScalar colorMax = SkIntToScalar(0xFF);
43    SkString result;
44    SkPDFScalar::Append(
45            SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), &result);
46    result.append(" ");
47    SkPDFScalar::Append(
48            SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), &result);
49    result.append(" ");
50    SkPDFScalar::Append(
51            SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), &result);
52    result.append(" ");
53    return result;
54}
55
56SkPaint calculateTextPaint(const SkPaint& paint) {
57    SkPaint result = paint;
58    if (result.isFakeBoldText()) {
59        SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(),
60                                                    kStdFakeBoldInterpKeys,
61                                                    kStdFakeBoldInterpValues,
62                                                    kStdFakeBoldInterpLength);
63        SkScalar width = SkScalarMul(result.getTextSize(), fakeBoldScale);
64        if (result.getStyle() == SkPaint::kFill_Style)
65            result.setStyle(SkPaint::kStrokeAndFill_Style);
66        else
67            width += result.getStrokeWidth();
68        result.setStrokeWidth(width);
69    }
70    return result;
71}
72
73// Stolen from measure_text in SkDraw.cpp and then tweaked.
74void alignText(SkDrawCacheProc glyphCacheProc, const SkPaint& paint,
75               const uint16_t* glyphs, size_t len, SkScalar* x, SkScalar* y,
76               SkScalar* width) {
77    if (paint.getTextAlign() == SkPaint::kLeft_Align && width == NULL)
78        return;
79
80    SkMatrix ident;
81    ident.reset();
82    SkAutoGlyphCache autoCache(paint, &ident);
83    SkGlyphCache* cache = autoCache.getCache();
84
85    const char* start = (char*)glyphs;
86    const char* stop = (char*)(glyphs + len);
87    SkFixed xAdv = 0, yAdv = 0;
88
89    // TODO(vandebo) This probably needs to take kerning into account.
90    while (start < stop) {
91        const SkGlyph& glyph = glyphCacheProc(cache, &start, 0, 0);
92        xAdv += glyph.fAdvanceX;
93        yAdv += glyph.fAdvanceY;
94    };
95    if (width)
96        *width = SkFixedToScalar(xAdv);
97    if (paint.getTextAlign() == SkPaint::kLeft_Align)
98        return;
99
100    SkScalar xAdj = SkFixedToScalar(xAdv);
101    SkScalar yAdj = SkFixedToScalar(yAdv);
102    if (paint.getTextAlign() == SkPaint::kCenter_Align) {
103        xAdj = SkScalarHalf(xAdj);
104        yAdj = SkScalarHalf(yAdj);
105    }
106    *x = *x - xAdj;
107    *y = *y - yAdj;
108}
109
110}  // namespace
111
112////////////////////////////////////////////////////////////////////////////////
113
114SkDevice* SkPDFDeviceFactory::newDevice(SkCanvas*, SkBitmap::Config config,
115                                        int width, int height, bool isOpaque,
116                                        bool isForLayer) {
117    SkPDFDevice::OriginTransform flip = SkPDFDevice::kFlip_OriginTransform;
118    if (isForLayer) {
119        flip = SkPDFDevice::kNoFlip_OriginTransform;
120    }
121    return SkNEW_ARGS(SkPDFDevice, (width, height, flip));
122}
123
124static inline SkBitmap makeABitmap(int width, int height) {
125    SkBitmap bitmap;
126    bitmap.setConfig(SkBitmap::kNo_Config, width, height);
127    return bitmap;
128}
129
130SkPDFDevice::SkPDFDevice(int width, int height, OriginTransform flipOrigin)
131    : SkDevice(NULL, makeABitmap(width, height), false),
132      fWidth(width),
133      fHeight(height),
134      fFlipOrigin(flipOrigin),
135      fGraphicStackIndex(0) {
136    fGraphicStack[0].fColor = SK_ColorBLACK;
137    fGraphicStack[0].fTextSize = SK_ScalarNaN;  // This has no default value.
138    fGraphicStack[0].fTextScaleX = SK_Scalar1;
139    fGraphicStack[0].fTextFill = SkPaint::kFill_Style;
140    fGraphicStack[0].fFont = NULL;
141    fGraphicStack[0].fGraphicState = NULL;
142    fGraphicStack[0].fClip.setRect(0,0, width, height);
143    fGraphicStack[0].fTransform.reset();
144
145    if (flipOrigin == kFlip_OriginTransform) {
146        fContent.printf("1 0 0 -1 0 %d cm\n", fHeight);
147    }
148}
149
150SkPDFDevice::~SkPDFDevice() {
151    fGraphicStateResources.unrefAll();
152    fXObjectResources.unrefAll();
153    fFontResources.unrefAll();
154}
155
156void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
157                                const SkRegion& region,
158                                const SkClipStack&) {
159    // See the comment in the header file above GraphicStackEntry.
160    if (region != fGraphicStack[fGraphicStackIndex].fClip) {
161        while (fGraphicStackIndex > 0)
162            popGS();
163        pushGS();
164
165        SkPath clipPath;
166        if (region.getBoundaryPath(&clipPath)) {
167            SkPDFUtils::EmitPath(clipPath, &fContent);
168
169            SkPath::FillType clipFill = clipPath.getFillType();
170            NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType,
171                            false);
172            NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType,
173                            false);
174            if (clipFill == SkPath::kEvenOdd_FillType)
175                fContent.append("W* n ");
176            else
177                fContent.append("W n ");
178        }
179
180        fGraphicStack[fGraphicStackIndex].fClip = region;
181    }
182    setTransform(matrix);
183}
184
185void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
186    SkMatrix identityTransform;
187    identityTransform.reset();
188    SkMatrix curTransform = setTransform(identityTransform);
189
190    SkPaint newPaint = paint;
191    newPaint.setStyle(SkPaint::kFill_Style);
192    updateGSFromPaint(newPaint, false);
193
194    SkRect all = SkRect::MakeWH(width() + 1, height() + 1);
195    drawRect(d, all, newPaint);
196    setTransform(curTransform);
197}
198
199void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
200                             size_t count, const SkPoint* points,
201                             const SkPaint& paint) {
202    if (count == 0)
203        return;
204
205    switch (mode) {
206        case SkCanvas::kPolygon_PointMode:
207            updateGSFromPaint(paint, false);
208            SkPDFUtils::MoveTo(points[0].fX, points[0].fY, &fContent);
209            for (size_t i = 1; i < count; i++) {
210                SkPDFUtils::AppendLine(points[i].fX, points[i].fY, &fContent);
211            }
212            SkPDFUtils::StrokePath(&fContent);
213            break;
214        case SkCanvas::kLines_PointMode:
215            updateGSFromPaint(paint, false);
216            for (size_t i = 0; i < count/2; i++) {
217                SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY,
218                                   &fContent);
219                SkPDFUtils::AppendLine(points[i * 2 + 1].fX,
220                                       points[i * 2 + 1].fY, &fContent);
221                SkPDFUtils::StrokePath(&fContent);
222            }
223            break;
224        case SkCanvas::kPoints_PointMode:
225            if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
226                updateGSFromPaint(paint, false);
227                for (size_t i = 0; i < count; i++) {
228                    SkPDFUtils::MoveTo(points[i].fX, points[i].fY, &fContent);
229                    SkPDFUtils::StrokePath(&fContent);
230                }
231            } else {
232                // PDF won't draw a single point with square/butt caps because
233                // the orientation is ambiguous.  Draw a rectangle instead.
234                SkPaint newPaint = paint;
235                newPaint.setStyle(SkPaint::kFill_Style);
236                SkScalar strokeWidth = paint.getStrokeWidth();
237                SkScalar halfStroke = strokeWidth * SK_ScalarHalf;
238                for (size_t i = 0; i < count; i++) {
239                    SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY,
240                                                0, 0);
241                    r.inset(-halfStroke, -halfStroke);
242                    drawRect(d, r, newPaint);
243                }
244            }
245            break;
246        default:
247            SkASSERT(false);
248    }
249}
250
251void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
252                           const SkPaint& paint) {
253    if (paint.getPathEffect()) {
254        // Create a path for the rectangle and apply the path effect to it.
255        SkPath path;
256        path.addRect(r);
257        paint.getFillPath(path, &path);
258
259        SkPaint noEffectPaint(paint);
260        SkSafeUnref(noEffectPaint.setPathEffect(NULL));
261        drawPath(d, path, noEffectPaint, NULL, true);
262        return;
263    }
264    updateGSFromPaint(paint, false);
265
266    // Skia has 0,0 at top left, pdf at bottom left.  Do the right thing.
267    SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop;
268    SkPDFUtils::AppendRectangle(r.fLeft, bottom, r.width(), r.height(),
269                                &fContent);
270    SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
271                          &fContent);
272}
273
274void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
275                           const SkPaint& paint, const SkMatrix* prePathMatrix,
276                           bool pathIsMutable) {
277    NOT_IMPLEMENTED(prePathMatrix != NULL, true);
278
279    if (paint.getPathEffect()) {
280        // Apply the path effect to path and draw it that way.
281        SkPath noEffectPath;
282        paint.getFillPath(path, &noEffectPath);
283
284        SkPaint noEffectPaint(paint);
285        SkSafeUnref(noEffectPaint.setPathEffect(NULL));
286        drawPath(d, noEffectPath, noEffectPaint, NULL, true);
287        return;
288    }
289    updateGSFromPaint(paint, false);
290
291    SkPDFUtils::EmitPath(path, &fContent);
292    SkPDFUtils::PaintPath(paint.getStyle(), path.getFillType(), &fContent);
293}
294
295void SkPDFDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
296                             const SkIRect* srcRect,
297                             const SkMatrix& matrix, const SkPaint& paint) {
298    SkMatrix transform = matrix;
299    transform.postConcat(fGraphicStack[fGraphicStackIndex].fTransform);
300    internalDrawBitmap(transform, bitmap, srcRect, paint);
301}
302
303void SkPDFDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
304                             int x, int y, const SkPaint& paint) {
305    SkMatrix matrix;
306    matrix.setTranslate(x, y);
307    internalDrawBitmap(matrix, bitmap, NULL, paint);
308}
309
310void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
311                           SkScalar x, SkScalar y, const SkPaint& paint) {
312    SkPaint textPaint = calculateTextPaint(paint);
313    updateGSFromPaint(textPaint, true);
314
315    // We want the text in glyph id encoding and a writable buffer, so we end
316    // up making a copy either way.
317    size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
318    uint16_t* glyphIDs =
319        (uint16_t*)sk_malloc_flags(numGlyphs * 2,
320                                   SK_MALLOC_TEMP | SK_MALLOC_THROW);
321    SkAutoFree autoFreeGlyphIDs(glyphIDs);
322    if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
323        paint.textToGlyphs(text, len, glyphIDs);
324        textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
325    } else {
326        SkASSERT((len & 1) == 0);
327        SkASSERT(len / 2 == numGlyphs);
328        memcpy(glyphIDs, text, len);
329    }
330
331    SkScalar width;
332    SkScalar* widthPtr = NULL;
333    if (textPaint.isUnderlineText() || textPaint.isStrikeThruText())
334        widthPtr = &width;
335
336    SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
337    alignText(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, widthPtr);
338    fContent.append("BT\n");
339    setTextTransform(x, y, textPaint.getTextSkewX());
340    size_t consumedGlyphCount = 0;
341    while (numGlyphs > consumedGlyphCount) {
342        updateFont(textPaint, glyphIDs[consumedGlyphCount]);
343        SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
344        size_t availableGlyphs =
345            font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount,
346                                          numGlyphs - consumedGlyphCount);
347        fContent.append(SkPDFString::formatString(glyphIDs + consumedGlyphCount,
348                                                  availableGlyphs,
349                                                  font->multiByteGlyphs()));
350        consumedGlyphCount += availableGlyphs;
351        fContent.append(" Tj\n");
352    }
353    fContent.append("ET\n");
354
355    // Draw underline and/or strikethrough if the paint has them.
356    // drawPosText() and drawTextOnPath() don't draw underline or strikethrough
357    // because the raster versions don't.  Use paint instead of textPaint
358    // because we may have changed strokeWidth to do fakeBold text.
359    if (paint.isUnderlineText() || paint.isStrikeThruText()) {
360        SkScalar textSize = paint.getTextSize();
361        SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
362
363        if (paint.isUnderlineText()) {
364            SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y);
365            SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
366            drawRect(d, r, paint);
367        }
368        if (paint.isStrikeThruText()) {
369            SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y);
370            SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
371            drawRect(d, r, paint);
372        }
373    }
374}
375
376void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len,
377                              const SkScalar pos[], SkScalar constY,
378                              int scalarsPerPos, const SkPaint& paint) {
379    SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
380    SkPaint textPaint = calculateTextPaint(paint);
381    updateGSFromPaint(textPaint, true);
382
383    // Make sure we have a glyph id encoding.
384    SkAutoFree glyphStorage;
385    uint16_t* glyphIDs;
386    size_t numGlyphs;
387    if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
388        numGlyphs = paint.textToGlyphs(text, len, NULL);
389        glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
390                                              SK_MALLOC_TEMP | SK_MALLOC_THROW);
391        glyphStorage.set(glyphIDs);
392        paint.textToGlyphs(text, len, glyphIDs);
393        textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
394    } else {
395        SkASSERT((len & 1) == 0);
396        numGlyphs = len / 2;
397        glyphIDs = (uint16_t*)text;
398    }
399
400    SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
401    fContent.append("BT\n");
402    updateFont(textPaint, glyphIDs[0]);
403    for (size_t i = 0; i < numGlyphs; i++) {
404        SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
405        uint16_t encodedValue = glyphIDs[i];
406        if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) {
407            updateFont(textPaint, glyphIDs[i]);
408            i--;
409            continue;
410        }
411        SkScalar x = pos[i * scalarsPerPos];
412        SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
413        alignText(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
414        setTextTransform(x, y, textPaint.getTextSkewX());
415        fContent.append(SkPDFString::formatString(&encodedValue, 1,
416                                                  font->multiByteGlyphs()));
417        fContent.append(" Tj\n");
418    }
419    fContent.append("ET\n");
420}
421
422void SkPDFDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len,
423                                 const SkPath& path, const SkMatrix* matrix,
424                                 const SkPaint& paint) {
425    NOT_IMPLEMENTED("drawTextOnPath", true);
426}
427
428void SkPDFDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
429                               int vertexCount, const SkPoint verts[],
430                               const SkPoint texs[], const SkColor colors[],
431                               SkXfermode* xmode, const uint16_t indices[],
432                               int indexCount, const SkPaint& paint) {
433    NOT_IMPLEMENTED("drawVerticies", true);
434}
435
436void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
437                             const SkPaint& paint) {
438    if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
439        // If we somehow get a raster device, do what our parent would do.
440        SkDevice::drawDevice(d, device, x, y, paint);
441        return;
442    }
443    // Assume that a vector capable device means that it's a PDF Device.
444    SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
445
446    SkMatrix matrix;
447    matrix.setTranslate(x, y);
448    SkMatrix curTransform = setTransform(matrix);
449    updateGSFromPaint(paint, false);
450
451    SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
452    fXObjectResources.push(xobject);  // Transfer reference.
453    fContent.append("/X");
454    fContent.appendS32(fXObjectResources.count() - 1);
455    fContent.append(" Do\n");
456    setTransform(curTransform);
457}
458
459const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
460    if (fResourceDict.get() == NULL) {
461        fResourceDict = new SkPDFDict;
462        fResourceDict->unref();  // SkRefPtr and new both took a reference.
463
464        if (fGraphicStateResources.count()) {
465            SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
466            extGState->unref();  // SkRefPtr and new both took a reference.
467            for (int i = 0; i < fGraphicStateResources.count(); i++) {
468                SkString nameString("G");
469                nameString.appendS32(i);
470                extGState->insert(
471                        nameString.c_str(),
472                        new SkPDFObjRef(fGraphicStateResources[i]))->unref();
473            }
474            fResourceDict->insert("ExtGState", extGState.get());
475        }
476
477        if (fXObjectResources.count()) {
478            SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
479            xObjects->unref();  // SkRefPtr and new both took a reference.
480            for (int i = 0; i < fXObjectResources.count(); i++) {
481                SkString nameString("X");
482                nameString.appendS32(i);
483                xObjects->insert(
484                        nameString.c_str(),
485                        new SkPDFObjRef(fXObjectResources[i]))->unref();
486            }
487            fResourceDict->insert("XObject", xObjects.get());
488        }
489
490        if (fFontResources.count()) {
491            SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
492            fonts->unref();  // SkRefPtr and new both took a reference.
493            for (int i = 0; i < fFontResources.count(); i++) {
494                SkString nameString("F");
495                nameString.appendS32(i);
496                fonts->insert(nameString.c_str(),
497                              new SkPDFObjRef(fFontResources[i]))->unref();
498            }
499            fResourceDict->insert("Font", fonts.get());
500        }
501
502        // For compatibility, add all proc sets (only used for output to PS
503        // devices).
504        const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"};
505        SkRefPtr<SkPDFArray> procSets = new SkPDFArray();
506        procSets->unref();  // SkRefPtr and new both took a reference.
507        procSets->reserve(SK_ARRAY_COUNT(procs));
508        for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++)
509            procSets->append(new SkPDFName(procs[i]))->unref();
510        fResourceDict->insert("ProcSet", procSets.get());
511    }
512    return fResourceDict;
513}
514
515void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
516    resourceList->setReserve(resourceList->count() +
517                             fGraphicStateResources.count() +
518                             fXObjectResources.count() +
519                             fFontResources.count());
520    for (int i = 0; i < fGraphicStateResources.count(); i++) {
521        resourceList->push(fGraphicStateResources[i]);
522        fGraphicStateResources[i]->ref();
523        fGraphicStateResources[i]->getResources(resourceList);
524    }
525    for (int i = 0; i < fXObjectResources.count(); i++) {
526        resourceList->push(fXObjectResources[i]);
527        fXObjectResources[i]->ref();
528        fXObjectResources[i]->getResources(resourceList);
529    }
530    for (int i = 0; i < fFontResources.count(); i++) {
531        resourceList->push(fFontResources[i]);
532        fFontResources[i]->ref();
533        fFontResources[i]->getResources(resourceList);
534    }
535}
536
537SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
538    SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
539    zero->unref();  // SkRefPtr and new both took a reference.
540
541    SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
542    mediaBox->unref();  // SkRefPtr and new both took a reference.
543    mediaBox->reserve(4);
544    mediaBox->append(zero.get());
545    mediaBox->append(zero.get());
546    mediaBox->append(new SkPDFInt(fWidth))->unref();
547    mediaBox->append(new SkPDFInt(fHeight))->unref();
548    return mediaBox;
549}
550
551SkStream* SkPDFDevice::content() const {
552    size_t offset = fContent.size();
553    char* data = (char*)sk_malloc_throw(offset + fGraphicStackIndex * 2);
554    memcpy(data, fContent.c_str(), offset);
555    for (int i = 0; i < fGraphicStackIndex; i++) {
556        data[offset++] = 'Q';
557        data[offset++] = '\n';
558    }
559    SkMemoryStream* result = new SkMemoryStream;
560    result->setMemoryOwned(data, offset);
561    return result;
562}
563
564// Private
565
566// TODO(vandebo) handle these cases.
567#define PAINTCHECK(x,y) NOT_IMPLEMENTED(newPaint.x() y, false)
568
569void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) {
570    SkASSERT(newPaint.getPathEffect() == NULL);
571
572    PAINTCHECK(getMaskFilter, != NULL);
573    PAINTCHECK(getShader, != NULL);
574    PAINTCHECK(getColorFilter, != NULL);
575
576    SkRefPtr<SkPDFGraphicState> newGraphicState =
577        SkPDFGraphicState::getGraphicStateForPaint(newPaint);
578    newGraphicState->unref();  // getGraphicState and SkRefPtr both took a ref.
579    // newGraphicState has been canonicalized so we can directly compare
580    // pointers.
581    if (fGraphicStack[fGraphicStackIndex].fGraphicState !=
582            newGraphicState.get()) {
583        int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
584        if (resourceIndex < 0) {
585            resourceIndex = fGraphicStateResources.count();
586            fGraphicStateResources.push(newGraphicState.get());
587            newGraphicState->ref();
588        }
589        fContent.append("/G");
590        fContent.appendS32(resourceIndex);
591        fContent.append(" gs\n");
592        fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState.get();
593    }
594
595    SkColor newColor = newPaint.getColor();
596    newColor = SkColorSetA(newColor, 0xFF);
597    if (fGraphicStack[fGraphicStackIndex].fColor != newColor) {
598        SkString colorString = toPDFColor(newColor);
599        fContent.append(colorString);
600        fContent.append("RG ");
601        fContent.append(colorString);
602        fContent.append("rg\n");
603        fGraphicStack[fGraphicStackIndex].fColor = newColor;
604    }
605
606    if (forText) {
607        if (fGraphicStack[fGraphicStackIndex].fTextScaleX !=
608                newPaint.getTextScaleX()) {
609            SkScalar scale = newPaint.getTextScaleX();
610            SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100));
611            SkPDFScalar::Append(pdfScale, &fContent);
612            fContent.append(" Tz\n");
613            fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
614        }
615
616        if (fGraphicStack[fGraphicStackIndex].fTextFill !=
617                newPaint.getStyle()) {
618            SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
619            SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
620                              enum_must_match_value);
621            SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
622                              enum_must_match_value);
623            fContent.appendS32(newPaint.getStyle());
624            fContent.append(" Tr\n");
625            fGraphicStack[fGraphicStackIndex].fTextFill = newPaint.getStyle();
626        }
627    }
628}
629
630void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
631    SkTypeface* typeface = paint.getTypeface();
632    if (fGraphicStack[fGraphicStackIndex].fTextSize != paint.getTextSize() ||
633            fGraphicStack[fGraphicStackIndex].fFont == NULL ||
634            fGraphicStack[fGraphicStackIndex].fFont->typeface() != typeface ||
635            !fGraphicStack[fGraphicStackIndex].fFont->hasGlyph(glyphID)) {
636        int fontIndex = getFontResourceIndex(typeface, glyphID);
637        fContent.append("/F");
638        fContent.appendS32(fontIndex);
639        fContent.append(" ");
640        SkPDFScalar::Append(paint.getTextSize(), &fContent);
641        fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize();
642        fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex];
643    }
644}
645
646int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
647    SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(typeface, glyphID);
648    newFont->unref();  // getFontResource and SkRefPtr both took a ref.
649    int resourceIndex = fFontResources.find(newFont.get());
650    if (resourceIndex < 0) {
651        resourceIndex = fFontResources.count();
652        fFontResources.push(newFont.get());
653        newFont->ref();
654    }
655    return resourceIndex;
656}
657
658void SkPDFDevice::pushGS() {
659    SkASSERT(fGraphicStackIndex < 2);
660    fContent.append("q\n");
661    fGraphicStackIndex++;
662    fGraphicStack[fGraphicStackIndex] = fGraphicStack[fGraphicStackIndex - 1];
663}
664
665void SkPDFDevice::popGS() {
666    SkASSERT(fGraphicStackIndex > 0);
667    fContent.append("Q\n");
668    fGraphicStackIndex--;
669}
670
671void SkPDFDevice::setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX) {
672    // Flip the text about the x-axis to account for origin swap and include
673    // the passed parameters.
674    fContent.append("1 0 ");
675    SkPDFScalar::Append(0 - textSkewX, &fContent);
676    fContent.append(" -1 ");
677    SkPDFScalar::Append(x, &fContent);
678    fContent.append(" ");
679    SkPDFScalar::Append(y, &fContent);
680    fContent.append(" Tm\n");
681}
682
683void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
684                                     const SkBitmap& bitmap,
685                                     const SkIRect* srcRect,
686                                     const SkPaint& paint) {
687    SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height());
688    if (srcRect && !subset.intersect(*srcRect))
689        return;
690
691    SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint);
692    if (!image)
693        return;
694
695    SkMatrix scaled;
696    // Adjust for origin flip.
697    scaled.setScale(1, -1);
698    scaled.postTranslate(0, 1);
699    // Scale the image up from 1x1 to WxH.
700    scaled.postScale(subset.width(), subset.height());
701    scaled.postConcat(matrix);
702    SkMatrix curTransform = setTransform(scaled);
703    updateGSFromPaint(paint, false);
704
705    fXObjectResources.push(image);  // Transfer reference.
706    fContent.append("/X");
707    fContent.appendS32(fXObjectResources.count() - 1);
708    fContent.append(" Do\n");
709    setTransform(curTransform);
710}
711
712SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
713    SkMatrix old = fGraphicStack[fGraphicStackIndex].fTransform;
714    if (old == m)
715        return old;
716
717    if (old.getType() != SkMatrix::kIdentity_Mask) {
718        SkASSERT(fGraphicStackIndex > 0);
719        SkASSERT(fGraphicStack[fGraphicStackIndex - 1].fTransform.getType() ==
720                 SkMatrix::kIdentity_Mask);
721        SkASSERT(fGraphicStack[fGraphicStackIndex].fClip ==
722                 fGraphicStack[fGraphicStackIndex - 1].fClip);
723        popGS();
724    }
725    if (m.getType() == SkMatrix::kIdentity_Mask)
726        return old;
727
728    if (fGraphicStackIndex == 0 || fGraphicStack[fGraphicStackIndex].fClip !=
729            fGraphicStack[fGraphicStackIndex - 1].fClip)
730        pushGS();
731
732    SkScalar transform[6];
733    SkAssertResult(m.pdfTransform(transform));
734    for (size_t i = 0; i < SK_ARRAY_COUNT(transform); i++) {
735        SkPDFScalar::Append(transform[i], &fContent);
736        fContent.append(" ");
737    }
738    fContent.append("cm\n");
739    fGraphicStack[fGraphicStackIndex].fTransform = m;
740
741    return old;
742}
743