SkPDF: refactor streams, experimental threading

All PDF Streams are immediatly serialized, and are never long-lived
    in memory.

    if EXPERIMENTAL fExecutor is set on the Document, streams are
    compressed in parallel.

    Results for PDFBigDocBench:

        without patch                    1807885.01 μs
        with    patch without executor   1802808.35 μs
        with    patch with    executor    246313.72 μs

    SkPDFStreamOut() function replaces SkPDFStream classes.

    Page resources are all serialized early.

    Several Document-level objects are serialzied early.

    SkUUID introduced as top-level object.

    Many {insert|append}ObjRef() converted to memory efficient
    {insert|append}Ref().

Bug: skia:8630
Change-Id: Ic336917d0c8b9ac1c2423b43bfe9b49a3533fbff
Reviewed-on: https://skia-review.googlesource.com/c/176588
Auto-Submit: Hal Canary <halcanary@google.com>
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Hal Canary <halcanary@google.com>
This commit is contained in:
Hal Canary 2018-12-10 19:59:07 -05:00 committed by Skia Commit-Bot
parent 7dfe6d9ea7
commit 9a3f554154
28 changed files with 706 additions and 687 deletions

View File

@ -10,6 +10,7 @@
#include "Resources.h"
#include "SkAutoPixmapStorage.h"
#include "SkData.h"
#include "SkExecutor.h"
#include "SkFloatToDecimal.h"
#include "SkGradientShader.h"
#include "SkImage.h"
@ -79,20 +80,6 @@ DEF_BENCH(return new PDFScalarBench("PDFScalar_random", next_any);)
#include "SkPDFUtils.h"
namespace {
static void test_pdf_object_serialization(const sk_sp<SkPDFObject> object) {
// SkDebugWStream wStream;
SkNullWStream wStream;
SkPDFObjNumMap objNumMap;
objNumMap.addObjectRecursively(object.get());
for (size_t i = 0; i < objNumMap.objects().size(); ++i) {
SkPDFObject* object = objNumMap.objects()[i].get();
wStream.writeDecAsText(i + 1);
wStream.writeText(" 0 obj\n");
object->emitObject(&wStream);
wStream.writeText("\nendobj\n");
}
}
class PDFImageBench : public Benchmark {
public:
PDFImageBench() {}
@ -184,11 +171,11 @@ protected:
SkASSERT(fAsset);
if (!fAsset) { return; }
while (loops-- > 0) {
sk_sp<SkPDFObject> object =
sk_make_sp<SkPDFSharedStream>(
std::unique_ptr<SkStreamAsset>(fAsset->duplicate()));
test_pdf_object_serialization(object);
}
SkNullWStream wStream;
SkPDFDocument doc(&wStream, SkPDF::Metadata());
doc.beginPage(256, 256);
(void)SkPDFStreamOut(nullptr, fAsset->duplicate(), &doc, true);
}
}
private:
@ -229,8 +216,9 @@ struct PDFShaderBench : public Benchmark {
while (loops-- > 0) {
SkNullWStream nullStream;
SkPDFDocument doc(&nullStream, SkPDF::Metadata());
sk_sp<SkPDFObject> shader = SkPDFMakeShader(&doc, fShader.get(), SkMatrix::I(),
{0, 0, 400, 400}, SK_ColorBLACK);
doc.beginPage(256, 256);
(void) SkPDFMakeShader(&doc, fShader.get(), SkMatrix::I(),
{0, 0, 400, 400}, SK_ColorBLACK);
}
}
};
@ -262,5 +250,125 @@ DEF_BENCH(return new PDFColorComponentBench;)
DEF_BENCH(return new PDFShaderBench;)
DEF_BENCH(return new WritePDFTextBenchmark;)
#if 0
#include "SkExecutor.h"
namespace {
void big_pdf_test(const SkBitmap& background) {
static const char* kText[] = {
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do",
"eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad",
"minim veniam, quis nostrud exercitation ullamco laboris nisi ut",
"aliquip ex ea commodo consequat. Duis aute irure dolor in",
"reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla",
"pariatur. Excepteur sint occaecat cupidatat non proident, sunt in",
"culpa qui officia deserunt mollit anim id est laborum.",
"",
"Sed ut perspiciatis, unde omnis iste natus error sit voluptatem",
"accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae",
"ab illo inventore veritatis et quasi architecto beatae vitae dicta",
"sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit,",
"aspernatur aut odit aut fugit, sed quia consequuntur magni dolores",
"eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,",
"qui dolorem ipsum, quia dolor sit amet consectetur adipiscing velit,",
"sed quia non numquam do eius modi tempora incididunt, ut labore et",
"dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam,",
"quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi",
"ut aliquid ex ea commodi consequatur? Quis autem vel eum iure",
"reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae",
"consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla",
"pariatur?",
"",
"At vero eos et accusamus et iusto odio dignissimos ducimus, qui",
"blanditiis praesentium voluptatum deleniti atque corrupti, quos",
"dolores et quas molestias excepturi sint, obcaecati cupiditate non",
"provident, similique sunt in culpa, qui officia deserunt mollitia",
"animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis",
"est et expedita distinctio. Nam libero tempore, cum soluta nobis est",
"eligendi optio, cumque nihil impedit, quo minus id, quod maxime",
"placeat, facere possimus, omnis voluptas assumenda est, omnis dolor",
"repellendus. Temporibus autem quibusdam et aut officiis debitis aut",
"rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint",
"et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente",
"delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut",
"perferendis doloribus asperiores repellat",
"",
"Sed ut perspiciatis, unde omnis iste natus error sit voluptatem",
"accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae",
"ab illo inventore veritatis et quasi architecto beatae vitae dicta",
"sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit,",
"aspernatur aut odit aut fugit, sed quia consequuntur magni dolores",
"eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,",
"qui dolorem ipsum, quia dolor sit amet consectetur adipiscing velit,",
"sed quia non numquam do eius modi tempora incididunt, ut labore et",
"dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam,",
"quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi",
"ut aliquid ex ea commodi consequatur? Quis autem vel eum iure",
"reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae",
"consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla",
"pariatur?",
"",
};
//SkFILEWStream wStream("/tmp/big_pdf.pdf");
SkNullWStream wStream;
SkPDF::Metadata metadata;
//std::unique_ptr<SkExecutor> executor = SkExecutor::MakeFIFOThreadPool();
//metadata.fExecutor = executor.get();
sk_sp<SkDocument> doc = SkPDF::MakeDocument(&wStream, metadata);
SkCanvas* canvas = nullptr;
float x = 36;
float y = 36;
constexpr size_t kLineCount = SK_ARRAY_COUNT(kText);
constexpr int kLoopCount = 200;
SkPaint paint;
paint.setTextEncoding(kUTF8_SkTextEncoding);
for (int loop = 0; loop < kLoopCount; ++loop) {
for (size_t line = 0; line < kLineCount; ++line) {
y += paint.getFontSpacing();
if (!canvas || y > 792 - 36) {
y = 36 + paint.getFontSpacing();
canvas = doc->beginPage(612, 792);
background.notifyPixelsChanged();
canvas->drawBitmap(background, 0, 0);
}
canvas->drawText(kText[line], strlen(kText[line]), x, y, paint);
}
}
}
SkBitmap make_background() {
SkBitmap background;
SkBitmap bitmap;
bitmap.allocN32Pixels(32, 32);
bitmap.eraseColor(SK_ColorWHITE);
SkCanvas tmp(bitmap);
SkPaint gray;
gray.setColor(SkColorSetARGB(0xFF, 0xEE, 0xEE, 0xEE));
tmp.drawRect({0,0,16,16}, gray);
tmp.drawRect({16,16,32,32}, gray);
SkPaint shader;
shader.setShader(
SkShader::MakeBitmapShader(
bitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
background.allocN32Pixels(612, 792);
SkCanvas tmp2(background);
tmp2.drawPaint(shader);
return background;
}
struct PDFBigDocBench : public Benchmark {
SkBitmap fBackground;
void onDelayedSetup() override { fBackground = make_background(); }
const char* onGetName() override { return "PDFBigDocBench"; }
bool isSuitableFor(Backend backend) override {
return backend == kNonRendering_Backend;
}
void onDraw(int loops, SkCanvas*) override {
while (loops-- > 0) { big_pdf_test(fBackground); }
}
};
} // namespace
DEF_BENCH(return new PDFBigDocBench;)
#endif
#endif // SK_SUPPORT_PDF

View File

@ -1638,6 +1638,10 @@ Error PDFSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const
metadata.fCreator = "Skia/DM";
metadata.fRasterDPI = fRasterDpi;
metadata.fPDFA = fPDFA;
#if 0
std::unique_ptr<SkExecutor> executor = SkExecutor::MakeFIFOThreadPool();
metadata.fExecutor = executor.get();
#endif
sk_sp<SkDocument> doc = SkPDF::MakeDocument(dst, metadata);
if (!doc) {
return "SkPDF::MakeDocument() returned nullptr";

View File

@ -9,6 +9,8 @@
#include "SkString.h"
#include "SkTime.h"
class SkExecutor;
namespace SkPDF {
/** Table 333 in PDF 32000-1:2008
@ -141,6 +143,15 @@ struct Metadata {
* should retain ownership.
*/
const StructureElementNode* fStructureElementTreeRoot = nullptr;
/** Executor to handle threaded work within PDF Backend. If this is nullptr,
then all work will be done serially on the main thread. To have worker
threads assist with various tasks, set this to a valid SkExecutor
instance. Currently used for executing Deflate algorithm in parallel.
Experimental.
*/
SkExecutor* fExecutor = nullptr;
};
/** Associate a node ID with subsequent drawing commands in an

View File

@ -10,6 +10,7 @@
#include "SkColorData.h"
#include "SkData.h"
#include "SkDeflate.h"
#include "SkExecutor.h"
#include "SkImage.h"
#include "SkImageInfoPriv.h"
#include "SkJpegInfo.h"
@ -120,12 +121,13 @@ static SkPDFIndirectReference do_deflated_alpha(const SkPixmap& pm, SkPDFDocumen
return ref;
}
static SkPDFIndirectReference do_deflated_image(const SkPixmap& pm,
SkPDFDocument* doc,
bool isOpaque) {
static void do_deflated_image(const SkPixmap& pm,
SkPDFDocument* doc,
bool isOpaque,
SkPDFIndirectReference ref) {
SkPDFIndirectReference sMask;
if (!isOpaque) {
sMask = doc->reserve();
sMask = doc->reserveRef();
}
SkDynamicMemoryWStream buffer;
SkDeflateWStream deflateWStream(&buffer);
@ -167,7 +169,6 @@ static SkPDFIndirectReference do_deflated_image(const SkPixmap& pm,
deflateWStream.write(byteBuffer, dst - byteBuffer);
}
deflateWStream.finalize();
SkPDFIndirectReference ref = doc->reserve();
SkWStream* stream = doc->beginObject(ref);
emit_dict(stream, pm.info().dimensions(), colorSpace,
sMask.fValue != -1 ? &sMask : nullptr,
@ -177,11 +178,10 @@ static SkPDFIndirectReference do_deflated_image(const SkPixmap& pm,
if (!isOpaque) {
do_deflated_alpha(pm, doc, sMask);
}
return ref;
}
static bool do_jpeg(const SkData& data, SkPDFDocument* doc, SkISize size,
SkPDFIndirectReference* result) {
SkPDFIndirectReference ref) {
SkISize jpegSize;
SkEncodedInfo::Color jpegColorType;
SkEncodedOrigin exifOrientation;
@ -199,10 +199,8 @@ static bool do_jpeg(const SkData& data, SkPDFDocument* doc, SkISize size,
#ifdef SK_PDF_IMAGE_STATS
gJpegImageObjects.fetch_add(1);
#endif
SkPDFIndirectReference ref = doc->reserve();
*result = ref;
SkWStream* stream = doc->beginObject(ref);
SkWStream* stream = doc->beginObject(ref);
SkPDFDict pdfDict("XObject");
pdfDict.insertName("Subtype", "Image");
pdfDict.insertInt("Width", jpegSize.width());
@ -247,26 +245,44 @@ static SkBitmap to_pixels(const SkImage* image) {
return bm;
}
SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img,
SkPDFDocument* doc,
int encodingQuality) {
SkPDFIndirectReference result;
void serialize_image(const SkImage* img,
int encodingQuality,
SkPDFDocument* doc,
SkPDFIndirectReference ref) {
SkASSERT(img);
SkASSERT(doc);
SkASSERT(encodingQuality >= 0);
SkISize dimensions = img->dimensions();
sk_sp<SkData> data = img->refEncodedData();
if (data && do_jpeg(*data, doc, dimensions, &result)) {
return result;
if (data && do_jpeg(*data, doc, dimensions, ref)) {
return;
}
SkBitmap bm = to_pixels(img);
SkPixmap pm = bm.pixmap();
bool isOpaque = pm.isOpaque() || pm.computeIsOpaque();
if (encodingQuality <= 100 && isOpaque) {
sk_sp<SkData> data = img->encodeToData(SkEncodedImageFormat::kJPEG, encodingQuality);
if (data && do_jpeg(*data, doc, dimensions, &result)) {
return result;
if (data && do_jpeg(*data, doc, dimensions, ref)) {
return;
}
}
return do_deflated_image(pm, doc, isOpaque);
do_deflated_image(pm, doc, isOpaque, ref);
}
SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img,
SkPDFDocument* doc,
int encodingQuality) {
SkASSERT(img);
SkASSERT(doc);
SkPDFIndirectReference ref = doc->reserveRef();
if (SkExecutor* executor = doc->executor()) {
SkRef(img);
executor->add([img, encodingQuality, doc, ref]() {
serialize_image(img, encodingQuality, doc, ref);
SkSafeUnref(img);
});
return ref;
}
serialize_image(img, encodingQuality, doc, ref);
return ref;
}

View File

@ -34,11 +34,11 @@ public:
SkPDFCanon& operator=(SkPDFCanon&&);
SkPDFCanon& operator=(const SkPDFCanon&) = delete;
SkTHashMap<SkPDFImageShaderKey, sk_sp<SkPDFObject>> fImageShaderMap;
SkTHashMap<SkPDFImageShaderKey, SkPDFIndirectReference> fImageShaderMap;
SkPDFGradientShader::HashMap fGradientPatternMap;
SkTHashMap<SkBitmapKey, sk_sp<SkPDFObject>> fPDFBitmapMap;
SkTHashMap<SkBitmapKey, SkPDFIndirectReference> fPDFBitmapMap;
SkTHashMap<uint32_t, std::unique_ptr<SkAdvancedTypefaceMetrics>> fTypefaceMetrics;
SkTHashMap<uint32_t, std::vector<SkString>> fType1GlyphNames;
@ -47,12 +47,11 @@ public:
SkTHashMap<uint32_t, SkPDFIndirectReference> fType3FontDescriptors;
SkTHashMap<uint64_t, SkPDFFont> fFontMap;
SkTHashMap<SkPDFStrokeGraphicState, sk_sp<SkPDFDict>> fStrokeGSMap;
SkTHashMap<SkPDFFillGraphicState, sk_sp<SkPDFDict>> fFillGSMap;
SkTHashMap<SkPDFStrokeGraphicState, SkPDFIndirectReference> fStrokeGSMap;
SkTHashMap<SkPDFFillGraphicState, SkPDFIndirectReference> fFillGSMap;
sk_sp<SkPDFStream> fInvertFunction;
sk_sp<SkPDFDict> fNoSmaskGraphicState;
sk_sp<SkPDFArray> fRangeObject;
SkPDFIndirectReference fInvertFunction;
SkPDFIndirectReference fNoSmaskGraphicState;
};
#endif // SkPDFCanon_DEFINED

View File

@ -107,6 +107,11 @@ sk_sp<SkImage> alpha_image_to_greyscale_image(const SkImage* mask) {
return SkImage::MakeFromBitmap(greyBitmap);
}
static int add_resource(SkTHashSet<SkPDFIndirectReference>& resources, SkPDFIndirectReference ref) {
resources.add(ref);
return ref.fValue;
}
static void draw_points(SkCanvas::PointMode mode,
size_t count,
const SkPoint* points,
@ -429,7 +434,7 @@ public:
if (shape->isEmpty()) {
shape = nullptr;
}
fDevice->finishContentEntry(fClipStack, fBlendMode, std::move(fDstFormXObject), shape);
fDevice->finishContentEntry(fClipStack, fBlendMode, fDstFormXObject, shape);
}
}
@ -474,7 +479,7 @@ private:
SkPDFDevice* fDevice = nullptr;
SkDynamicMemoryWStream* fContentStream = nullptr;
SkBlendMode fBlendMode;
sk_sp<SkPDFObject> fDstFormXObject;
SkPDFIndirectReference fDstFormXObject;
SkPath fShape;
const SkClipStack* fClipStack;
};
@ -497,9 +502,9 @@ void SkPDFDevice::reset() {
fLinkToURLs = std::vector<RectWithData>();
fLinkToDestinations = std::vector<RectWithData>();
fNamedDestinations = std::vector<NamedDestination>();
fGraphicStateResources = std::vector<sk_sp<SkPDFObject>>();
fXObjectResources = std::vector<sk_sp<SkPDFObject>>();
fShaderResources = std::vector<sk_sp<SkPDFObject>>();
fGraphicStateResources.reset();
fXObjectResources.reset();
fShaderResources.reset();
fFontResources.reset();
fContent.reset();
fActiveStackState = GraphicStackState();
@ -797,23 +802,24 @@ static int find_or_add(std::vector<sk_sp<T>>* vec, sk_sp<U> object) {
return index;
}
void SkPDFDevice::setGraphicState(sk_sp<SkPDFObject> gs, SkDynamicMemoryWStream* content) {
SkPDFUtils::ApplyGraphicState(find_or_add(&fGraphicStateResources, std::move(gs)), content);
void SkPDFDevice::setGraphicState(SkPDFIndirectReference gs, SkDynamicMemoryWStream* content) {
SkPDFUtils::ApplyGraphicState(add_resource(fGraphicStateResources, gs), content);
}
void SkPDFDevice::addSMaskGraphicState(sk_sp<SkPDFDevice> maskDevice,
SkDynamicMemoryWStream* contentStream) {
this->setGraphicState(SkPDFGraphicState::GetSMaskGraphicState(
maskDevice->makeFormXObjectFromDevice(true), false,
SkPDFGraphicState::kLuminosity_SMaskMode, this->getCanon()), contentStream);
SkPDFGraphicState::kLuminosity_SMaskMode, fDocument), contentStream);
}
void SkPDFDevice::clearMaskOnGraphicState(SkDynamicMemoryWStream* contentStream) {
// The no-softmask graphic state is used to "turn off" the mask for later draw calls.
sk_sp<SkPDFDict>& noSMaskGS = this->getCanon()->fNoSmaskGraphicState;
SkPDFIndirectReference& noSMaskGS = this->getCanon()->fNoSmaskGraphicState;
if (!noSMaskGS) {
noSMaskGS = sk_make_sp<SkPDFDict>("ExtGState");
noSMaskGS->insertName("SMask", "None");
SkPDFDict tmp("ExtGState");
tmp.insertName("SMask", "None");
noSMaskGS = fDocument->emit(tmp);
}
this->setGraphicState(noSMaskGS, contentStream);
}
@ -1240,12 +1246,10 @@ void SkPDFDevice::internalDrawGlyphRun(
// Not yet specified font or need to switch font.
font = SkPDFFont::GetFontResource(fDocument, glyphCache.get(), typeface, gid);
SkASSERT(font); // All preconditions for SkPDFFont::GetFontResource are met.
SkPDFIndirectReference ref = font->indirectReference();
fFontResources.add(ref);
glyphPositioner.flush();
glyphPositioner.setWideChars(font->multiByteGlyphs());
SkPDFWriteResourceName(out, SkPDFResourceType::kFont, ref.fValue);
SkPDFWriteResourceName(out, SkPDFResourceType::kFont,
add_resource(fFontResources, font->indirectReference()));
out->writeText(" ");
SkPDFUtils::AppendScalar(textSize, out);
out->writeText(" Tf\n");
@ -1275,9 +1279,10 @@ void SkPDFDevice::drawVertices(const SkVertices*, const SkVertices::Bone[], int,
// TODO: implement drawVertices
}
void SkPDFDevice::drawFormXObject(sk_sp<SkPDFObject> xObject, SkDynamicMemoryWStream* content) {
void SkPDFDevice::drawFormXObject(SkPDFIndirectReference xObject, SkDynamicMemoryWStream* content) {
SkASSERT(xObject);
SkPDFWriteResourceName(content, SkPDFResourceType::kXObject,
find_or_add(&fXObjectResources, std::move(xObject)));
add_resource(fXObjectResources, xObject));
content->writeText(" Do\n");
}
@ -1335,18 +1340,20 @@ sk_sp<SkSurface> SkPDFDevice::makeSurface(const SkImageInfo& info, const SkSurfa
return SkSurface::MakeRaster(info, &props);
}
static std::vector<SkPDFIndirectReference> sort(const SkTHashSet<SkPDFIndirectReference>& src) {
std::vector<SkPDFIndirectReference> dst;
dst.reserve(src.count());
src.foreach([&dst](SkPDFIndirectReference ref) { dst.push_back(ref); } );
std::sort(dst.begin(), dst.end(),
[](SkPDFIndirectReference a, SkPDFIndirectReference b) { return a.fValue < b.fValue; });
return dst;
}
sk_sp<SkPDFDict> SkPDFDevice::makeResourceDict() {
std::vector<SkPDFIndirectReference> fonts;
fonts.reserve(fFontResources.count());
fFontResources.foreach([&fonts](SkPDFIndirectReference ref) { fonts.push_back(ref); } );
fFontResources.reset();
std::sort(fonts.begin(), fonts.end(),
[](SkPDFIndirectReference a, SkPDFIndirectReference b) { return a.fValue < b.fValue; });
return SkPDFMakeResourceDict(std::move(fGraphicStateResources),
std::move(fShaderResources),
std::move(fXObjectResources),
std::move(fonts));
return SkPDFMakeResourceDict(sort(fGraphicStateResources),
sort(fShaderResources),
sort(fXObjectResources),
sort(fFontResources));
}
std::unique_ptr<SkStreamAsset> SkPDFDevice::content() {
@ -1446,13 +1453,13 @@ sk_sp<SkPDFArray> SkPDFDevice::getAnnotations() {
for (const RectWithData& rectWithURL : fLinkToURLs) {
SkRect r;
fInitialTransform.mapRect(&r, rectWithURL.rect);
array->appendObjRef(create_link_to_url(rectWithURL.data.get(), r));
array->appendRef(fDocument->emit(*create_link_to_url(rectWithURL.data.get(), r)));
}
for (const RectWithData& linkToDestination : fLinkToDestinations) {
SkRect r;
fInitialTransform.mapRect(&r, linkToDestination.rect);
array->appendObjRef(
create_link_named_dest(linkToDestination.data.get(), r));
array->appendRef(
fDocument->emit(*create_link_named_dest(linkToDestination.data.get(), r)));
}
return array;
}
@ -1461,6 +1468,7 @@ void SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) const {
for (const NamedDestination& dest : fNamedDestinations) {
auto pdfDest = sk_make_sp<SkPDFArray>();
pdfDest->reserve(5);
// TODO(halcanry) reserve an IndirectReference for each page with beginPage()
pdfDest->appendObjRef(sk_ref_sp(page));
pdfDest->appendName("XYZ");
SkPoint p = fInitialTransform.mapXY(dest.point.x(), dest.point.y());
@ -1472,7 +1480,7 @@ void SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) const {
}
}
sk_sp<SkPDFObject> SkPDFDevice::makeFormXObjectFromDevice(bool alpha) {
SkPDFIndirectReference SkPDFDevice::makeFormXObjectFromDevice(bool alpha) {
SkMatrix inverseTransform = SkMatrix::I();
if (!fInitialTransform.isIdentity()) {
if (!fInitialTransform.invert(&inverseTransform)) {
@ -1482,8 +1490,8 @@ sk_sp<SkPDFObject> SkPDFDevice::makeFormXObjectFromDevice(bool alpha) {
}
const char* colorSpace = alpha ? "DeviceGray" : nullptr;
sk_sp<SkPDFObject> xobject =
SkPDFMakeFormXObject(this->content(),
SkPDFIndirectReference xobject =
SkPDFMakeFormXObject(fDocument, this->content(),
SkPDFMakeArray(0, 0, this->width(), this->height()),
this->makeResourceDict(), inverseTransform, colorSpace);
// We always draw the form xobjects that we create back into the device, so
@ -1493,10 +1501,11 @@ sk_sp<SkPDFObject> SkPDFDevice::makeFormXObjectFromDevice(bool alpha) {
return xobject;
}
void SkPDFDevice::drawFormXObjectWithMask(sk_sp<SkPDFObject> xObject,
sk_sp<SkPDFObject> mask,
void SkPDFDevice::drawFormXObjectWithMask(SkPDFIndirectReference xObject,
SkPDFIndirectReference sMask,
SkBlendMode mode,
bool invertClip) {
SkASSERT(sMask);
SkPaint paint;
paint.setBlendMode(mode);
ScopedContentEntry content(this, nullptr, SkMatrix::I(), paint);
@ -1504,9 +1513,9 @@ void SkPDFDevice::drawFormXObjectWithMask(sk_sp<SkPDFObject> xObject,
return;
}
this->setGraphicState(SkPDFGraphicState::GetSMaskGraphicState(
std::move(mask), invertClip, SkPDFGraphicState::kAlpha_SMaskMode,
fDocument->canon()), content.stream());
this->drawFormXObject(std::move(xObject), content.stream());
sMask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode,
fDocument), content.stream());
this->drawFormXObject(xObject, content.stream());
this->clearMaskOnGraphicState(content.stream());
}
@ -1519,8 +1528,8 @@ SkDynamicMemoryWStream* SkPDFDevice::setUpContentEntry(const SkClipStack* clipSt
const SkMatrix& matrix,
const SkPaint& paint,
bool hasText,
sk_sp<SkPDFObject>* dst) {
*dst = nullptr;
SkPDFIndirectReference* dst) {
SkASSERT(!*dst);
SkBlendMode blendMode = paint.getBlendMode();
// Dst xfer mode doesn't draw source at all.
@ -1570,7 +1579,7 @@ SkDynamicMemoryWStream* SkPDFDevice::setUpContentEntry(const SkClipStack* clipSt
void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack,
SkBlendMode blendMode,
sk_sp<SkPDFObject> dst,
SkPDFIndirectReference dst,
SkPath* shape) {
SkASSERT(blendMode != SkBlendMode::kDst);
if (treat_as_regular_pdf_blend_mode(blendMode)) {
@ -1618,7 +1627,7 @@ void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack,
SkPaint stockPaint;
sk_sp<SkPDFObject> srcFormXObject;
SkPDFIndirectReference srcFormXObject;
if (this->isContentEmpty()) {
// If nothing was drawn and there's no shape, then the draw was a
// no-op, but dst needs to be restored for that to be true.
@ -1628,7 +1637,7 @@ void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack,
if (shape == nullptr || blendMode == SkBlendMode::kDstOut ||
blendMode == SkBlendMode::kSrcATop) {
ScopedContentEntry content(this, nullptr, SkMatrix::I(), stockPaint);
this->drawFormXObject(std::move(dst), content.stream());
this->drawFormXObject(dst, content.stream());
return;
} else {
blendMode = SkBlendMode::kClear;
@ -1691,8 +1700,8 @@ void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack,
if (blendMode == SkBlendMode::kSrcIn ||
blendMode == SkBlendMode::kSrcOut ||
blendMode == SkBlendMode::kSrcATop) {
this->drawFormXObjectWithMask(std::move(srcFormXObject), std::move(dst),
SkBlendMode::kSrcOver, blendMode == SkBlendMode::kSrcOut);
this->drawFormXObjectWithMask(srcFormXObject, dst, SkBlendMode::kSrcOver,
blendMode == SkBlendMode::kSrcOut);
return;
} else {
SkBlendMode mode = SkBlendMode::kSrcOver;
@ -1700,8 +1709,7 @@ void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack,
this->drawFormXObjectWithMask(srcFormXObject, dst, SkBlendMode::kSrcOver, false);
mode = SkBlendMode::kMultiply;
}
this->drawFormXObjectWithMask(std::move(dst), std::move(srcFormXObject), mode,
blendMode == SkBlendMode::kDstOut);
this->drawFormXObjectWithMask(dst, srcFormXObject, mode, blendMode == SkBlendMode::kDstOut);
return;
}
}
@ -1728,7 +1736,6 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
entry->fShaderIndex = -1;
// PDF treats a shader as a color, so we only set one or the other.
sk_sp<SkPDFObject> pdfShader;
SkShader* shader = paint.getShader();
if (shader) {
if (SkShader::kColor_GradientType == shader->asAGradient(nullptr)) {
@ -1759,24 +1766,25 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
SkIRect bounds;
clipStackBounds.roundOut(&bounds);
pdfShader = SkPDFMakeShader(fDocument, shader, transform, bounds, paint.getColor());
SkPDFIndirectReference pdfShader
= SkPDFMakeShader(fDocument, shader, transform, bounds, paint.getColor());
if (pdfShader) {
// pdfShader has been canonicalized so we can directly compare pointers.
entry->fShaderIndex = find_or_add(&fShaderResources, std::move(pdfShader));
entry->fShaderIndex = add_resource(fShaderResources, pdfShader);
}
}
}
sk_sp<SkPDFDict> newGraphicState;
SkPDFIndirectReference newGraphicState;
if (color == paint.getColor4f()) {
newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), paint);
newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument, paint);
} else {
SkPaint newPaint = paint;
newPaint.setColor4f(color, nullptr);
newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), newPaint);
newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument, newPaint);
}
entry->fGraphicStateIndex = find_or_add(&fGraphicStateResources, std::move(newGraphicState));
entry->fGraphicStateIndex = add_resource(fGraphicStateResources, std::move(newGraphicState));
if (hasText) {
entry->fTextScaleX = paint.getTextScaleX();
@ -1814,15 +1822,6 @@ static bool is_integral(const SkRect& r) {
is_integer(r.bottom());
}
namespace {
// This struct will go away when fIndirectReference goes away.
struct PDFObj final : public SkPDFObject {
PDFObj(SkPDFIndirectReference ref) { fIndirectReference = ref; }
// emitObject() is never called since the Object already has a indirect ref.
void emitObject(SkWStream*) const override { SK_ABORT("DO NOT REACH HERE"); }
};
} // namespace
void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset,
const SkRect* src,
const SkRect& dst,
@ -2046,18 +2045,17 @@ void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset,
}
SkBitmapKey key = imageSubset.key();
sk_sp<SkPDFObject>* pdfimagePtr = fDocument->canon()->fPDFBitmapMap.find(key);
sk_sp<SkPDFObject> pdfimage = pdfimagePtr ? *pdfimagePtr : nullptr;
if (!pdfimage) {
SkPDFIndirectReference* pdfimagePtr = fDocument->canon()->fPDFBitmapMap.find(key);
SkPDFIndirectReference pdfimage = pdfimagePtr ? *pdfimagePtr : SkPDFIndirectReference();
if (!pdfimagePtr) {
SkASSERT(imageSubset);
auto ref = SkPDFSerializeImage(imageSubset.image().get(), fDocument,
pdfimage = SkPDFSerializeImage(imageSubset.image().get(), fDocument,
fDocument->metadata().fEncodingQuality);
SkASSERT(ref.fValue > 0);
pdfimage = sk_make_sp<PDFObj>(ref);
SkASSERT((key != SkBitmapKey{{0, 0, 0, 0}, 0}));
fDocument->canon()->fPDFBitmapMap.set(key, pdfimage);
}
this->drawFormXObject(std::move(pdfimage), content.stream());
SkASSERT(pdfimage != SkPDFIndirectReference());
this->drawFormXObject(pdfimage, content.stream());
}
///////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -33,7 +33,6 @@ class SkPDFDocument;
class SkPDFDict;
class SkPDFFont;
class SkPDFObject;
class SkPDFStream;
class SkRRect;
struct SkPDFIndirectReference;
@ -167,9 +166,9 @@ private:
std::vector<RectWithData> fLinkToDestinations;
std::vector<NamedDestination> fNamedDestinations;
std::vector<sk_sp<SkPDFObject>> fGraphicStateResources;
std::vector<sk_sp<SkPDFObject>> fXObjectResources;
std::vector<sk_sp<SkPDFObject>> fShaderResources;
SkTHashSet<SkPDFIndirectReference> fGraphicStateResources;
SkTHashSet<SkPDFIndirectReference> fXObjectResources;
SkTHashSet<SkPDFIndirectReference> fShaderResources;
SkTHashSet<SkPDFIndirectReference> fFontResources;
int fNodeId;
@ -199,10 +198,10 @@ private:
SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
// Set alpha to true if making a transparency group form x-objects.
sk_sp<SkPDFObject> makeFormXObjectFromDevice(bool alpha = false);
SkPDFIndirectReference makeFormXObjectFromDevice(bool alpha = false);
void drawFormXObjectWithMask(sk_sp<SkPDFObject> xObject,
sk_sp<SkPDFObject> mask,
void drawFormXObjectWithMask(SkPDFIndirectReference xObject,
SkPDFIndirectReference sMask,
SkBlendMode,
bool invertClip);
@ -211,11 +210,11 @@ private:
// setUpContentEntry and finishContentEntry can be used directly, but
// the preferred method is to use the ScopedContentEntry helper class.
SkDynamicMemoryWStream* setUpContentEntry(const SkClipStack* clipStack,
const SkMatrix& matrix,
const SkPaint& paint,
bool hasText,
sk_sp<SkPDFObject>* dst);
void finishContentEntry(const SkClipStack*, SkBlendMode, sk_sp<SkPDFObject> dst, SkPath* shape);
const SkMatrix& matrix,
const SkPaint& paint,
bool hasText,
SkPDFIndirectReference* dst);
void finishContentEntry(const SkClipStack*, SkBlendMode, SkPDFIndirectReference, SkPath*);
bool isContentEmpty();
void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
@ -248,8 +247,8 @@ private:
void addSMaskGraphicState(sk_sp<SkPDFDevice> maskDevice, SkDynamicMemoryWStream*);
void clearMaskOnGraphicState(SkDynamicMemoryWStream*);
void setGraphicState(sk_sp<SkPDFObject>, SkDynamicMemoryWStream*);
void drawFormXObject(sk_sp<SkPDFObject>, SkDynamicMemoryWStream*);
void setGraphicState(SkPDFIndirectReference gs, SkDynamicMemoryWStream*);
void drawFormXObject(SkPDFIndirectReference xObject, SkDynamicMemoryWStream*);
bool hasEmptyClip() const { return this->cs().isEmpty(this->bounds()); }

View File

@ -47,10 +47,6 @@ SkPDFObjectSerializer::SkPDFObjectSerializer() = default;
SkPDFObjectSerializer::~SkPDFObjectSerializer() = default;
SkPDFObjectSerializer::SkPDFObjectSerializer(SkPDFObjectSerializer&&) = default;
SkPDFObjectSerializer& SkPDFObjectSerializer::operator=(SkPDFObjectSerializer&&) = default;
#define SKPDF_MAGIC "\xD3\xEB\xE9\xE1"
#ifndef SK_BUILD_FOR_WIN
@ -59,16 +55,13 @@ static_assert((SKPDF_MAGIC[1] & 0x7F) == "Skia"[1], "");
static_assert((SKPDF_MAGIC[2] & 0x7F) == "Skia"[2], "");
static_assert((SKPDF_MAGIC[3] & 0x7F) == "Skia"[3], "");
#endif
void SkPDFObjectSerializer::serializeHeader(SkWStream* wStream,
const SkPDF::Metadata& md) {
void SkPDFObjectSerializer::serializeHeader(SkWStream* wStream) {
fBaseOffset = wStream->bytesWritten();
static const char kHeader[] = "%PDF-1.4\n%" SKPDF_MAGIC "\n";
wStream->writeText(kHeader);
// The PDF spec recommends including a comment with four
// bytes, all with their high bits set. "\xD3\xEB\xE9\xE1" is
// "Skia" with the high bits set.
fInfoDict = SkPDFMetadata::MakeDocumentInformationDict(md);
this->serializeObject(fInfoDict, wStream);
}
#undef SKPDF_MAGIC
@ -86,8 +79,7 @@ void SkPDFObjectSerializer::endObject(SkWStream* wStream) {
void SkPDFObjectSerializer::serializeObject(const sk_sp<SkPDFObject>& object,
SkWStream* wStream) {
SkPDFObjNumMap objNumMap;
objNumMap.fNextObjectNumber = fNextObjectNumber;
SkPDFObjNumMap objNumMap(this);
objNumMap.addObjectRecursively(object.get());
for (const sk_sp<SkPDFObject>& object : objNumMap.fObjects) {
@ -96,18 +88,18 @@ void SkPDFObjectSerializer::serializeObject(const sk_sp<SkPDFObject>& object,
this->endObject(wStream);
object->drop();
}
fNextObjectNumber = objNumMap.fNextObjectNumber; // save for later.
}
// Xref table and footer
void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream,
const sk_sp<SkPDFObject> docCatalog,
sk_sp<SkPDFObject> id) {
SkPDFIndirectReference infoDict,
SkPDFIndirectReference docCatalog,
SkUUID uuid) {
int xRefFileOffset = this->offset(wStream).fValue;
// Include the special zeroth object in the count.
int objCount = SkToS32(fNextObjectNumber);
int objCount = SkToS32(fNextObjectNumber.load());
wStream->writeText("xref\n0 ");
wStream->writeDecAsText(objCount);
wStream->writeText("\n0000000000 65535 f \n");
@ -118,12 +110,12 @@ void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream,
}
SkPDFDict trailerDict;
trailerDict.insertInt("Size", objCount);
SkASSERT(docCatalog);
trailerDict.insertObjRef("Root", docCatalog);
SkASSERT(fInfoDict);
trailerDict.insertObjRef("Info", std::move(fInfoDict));
if (id) {
trailerDict.insertObject("ID", std::move(id));
SkASSERT(docCatalog != SkPDFIndirectReference());
trailerDict.insertRef("Root", docCatalog);
SkASSERT(infoDict != SkPDFIndirectReference());
trailerDict.insertRef("Info", infoDict);
if (SkUUID() != uuid) {
trailerDict.insertObject("ID", SkPDFMetadata::MakePdfId(uuid, uuid));
}
wStream->writeText("trailer\n");
trailerDict.emitObject(wStream);
@ -216,6 +208,7 @@ SkPDFDocument::SkPDFDocument(SkWStream* stream,
if (fMetadata.fStructureElementTreeRoot) {
fTagRoot = recursiveBuildTagTree(*fMetadata.fStructureElementTreeRoot, nullptr);
}
fExecutor = metadata.fExecutor;
}
SkPDFDocument::~SkPDFDocument() {
@ -225,28 +218,34 @@ SkPDFDocument::~SkPDFDocument() {
SkPDFIndirectReference SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) {
SkASSERT(object);
fObjectSerializer.serializeObject(object, this->getStream());
if (object->fIndirectReference == SkPDFIndirectReference()) {
SkAutoMutexAcquire autoMutexAcquire(fMutex);
fObjectSerializer.serializeObject(object, this->getStream());
}
SkASSERT(object->fIndirectReference != SkPDFIndirectReference());
return object->fIndirectReference;
}
SkPDFIndirectReference SkPDFDocument::emit(const SkPDFObject& object){
SkPDFIndirectReference ref = this->reserve();
SkPDFIndirectReference SkPDFDocument::emit(const SkPDFObject& object, SkPDFIndirectReference ref){
object.emitObject(this->beginObject(ref));
this->endObject();
return ref;
}
SkPDFIndirectReference SkPDFDocument::reserve() {
SkPDFIndirectReference SkPDFDocument::reserveRef() {
++fOutstandingRefs;
return SkPDFIndirectReference{fObjectSerializer.fNextObjectNumber++};
return fObjectSerializer.reserve();
};
SkWStream* SkPDFDocument::beginObject(SkPDFIndirectReference ref) {
--fOutstandingRefs;
fMutex.acquire();
return fObjectSerializer.beginObject(ref, this->getStream());
};
void SkPDFDocument::endObject() {
fObjectSerializer.endObject(this->getStream());
fMutex.release();
fSemaphore.signal();
};
static SkSize operator*(SkISize u, SkScalar s) { return SkSize{u.width() * s, u.height() * s}; }
@ -256,18 +255,22 @@ SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height) {
SkASSERT(fCanvas.imageInfo().dimensions().isZero());
if (fPages.empty()) {
// if this is the first page if the document.
fObjectSerializer.serializeHeader(this->getStream(), fMetadata);
{
SkAutoMutexAcquire autoMutexAcquire(fMutex);
fObjectSerializer.serializeHeader(this->getStream());
}
fInfoDict = this->serialize(SkPDFMetadata::MakeDocumentInformationDict(fMetadata));
fDests = sk_make_sp<SkPDFDict>();
if (fMetadata.fPDFA) {
SkPDFMetadata::UUID uuid = SkPDFMetadata::CreateUUID(fMetadata);
fUUID = SkPDFMetadata::CreateUUID(fMetadata);
// We use the same UUID for Document ID and Instance ID since this
// is the first revision of this document (and Skia does not
// support revising existing PDF documents).
// If we are not in PDF/A mode, don't use a UUID since testing
// works best with reproducible outputs.
fID = SkPDFMetadata::MakePdfId(uuid, uuid);
fXMP = SkPDFMetadata::MakeXMPObject(fMetadata, uuid, uuid);
fObjectSerializer.serializeObject(fXMP, this->getStream());
fXMP = this->serialize(
SkPDFMetadata::MakeXMPObject(fMetadata, fUUID, fUUID));
}
}
// By scaling the page at the device level, we will create bitmap layer
@ -295,7 +298,7 @@ void SkPDFDocument::onEndPage() {
auto page = sk_make_sp<SkPDFDict>("Page");
SkSize mediaSize = fPageDevice->imageInfo().dimensions() * fInverseRasterScale;
auto contentObject = sk_make_sp<SkPDFStream>(fPageDevice->content());
std::unique_ptr<SkStreamAsset> pageContent = fPageDevice->content();
auto resourceDict = fPageDevice->makeResourceDict();
auto annotations = fPageDevice->getAnnotations();
fPageDevice->appendDestinations(fDests.get(), page.get());
@ -307,8 +310,7 @@ void SkPDFDocument::onEndPage() {
if (annotations) {
page->insertObject("Annots", std::move(annotations));
}
this->serialize(contentObject);
page->insertObjRef("Contents", std::move(contentObject));
page->insertRef("Contents", SkPDFStreamOut(nullptr, std::move(pageContent), this));
// The StructParents unique identifier for each page is just its
// 0-based page index.
page->insertInt("StructParents", static_cast<int>(fPages.size()));
@ -320,14 +322,14 @@ void SkPDFDocument::onAbort() {
}
void SkPDFDocument::reset() {
fObjectSerializer = SkPDFObjectSerializer();
reset_object(&fObjectSerializer);
fCanon = SkPDFCanon();
reset_object(&fCanvas);
fPages = std::vector<sk_sp<SkPDFDict>>();
fDests = nullptr;
fPageDevice = nullptr;
fID = nullptr;
fXMP = nullptr;
fUUID = SkUUID();
fXMP = SkPDFIndirectReference();
fMetadata = SkPDF::Metadata();
fRasterScale = 1;
fInverseRasterScale = 1;
@ -447,14 +449,14 @@ static sk_sp<SkData> SkSrgbIcm() {
return SkData::MakeWithoutCopy(kProfile, kProfileLength);
}
static sk_sp<SkPDFStream> make_srgb_color_profile() {
sk_sp<SkPDFStream> stream = sk_make_sp<SkPDFStream>(SkSrgbIcm());
stream->dict()->insertInt("N", 3);
stream->dict()->insertObject("Range", SkPDFMakeArray(0, 1, 0, 1, 0, 1));
return stream;
static SkPDFIndirectReference make_srgb_color_profile(SkPDFDocument* doc) {
sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
dict->insertInt("N", 3);
dict->insertObject("Range", SkPDFMakeArray(0, 1, 0, 1, 0, 1));
return SkPDFStreamOut(std::move(dict), SkMemoryStream::Make(SkSrgbIcm()), doc, true);
}
static sk_sp<SkPDFArray> make_srgb_output_intents() {
static sk_sp<SkPDFArray> make_srgb_output_intents(SkPDFDocument* doc) {
// sRGB is specified by HTML, CSS, and SVG.
auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent");
outputIntent->insertName("S", "GTS_PDFA1");
@ -462,8 +464,7 @@ static sk_sp<SkPDFArray> make_srgb_output_intents() {
outputIntent->insertString("OutputConditionIdentifier",
"Custom");
outputIntent->insertString("Info","sRGB IEC61966-2.1");
outputIntent->insertObjRef("DestOutputProfile",
make_srgb_color_profile());
outputIntent->insertRef("DestOutputProfile", make_srgb_color_profile(doc));
auto intentArray = sk_make_sp<SkPDFArray>();
intentArray->appendObject(std::move(outputIntent));
return intentArray;
@ -533,11 +534,11 @@ void SkPDFDocument::onClose(SkWStream* stream) {
}
auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
if (fMetadata.fPDFA) {
SkASSERT(fXMP);
docCatalog->insertObjRef("Metadata", fXMP);
SkASSERT(fXMP != SkPDFIndirectReference());
docCatalog->insertRef("Metadata", fXMP);
// Don't specify OutputIntents if we are not in PDF/A mode since
// no one has ever asked for this feature.
docCatalog->insertObject("OutputIntents", make_srgb_output_intents());
docCatalog->insertObject("OutputIntents", make_srgb_output_intents(this));
}
sk_sp<SkPDFDict> pageTree = generate_page_tree(fPages);
@ -593,15 +594,23 @@ void SkPDFDocument::onClose(SkWStream* stream) {
}
}
}
fObjectSerializer.serializeObject(docCatalog, this->getStream());
auto docCatalogRef = this->serialize(docCatalog);
for (const SkPDFFont* f : get_fonts(fCanon)) {
f->emitSubset(this);
}
SkASSERT(fOutstandingRefs == 0);
fObjectSerializer.serializeFooter(this->getStream(), docCatalog, fID);
while (fOutstandingRefs > 0) {
fSemaphore.wait();
--fOutstandingRefs;
}
{
SkAutoMutexAcquire autoMutexAcquire(fMutex);
fObjectSerializer.serializeFooter(this->getStream(), fInfoDict, docCatalogRef, fUUID);
}
this->reset();
}
///////////////////////////////////////////////////////////////////////////////
void SkPDF::SetNodeId(SkCanvas* canvas, int nodeID) {

View File

@ -8,13 +8,17 @@
#define SkPDFDocumentPriv_DEFINED
#include "SkCanvas.h"
#include "SkPDFDocument.h"
#include "SkPDFCanon.h"
#include "SkPDFDocument.h"
#include "SkPDFFont.h"
#include "SkPDFMetadata.h"
#include "SkUUID.h"
#include <atomic>
class SkPDFDevice;
class SkPDFTag;
class SkExecutor;
const char* SkPDFGetNodeIdKey();
@ -31,23 +35,26 @@ struct SkPDFOffsetMap {
// Logically part of SkPDFDocument (like SkPDFCanon), but separate to
// keep similar functionality together.
struct SkPDFObjectSerializer {
int fNextObjectNumber = 1;
std::atomic<int> fNextObjectNumber = {1};
SkPDFIndirectReference reserve() { return SkPDFIndirectReference{fNextObjectNumber++}; }
SkPDFOffsetMap fOffsets;
sk_sp<SkPDFObject> fInfoDict;
size_t fBaseOffset = SIZE_MAX;
SkPDFObjectSerializer();
~SkPDFObjectSerializer();
SkPDFObjectSerializer(SkPDFObjectSerializer&&);
SkPDFObjectSerializer& operator=(SkPDFObjectSerializer&&);
SkPDFObjectSerializer(SkPDFObjectSerializer&&) = delete;
SkPDFObjectSerializer& operator=(SkPDFObjectSerializer&&) = delete;
SkPDFObjectSerializer(const SkPDFObjectSerializer&) = delete;
SkPDFObjectSerializer& operator=(const SkPDFObjectSerializer&) = delete;
SkWStream* beginObject(SkPDFIndirectReference, SkWStream*);
void endObject(SkWStream*);
void serializeHeader(SkWStream*, const SkPDF::Metadata&);
void serializeHeader(SkWStream*);
void serializeObject(const sk_sp<SkPDFObject>&, SkWStream*);
void serializeFooter(SkWStream*, const sk_sp<SkPDFObject>, sk_sp<SkPDFObject>);
void serializeFooter(SkWStream*,
SkPDFIndirectReference infoDict,
SkPDFIndirectReference docCatalog,
SkUUID uuid);
SkPDFFileOffset offset(SkWStream*);
};
@ -73,7 +80,8 @@ public:
after calling serialize, since those changes will be too late.
*/
SkPDFIndirectReference serialize(const sk_sp<SkPDFObject>&);
SkPDFIndirectReference emit(const SkPDFObject&);
SkPDFIndirectReference emit(const SkPDFObject&, SkPDFIndirectReference);
SkPDFIndirectReference emit(const SkPDFObject& o) { return this->emit(o, this->reserveRef()); }
SkPDFCanon* canon() { return &fCanon; }
const SkPDF::Metadata& metadata() const { return fMetadata; }
@ -81,10 +89,12 @@ public:
// Returns -1 if no mark ID.
int getMarkIdForNodeId(int nodeId);
SkPDFIndirectReference reserve();
SkPDFIndirectReference reserveRef();
SkWStream* beginObject(SkPDFIndirectReference);
void endObject();
SkExecutor* executor() const { return fExecutor; }
private:
sk_sp<SkPDFTag> recursiveBuildTagTree(const SkPDF::StructureElementNode& node,
sk_sp<SkPDFTag> parent);
@ -95,11 +105,13 @@ private:
std::vector<sk_sp<SkPDFDict>> fPages;
sk_sp<SkPDFDict> fDests;
sk_sp<SkPDFDevice> fPageDevice;
sk_sp<SkPDFObject> fID;
sk_sp<SkPDFObject> fXMP;
SkUUID fUUID;
SkPDFIndirectReference fInfoDict;
SkPDFIndirectReference fXMP;
SkPDF::Metadata fMetadata;
SkScalar fRasterScale = 1;
SkScalar fInverseRasterScale = 1;
SkExecutor* fExecutor = nullptr;
// For tagged PDFs.
@ -110,7 +122,9 @@ private:
// A mapping from node ID to tag for fast lookup.
SkTHashMap<int, sk_sp<SkPDFTag>> fNodeIdToTag;
int fOutstandingRefs = 0;
std::atomic<int> fOutstandingRefs = {0};
SkMutex fMutex;
SkSemaphore fSemaphore;
void reset();
};

View File

@ -225,7 +225,7 @@ SkPDFFont* SkPDFFont::GetFontResource(SkPDFDocument* doc,
firstNonZeroGlyph = subsetCode;
lastGlyph = SkToU16(SkTMin<int>((int)lastGlyph, 254 + (int)subsetCode));
}
auto ref = doc->reserve();
auto ref = doc->reserveRef();
return canon->fFontMap.set(
fontID, SkPDFFont(std::move(typeface), firstNonZeroGlyph, lastGlyph, type, ref));
}
@ -321,11 +321,13 @@ static void emit_subset_type0(const SkPDFFont& font, SkPDFDocument* doc) {
stream_to_data(std::move(fontAsset)), font.glyphUsage(),
metrics.fFontName.c_str(), ttcIndex);
if (subsetFontData) {
size_t len = subsetFontData->size();
sk_sp<SkPDFStream> subsetStream = sk_make_sp<SkPDFStream>(
std::move(subsetFontData));
subsetStream->dict()->insertInt("Length1", SkToInt(len));
descriptor->insertRef("FontFile2", doc->serialize(subsetStream));
sk_sp<SkPDFDict> tmp = sk_make_sp<SkPDFDict>();
tmp->insertInt("Length1", SkToInt(subsetFontData->size()));
descriptor->insertRef(
"FontFile2",
SkPDFStreamOut(std::move(tmp),
SkMemoryStream::Make(std::move(subsetFontData)),
doc, true));
break;
}
// If subsetting fails, fall back to original font data.
@ -335,15 +337,19 @@ static void emit_subset_type0(const SkPDFFont& font, SkPDFDocument* doc) {
if (!fontAsset || fontAsset->getLength() == 0) { break; }
}
#endif // SK_PDF_SUBSET_SUPPORTED
auto fontStream = sk_make_sp<SkPDFSharedStream>(std::move(fontAsset));
fontStream->dict()->insertInt("Length1", fontSize);
descriptor->insertRef("FontFile2", doc->serialize(fontStream));
sk_sp<SkPDFDict> tmp = sk_make_sp<SkPDFDict>();
tmp->insertInt("Length1", fontSize);
descriptor->insertRef("FontFile2",
SkPDFStreamOut(std::move(tmp), std::move(fontAsset),
doc, true));
break;
}
case SkAdvancedTypefaceMetrics::kType1CID_Font: {
auto fontStream = sk_make_sp<SkPDFSharedStream>(std::move(fontAsset));
fontStream->dict()->insertName("Subtype", "CIDFontType0C");
descriptor->insertRef("FontFile3", doc->serialize(fontStream));
sk_sp<SkPDFDict> tmp = sk_make_sp<SkPDFDict>();
tmp->insertName("Subtype", "CIDFontType0C");
descriptor->insertRef("FontFile3",
SkPDFStreamOut(std::move(tmp), std::move(fontAsset),
doc, true));
break;
}
default:
@ -398,17 +404,15 @@ static void emit_subset_type0(const SkPDFFont& font, SkPDFDocument* doc) {
const std::vector<SkUnichar>& glyphToUnicode =
SkPDFFont::GetUnicodeMap(font.typeface(), canon);
SkASSERT(SkToSizeT(font.typeface()->countGlyphs()) == glyphToUnicode.size());
fontDict.insertRef("ToUnicode",
doc->serialize(
SkPDFMakeToUnicodeCmap(glyphToUnicode.data(),
&font.glyphUsage(),
font.multiByteGlyphs(),
font.firstGlyphID(),
font.lastGlyphID())));
std::unique_ptr<SkStreamAsset> toUnicode =
SkPDFMakeToUnicodeCmap(glyphToUnicode.data(),
&font.glyphUsage(),
font.multiByteGlyphs(),
font.firstGlyphID(),
font.lastGlyphID());
fontDict.insertRef("ToUnicode", SkPDFStreamOut(nullptr, std::move(toUnicode), doc));
SkWStream* stream = doc->beginObject(font.indirectReference());
fontDict.emitObject(stream);
doc->endObject();
doc->emit(fontDict, font.indirectReference());
}
///////////////////////////////////////////////////////////////////////////////
@ -431,11 +435,13 @@ static SkPDFIndirectReference make_type1_font_descriptor(SkPDFDocument* doc,
sk_sp<SkData> fontData = SkPDFConvertType1FontStream(std::move(rawFontData),
&header, &data, &trailer);
if (fontData) {
SkPDFStream fontStream(std::move(fontData));
fontStream.dict()->insertInt("Length1", header);
fontStream.dict()->insertInt("Length2", data);
fontStream.dict()->insertInt("Length3", trailer);
descriptor.insertRef("FontFile", doc->emit(fontStream));
sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
dict->insertInt("Length1", header);
dict->insertInt("Length2", data);
dict->insertInt("Length3", trailer);
auto fontStream = SkMemoryStream::Make(std::move(fontData));
descriptor.insertRef("FontFile", SkPDFStreamOut(std::move(dict),
std::move(fontStream), doc, true));
}
}
}
@ -514,9 +520,7 @@ static void emit_subset_type1(const SkPDFFont& pdfFont, SkPDFDocument* doc) {
encoding->insertObject("Differences", std::move(encDiffs));
font.insertObject("Encoding", std::move(encoding));
SkWStream* stream = doc->beginObject(pdfFont.indirectReference());
font.emitObject(stream);
doc->endObject();
doc->emit(font, pdfFont.indirectReference());
}
void SkPDFFont::GetType1GlyphNames(const SkTypeface& face, SkString* dst) {
@ -717,7 +721,8 @@ static void emit_subset_type3(const SkPDFFont& pdfFont, SkPDFDocument* doc) {
content.writeText(" Do\n");
}
}
charProcs->insertRef(characterName, doc->emit(SkPDFStream(content.detachAsStream())));
charProcs->insertRef(characterName, SkPDFStreamOut(nullptr,
content.detachAsStream(), doc));
}
encDiffs->appendName(std::move(characterName));
widthArray->appendScalar(advance);
@ -750,20 +755,18 @@ static void emit_subset_type3(const SkPDFFont& pdfFont, SkPDFDocument* doc) {
const std::vector<SkUnichar>& glyphToUnicode = SkPDFFont::GetUnicodeMap(typeface, canon);
SkASSERT(glyphToUnicode.size() == SkToSizeT(typeface->countGlyphs()));
sk_sp<SkPDFStream> toUnicodeCmap = SkPDFMakeToUnicodeCmap(glyphToUnicode.data(),
&subset,
false,
firstGlyphID,
lastGlyphID);
font.insertRef("ToUnicode", doc->serialize(toUnicodeCmap));
auto toUnicodeCmap = SkPDFMakeToUnicodeCmap(glyphToUnicode.data(),
&subset,
false,
firstGlyphID,
lastGlyphID);
font.insertRef("ToUnicode", SkPDFStreamOut(nullptr, std::move(toUnicodeCmap), doc));
font.insertRef("FontDescriptor", type3_descriptor(doc, typeface, cache.get()));
font.insertObject("Widths", std::move(widthArray));
font.insertObject("Encoding", std::move(encoding));
font.insertObject("CharProcs", std::move(charProcs));
SkWStream* stream = doc->beginObject(pdfFont.indirectReference());
font.emitObject(stream);
doc->endObject();
doc->emit(font, pdfFont.indirectReference());
}

