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