// static SkPDFGraphicState* SkPDFGraphicState::GetSMaskGraphicState( SkPDFFormXObject* sMask, bool invert) { // The practical chances of using the same mask more than once are unlikely // enough that it's not worth canonicalizing. SkAutoMutexAcquire lock(CanonicalPaintsMutex()); SkRefPtr<SkPDFDict> sMaskDict = new SkPDFDict("Mask"); sMaskDict->unref(); // SkRefPtr and new both took a reference. sMaskDict->insertName("S", "Alpha"); sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref(); SkPDFGraphicState* result = new SkPDFGraphicState; result->fPopulated = true; result->fSMask = true; result->insertName("Type", "ExtGState"); result->insert("SMask", sMaskDict.get()); result->fResources.push(sMask); sMask->ref(); if (invert) { SkPDFObject* invertFunction = GetInvertFunction(); result->fResources.push(invertFunction); invertFunction->ref(); sMaskDict->insert("TR", new SkPDFObjRef(invertFunction))->unref(); } return result; }
// static SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader, const SkMatrix& matrix, const SkIRect& surfaceBBox) { SkPDFObject* result; SkAutoMutexAcquire lock(CanonicalShadersMutex()); SkAutoTDelete<State> shaderState(new State(shader, matrix, surfaceBBox)); ShaderCanonicalEntry entry(NULL, shaderState.get()); int index = CanonicalShaders().find(entry); if (index >= 0) { result = CanonicalShaders()[index].fPDFShader; result->ref(); return result; } // The PDFShader takes ownership of the shaderSate. if (shaderState.get()->fType == SkShader::kNone_GradientType) { result = new SkPDFImageShader(shaderState.detach()); } else { SkPDFFunctionShader* functionShader = new SkPDFFunctionShader(shaderState.detach()); if (!functionShader->isValid()) { delete functionShader; return NULL; } result = functionShader; } entry.fPDFShader = result; CanonicalShaders().push(entry); return result; // return the reference that came from new. }
void SkPDFObjRef::addResources(SkTSet<SkPDFObject*>* resourceSet, SkPDFCatalog* catalog) const { SkPDFObject* obj = catalog->getSubstituteObject(fObj); SkASSERT(obj); if (resourceSet->add(obj)) { obj->addResources(resourceSet, catalog); } }
void PDFDefaultBitmap::addResources( SkPDFObjNumMap* catalog, const SkPDFSubstituteMap& substitutes) const { if (fSMask.get()) { SkPDFObject* obj = substitutes.getSubstitute(fSMask.get()); SkASSERT(obj); if (catalog->addObject(obj)) { obj->addResources(catalog, substitutes); } } }
// static SkPDFObject* SkPDFShader::GetPDFShaderByState(State* inState) { SkPDFObject* result; SkAutoTDelete<State> shaderState(inState); if (shaderState.get()->fType == SkShader::kNone_GradientType && shaderState.get()->fImage.isNull()) { // TODO(vandebo) This drops SKComposeShader on the floor. We could // handle compose shader by pulling things up to a layer, drawing with // the first shader, applying the xfer mode and drawing again with the // second shader, then applying the layer to the original drawing. return NULL; } ShaderCanonicalEntry entry(NULL, shaderState.get()); int index = CanonicalShaders().find(entry); if (index >= 0) { result = CanonicalShaders()[index].fPDFShader; result->ref(); return result; } bool valid = false; // The PDFShader takes ownership of the shaderSate. if (shaderState.get()->fType == SkShader::kNone_GradientType) { SkPDFImageShader* imageShader = new SkPDFImageShader(shaderState.detach()); valid = imageShader->isValid(); result = imageShader; } else { if (shaderState.get()->GradientHasAlpha()) { SkPDFAlphaFunctionShader* gradientShader = SkNEW_ARGS(SkPDFAlphaFunctionShader, (shaderState.detach())); valid = gradientShader->isValid(); result = gradientShader; } else { SkPDFFunctionShader* functionShader = SkNEW_ARGS(SkPDFFunctionShader, (shaderState.detach())); valid = functionShader->isValid(); result = functionShader; } } if (!valid) { delete result; return NULL; } entry.fPDFShader = result; CanonicalShaders().push(entry); return result; // return the reference that came from new. }
// Serialize all objects in the fObjNumMap that have not yet been serialized; void SkPDFObjectSerializer::serializeObjects(SkWStream* wStream) { const SkTArray<sk_sp<SkPDFObject>>& objects = fObjNumMap.objects(); while (fNextToBeSerialized < objects.count()) { SkPDFObject* object = objects[fNextToBeSerialized].get(); int32_t index = fNextToBeSerialized + 1; // Skip object 0. // "The first entry in the [XREF] table (object number 0) is // always free and has a generation number of 65,535; it is // the head of the linked list of free objects." SkASSERT(fOffsets.count() == fNextToBeSerialized); fOffsets.push(this->offset(wStream)); wStream->writeDecAsText(index); wStream->writeText(" 0 obj\n"); // Generation number is always 0. object->emitObject(wStream, fObjNumMap); wStream->writeText("\nendobj\n"); object->drop(); ++fNextToBeSerialized; } }
void SkPDFObject::emit(SkWStream* stream, SkPDFCatalog* catalog, bool indirect) { SkPDFObject* realObject = catalog->getSubstituteObject(this); return realObject->emitObject(stream, catalog, indirect); }
static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices, SkWStream* stream) { if (pageDevices.isEmpty()) { return false; } SkTDArray<SkPDFDict*> pages; SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict)); for (int i = 0; i < pageDevices.count(); i++) { SkASSERT(pageDevices[i]); SkASSERT(i == 0 || pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon()); SkAutoTUnref<SkPDFDict> page(create_pdf_page(pageDevices[i])); pageDevices[i]->appendDestinations(dests, page.get()); pages.push(page.detach()); } SkTDArray<SkPDFDict*> pageTree; SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog"))); SkPDFDict* pageTreeRoot; generate_page_tree(pages, &pageTree, &pageTreeRoot); docCatalog->insertObjRef("Pages", SkRef(pageTreeRoot)); if (dests->size() > 0) { docCatalog->insertObjRef("Dests", dests.detach()); } /* TODO(vandebo): output intent SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent"); outputIntent->insertName("S", "GTS_PDFA1"); outputIntent->insertString("OutputConditionIdentifier", "sRGB"); SkAutoTUnref<SkPDFArray> intentArray(new SkPDFArray); intentArray->appendObject(SkRef(outputIntent.get())); docCatalog->insertObject("OutputIntent", intentArray.detach()); */ // Build font subsetting info before proceeding. SkPDFSubstituteMap substitutes; perform_font_subsetting(pageDevices, &substitutes); SkPDFObjNumMap objNumMap; if (objNumMap.addObject(docCatalog.get())) { docCatalog->addResources(&objNumMap, substitutes); } size_t baseOffset = stream->bytesWritten(); emit_pdf_header(stream); SkTDArray<int32_t> offsets; for (int i = 0; i < objNumMap.objects().count(); ++i) { SkPDFObject* object = objNumMap.objects()[i]; size_t offset = stream->bytesWritten(); // This assert checks that size(pdf_header) > 0 and that // the output stream correctly reports bytesWritten(). SkASSERT(offset > baseOffset); offsets.push(SkToS32(offset - baseOffset)); SkASSERT(object == substitutes.getSubstitute(object)); SkASSERT(objNumMap.getObjectNumber(object) == i + 1); stream->writeDecAsText(i + 1); stream->writeText(" 0 obj\n"); // Generation number is always 0. object->emitObject(stream, objNumMap, substitutes); stream->writeText("\nendobj\n"); } int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset); // Include the zeroth object in the count. int32_t objCount = SkToS32(offsets.count() + 1); stream->writeText("xref\n0 "); stream->writeDecAsText(objCount); stream->writeText("\n0000000000 65535 f \n"); for (int i = 0; i < offsets.count(); i++) { SkASSERT(offsets[i] > 0); stream->writeBigDecAsText(offsets[i], 10); stream->writeText(" 00000 n \n"); } emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount, xRefFileOffset); // The page tree has both child and parent pointers, so it creates a // reference cycle. We must clear that cycle to properly reclaim memory. for (int i = 0; i < pageTree.count(); i++) { pageTree[i]->clear(); } pageTree.safeUnrefAll(); pages.unrefAll(); return true; }