View File

@ -9,20 +9,20 @@
#include "SkPDFFormXObject.h"
#include "SkPDFUtils.h"
sk_sp<SkPDFObject> SkPDFMakeFormXObject(std::unique_ptr<SkStreamAsset> content,
sk_sp<SkPDFArray> mediaBox,
sk_sp<SkPDFDict> resourceDict,
const SkMatrix& inverseTransform,
const char* colorSpace) {
auto form = sk_make_sp<SkPDFStream>(std::move(content));
form->dict()->insertName("Type", "XObject");
form->dict()->insertName("Subtype", "Form");
SkPDFIndirectReference SkPDFMakeFormXObject(SkPDFDocument* doc,
std::unique_ptr<SkStreamAsset> content,
sk_sp<SkPDFArray> mediaBox,
sk_sp<SkPDFDict> resourceDict,
const SkMatrix& inverseTransform,
const char* colorSpace) {
sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
dict->insertName("Type", "XObject");
dict->insertName("Subtype", "Form");
if (!inverseTransform.isIdentity()) {
sk_sp<SkPDFObject> mat(SkPDFUtils::MatrixToArray(inverseTransform));
form->dict()->insertObject("Matrix", std::move(mat));
dict->insertObject("Matrix", SkPDFUtils::MatrixToArray(inverseTransform));
}
form->dict()->insertObject("Resources", std::move(resourceDict));
form->dict()->insertObject("BBox", std::move(mediaBox));
dict->insertObject("Resources", std::move(resourceDict));
dict->insertObject("BBox", std::move(mediaBox));
// Right now FormXObject is only used for saveLayer, which implies
// isolated blending. Do this conditionally if that changes.
@ -34,6 +34,6 @@ sk_sp<SkPDFObject> SkPDFMakeFormXObject(std::unique_ptr<SkStreamAsset> content,
group->insertName("CS", colorSpace);
}
group->insertBool("I", true); // Isolated.
form->dict()->insertObject("Group", std::move(group));
return std::move(form);
dict->insertObject("Group", std::move(group));
return SkPDFStreamOut(std::move(dict), std::move(content), doc);
}

View File

@ -12,14 +12,17 @@
#include "SkPDFDevice.h"
#include "SkPDFTypes.h"
class SkPDFDocument;
/** A form XObject is a self contained description of a graphics
object. A form XObject is a page object with slightly different
syntax, that can be drawn into a page content stream, just like a
bitmap XObject can be drawn into a page content stream.
*/
sk_sp<SkPDFObject> SkPDFMakeFormXObject(std::unique_ptr<SkStreamAsset> content,
sk_sp<SkPDFArray> mediaBox,
sk_sp<SkPDFDict> resourceDict,
const SkMatrix& inverseTransform,
const char* colorSpace);
SkPDFIndirectReference SkPDFMakeFormXObject(SkPDFDocument* doc,
std::unique_ptr<SkStreamAsset> content,
sk_sp<SkPDFArray> mediaBox,
sk_sp<SkPDFDict> resourceDict,
const SkMatrix& inverseTransform,
const char* colorSpace);
#endif

View File

@ -13,6 +13,7 @@
#include "SkPDFFormXObject.h"
#include "SkPDFGraphicState.h"
#include "SkPDFResourceDict.h"
#include "SkPDFTypes.h"
#include "SkPDFUtils.h"
static uint32_t hash(const SkShader::GradientInfo& v) {
@ -573,20 +574,19 @@ static bool split_perspective(const SkMatrix in, SkMatrix* affine,
return true;
}
static sk_sp<SkPDFStream> make_ps_function(
std::unique_ptr<SkStreamAsset> psCode,
sk_sp<SkPDFArray> domain,
sk_sp<SkPDFObject> range) {
auto result = sk_make_sp<SkPDFStream>(std::move(psCode));
result->dict()->insertInt("FunctionType", 4);
result->dict()->insertObject("Domain", std::move(domain));
result->dict()->insertObject("Range", std::move(range));
return result;
static SkPDFIndirectReference make_ps_function(std::unique_ptr<SkStreamAsset> psCode,
sk_sp<SkPDFArray> domain,
sk_sp<SkPDFObject> range,
SkPDFDocument* doc) {
sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
dict->insertInt("FunctionType", 4);
dict->insertObject("Domain", std::move(domain));
dict->insertObject("Range", std::move(range));
return SkPDFStreamOut(std::move(dict), std::move(psCode), doc);
}
static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
const SkPDFGradientShader::Key& state) {
static SkPDFIndirectReference make_function_shader(SkPDFDocument* doc,
const SkPDFGradientShader::Key& state) {
SkPoint transformPoints[2];
const SkShader::GradientInfo& info = state.fInfo;
SkMatrix finalMatrix = state.fCanvasTransform;
@ -670,7 +670,7 @@ static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
case SkShader::kColor_GradientType:
case SkShader::kNone_GradientType:
default:
return nullptr;
return SkPDFIndirectReference();
}
// Move any scaling (assuming a unit gradient) or translation
@ -691,14 +691,14 @@ static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
if (finalMatrix.hasPerspective()) {
if (!split_perspective(finalMatrix,
&finalMatrix, &perspectiveInverseOnly)) {
return nullptr;
return SkPDFIndirectReference();
}
}
SkRect bbox;
bbox.set(state.fBBox);
if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &bbox)) {
return nullptr;
return SkPDFIndirectReference();
}
SkDynamicMemoryWStream functionCode;
@ -707,7 +707,7 @@ static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
if (state.fType == SkShader::kConical_GradientType) {
SkMatrix inverseMapperMatrix;
if (!mapperMatrix.invert(&inverseMapperMatrix)) {
return nullptr;
return SkPDFIndirectReference();
}
inverseMapperMatrix.mapPoints(infoCopy.fPoint, 2);
infoCopy.fRadius[0] = inverseMapperMatrix.mapRadius(info.fRadius[0]);
@ -735,13 +735,10 @@ static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
bbox.bottom());
pdfShader->insertObject("Domain", domain);
sk_sp<SkPDFArray>& rangeObject = canon->fRangeObject;
if (!rangeObject) {
rangeObject = SkPDFMakeArray(0, 1, 0, 1, 0, 1);
}
pdfShader->insertObjRef("Function",
make_ps_function(functionCode.detachAsStream(), std::move(domain),
rangeObject));
sk_sp<SkPDFArray> rangeObject = SkPDFMakeArray(0, 1, 0, 1, 0, 1);
pdfShader->insertRef("Function",
make_ps_function(functionCode.detachAsStream(), std::move(domain),
std::move(rangeObject), doc));
}
pdfShader->insertInt("ShadingType", shadingType);
@ -752,38 +749,40 @@ static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
pdfFunctionShader->insertObject("Matrix", SkPDFUtils::MatrixToArray(finalMatrix));
pdfFunctionShader->insertObject("Shading", std::move(pdfShader));
return pdfFunctionShader;
return doc->serialize(pdfFunctionShader);
}
static sk_sp<SkPDFObject> find_pdf_shader(SkPDFDocument* doc,
SkPDFGradientShader::Key key,
bool keyHasAlpha);
static SkPDFIndirectReference find_pdf_shader(SkPDFDocument* doc,
SkPDFGradientShader::Key key,
bool keyHasAlpha);
static sk_sp<SkPDFDict> get_gradient_resource_dict(sk_sp<SkPDFObject> functionShader,
sk_sp<SkPDFObject> gState) {
std::vector<sk_sp<SkPDFObject>> patternShaders;
if (functionShader) {
patternShaders.push_back(std::move(functionShader));
static sk_sp<SkPDFDict> get_gradient_resource_dict(SkPDFIndirectReference functionShader,
SkPDFIndirectReference gState) {
std::vector<SkPDFIndirectReference> patternShaders;
if (functionShader != SkPDFIndirectReference()) {
patternShaders.push_back(functionShader);
}
std::vector<sk_sp<SkPDFObject>> graphicStates;
if (gState) {
graphicStates.push_back(std::move(gState));
std::vector<SkPDFIndirectReference> graphicStates;
if (gState != SkPDFIndirectReference()) {
graphicStates.push_back(gState);
}
return SkPDFMakeResourceDict(std::move(graphicStates),
std::move(patternShaders),
std::vector<sk_sp<SkPDFObject>>(),
std::vector<SkPDFIndirectReference>(),
std::vector<SkPDFIndirectReference>());
}
// Creates a content stream which fills the pattern P0 across bounds.
// @param gsIndex A graphics state resource index to apply, or <0 if no
// graphics state to apply.
static std::unique_ptr<SkStreamAsset> create_pattern_fill_content(int gsIndex, SkRect& bounds) {
static std::unique_ptr<SkStreamAsset> create_pattern_fill_content(int gsIndex,
int patternIndex,
SkRect& bounds) {
SkDynamicMemoryWStream content;
if (gsIndex >= 0) {
SkPDFUtils::ApplyGraphicState(gsIndex, &content);
}
SkPDFUtils::ApplyPattern(0, &content);
SkPDFUtils::ApplyPattern(patternIndex, &content);
SkPDFUtils::AppendRectangle(bounds, &content);
SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kEvenOdd_FillType, &content);
return content.detachAsStream();
@ -818,7 +817,7 @@ static SkPDFGradientShader::Key clone_key(const SkPDFGradientShader::Key& k) {
return clone;
}
static sk_sp<SkPDFObject> create_smask_graphic_state(SkPDFDocument* doc,
static SkPDFIndirectReference create_smask_graphic_state(SkPDFDocument* doc,
const SkPDFGradientShader::Key& state) {
SkASSERT(state.fType != SkShader::kNone_GradientType);
SkPDFGradientShader::Key luminosityState = clone_key(state);
@ -829,21 +828,23 @@ static sk_sp<SkPDFObject> create_smask_graphic_state(SkPDFDocument* doc,
luminosityState.fHash = hash(luminosityState);
SkASSERT(!gradient_has_alpha(luminosityState));
sk_sp<SkPDFObject> luminosityShader = find_pdf_shader(doc, std::move(luminosityState), false);
sk_sp<SkPDFDict> resources = get_gradient_resource_dict(std::move(luminosityShader), nullptr);
SkPDFIndirectReference luminosityShader = find_pdf_shader(doc, std::move(luminosityState), false);
sk_sp<SkPDFDict> resources = get_gradient_resource_dict(luminosityShader,
SkPDFIndirectReference());
SkRect bbox = SkRect::Make(state.fBBox);
sk_sp<SkPDFObject> alphaMask = SkPDFMakeFormXObject(create_pattern_fill_content(-1, bbox),
SkPDFUtils::RectToArray(bbox),
std::move(resources),
SkMatrix::I(),
"DeviceRGB");
SkPDFIndirectReference alphaMask =
SkPDFMakeFormXObject(doc,
create_pattern_fill_content(-1, luminosityShader.fValue, bbox),
SkPDFUtils::RectToArray(bbox),
std::move(resources),
SkMatrix::I(),
"DeviceRGB");
return SkPDFGraphicState::GetSMaskGraphicState(
std::move(alphaMask), false,
SkPDFGraphicState::kLuminosity_SMaskMode, doc->canon());
alphaMask, false, SkPDFGraphicState::kLuminosity_SMaskMode, doc);
}
static sk_sp<SkPDFStream> make_alpha_function_shader(SkPDFDocument* doc,
const SkPDFGradientShader::Key& state) {
static SkPDFIndirectReference make_alpha_function_shader(SkPDFDocument* doc,
const SkPDFGradientShader::Key& state) {
SkASSERT(state.fType != SkShader::kNone_GradientType);
SkPDFGradientShader::Key opaqueState = clone_key(state);
for (int i = 0; i < opaqueState.fInfo.fColorCount; i++) {
@ -853,24 +854,22 @@ static sk_sp<SkPDFStream> make_alpha_function_shader(SkPDFDocument* doc,
SkASSERT(!gradient_has_alpha(opaqueState));
SkRect bbox = SkRect::Make(state.fBBox);
sk_sp<SkPDFObject> colorShader = find_pdf_shader(doc, std::move(opaqueState), false);
SkPDFIndirectReference colorShader = find_pdf_shader(doc, std::move(opaqueState), false);
if (!colorShader) {
return nullptr;
return SkPDFIndirectReference();
}
// Create resource dict with alpha graphics state as G0 and
// pattern shader as P0, then write content stream.
sk_sp<SkPDFObject> alphaGs = create_smask_graphic_state(doc, state);
SkPDFIndirectReference alphaGsRef = create_smask_graphic_state(doc, state);
sk_sp<SkPDFDict> resourceDict =
get_gradient_resource_dict(std::move(colorShader), std::move(alphaGs));
sk_sp<SkPDFDict> resourceDict = get_gradient_resource_dict(colorShader, alphaGsRef);
std::unique_ptr<SkStreamAsset> colorStream(create_pattern_fill_content(0, bbox));
auto alphaFunctionShader = sk_make_sp<SkPDFStream>(std::move(colorStream));
SkPDFUtils::PopulateTilingPatternDict(alphaFunctionShader->dict(), bbox,
std::unique_ptr<SkStreamAsset> colorStream =
create_pattern_fill_content(alphaGsRef.fValue, colorShader.fValue, bbox);
sk_sp<SkPDFDict> alphaFunctionShader = sk_make_sp<SkPDFDict>();
SkPDFUtils::PopulateTilingPatternDict(alphaFunctionShader.get(), bbox,
std::move(resourceDict), SkMatrix::I());
return alphaFunctionShader;
return SkPDFStreamOut(std::move(alphaFunctionShader), std::move(colorStream), doc);
}
static SkPDFGradientShader::Key make_key(const SkShader* shader,
@ -896,25 +895,25 @@ static SkPDFGradientShader::Key make_key(const SkShader* shader,
return key;
}
static sk_sp<SkPDFObject> find_pdf_shader(SkPDFDocument* doc,
SkPDFGradientShader::Key key,
bool keyHasAlpha) {
static SkPDFIndirectReference find_pdf_shader(SkPDFDocument* doc,
SkPDFGradientShader::Key key,
bool keyHasAlpha) {
SkASSERT(gradient_has_alpha(key) == keyHasAlpha);
SkPDFCanon* canon = doc->canon();
if (sk_sp<SkPDFObject>* ptr = canon->fGradientPatternMap.find(key)) {
auto& gradientPatternMap = doc->canon()->fGradientPatternMap;
if (SkPDFIndirectReference* ptr = gradientPatternMap.find(key)) {
return *ptr;
}
sk_sp<SkPDFObject> pdfShader;
SkPDFIndirectReference pdfShader;
if (keyHasAlpha) {
pdfShader = make_alpha_function_shader(doc, key);
} else {
pdfShader = make_function_shader(canon, key);
pdfShader = make_function_shader(doc, key);
}
canon->fGradientPatternMap.set(std::move(key), pdfShader);
gradientPatternMap.set(std::move(key), pdfShader);
return pdfShader;
}
sk_sp<SkPDFObject> SkPDFGradientShader::Make(SkPDFDocument* doc,
SkPDFIndirectReference SkPDFGradientShader::Make(SkPDFDocument* doc,
SkShader* shader,
const SkMatrix& canvasTransform,
const SkIRect& bbox) {

View File

@ -17,10 +17,10 @@ struct SkIRect;
namespace SkPDFGradientShader {
sk_sp<SkPDFObject> Make(SkPDFDocument* doc,
SkShader* shader,
const SkMatrix& matrix,
const SkIRect& surfaceBBox);
SkPDFIndirectReference Make(SkPDFDocument* doc,
SkShader* shader,
const SkMatrix& matrix,
const SkIRect& surfaceBBox);
struct Key {
SkShader::GradientType fType;
@ -37,7 +37,7 @@ struct KeyHash {
uint32_t operator()(const Key& k) const { return k.fHash; }
};
using HashMap = SkTHashMap<Key, sk_sp<SkPDFObject>, KeyHash>;
using HashMap = SkTHashMap<Key, SkPDFIndirectReference, KeyHash>;
inline bool operator==(const SkShader::GradientInfo& u, const SkShader::GradientInfo& v) {
return u.fColorCount == v.fColorCount

View File

@ -9,6 +9,7 @@
#include "SkData.h"
#include "SkPDFCanon.h"
#include "SkPDFDocumentPriv.h"
#include "SkPDFFormXObject.h"
#include "SkPDFUtils.h"
#include "SkPaint.h"
@ -52,21 +53,23 @@ static uint8_t pdf_blend_mode(SkBlendMode mode) {
return SkToU8((unsigned)mode);
}
sk_sp<SkPDFDict> SkPDFGraphicState::GetGraphicStateForPaint(SkPDFCanon* canon,
const SkPaint& p) {
SkPDFIndirectReference SkPDFGraphicState::GetGraphicStateForPaint(SkPDFDocument* doc,
const SkPaint& p) {
SkPDFCanon* canon = doc->canon();
SkASSERT(canon);
if (SkPaint::kFill_Style == p.getStyle()) {
SkPDFFillGraphicState fillKey = {p.getColor4f().fA, pdf_blend_mode(p.getBlendMode())};
auto& fillMap = canon->fFillGSMap;
if (sk_sp<SkPDFDict>* statePtr = fillMap.find(fillKey)) {
if (SkPDFIndirectReference* statePtr = fillMap.find(fillKey)) {
return *statePtr;
}
auto state = sk_make_sp<SkPDFDict>();
state->reserve(2);
state->insertColorComponentF("ca", fillKey.fAlpha);
state->insertName("BM", as_pdf_blend_mode_name((SkBlendMode)fillKey.fBlendMode));
fillMap.set(fillKey, state);
return state;
SkPDFDict state;
state.reserve(2);
state.insertColorComponentF("ca", fillKey.fAlpha);
state.insertName("BM", as_pdf_blend_mode_name((SkBlendMode)fillKey.fBlendMode));
SkPDFIndirectReference ref = doc->emit(state);
fillMap.set(fillKey, ref);
return ref;
} else {
SkPDFStrokeGraphicState strokeKey = {
p.getStrokeWidth(),
@ -77,65 +80,63 @@ sk_sp<SkPDFDict> SkPDFGraphicState::GetGraphicStateForPaint(SkPDFCanon* canon,
pdf_blend_mode(p.getBlendMode())
};
auto& sMap = canon->fStrokeGSMap;
if (sk_sp<SkPDFDict>* statePtr = sMap.find(strokeKey)) {
if (SkPDFIndirectReference* statePtr = sMap.find(strokeKey)) {
return *statePtr;
}
auto state = sk_make_sp<SkPDFDict>();
state->reserve(8);
state->insertColorComponentF("CA", strokeKey.fAlpha);
state->insertColorComponentF("ca", strokeKey.fAlpha);
state->insertInt("LC", to_stroke_cap(strokeKey.fStrokeCap));
state->insertInt("LJ", to_stroke_join(strokeKey.fStrokeJoin));
state->insertScalar("LW", strokeKey.fStrokeWidth);
state->insertScalar("ML", strokeKey.fStrokeMiter);
state->insertBool("SA", true); // SA = Auto stroke adjustment.
state->insertName("BM", as_pdf_blend_mode_name((SkBlendMode)strokeKey.fBlendMode));
sMap.set(strokeKey, state);
return state;
SkPDFDict state;
state.reserve(8);
state.insertColorComponentF("CA", strokeKey.fAlpha);
state.insertColorComponentF("ca", strokeKey.fAlpha);
state.insertInt("LC", to_stroke_cap(strokeKey.fStrokeCap));
state.insertInt("LJ", to_stroke_join(strokeKey.fStrokeJoin));
state.insertScalar("LW", strokeKey.fStrokeWidth);
state.insertScalar("ML", strokeKey.fStrokeMiter);
state.insertBool("SA", true); // SA = Auto stroke adjustment.
state.insertName("BM", as_pdf_blend_mode_name((SkBlendMode)strokeKey.fBlendMode));
SkPDFIndirectReference ref = doc->emit(state);
sMap.set(strokeKey, ref);
return ref;
}
}
////////////////////////////////////////////////////////////////////////////////
static sk_sp<SkPDFStream> make_invert_function() {
static SkPDFIndirectReference make_invert_function(SkPDFDocument* doc) {
// Acrobat crashes if we use a type 0 function, kpdf crashes if we use
// a type 2 function, so we use a type 4 function.
auto domainAndRange = SkPDFMakeArray(0, 1);
static const char psInvert[] = "{1 exch sub}";
// Do not copy the trailing '\0' into the SkData.
auto invertFunction = sk_make_sp<SkPDFStream>(
SkData::MakeWithoutCopy(psInvert, strlen(psInvert)));
invertFunction->dict()->insertInt("FunctionType", 4);
invertFunction->dict()->insertObject("Domain", domainAndRange);
invertFunction->dict()->insertObject("Range", std::move(domainAndRange));
return invertFunction;
auto invertFunction = SkData::MakeWithoutCopy(psInvert, strlen(psInvert));
sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
dict->insertInt("FunctionType", 4);
dict->insertObject("Domain", SkPDFMakeArray(0, 1));
dict->insertObject("Range", SkPDFMakeArray(0, 1));
return SkPDFStreamOut(std::move(dict), SkMemoryStream::Make(std::move(invertFunction)), doc);
}
sk_sp<SkPDFDict> SkPDFGraphicState::GetSMaskGraphicState(
sk_sp<SkPDFObject> sMask,
bool invert,
SkPDFSMaskMode sMaskMode,
SkPDFCanon* canon) {
SkPDFIndirectReference SkPDFGraphicState::GetSMaskGraphicState(SkPDFIndirectReference sMask,
bool invert,
SkPDFSMaskMode sMaskMode,
SkPDFDocument* doc) {
// The practical chances of using the same mask more than once are unlikely
// enough that it's not worth canonicalizing.
SkPDFCanon* canon = doc->canon();
auto sMaskDict = sk_make_sp<SkPDFDict>("Mask");
if (sMaskMode == kAlpha_SMaskMode) {
sMaskDict->insertName("S", "Alpha");
} else if (sMaskMode == kLuminosity_SMaskMode) {
sMaskDict->insertName("S", "Luminosity");
}
sMaskDict->insertObjRef("G", std::move(sMask));
sMaskDict->insertRef("G", sMask);
if (invert) {
// Instead of calling SkPDFGraphicState::MakeInvertFunction,
// let the canon deduplicate this object.
sk_sp<SkPDFStream>& invertFunction = canon->fInvertFunction;
if (!invertFunction) {
invertFunction = make_invert_function();
if (canon->fInvertFunction == SkPDFIndirectReference()) {
canon->fInvertFunction = make_invert_function(doc);
}
sMaskDict->insertObjRef("TR", invertFunction);
sMaskDict->insertRef("TR", canon->fInvertFunction);
}
auto result = sk_make_sp<SkPDFDict>("ExtGState");
result->insertObject("SMask", std::move(sMaskDict));
return result;
SkPDFDict result("ExtGState");
result.insertObject("SMask", std::move(sMaskDict));
return doc->emit(result);
}

View File

@ -29,7 +29,7 @@ namespace SkPDFGraphicState {
/** Get the graphic state for the passed SkPaint.
*/
sk_sp<SkPDFDict> GetGraphicStateForPaint(SkPDFCanon*, const SkPaint&);
SkPDFIndirectReference GetGraphicStateForPaint(SkPDFDocument*, const SkPaint&);
/** Make a graphic state that only sets the passed soft mask.
* @param sMask The form xobject to use as a soft mask.
@ -38,12 +38,10 @@ namespace SkPDFGraphicState {
*
* These are not de-duped.
*/
sk_sp<SkPDFDict> GetSMaskGraphicState(sk_sp<SkPDFObject> sMask,
bool invert,
SkPDFSMaskMode sMaskMode,
SkPDFCanon* canon);
sk_sp<SkPDFStream> MakeInvertFunction();
SkPDFIndirectReference GetSMaskGraphicState(SkPDFIndirectReference sMask,
bool invert,
SkPDFSMaskMode sMaskMode,
SkPDFDocument* doc);
}
SK_BEGIN_REQUIRE_DENSE

View File

@ -205,7 +205,7 @@ void SkPDFAppendCmapSections(const SkUnichar* glyphToUnicode,
append_bfrange_section(bfrangeEntries, multiByteGlyphs, cmap);
}
sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
std::unique_ptr<SkStreamAsset> SkPDFMakeToUnicodeCmap(
const SkUnichar* glyphToUnicode,
const SkPDFGlyphUse* subset,
bool multiByteGlyphs,
@ -216,6 +216,5 @@ sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
SkPDFAppendCmapSections(glyphToUnicode, subset, &cmap, multiByteGlyphs,
firstGlyphID, lastGlyphID);
append_cmap_footer(&cmap);
return sk_make_sp<SkPDFStream>(
std::unique_ptr<SkStreamAsset>(cmap.detachAsStream()));
return cmap.detachAsStream();
}

View File

@ -10,7 +10,7 @@
#include "SkPDFFont.h"
#include "SkStream.h"
sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
std::unique_ptr<SkStreamAsset> SkPDFMakeToUnicodeCmap(
const SkUnichar* glyphToUnicode,
const SkPDFGlyphUse* subset,
bool multiByteGlyphs,

View File

@ -150,8 +150,7 @@ sk_sp<SkPDFObject> SkPDFMetadata::MakeDocumentInformationDict(
return std::move(dict);
}
SkPDFMetadata::UUID SkPDFMetadata::CreateUUID(
const SkPDF::Metadata& metadata) {
SkUUID SkPDFMetadata::CreateUUID(const SkPDF::Metadata& metadata) {
// The main requirement is for the UUID to be unique; the exact
// format of the data that will be hashed is not important.
SkMD5 md5;
@ -177,22 +176,22 @@ SkPDFMetadata::UUID SkPDFMetadata::CreateUUID(
// See RFC 4122, page 6-7.
digest.data[6] = (digest.data[6] & 0x0F) | 0x30;
digest.data[8] = (digest.data[6] & 0x3F) | 0x80;
static_assert(sizeof(digest) == sizeof(UUID), "uuid_size");
SkPDFMetadata::UUID uuid;
static_assert(sizeof(digest) == sizeof(SkUUID), "uuid_size");
SkUUID uuid;
memcpy(&uuid, &digest, sizeof(digest));
return uuid;
}
sk_sp<SkPDFObject> SkPDFMetadata::MakePdfId(const UUID& doc,
const UUID& instance) {
sk_sp<SkPDFObject> SkPDFMetadata::MakePdfId(const SkUUID& doc,
const SkUUID& instance) {
// /ID [ <81b14aafa313db63dbd6f981e49f94f4>
// <81b14aafa313db63dbd6f981e49f94f4> ]
auto array = sk_make_sp<SkPDFArray>();
static_assert(sizeof(SkPDFMetadata::UUID) == 16, "uuid_size");
static_assert(sizeof(SkUUID) == 16, "uuid_size");
array->appendString(
SkString(reinterpret_cast<const char*>(&doc), sizeof(UUID)));
SkString(reinterpret_cast<const char*>(&doc), sizeof(SkUUID)));
array->appendString(
SkString(reinterpret_cast<const char*>(&instance), sizeof(UUID)));
SkString(reinterpret_cast<const char*>(&instance), sizeof(SkUUID)));
return std::move(array);
}
@ -208,7 +207,7 @@ static void hexify(const uint8_t** inputPtr, char** outputPtr, int count) {
}
}
static SkString uuid_to_string(const SkPDFMetadata::UUID& uuid) {
static SkString uuid_to_string(const SkUUID& uuid) {
// 8-4-4-4-12
char buffer[36]; // [32 + 4]
char* ptr = buffer;
@ -306,8 +305,8 @@ const SkString escape_xml(const SkString& input,
sk_sp<SkPDFObject> SkPDFMetadata::MakeXMPObject(
const SkPDF::Metadata& metadata,
const UUID& doc,
const UUID& instance) {
const SkUUID& doc,
const SkUUID& instance) {
static const char templateString[] =
"<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n"
"<x:xmpmeta xmlns:x=\"adobe:ns:meta/\"\n"

View File

@ -9,22 +9,19 @@
#define SkPDFMetadata_DEFINED
#include "SkPDFDocument.h"
#include "SkUUID.h"
class SkPDFObject;
namespace SkPDFMetadata {
sk_sp<SkPDFObject> MakeDocumentInformationDict(const SkPDF::Metadata&);
struct UUID {
uint8_t fData[16];
};
SkUUID CreateUUID(const SkPDF::Metadata&);
UUID CreateUUID(const SkPDF::Metadata&);
sk_sp<SkPDFObject> MakePdfId(const UUID& doc, const UUID& instance);
sk_sp<SkPDFObject> MakePdfId(const SkUUID& doc, const SkUUID& instance);
sk_sp<SkPDFObject> MakeXMPObject(const SkPDF::Metadata&,
const UUID& doc,
const UUID& instance);
const SkUUID& doc,
const SkUUID& instance);
}
#endif // SkPDFMetadata_DEFINED

View File

@ -59,18 +59,6 @@ static SkString resource(SkPDFResourceType type, int index) {
return SkString(buffer, (size_t)(end - buffer));
}
static void add_subdict(std::vector<sk_sp<SkPDFObject>> resourceList,
SkPDFResourceType type,
SkPDFDict* dst) {
if (!resourceList.empty()) {
auto resources = sk_make_sp<SkPDFDict>();
for (size_t i = 0; i < resourceList.size(); i++) {
resources->insertObjRef(resource(type, SkToInt(i)), std::move(resourceList[i]));
}
dst->insertObject(resource_name(type), std::move(resources));
}
}
static void add_subdict(const std::vector<SkPDFIndirectReference>& resourceList,
SkPDFResourceType type,
SkPDFDict* dst) {
@ -83,14 +71,14 @@ static void add_subdict(const std::vector<SkPDFIndirectReference>& resourceList,
}
}
sk_sp<SkPDFDict> SkPDFMakeResourceDict(std::vector<sk_sp<SkPDFObject>> graphicStateResources,
std::vector<sk_sp<SkPDFObject>> shaderResources,
std::vector<sk_sp<SkPDFObject>> xObjectResources,
sk_sp<SkPDFDict> SkPDFMakeResourceDict(std::vector<SkPDFIndirectReference> graphicStateResources,
std::vector<SkPDFIndirectReference> shaderResources,
std::vector<SkPDFIndirectReference> xObjectResources,
std::vector<SkPDFIndirectReference> fontResources) {
auto dict = sk_make_sp<SkPDFDict>();
add_subdict(std::move(graphicStateResources), SkPDFResourceType::kExtGState, dict.get());
add_subdict(std::move(shaderResources), SkPDFResourceType::kPattern, dict.get());
add_subdict(std::move(xObjectResources), SkPDFResourceType::kXObject, dict.get());
add_subdict(fontResources, SkPDFResourceType::kFont, dict.get());
add_subdict(graphicStateResources, SkPDFResourceType::kExtGState, dict.get());
add_subdict(shaderResources, SkPDFResourceType::kPattern, dict.get());
add_subdict(xObjectResources, SkPDFResourceType::kXObject, dict.get());
add_subdict(fontResources, SkPDFResourceType::kFont, dict.get());
return dict;
}

View File

@ -32,9 +32,9 @@ enum class SkPDFResourceType {
*
* Any arguments can be nullptr.
*/
sk_sp<SkPDFDict> SkPDFMakeResourceDict(std::vector<sk_sp<SkPDFObject>> graphicStateResources,
std::vector<sk_sp<SkPDFObject>> shaderResources,
std::vector<sk_sp<SkPDFObject>> xObjectResources,
sk_sp<SkPDFDict> SkPDFMakeResourceDict(std::vector<SkPDFIndirectReference> graphicStateResources,
std::vector<SkPDFIndirectReference> shaderResources,
std::vector<SkPDFIndirectReference> xObjectResources,
std::vector<SkPDFIndirectReference> fontResources);
/**

View File

@ -37,9 +37,9 @@ static void draw_bitmap_matrix(SkCanvas* canvas, const SkBitmap& bm,
canvas->drawBitmap(bm, 0, 0, &paint);
}
static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc,
const SkPDFImageShaderKey& key,
SkImage* image) {
static SkPDFIndirectReference make_image_shader(SkPDFDocument* doc,
const SkPDFImageShaderKey& key,
SkImage* image) {
SkASSERT(image);
// The image shader pattern cell will be drawn into a separate device
@ -52,7 +52,7 @@ static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc,
finalMatrix.preConcat(key.fShaderTransform);
SkRect deviceBounds = SkRect::Make(key.fBBox);
if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &deviceBounds)) {
return nullptr;
return SkPDFIndirectReference();
}
SkRect bitmapBounds = SkRect::Make(image->bounds());
@ -244,22 +244,23 @@ static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc,
}
}
auto imageShader = sk_make_sp<SkPDFStream>(patternDevice->content());
auto imageShader = patternDevice->content();
sk_sp<SkPDFDict> resourceDict = patternDevice->makeResourceDict();
SkPDFUtils::PopulateTilingPatternDict(imageShader->dict(), patternBBox,
sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
SkPDFUtils::PopulateTilingPatternDict(dict.get(), patternBBox,
std::move(resourceDict), finalMatrix);
return imageShader;
return SkPDFStreamOut(std::move(dict), std::move(imageShader), doc);
}
// Generic fallback for unsupported shaders:
// * allocate a surfaceBBox-sized bitmap
// * shade the whole area
// * use the result as a bitmap shader
static sk_sp<SkPDFObject> make_fallback_shader(SkPDFDocument* doc,
SkShader* shader,
const SkMatrix& canvasTransform,
const SkIRect& surfaceBBox,
SkColor paintColor) {
static SkPDFIndirectReference make_fallback_shader(SkPDFDocument* doc,
SkShader* shader,
const SkMatrix& canvasTransform,
const SkIRect& surfaceBBox,
SkColor paintColor) {
// 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
@ -280,7 +281,7 @@ static sk_sp<SkPDFObject> make_fallback_shader(SkPDFDocument* doc,
// MakeImageShader's behavior).
SkRect shaderRect = SkRect::Make(surfaceBBox);
if (!SkPDFUtils::InverseTransformBBox(canvasTransform, &shaderRect)) {
return nullptr;
return SkPDFIndirectReference();
}
// Clamp the bitmap size to about 1M pixels
static const SkScalar kMaxBitmapArea = 1024 * 1024;
@ -324,18 +325,18 @@ static SkColor adjust_color(SkShader* shader, SkColor paintColor) {
return paintColor & SK_ColorBLACK;
}
sk_sp<SkPDFObject> SkPDFMakeShader(SkPDFDocument* doc,
SkShader* shader,
const SkMatrix& canvasTransform,
const SkIRect& surfaceBBox,
SkColor paintColor) {
SkPDFIndirectReference SkPDFMakeShader(SkPDFDocument* doc,
SkShader* shader,
const SkMatrix& canvasTransform,
const SkIRect& surfaceBBox,
SkColor paintColor) {
SkASSERT(shader);
SkASSERT(doc);
if (SkShader::kNone_GradientType != shader->asAGradient(nullptr)) {
return SkPDFGradientShader::Make(doc, shader, canvasTransform, surfaceBBox);
}
if (surfaceBBox.isEmpty()) {
return nullptr;
return SkPDFIndirectReference();
}
SkBitmap image;
SkPDFImageShaderKey key = {
@ -350,11 +351,11 @@ sk_sp<SkPDFObject> SkPDFMakeShader(SkPDFDocument* doc,
if (SkImage* skimg = shader->isAImage(&key.fShaderTransform, key.fImageTileModes)) {
key.fBitmapKey = SkBitmapKeyFromImage(skimg);
SkPDFCanon* canon = doc->canon();
sk_sp<SkPDFObject>* shaderPtr = canon->fImageShaderMap.find(key);
SkPDFIndirectReference* shaderPtr = canon->fImageShaderMap.find(key);
if (shaderPtr) {
return *shaderPtr;
}
sk_sp<SkPDFObject> pdfShader = make_image_shader(doc, key, skimg);
SkPDFIndirectReference pdfShader = make_image_shader(doc, key, skimg);
canon->fImageShaderMap.set(std::move(key), pdfShader);
return pdfShader;
}

View File

@ -38,11 +38,11 @@ struct SkIRect;
* @param paintColor Color+Alpha of the paint. Color is usually ignored,
* unless it is a alpha shader.
*/
sk_sp<SkPDFObject> SkPDFMakeShader(SkPDFDocument* doc,
SkShader* shader,
const SkMatrix& ctm,
const SkIRect& surfaceBBox,
SkColor paintColor);
SkPDFIndirectReference SkPDFMakeShader(SkPDFDocument* doc,
SkShader* shader,
const SkMatrix& ctm,
const SkIRect& surfaceBBox,
SkColor paintColor);
SK_BEGIN_REQUIRE_DENSE
struct SkPDFImageShaderKey {

View File

@ -9,7 +9,9 @@
#include "SkData.h"
#include "SkDeflate.h"
#include "SkExecutor.h"
#include "SkMakeUnique.h"
#include "SkPDFDocumentPriv.h"
#include "SkPDFUtils.h"
#include "SkStream.h"
#include "SkStreamPriv.h"
@ -203,6 +205,7 @@ void SkPDFUnion::emitObject(SkWStream* stream) const {
fObject->emitObject(stream);
return;
case Type::kRef:
SkASSERT(fIntValue >= 0);
stream->writeDecAsText(fIntValue);
stream->writeText(" 0 R"); // Generation number is always 0.
return;
@ -496,143 +499,71 @@ void SkPDFDict::insertString(const char key[], SkString value) {
////////////////////////////////////////////////////////////////////////////////
SkPDFSharedStream::SkPDFSharedStream(std::unique_ptr<SkStreamAsset> data)
: fAsset(std::move(data)) {
SkASSERT(fAsset);
}
SkPDFSharedStream::~SkPDFSharedStream() { this->drop(); }
void SkPDFSharedStream::drop() {
fAsset = nullptr;
fDict.drop();
}
#ifdef SK_PDF_LESS_COMPRESSION
void SkPDFSharedStream::emitObject(SkWStream* stream) const {
SkASSERT(fAsset);
std::unique_ptr<SkStreamAsset> dup(fAsset->duplicate());
SkASSERT(dup && dup->hasLength());
size_t length = dup->getLength();
stream->writeText("<<");
fDict.emitAll(stream);
stream->writeText("\n");
SkPDFUnion::Name("Length").emitObject(stream);
stream->writeText(" ");
SkPDFUnion::Int(length).emitObject(stream);
stream->writeText("\n>>stream\n");
SkStreamCopy(stream, dup.get());
stream->writeText("\nendstream");
}
#else
void SkPDFSharedStream::emitObject(SkWStream* stream) const {
SkASSERT(fAsset);
SkDynamicMemoryWStream buffer;
SkDeflateWStream deflateWStream(&buffer);
// Since emitObject is const, this function doesn't change the dictionary.
std::unique_ptr<SkStreamAsset> dup(fAsset->duplicate()); // Cheap copy
SkASSERT(dup);
SkStreamCopy(&deflateWStream, dup.get());
deflateWStream.finalize();
size_t length = buffer.bytesWritten();
stream->writeText("<<");
fDict.emitAll(stream);
stream->writeText("\n");
SkPDFUnion::Name("Length").emitObject(stream);
stream->writeText(" ");
SkPDFUnion::Int(length).emitObject(stream);
stream->writeText("\n");
SkPDFUnion::Name("Filter").emitObject(stream);
stream->writeText(" ");
SkPDFUnion::Name("FlateDecode").emitObject(stream);
stream->writeText(">>");
stream->writeText(" stream\n");
buffer.writeToAndReset(stream);
stream->writeText("\nendstream");
}
#endif
void SkPDFSharedStream::addResources(
SkPDFObjNumMap* catalog) const {
SkASSERT(fAsset);
fDict.addResources(catalog);
}
////////////////////////////////////////////////////////////////////////////////
SkPDFStream::SkPDFStream(sk_sp<SkData> data) {
this->setData(skstd::make_unique<SkMemoryStream>(std::move(data)));
}
SkPDFStream::SkPDFStream(std::unique_ptr<SkStreamAsset> stream) {
this->setData(std::move(stream));
}
SkPDFStream::SkPDFStream() {}
SkPDFStream::~SkPDFStream() {}
void SkPDFStream::addResources(SkPDFObjNumMap* catalog) const {
SkASSERT(fCompressedData);
fDict.addResources(catalog);
}
void SkPDFStream::drop() {
fCompressedData.reset(nullptr);
fDict.drop();
}
void SkPDFStream::emitObject(SkWStream* stream) const {
SkASSERT(fCompressedData);
fDict.emitObject(stream);
// duplicate (a cheap operation) preserves const on fCompressedData.
std::unique_ptr<SkStreamAsset> dup(fCompressedData->duplicate());
SkASSERT(dup);
SkASSERT(dup->hasLength());
stream->writeText(" stream\n");
stream->writeStream(dup.get(), dup->getLength());
stream->writeText("\nendstream");
}
void SkPDFStream::setData(std::unique_ptr<SkStreamAsset> stream) {
SkASSERT(!fCompressedData); // Only call this function once.
SkASSERT(stream);
static void serialize_stream(const SkPDFDict* origDict,
SkStreamAsset* stream,
bool deflate,
SkPDFDocument* doc,
SkPDFIndirectReference ref) {
// Code assumes that the stream starts at the beginning.
SkASSERT(stream && stream->hasLength());
#ifdef SK_PDF_LESS_COMPRESSION
fCompressedData = std::move(stream);
SkASSERT(fCompressedData && fCompressedData->hasLength());
fDict.insertInt("Length", fCompressedData->getLength());
#else
SkASSERT(stream->hasLength());
SkDynamicMemoryWStream compressedData;
SkDeflateWStream deflateWStream(&compressedData);
if (stream->getLength() > 0) {
SkStreamCopy(&deflateWStream, stream.get());
std::unique_ptr<SkStreamAsset> tmp;
SkPDFDict dict;
static const size_t kMinimumSavings = strlen("/Filter_/FlateDecode_");
if (deflate && stream->getLength() > kMinimumSavings) {
SkDynamicMemoryWStream compressedData;
SkDeflateWStream deflateWStream(&compressedData);
SkStreamCopy(&deflateWStream, stream);
deflateWStream.finalize();
if (stream->getLength() > compressedData.bytesWritten() + kMinimumSavings) {
tmp = compressedData.detachAsStream();
stream = tmp.get();
dict.insertName("Filter", "FlateDecode");
} else {
SkAssertResult(stream->rewind());
}
}
deflateWStream.finalize();
size_t compressedLength = compressedData.bytesWritten();
size_t originalLength = stream->getLength();
dict.insertInt("Length", stream->getLength());
if (originalLength <= compressedLength + strlen("/Filter_/FlateDecode_")) {
SkAssertResult(stream->rewind());
fCompressedData = std::move(stream);
fDict.insertInt("Length", originalLength);
return;
SkWStream* dst = doc->beginObject(ref);
dst->writeText("<<");
if (origDict) {
origDict->emitAll(dst);
}
fCompressedData = compressedData.detachAsStream();
fDict.insertName("Filter", "FlateDecode");
fDict.insertInt("Length", compressedLength);
#endif
dict.emitAll(dst);
dst->writeText(">> stream\n");
dst->writeStream(stream, stream->getLength());
dst->writeText("\nendstream");
doc->endObject();
}
SkPDFIndirectReference SkPDFStreamOut(sk_sp<SkPDFDict> dict,
std::unique_ptr<SkStreamAsset> content,
SkPDFDocument* doc,
bool deflate) {
SkPDFIndirectReference ref = doc->reserveRef();
if (SkExecutor* executor = doc->executor()) {
SkPDFDict* dictPtr = dict.release();
SkStreamAsset* contentPtr = content.release();
// Pass ownership of both pointers into a std::function, which should
// only be executed once.
executor->add([dictPtr, contentPtr, deflate, doc, ref]() {
serialize_stream(dictPtr, contentPtr, deflate, doc, ref);
SkSafeUnref(dictPtr);
delete contentPtr;
});
return ref;
}
serialize_stream(dict.get(), content.get(), deflate, doc, ref);
return ref;
}
////////////////////////////////////////////////////////////////////////////////
void SkPDFObjNumMap::addObjectRecursively(SkPDFObject* obj) {
SkASSERT(fIndirectReferenceSource != nullptr);
if (obj && obj->fIndirectReference.fValue == -1) {
obj->fIndirectReference.fValue = fNextObjectNumber++;
obj->fIndirectReference = fIndirectReferenceSource->reserve();
fObjects.emplace_back(sk_ref_sp(obj));
obj->addResources(this);
}

View File

@ -27,6 +27,7 @@ class SkPDFObject;
class SkStreamAsset;
class SkString;
class SkWStream;
struct SkPDFObjectSerializer;
#ifdef SK_PDF_IMAGE_STATS
#include <atomic>
@ -34,6 +35,7 @@ class SkWStream;
struct SkPDFIndirectReference {
int fValue = -1;
explicit operator bool() { return fValue != -1; }
};
inline static bool operator==(SkPDFIndirectReference u, SkPDFIndirectReference v) {
@ -44,6 +46,7 @@ inline static bool operator!=(SkPDFIndirectReference u, SkPDFIndirectReference v
return u.fValue != v.fValue;
}
/** \class SkPDFObject
A PDF Object is the base class for primitive elements in a PDF file. A
@ -352,67 +355,16 @@ private:
SkDEBUGCODE(bool fDumped;)
};
/** \class SkPDFSharedStream
#ifdef SK_PDF_LESS_COMPRESSION
static constexpr bool kSkPDFDefaultDoDeflate = false;
#else
static constexpr bool kSkPDFDefaultDoDeflate = true;
#endif
This class takes an asset and assumes that it is backed by
long-lived shared data (for example, an open file
descriptor). That is: no memory savings can be made by holding on
to a compressed version instead.
*/
class SkPDFSharedStream final : public SkPDFObject {
public:
SkPDFSharedStream(std::unique_ptr<SkStreamAsset> data);
~SkPDFSharedStream() override;
SkPDFDict* dict() { return &fDict; }
void emitObject(SkWStream*) const override;
void addResources(SkPDFObjNumMap*) const override;
void drop() override;
private:
std::unique_ptr<SkStreamAsset> fAsset;
SkPDFDict fDict;
typedef SkPDFObject INHERITED;
};
/** \class SkPDFStream
This class takes an asset and assumes that it is the only owner of
the asset's data. It immediately compresses the asset to save
memory.
*/
class SkPDFStream final : public SkPDFObject {
public:
/** Create a PDF stream. A Length entry is automatically added to the
* stream dictionary.
* @param data The data part of the stream.
* @param stream The data part of the stream. */
explicit SkPDFStream(sk_sp<SkData> data);
explicit SkPDFStream(std::unique_ptr<SkStreamAsset> stream);
~SkPDFStream() override;
SkPDFDict* dict() { return &fDict; }
// The SkPDFObject interface.
void emitObject(SkWStream* stream) const override;
void addResources(SkPDFObjNumMap*) const final;
void drop() override;
protected:
/* Create a PDF stream with no data. The setData method must be called to
* set the data. */
SkPDFStream();
/** Only call this function once. */
void setData(std::unique_ptr<SkStreamAsset> stream);
private:
std::unique_ptr<SkStreamAsset> fCompressedData;
SkPDFDict fDict;
typedef SkPDFDict INHERITED;
};
SkPDFIndirectReference SkPDFStreamOut(sk_sp<SkPDFDict> dict,
std::unique_ptr<SkStreamAsset> stream,
SkPDFDocument* doc,
bool deflate = kSkPDFDefaultDoDeflate);
////////////////////////////////////////////////////////////////////////////////
@ -423,6 +375,8 @@ private:
*/
class SkPDFObjNumMap : SkNoncopyable {
public:
SkPDFObjNumMap(SkPDFObjectSerializer* s) : fIndirectReferenceSource(s) {}
/** Add the passed object to the catalog, as well as all its dependencies.
* @param obj The object to add. If nullptr, this is a noop.
*/
@ -438,8 +392,8 @@ public:
private:
friend struct SkPDFObjectSerializer;
SkPDFObjectSerializer* fIndirectReferenceSource;
std::vector<sk_sp<SkPDFObject>> fObjects;
int fNextObjectNumber = 1;
};
////////////////////////////////////////////////////////////////////////////////

18
src/pdf/SkUUID.h Normal file
View File

@ -0,0 +1,18 @@
// Copyright 2018 Google LLC.
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
#ifndef SkUUID_DEFINED
#define SkUUID_DEFINED
#include <cstdint>
#include <cstring>
struct SkUUID {
uint8_t fData[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
};
static inline bool operator==(const SkUUID& u, const SkUUID& v) {
return 0 == memcmp(u.fData, v.fData, sizeof(u.fData));
}
static inline bool operator!=(const SkUUID& u, const SkUUID& v) { return !(u == v); }
#endif // SkUUID_DEFINED

View File

@ -23,6 +23,7 @@
#include "SkPDFCanon.h"
#include "SkPDFDevice.h"
#include "SkPDFDocument.h"
#include "SkPDFDocumentPriv.h"
#include "SkPDFFont.h"
#include "SkPDFTypes.h"
#include "SkPDFUtils.h"
@ -77,51 +78,11 @@ static void assert_emit_eq(skiatest::Reporter* reporter,
assert_eq(reporter, result, string);
}
static void TestPDFStream(skiatest::Reporter* reporter) {
char streamBytes[] = "Test\nFoo\tBar";
auto streamData = skstd::make_unique<SkMemoryStream>(
streamBytes, strlen(streamBytes), true);
auto stream = sk_make_sp<SkPDFStream>(std::move(streamData));
assert_emit_eq(reporter,
*stream,
"<</Length 12>> stream\nTest\nFoo\tBar\nendstream");
stream->dict()->insertInt("Attribute", 42);
assert_emit_eq(reporter,
*stream,
"<</Length 12\n/Attribute 42>> stream\n"
"Test\nFoo\tBar\nendstream");
{
char streamBytes2[] = "This is a longer string, so that compression "
"can do something with it. With shorter strings, "
"the short circuit logic cuts in and we end up "
"with an uncompressed string.";
auto stream = sk_make_sp<SkPDFStream>(
SkData::MakeWithCopy(streamBytes2, strlen(streamBytes2)));
SkDynamicMemoryWStream compressedByteStream;
SkDeflateWStream deflateWStream(&compressedByteStream);
deflateWStream.write(streamBytes2, strlen(streamBytes2));
deflateWStream.finalize();
SkDynamicMemoryWStream expected;
expected.writeText("<</Filter /FlateDecode\n/Length 116>> stream\n");
compressedByteStream.writeToStream(&expected);
compressedByteStream.reset();
expected.writeText("\nendstream");
sk_sp<SkData> expectedResultData2(expected.detachAsData());
SkString result = emit_to_string(*stream);
#ifndef SK_PDF_LESS_COMPRESSION
assert_eql(reporter,
result,
(const char*)expectedResultData2->data(),
expectedResultData2->size());
#endif
}
}
static void TestObjectNumberMap(skiatest::Reporter* reporter) {
SkPDFObjNumMap objNumMap;
SkPDFObjectSerializer pdfObjectSerializer;
SkNullWStream nullstream;
pdfObjectSerializer.serializeHeader(&nullstream);
SkPDFObjNumMap objNumMap(&pdfObjectSerializer);
sk_sp<SkPDFArray> a1(new SkPDFArray);
sk_sp<SkPDFArray> a2(new SkPDFArray);
sk_sp<SkPDFArray> a3(new SkPDFArray);
@ -145,7 +106,10 @@ static void TestObjectRef(skiatest::Reporter* reporter) {
sk_sp<SkPDFArray> a2(new SkPDFArray);
a2->appendObjRef(a1);
SkPDFObjNumMap catalog;
SkPDFObjectSerializer pdfObjectSerializer;
SkNullWStream nullstream;
pdfObjectSerializer.serializeHeader(&nullstream);
SkPDFObjNumMap catalog(&pdfObjectSerializer);
catalog.addObjectRecursively(a1.get());
REPORTER_ASSERT(reporter, catalog.getObjectNumber(a1.get()) == 1);
@ -264,7 +228,10 @@ static void TestPDFArray(skiatest::Reporter* reporter) {
"(Another String) [-1]]");
sk_sp<SkPDFArray> referencedArray(new SkPDFArray);
SkPDFObjNumMap catalog;
SkPDFObjectSerializer pdfObjectSerializer;
SkNullWStream nullstream;
pdfObjectSerializer.serializeHeader(&nullstream);
SkPDFObjNumMap catalog(&pdfObjectSerializer);
catalog.addObjectRecursively(referencedArray.get());
REPORTER_ASSERT(reporter, catalog.getObjectNumber(
referencedArray.get()) == 1);
@ -328,8 +295,12 @@ static void TestPDFDict(skiatest::Reporter* reporter) {
assert_emit_eq(reporter, *dict, "<</Type /DType>>");
sk_sp<SkPDFArray> referencedArray(new SkPDFArray);
SkPDFObjNumMap catalog;
SkPDFObjectSerializer pdfObjectSerializer;
SkNullWStream nullstream;
pdfObjectSerializer.serializeHeader(&nullstream);
SkPDFObjNumMap catalog(&pdfObjectSerializer);
catalog.addObjectRecursively(referencedArray.get());
REPORTER_ASSERT(reporter, catalog.getObjectNumber(
referencedArray.get()) == 1);
dict->insertObjRef("n1", std::move(referencedArray));
@ -341,7 +312,6 @@ DEF_TEST(SkPDF_Primitives, reporter) {
TestPDFUnion(reporter);
TestPDFArray(reporter);
TestPDFDict(reporter);
TestPDFStream(reporter);
TestObjectNumberMap(reporter);
TestObjectRef(reporter);
test_issue1083();