SkPDF: move image serialization over to new immediate mode.
Change-Id: If2bd4cd61bc3231edd860b0e08f78a6f24bd3094 Reviewed-on: https://skia-review.googlesource.com/c/171532 Reviewed-by: Ben Wagner <bungeman@google.com> Commit-Queue: Hal Canary <halcanary@google.com>
This commit is contained in:
parent
f2a7a20b32
commit
a121183204
@ -119,12 +119,10 @@ protected:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while (loops-- > 0) {
|
while (loops-- > 0) {
|
||||||
auto object = SkPDFCreateBitmapObject(fImage);
|
SkNullWStream nullStream;
|
||||||
SkASSERT(object);
|
SkPDFDocument doc(&nullStream, SkPDF::Metadata());
|
||||||
if (!object) {
|
doc.beginPage(256, 256);
|
||||||
return;
|
(void)SkPDFSerializeImage(fImage.get(), &doc);
|
||||||
}
|
|
||||||
test_pdf_object_serialization(object);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,12 +154,10 @@ protected:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while (loops-- > 0) {
|
while (loops-- > 0) {
|
||||||
auto object = SkPDFCreateBitmapObject(fImage);
|
SkNullWStream nullStream;
|
||||||
SkASSERT(object);
|
SkPDFDocument doc(&nullStream, SkPDF::Metadata());
|
||||||
if (!object) {
|
doc.beginPage(256, 256);
|
||||||
return;
|
(void)SkPDFSerializeImage(fImage.get(), &doc);
|
||||||
}
|
|
||||||
test_pdf_object_serialization(object);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,28 +13,12 @@
|
|||||||
#include "SkImage.h"
|
#include "SkImage.h"
|
||||||
#include "SkImageInfoPriv.h"
|
#include "SkImageInfoPriv.h"
|
||||||
#include "SkJpegInfo.h"
|
#include "SkJpegInfo.h"
|
||||||
#include "SkPDFCanon.h"
|
#include "SkPDFDocumentPriv.h"
|
||||||
#include "SkPDFTypes.h"
|
#include "SkPDFTypes.h"
|
||||||
#include "SkPDFUtils.h"
|
#include "SkPDFUtils.h"
|
||||||
#include "SkStream.h"
|
#include "SkStream.h"
|
||||||
#include "SkTo.h"
|
#include "SkTo.h"
|
||||||
|
|
||||||
bool image_compute_is_opaque(const SkImage* image) {
|
|
||||||
if (image->isOpaque()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// keep output PDF small at cost of possible resource use.
|
|
||||||
SkBitmap bm;
|
|
||||||
// if image can not be read, treat as transparent.
|
|
||||||
return SkPDFUtils::ToBitmap(image, &bm) && SkBitmap::ComputeIsOpaque(bm);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static const char kStreamBegin[] = " stream\n";
|
|
||||||
|
|
||||||
static const char kStreamEnd[] = "\nendstream";
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// write a single byte to a stream n times.
|
// write a single byte to a stream n times.
|
||||||
@ -56,7 +40,7 @@ static void fill_stream(SkWStream* out, char value, size_t n) {
|
|||||||
channel goes to black, and the should-be-transparent pixels are
|
channel goes to black, and the should-be-transparent pixels are
|
||||||
rendered as grey because of the separate soft mask and color
|
rendered as grey because of the separate soft mask and color
|
||||||
resizing. e.g.: gm/bitmappremul.cpp */
|
resizing. e.g.: gm/bitmappremul.cpp */
|
||||||
static SkColor get_neighbor_avg_color(const SkBitmap& bm, int xOrig, int yOrig) {
|
static SkColor get_neighbor_avg_color(const SkPixmap& bm, int xOrig, int yOrig) {
|
||||||
SkASSERT(kBGRA_8888_SkColorType == bm.colorType());
|
SkASSERT(kBGRA_8888_SkColorType == bm.colorType());
|
||||||
unsigned r = 0, g = 0, b = 0, n = 0;
|
unsigned r = 0, g = 0, b = 0, n = 0;
|
||||||
// Clamp the range to the edge of the bitmap.
|
// Clamp the range to the edge of the bitmap.
|
||||||
@ -65,7 +49,7 @@ static SkColor get_neighbor_avg_color(const SkBitmap& bm, int xOrig, int yOrig)
|
|||||||
int xmin = SkTMax(0, xOrig - 1);
|
int xmin = SkTMax(0, xOrig - 1);
|
||||||
int xmax = SkTMin(xOrig + 1, bm.width() - 1);
|
int xmax = SkTMin(xOrig + 1, bm.width() - 1);
|
||||||
for (int y = ymin; y <= ymax; ++y) {
|
for (int y = ymin; y <= ymax; ++y) {
|
||||||
const SkColor* scanline = bm.getAddr32(0, y);
|
const SkColor* scanline = bm.addr32(0, y);
|
||||||
for (int x = xmin; x <= xmax; ++x) {
|
for (int x = xmin; x <= xmax; ++x) {
|
||||||
SkColor color = scanline[x];
|
SkColor color = scanline[x];
|
||||||
if (color != SK_ColorTRANSPARENT) {
|
if (color != SK_ColorTRANSPARENT) {
|
||||||
@ -80,173 +64,150 @@ static SkColor get_neighbor_avg_color(const SkBitmap& bm, int xOrig, int yOrig)
|
|||||||
: SK_ColorTRANSPARENT;
|
: SK_ColorTRANSPARENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t pixel_count(const SkImage* image) {
|
static void emit_stream(SkDynamicMemoryWStream* src, SkWStream* dst) {
|
||||||
return SkToSizeT(image->width()) * SkToSizeT(image->height());
|
dst->writeText(" stream\n");
|
||||||
|
src->writeToAndReset(dst);
|
||||||
|
dst->writeText("\nendstream");
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t pdf_color_component_count(SkColorType ct) {
|
static void emit_dict(SkWStream* stream, SkISize size, const char* colorSpace,
|
||||||
// Single-channel formats remain that way, all others are converted to RGB
|
const SkPDFIndirectReference* smask, int length) {
|
||||||
return SkColorTypeIsAlphaOnly(ct) || SkColorTypeIsGray(ct) ? 1 : 3;
|
SkPDFDict pdfDict("XObject");
|
||||||
|
pdfDict.insertName("Subtype", "Image");
|
||||||
|
pdfDict.insertInt("Width", size.width());
|
||||||
|
pdfDict.insertInt("Height", size.height());
|
||||||
|
pdfDict.insertName("ColorSpace", colorSpace);
|
||||||
|
if (smask) {
|
||||||
|
pdfDict.insertRef("SMask", *smask);
|
||||||
|
}
|
||||||
|
pdfDict.insertInt("BitsPerComponent", 8);
|
||||||
|
pdfDict.insertName("Filter", "FlateDecode");
|
||||||
|
pdfDict.insertInt("Length", length);
|
||||||
|
pdfDict.emitObject(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void image_to_pdf_pixels(const SkImage* image, SkWStream* out) {
|
static SkPDFIndirectReference do_deflated_alpha(const SkPixmap& pm, SkPDFDocument* doc,
|
||||||
if (kAlpha_8_SkColorType == image->colorType()) {
|
SkPDFIndirectReference ref) {
|
||||||
fill_stream(out, '\x00', pixel_count(image));
|
SkDynamicMemoryWStream buffer;
|
||||||
} else if (kGray_8_SkColorType == image->colorType()) {
|
SkDeflateWStream deflateWStream(&buffer);
|
||||||
SkBitmap gray;
|
if (kAlpha_8_SkColorType == pm.colorType()) {
|
||||||
gray.allocPixels(SkImageInfo::Make(image->width(), image->height(),
|
SkASSERT(pm.rowBytes() == (size_t)pm.width());
|
||||||
kGray_8_SkColorType,
|
buffer.write(pm.addr8(), pm.width() * pm.height());
|
||||||
kPremul_SkAlphaType));
|
|
||||||
if (image->readPixels(gray.pixmap(), 0, 0)) {
|
|
||||||
out->write(gray.getPixels(), gray.computeByteSize());
|
|
||||||
} else {
|
|
||||||
fill_stream(out, '\x00', pixel_count(image));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
SkBitmap bgra;
|
SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType);
|
||||||
// TODO: makeColorSpace(sRGB) or actually tag the images
|
SkASSERT(pm.colorType() == kBGRA_8888_SkColorType);
|
||||||
bgra.allocPixels(SkImageInfo::Make(image->width(), image->height(),
|
SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4);
|
||||||
kBGRA_8888_SkColorType,
|
const uint32_t* ptr = pm.addr32();
|
||||||
kUnpremul_SkAlphaType));
|
const uint32_t* stop = ptr + pm.height() * pm.width();
|
||||||
if (image->readPixels(bgra.pixmap(), 0, 0)) {
|
|
||||||
SkAutoTMalloc<uint8_t> scanline(3 * bgra.width());
|
uint8_t byteBuffer[4092];
|
||||||
for (int y = 0; y < bgra.height(); ++y) {
|
uint8_t* bufferStop = byteBuffer + SK_ARRAY_COUNT(byteBuffer);
|
||||||
const SkColor* src = bgra.getAddr32(0, y);
|
uint8_t* dst = byteBuffer;
|
||||||
uint8_t* dst = scanline.get();
|
while (ptr != stop) {
|
||||||
for (int x = 0; x < bgra.width(); ++x) {
|
*dst++ = 0xFF & ((*ptr++) >> SK_BGRA_A32_SHIFT);
|
||||||
|
if (dst == bufferStop) {
|
||||||
|
deflateWStream.write(byteBuffer, sizeof(byteBuffer));
|
||||||
|
dst = byteBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deflateWStream.write(byteBuffer, dst - byteBuffer);
|
||||||
|
}
|
||||||
|
deflateWStream.finalize();
|
||||||
|
SkWStream* stream = doc->beginObject(ref);
|
||||||
|
emit_dict(stream, pm.info().dimensions(), "DeviceGray", nullptr, buffer.bytesWritten());
|
||||||
|
emit_stream(&buffer, stream);
|
||||||
|
doc->endObject();
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SkPDFIndirectReference do_deflated_image(const SkPixmap& pm,
|
||||||
|
SkPDFDocument* doc,
|
||||||
|
bool isOpaque) {
|
||||||
|
SkPDFIndirectReference sMask;
|
||||||
|
if (!isOpaque) {
|
||||||
|
sMask = doc->reserve();
|
||||||
|
}
|
||||||
|
SkDynamicMemoryWStream buffer;
|
||||||
|
SkDeflateWStream deflateWStream(&buffer);
|
||||||
|
const char* colorSpace = "DeviceGray";
|
||||||
|
switch (pm.colorType()) {
|
||||||
|
case kAlpha_8_SkColorType:
|
||||||
|
fill_stream(&deflateWStream, '\x00', pm.width() * pm.height());
|
||||||
|
break;
|
||||||
|
case kGray_8_SkColorType:
|
||||||
|
SkASSERT(sMask.fValue = -1);
|
||||||
|
SkASSERT(pm.rowBytes() == (size_t)pm.width());
|
||||||
|
deflateWStream.write(pm.addr8(), pm.width() * pm.height());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
colorSpace = "DeviceRGB";
|
||||||
|
SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType);
|
||||||
|
SkASSERT(pm.colorType() == kBGRA_8888_SkColorType);
|
||||||
|
SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4);
|
||||||
|
uint8_t byteBuffer[3072];
|
||||||
|
static_assert(SK_ARRAY_COUNT(byteBuffer) % 3 == 0, "");
|
||||||
|
uint8_t* bufferStop = byteBuffer + SK_ARRAY_COUNT(byteBuffer);
|
||||||
|
uint8_t* dst = byteBuffer;
|
||||||
|
for (int y = 0; y < pm.height(); ++y) {
|
||||||
|
const SkColor* src = pm.addr32(0, y);
|
||||||
|
for (int x = 0; x < pm.width(); ++x) {
|
||||||
SkColor color = *src++;
|
SkColor color = *src++;
|
||||||
if (SkColorGetA(color) == SK_AlphaTRANSPARENT) {
|
if (SkColorGetA(color) == SK_AlphaTRANSPARENT) {
|
||||||
color = get_neighbor_avg_color(bgra, x, y);
|
color = get_neighbor_avg_color(pm, x, y);
|
||||||
}
|
}
|
||||||
*dst++ = SkColorGetR(color);
|
*dst++ = SkColorGetR(color);
|
||||||
*dst++ = SkColorGetG(color);
|
*dst++ = SkColorGetG(color);
|
||||||
*dst++ = SkColorGetB(color);
|
*dst++ = SkColorGetB(color);
|
||||||
|
if (dst == bufferStop) {
|
||||||
|
deflateWStream.write(byteBuffer, sizeof(byteBuffer));
|
||||||
|
dst = byteBuffer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
out->write(scanline.get(), 3 * bgra.width());
|
|
||||||
}
|
}
|
||||||
} else {
|
deflateWStream.write(byteBuffer, dst - byteBuffer);
|
||||||
fill_stream(out, '\x00', 3 * pixel_count(image));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
deflateWStream.finalize();
|
||||||
|
SkPDFIndirectReference ref = doc->reserve();
|
||||||
|
SkWStream* stream = doc->beginObject(ref);
|
||||||
|
emit_dict(stream, pm.info().dimensions(), colorSpace,
|
||||||
|
sMask.fValue != -1 ? &sMask : nullptr,
|
||||||
|
buffer.bytesWritten());
|
||||||
|
emit_stream(&buffer, stream);
|
||||||
|
doc->endObject();
|
||||||
|
if (!isOpaque) {
|
||||||
|
do_deflated_alpha(pm, doc, sMask);
|
||||||
|
}
|
||||||
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
static bool do_jpeg(const SkData& data, SkPDFDocument* doc, SkISize size,
|
||||||
|
SkPDFIndirectReference* result) {
|
||||||
static void image_alpha_to_a8(const SkImage* image, SkWStream* out) {
|
SkISize jpegSize;
|
||||||
SkBitmap alpha;
|
SkEncodedInfo::Color jpegColorType;
|
||||||
alpha.allocPixels(SkImageInfo::MakeA8(image->width(), image->height()));
|
SkEncodedOrigin exifOrientation;
|
||||||
if (image->readPixels(alpha.pixmap(), 0, 0)) {
|
if (!SkGetJpegInfo(data.data(), data.size(), &jpegSize,
|
||||||
out->write(alpha.getPixels(), alpha.computeByteSize());
|
&jpegColorType, &exifOrientation)) {
|
||||||
} else {
|
return false;
|
||||||
fill_stream(out, '\xFF', pixel_count(image));
|
|
||||||
}
|
}
|
||||||
}
|
bool yuv = jpegColorType == SkEncodedInfo::kYUV_Color;
|
||||||
|
bool goodColorType = yuv || jpegColorType == SkEncodedInfo::kGray_Color;
|
||||||
static void emit_image_xobject(SkWStream* stream,
|
if (jpegSize != size // Sanity check.
|
||||||
const SkImage* image,
|
|| !goodColorType
|
||||||
bool alpha,
|
|| kTopLeft_SkEncodedOrigin != exifOrientation) {
|
||||||
const sk_sp<SkPDFObject>& smask) {
|
return false;
|
||||||
// Write to a temporary buffer to get the compressed length.
|
|
||||||
SkDynamicMemoryWStream buffer;
|
|
||||||
SkDeflateWStream deflateWStream(&buffer);
|
|
||||||
if (alpha) {
|
|
||||||
image_alpha_to_a8(image, &deflateWStream);
|
|
||||||
} else {
|
|
||||||
image_to_pdf_pixels(image, &deflateWStream);
|
|
||||||
}
|
}
|
||||||
deflateWStream.finalize(); // call before buffer.bytesWritten().
|
#ifdef SK_PDF_IMAGE_STATS
|
||||||
|
gJpegImageObjects.fetch_add(1);
|
||||||
|
#endif
|
||||||
|
SkPDFIndirectReference ref = doc->reserve();
|
||||||
|
*result = ref;
|
||||||
|
SkWStream* stream = doc->beginObject(ref);
|
||||||
|
|
||||||
SkPDFDict pdfDict("XObject");
|
SkPDFDict pdfDict("XObject");
|
||||||
pdfDict.insertName("Subtype", "Image");
|
pdfDict.insertName("Subtype", "Image");
|
||||||
pdfDict.insertInt("Width", image->width());
|
pdfDict.insertInt("Width", jpegSize.width());
|
||||||
pdfDict.insertInt("Height", image->height());
|
pdfDict.insertInt("Height", jpegSize.height());
|
||||||
if (alpha) {
|
if (yuv) {
|
||||||
pdfDict.insertName("ColorSpace", "DeviceGray");
|
|
||||||
} else if (1 == pdf_color_component_count(image->colorType())) {
|
|
||||||
pdfDict.insertName("ColorSpace", "DeviceGray");
|
|
||||||
} else {
|
|
||||||
pdfDict.insertName("ColorSpace", "DeviceRGB");
|
|
||||||
}
|
|
||||||
if (smask) {
|
|
||||||
pdfDict.insertObjRef("SMask", smask);
|
|
||||||
}
|
|
||||||
pdfDict.insertInt("BitsPerComponent", 8);
|
|
||||||
pdfDict.insertName("Filter", "FlateDecode");
|
|
||||||
pdfDict.insertInt("Length", buffer.bytesWritten());
|
|
||||||
pdfDict.emitObject(stream);
|
|
||||||
|
|
||||||
stream->writeText(kStreamBegin);
|
|
||||||
buffer.writeToAndReset(stream);
|
|
||||||
stream->writeText(kStreamEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
// This SkPDFObject only outputs the alpha layer of the given bitmap.
|
|
||||||
class PDFAlphaBitmap final : public SkPDFObject {
|
|
||||||
public:
|
|
||||||
PDFAlphaBitmap(sk_sp<SkImage> image) : fImage(std::move(image)) { SkASSERT(fImage); }
|
|
||||||
void emitObject(SkWStream* stream) const override {
|
|
||||||
SkASSERT(fImage);
|
|
||||||
emit_image_xobject(stream, fImage.get(), true, nullptr);
|
|
||||||
}
|
|
||||||
void drop() override { fImage = nullptr; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
sk_sp<SkImage> fImage;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
class PDFDefaultBitmap final : public SkPDFObject {
|
|
||||||
public:
|
|
||||||
void emitObject(SkWStream* stream) const override {
|
|
||||||
SkASSERT(fImage);
|
|
||||||
emit_image_xobject(stream, fImage.get(), false, fSMask);
|
|
||||||
}
|
|
||||||
void addResources(SkPDFObjNumMap* catalog) const override {
|
|
||||||
catalog->addObjectRecursively(fSMask.get());
|
|
||||||
}
|
|
||||||
void drop() override { fImage = nullptr; fSMask = nullptr; }
|
|
||||||
PDFDefaultBitmap(sk_sp<SkImage> image, sk_sp<SkPDFObject> smask)
|
|
||||||
: fImage(std::move(image)), fSMask(std::move(smask)) { SkASSERT(fImage); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
sk_sp<SkImage> fImage;
|
|
||||||
sk_sp<SkPDFObject> fSMask;
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
/**
|
|
||||||
* This PDFObject assumes that its constructor was handed YUV or
|
|
||||||
* Grayscale JFIF Jpeg-encoded data that can be directly embedded
|
|
||||||
* into a PDF.
|
|
||||||
*/
|
|
||||||
class PDFJpegBitmap final : public SkPDFObject {
|
|
||||||
public:
|
|
||||||
SkISize fSize;
|
|
||||||
sk_sp<SkData> fData;
|
|
||||||
bool fIsYUV;
|
|
||||||
PDFJpegBitmap(SkISize size, sk_sp<SkData> data, bool isYUV)
|
|
||||||
: fSize(size), fData(std::move(data)), fIsYUV(isYUV) { SkASSERT(fData); }
|
|
||||||
void emitObject(SkWStream*) const override;
|
|
||||||
void drop() override { fData = nullptr; }
|
|
||||||
};
|
|
||||||
|
|
||||||
void PDFJpegBitmap::emitObject(SkWStream* stream) const {
|
|
||||||
SkASSERT(fData);
|
|
||||||
SkPDFDict pdfDict("XObject");
|
|
||||||
pdfDict.insertName("Subtype", "Image");
|
|
||||||
pdfDict.insertInt("Width", fSize.width());
|
|
||||||
pdfDict.insertInt("Height", fSize.height());
|
|
||||||
if (fIsYUV) {
|
|
||||||
pdfDict.insertName("ColorSpace", "DeviceRGB");
|
pdfDict.insertName("ColorSpace", "DeviceRGB");
|
||||||
} else {
|
} else {
|
||||||
pdfDict.insertName("ColorSpace", "DeviceGray");
|
pdfDict.insertName("ColorSpace", "DeviceGray");
|
||||||
@ -254,60 +215,58 @@ void PDFJpegBitmap::emitObject(SkWStream* stream) const {
|
|||||||
pdfDict.insertInt("BitsPerComponent", 8);
|
pdfDict.insertInt("BitsPerComponent", 8);
|
||||||
pdfDict.insertName("Filter", "DCTDecode");
|
pdfDict.insertName("Filter", "DCTDecode");
|
||||||
pdfDict.insertInt("ColorTransform", 0);
|
pdfDict.insertInt("ColorTransform", 0);
|
||||||
pdfDict.insertInt("Length", SkToInt(fData->size()));
|
pdfDict.insertInt("Length", SkToInt(data.size()));
|
||||||
pdfDict.emitObject(stream);
|
pdfDict.emitObject(stream);
|
||||||
stream->writeText(kStreamBegin);
|
stream->writeText(" stream\n");
|
||||||
stream->write(fData->data(), fData->size());
|
stream->write(data.data(), data.size());
|
||||||
stream->writeText(kStreamEnd);
|
stream->writeText("\nendstream");
|
||||||
|
doc->endObject();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
} // namespace
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
static SkBitmap to_pixels(const SkImage* image) {
|
||||||
sk_sp<PDFJpegBitmap> make_jpeg_bitmap(sk_sp<SkData> data, SkISize size) {
|
SkBitmap bm;
|
||||||
SkISize jpegSize;
|
int w = image->width(),
|
||||||
SkEncodedInfo::Color jpegColorType;
|
h = image->height();
|
||||||
SkEncodedOrigin exifOrientation;
|
switch (image->colorType()) {
|
||||||
if (data && SkGetJpegInfo(data->data(), data->size(), &jpegSize,
|
case kAlpha_8_SkColorType:
|
||||||
&jpegColorType, &exifOrientation)) {
|
bm.allocPixels(SkImageInfo::MakeA8(w, h));
|
||||||
bool yuv = jpegColorType == SkEncodedInfo::kYUV_Color;
|
break;
|
||||||
bool goodColorType = yuv || jpegColorType == SkEncodedInfo::kGray_Color;
|
case kGray_8_SkColorType:
|
||||||
if (jpegSize == size // Sanity check.
|
bm.allocPixels(SkImageInfo::Make(w, h, kGray_8_SkColorType, kOpaque_SkAlphaType));
|
||||||
&& goodColorType
|
break;
|
||||||
&& kTopLeft_SkEncodedOrigin == exifOrientation) {
|
default: {
|
||||||
// hold on to data, not image.
|
// TODO: makeColorSpace(sRGB) or actually tag the images
|
||||||
#ifdef SK_PDF_IMAGE_STATS
|
SkAlphaType at = bm.isOpaque() ? kOpaque_SkAlphaType : kUnpremul_SkAlphaType;
|
||||||
gJpegImageObjects.fetch_add(1);
|
bm.allocPixels(SkImageInfo::Make(w, h, kBGRA_8888_SkColorType, at));
|
||||||
#endif
|
|
||||||
return sk_make_sp<PDFJpegBitmap>(jpegSize, std::move(data), yuv);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
if (!image->readPixels(bm.pixmap(), 0, 0)) {
|
||||||
|
bm.eraseColor(SkColorSetARGB(0xFF, 0, 0, 0));
|
||||||
|
}
|
||||||
|
return bm;
|
||||||
}
|
}
|
||||||
|
|
||||||
sk_sp<SkPDFObject> SkPDFCreateBitmapObject(sk_sp<SkImage> image, int encodingQuality) {
|
SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img,
|
||||||
SkASSERT(image);
|
SkPDFDocument* doc,
|
||||||
|
int encodingQuality) {
|
||||||
|
SkPDFIndirectReference result;
|
||||||
|
SkASSERT(img);
|
||||||
|
SkASSERT(doc);
|
||||||
SkASSERT(encodingQuality >= 0);
|
SkASSERT(encodingQuality >= 0);
|
||||||
SkISize dimensions = image->dimensions();
|
SkISize dimensions = img->dimensions();
|
||||||
sk_sp<SkData> data = image->refEncodedData();
|
sk_sp<SkData> data = img->refEncodedData();
|
||||||
if (auto jpeg = make_jpeg_bitmap(std::move(data), dimensions)) {
|
if (data && do_jpeg(*data, doc, dimensions, &result)) {
|
||||||
return std::move(jpeg);
|
return result;
|
||||||
}
|
}
|
||||||
|
SkBitmap bm = to_pixels(img);
|
||||||
const bool isOpaque = image_compute_is_opaque(image.get());
|
SkPixmap pm = bm.pixmap();
|
||||||
|
bool isOpaque = pm.isOpaque() || pm.computeIsOpaque();
|
||||||
if (encodingQuality <= 100 && isOpaque) {
|
if (encodingQuality <= 100 && isOpaque) {
|
||||||
data = image->encodeToData(SkEncodedImageFormat::kJPEG, encodingQuality);
|
sk_sp<SkData> data = img->encodeToData(SkEncodedImageFormat::kJPEG, encodingQuality);
|
||||||
if (auto jpeg = make_jpeg_bitmap(std::move(data), dimensions)) {
|
if (data && do_jpeg(*data, doc, dimensions, &result)) {
|
||||||
return std::move(jpeg);
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return do_deflated_image(pm, doc, isOpaque);
|
||||||
sk_sp<SkPDFObject> smask;
|
|
||||||
if (!isOpaque) {
|
|
||||||
smask = sk_make_sp<PDFAlphaBitmap>(image);
|
|
||||||
}
|
|
||||||
#ifdef SK_PDF_IMAGE_STATS
|
|
||||||
gRegularImageObjects.fetch_add(1);
|
|
||||||
#endif
|
|
||||||
return sk_make_sp<PDFDefaultBitmap>(std::move(image), std::move(smask));
|
|
||||||
}
|
}
|
||||||
|
@ -7,18 +7,16 @@
|
|||||||
#ifndef SkPDFBitmap_DEFINED
|
#ifndef SkPDFBitmap_DEFINED
|
||||||
#define SkPDFBitmap_DEFINED
|
#define SkPDFBitmap_DEFINED
|
||||||
|
|
||||||
#include "SkRefCnt.h"
|
|
||||||
|
|
||||||
class SkImage;
|
class SkImage;
|
||||||
class SkPDFObject;
|
class SkPDFDocument;
|
||||||
|
struct SkPDFIndirectReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SkPDFBitmap wraps a SkImage and serializes it as an image Xobject.
|
* Serialize a SkImage as an Image Xobject.
|
||||||
* It is designed to use a minimal amout of memory, aside from refing
|
|
||||||
* the image, and its emitObject() does not cache any data.
|
|
||||||
*
|
|
||||||
* quality > 100 means lossless
|
* quality > 100 means lossless
|
||||||
*/
|
*/
|
||||||
sk_sp<SkPDFObject> SkPDFCreateBitmapObject(sk_sp<SkImage>, int encodingQuality = 101);
|
SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img,
|
||||||
|
SkPDFDocument* doc,
|
||||||
|
int encodingQuality = 101);
|
||||||
|
|
||||||
#endif // SkPDFBitmap_DEFINED
|
#endif // SkPDFBitmap_DEFINED
|
||||||
|
@ -1801,6 +1801,15 @@ static bool is_integral(const SkRect& r) {
|
|||||||
is_integer(r.bottom());
|
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,
|
void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset,
|
||||||
const SkRect* src,
|
const SkRect* src,
|
||||||
const SkRect& dst,
|
const SkRect& dst,
|
||||||
@ -2028,12 +2037,10 @@ void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset,
|
|||||||
sk_sp<SkPDFObject> pdfimage = pdfimagePtr ? *pdfimagePtr : nullptr;
|
sk_sp<SkPDFObject> pdfimage = pdfimagePtr ? *pdfimagePtr : nullptr;
|
||||||
if (!pdfimage) {
|
if (!pdfimage) {
|
||||||
SkASSERT(imageSubset);
|
SkASSERT(imageSubset);
|
||||||
pdfimage = SkPDFCreateBitmapObject(imageSubset.release(),
|
auto ref = SkPDFSerializeImage(imageSubset.image().get(), fDocument,
|
||||||
fDocument->metadata().fEncodingQuality);
|
fDocument->metadata().fEncodingQuality);
|
||||||
if (!pdfimage) {
|
SkASSERT(ref.fValue > 0);
|
||||||
return;
|
pdfimage = sk_make_sp<PDFObj>(ref);
|
||||||
}
|
|
||||||
fDocument->serialize(pdfimage); // serialize images early.
|
|
||||||
SkASSERT((key != SkBitmapKey{{0, 0, 0, 0}, 0}));
|
SkASSERT((key != SkBitmapKey{{0, 0, 0, 0}, 0}));
|
||||||
fDocument->canon()->fPDFBitmapMap.set(key, pdfimage);
|
fDocument->canon()->fPDFBitmapMap.set(key, pdfimage);
|
||||||
}
|
}
|
||||||
|
@ -26,19 +26,15 @@ const char* SkPDFGetNodeIdKey() {
|
|||||||
|
|
||||||
void SkPDFOffsetMap::set(SkPDFIndirectReference iRef, SkPDFFileOffset offset) {
|
void SkPDFOffsetMap::set(SkPDFIndirectReference iRef, SkPDFFileOffset offset) {
|
||||||
SkASSERT(iRef.fValue > 0);
|
SkASSERT(iRef.fValue > 0);
|
||||||
SkASSERT(SkToSizeT(iRef.fValue - 1) == fOffsets.size());
|
size_t index = SkToSizeT(iRef.fValue - 1);
|
||||||
fOffsets.push_back(offset);
|
if (index == fOffsets.size()) {
|
||||||
// The following logic can be used in a later CL when we serialize objects
|
fOffsets.push_back(offset);
|
||||||
// out of order:
|
} else if (index < fOffsets.size()) {
|
||||||
//size_t index = SkToSizeT(iRef.fValue - 1);
|
fOffsets[index] = offset;
|
||||||
//if (index == fOffsets.size()) {
|
} else {
|
||||||
// fOffsets.push_back(offset);
|
fOffsets.resize(index + 1);
|
||||||
//} else if (index < fOffsets.size()) {
|
fOffsets[index] = offset;
|
||||||
// fOffsets[index] = offset;
|
}
|
||||||
//} else {
|
|
||||||
// fOffsets.resize(index + 1);
|
|
||||||
// fOffsets[index] = offset;
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SkPDFFileOffset SkPDFOffsetMap::get(SkPDFIndirectReference r) {
|
SkPDFFileOffset SkPDFOffsetMap::get(SkPDFIndirectReference r) {
|
||||||
@ -76,6 +72,18 @@ void SkPDFObjectSerializer::serializeHeader(SkWStream* wStream,
|
|||||||
}
|
}
|
||||||
#undef SKPDF_MAGIC
|
#undef SKPDF_MAGIC
|
||||||
|
|
||||||
|
SkWStream* SkPDFObjectSerializer::beginObject(SkPDFIndirectReference ref, SkWStream* wStream) {
|
||||||
|
SkASSERT(ref.fValue > 0);
|
||||||
|
fOffsets.set(ref, this->offset(wStream));
|
||||||
|
wStream->writeDecAsText(ref.fValue);
|
||||||
|
wStream->writeText(" 0 obj\n"); // Generation number is always 0.
|
||||||
|
return wStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkPDFObjectSerializer::endObject(SkWStream* wStream) {
|
||||||
|
wStream->writeText("\nendobj\n");
|
||||||
|
}
|
||||||
|
|
||||||
void SkPDFObjectSerializer::serializeObject(const sk_sp<SkPDFObject>& object,
|
void SkPDFObjectSerializer::serializeObject(const sk_sp<SkPDFObject>& object,
|
||||||
SkWStream* wStream) {
|
SkWStream* wStream) {
|
||||||
SkPDFObjNumMap objNumMap;
|
SkPDFObjNumMap objNumMap;
|
||||||
@ -83,16 +91,15 @@ void SkPDFObjectSerializer::serializeObject(const sk_sp<SkPDFObject>& object,
|
|||||||
objNumMap.addObjectRecursively(object.get());
|
objNumMap.addObjectRecursively(object.get());
|
||||||
|
|
||||||
for (const sk_sp<SkPDFObject>& object : objNumMap.fObjects) {
|
for (const sk_sp<SkPDFObject>& object : objNumMap.fObjects) {
|
||||||
fOffsets.set(object->fIndirectReference, this->offset(wStream));
|
this->beginObject(object->fIndirectReference, wStream);
|
||||||
wStream->writeDecAsText(object->fIndirectReference.fValue);
|
|
||||||
wStream->writeText(" 0 obj\n"); // Generation number is always 0.
|
|
||||||
object->emitObject(wStream);
|
object->emitObject(wStream);
|
||||||
wStream->writeText("\nendobj\n");
|
this->endObject(wStream);
|
||||||
object->drop();
|
object->drop();
|
||||||
}
|
}
|
||||||
fNextObjectNumber = objNumMap.fNextObjectNumber; // save for later.
|
fNextObjectNumber = objNumMap.fNextObjectNumber; // save for later.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Xref table and footer
|
// Xref table and footer
|
||||||
void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream,
|
void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream,
|
||||||
const sk_sp<SkPDFObject> docCatalog,
|
const sk_sp<SkPDFObject> docCatalog,
|
||||||
@ -105,6 +112,7 @@ void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream,
|
|||||||
wStream->writeDecAsText(objCount);
|
wStream->writeDecAsText(objCount);
|
||||||
wStream->writeText("\n0000000000 65535 f \n");
|
wStream->writeText("\n0000000000 65535 f \n");
|
||||||
for (int i = 1; i < objCount; ++i) {
|
for (int i = 1; i < objCount; ++i) {
|
||||||
|
SkASSERT(fOffsets.get(SkPDFIndirectReference{i}).fValue > 0);
|
||||||
wStream->writeBigDecAsText(fOffsets.get(SkPDFIndirectReference{i}).fValue, 10);
|
wStream->writeBigDecAsText(fOffsets.get(SkPDFIndirectReference{i}).fValue, 10);
|
||||||
wStream->writeText(" 00000 n \n");
|
wStream->writeText(" 00000 n \n");
|
||||||
}
|
}
|
||||||
@ -219,6 +227,19 @@ void SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) {
|
|||||||
fObjectSerializer.serializeObject(object, this->getStream());
|
fObjectSerializer.serializeObject(object, this->getStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SkPDFIndirectReference SkPDFDocument::reserve() {
|
||||||
|
++fOutstandingRefs;
|
||||||
|
return SkPDFIndirectReference{fObjectSerializer.fNextObjectNumber++};
|
||||||
|
};
|
||||||
|
|
||||||
|
SkWStream* SkPDFDocument::beginObject(SkPDFIndirectReference ref) {
|
||||||
|
--fOutstandingRefs;
|
||||||
|
return fObjectSerializer.beginObject(ref, this->getStream());
|
||||||
|
};
|
||||||
|
void SkPDFDocument::endObject() {
|
||||||
|
fObjectSerializer.endObject(this->getStream());
|
||||||
|
};
|
||||||
|
|
||||||
static SkSize operator*(SkISize u, SkScalar s) { return SkSize{u.width() * s, u.height() * s}; }
|
static SkSize operator*(SkISize u, SkScalar s) { return SkSize{u.width() * s, u.height() * s}; }
|
||||||
static SkSize operator*(SkSize u, SkScalar s) { return SkSize{u.width() * s, u.height() * s}; }
|
static SkSize operator*(SkSize u, SkScalar s) { return SkSize{u.width() * s, u.height() * s}; }
|
||||||
|
|
||||||
@ -554,10 +575,11 @@ void SkPDFDocument::onClose(SkWStream* stream) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build font subsetting info before calling addObjectRecursively().
|
// Build font subsetting info before serializing the catalog and all of
|
||||||
SkPDFCanon* canon = &fCanon;
|
// its transitive dependecies.
|
||||||
fFonts.foreach([canon](SkPDFFont* p){ p->getFontSubset(canon); });
|
fFonts.foreach([this](SkPDFFont* p){ p->getFontSubset(this); });
|
||||||
fObjectSerializer.serializeObject(docCatalog, this->getStream());
|
fObjectSerializer.serializeObject(docCatalog, this->getStream());
|
||||||
|
SkASSERT(fOutstandingRefs == 0);
|
||||||
fObjectSerializer.serializeFooter(this->getStream(), docCatalog, fID);
|
fObjectSerializer.serializeFooter(this->getStream(), docCatalog, fID);
|
||||||
this->reset();
|
this->reset();
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,8 @@ struct SkPDFObjectSerializer {
|
|||||||
SkPDFObjectSerializer(const SkPDFObjectSerializer&) = delete;
|
SkPDFObjectSerializer(const SkPDFObjectSerializer&) = delete;
|
||||||
SkPDFObjectSerializer& operator=(const SkPDFObjectSerializer&) = delete;
|
SkPDFObjectSerializer& operator=(const SkPDFObjectSerializer&) = delete;
|
||||||
|
|
||||||
|
SkWStream* beginObject(SkPDFIndirectReference, SkWStream*);
|
||||||
|
void endObject(SkWStream*);
|
||||||
void serializeHeader(SkWStream*, const SkPDF::Metadata&);
|
void serializeHeader(SkWStream*, const SkPDF::Metadata&);
|
||||||
void serializeObject(const sk_sp<SkPDFObject>&, SkWStream*);
|
void serializeObject(const sk_sp<SkPDFObject>&, SkWStream*);
|
||||||
void serializeFooter(SkWStream*, const sk_sp<SkPDFObject>, sk_sp<SkPDFObject>);
|
void serializeFooter(SkWStream*, const sk_sp<SkPDFObject>, sk_sp<SkPDFObject>);
|
||||||
@ -79,6 +81,10 @@ public:
|
|||||||
// Returns -1 if no mark ID.
|
// Returns -1 if no mark ID.
|
||||||
int getMarkIdForNodeId(int nodeId);
|
int getMarkIdForNodeId(int nodeId);
|
||||||
|
|
||||||
|
SkPDFIndirectReference reserve();
|
||||||
|
SkWStream* beginObject(SkPDFIndirectReference);
|
||||||
|
void endObject();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
sk_sp<SkPDFTag> recursiveBuildTagTree(const SkPDF::StructureElementNode& node,
|
sk_sp<SkPDFTag> recursiveBuildTagTree(const SkPDF::StructureElementNode& node,
|
||||||
sk_sp<SkPDFTag> parent);
|
sk_sp<SkPDFTag> parent);
|
||||||
@ -105,6 +111,8 @@ private:
|
|||||||
// A mapping from node ID to tag for fast lookup.
|
// A mapping from node ID to tag for fast lookup.
|
||||||
SkTHashMap<int, sk_sp<SkPDFTag>> fNodeIdToTag;
|
SkTHashMap<int, sk_sp<SkPDFTag>> fNodeIdToTag;
|
||||||
|
|
||||||
|
int fOutstandingRefs = 0;
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "SkPDFCanon.h"
|
#include "SkPDFCanon.h"
|
||||||
#include "SkPDFConvertType1FontStream.h"
|
#include "SkPDFConvertType1FontStream.h"
|
||||||
#include "SkPDFDevice.h"
|
#include "SkPDFDevice.h"
|
||||||
|
#include "SkPDFDocumentPriv.h"
|
||||||
#include "SkPDFMakeCIDGlyphWidthsArray.h"
|
#include "SkPDFMakeCIDGlyphWidthsArray.h"
|
||||||
#include "SkPDFMakeToUnicodeCmap.h"
|
#include "SkPDFMakeToUnicodeCmap.h"
|
||||||
#include "SkPDFResourceDict.h"
|
#include "SkPDFResourceDict.h"
|
||||||
@ -55,7 +56,7 @@ static const int32_t kPdfSymbolic = 4;
|
|||||||
struct SkPDFType0Font final : public SkPDFFont {
|
struct SkPDFType0Font final : public SkPDFFont {
|
||||||
SkPDFType0Font(SkPDFFont::Info, const SkAdvancedTypefaceMetrics&);
|
SkPDFType0Font(SkPDFFont::Info, const SkAdvancedTypefaceMetrics&);
|
||||||
~SkPDFType0Font() override;
|
~SkPDFType0Font() override;
|
||||||
void getFontSubset(SkPDFCanon*) override;
|
void getFontSubset(SkPDFDocument*) override;
|
||||||
#ifdef SK_DEBUG
|
#ifdef SK_DEBUG
|
||||||
void emitObject(SkWStream*) const override;
|
void emitObject(SkWStream*) const override;
|
||||||
bool fPopulated;
|
bool fPopulated;
|
||||||
@ -66,13 +67,13 @@ struct SkPDFType0Font final : public SkPDFFont {
|
|||||||
struct SkPDFType1Font final : public SkPDFFont {
|
struct SkPDFType1Font final : public SkPDFFont {
|
||||||
SkPDFType1Font(SkPDFFont::Info, const SkAdvancedTypefaceMetrics&, SkPDFCanon*);
|
SkPDFType1Font(SkPDFFont::Info, const SkAdvancedTypefaceMetrics&, SkPDFCanon*);
|
||||||
~SkPDFType1Font() override {}
|
~SkPDFType1Font() override {}
|
||||||
void getFontSubset(SkPDFCanon*) override {} // TODO(halcanary): implement
|
void getFontSubset(SkPDFDocument*) override {} // TODO(halcanary): implement
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SkPDFType3Font final : public SkPDFFont {
|
struct SkPDFType3Font final : public SkPDFFont {
|
||||||
SkPDFType3Font(SkPDFFont::Info, const SkAdvancedTypefaceMetrics&);
|
SkPDFType3Font(SkPDFFont::Info, const SkAdvancedTypefaceMetrics&);
|
||||||
~SkPDFType3Font() override {}
|
~SkPDFType3Font() override {}
|
||||||
void getFontSubset(SkPDFCanon*) override;
|
void getFontSubset(SkPDFDocument*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@ -343,7 +344,8 @@ static sk_sp<SkData> stream_to_data(std::unique_ptr<SkStreamAsset> stream) {
|
|||||||
}
|
}
|
||||||
#endif // SK_PDF_SUBSET_SUPPORTED
|
#endif // SK_PDF_SUBSET_SUPPORTED
|
||||||
|
|
||||||
void SkPDFType0Font::getFontSubset(SkPDFCanon* canon) {
|
void SkPDFType0Font::getFontSubset(SkPDFDocument* doc) {
|
||||||
|
SkPDFCanon* canon = doc->canon();
|
||||||
const SkAdvancedTypefaceMetrics* metricsPtr =
|
const SkAdvancedTypefaceMetrics* metricsPtr =
|
||||||
SkPDFFont::GetMetrics(this->typeface(), canon);
|
SkPDFFont::GetMetrics(this->typeface(), canon);
|
||||||
SkASSERT(metricsPtr);
|
SkASSERT(metricsPtr);
|
||||||
@ -644,12 +646,13 @@ static ImageAndOffset to_image(SkGlyphID gid, SkGlyphCache* cache) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_type3_font_info(SkPDFCanon* canon,
|
static void add_type3_font_info(SkPDFDocument* doc,
|
||||||
SkPDFDict* font,
|
SkPDFDict* font,
|
||||||
SkTypeface* typeface,
|
SkTypeface* typeface,
|
||||||
const SkPDFGlyphUse& subset,
|
const SkPDFGlyphUse& subset,
|
||||||
SkGlyphID firstGlyphID,
|
SkGlyphID firstGlyphID,
|
||||||
SkGlyphID lastGlyphID) {
|
SkGlyphID lastGlyphID) {
|
||||||
|
SkPDFCanon* canon = doc->canon();
|
||||||
const SkAdvancedTypefaceMetrics* metrics = SkPDFFont::GetMetrics(typeface, canon);
|
const SkAdvancedTypefaceMetrics* metrics = SkPDFFont::GetMetrics(typeface, canon);
|
||||||
SkASSERT(lastGlyphID >= firstGlyphID);
|
SkASSERT(lastGlyphID >= firstGlyphID);
|
||||||
// Remove unused glyphs at the end of the range.
|
// Remove unused glyphs at the end of the range.
|
||||||
@ -734,7 +737,7 @@ static void add_type3_font_info(SkPDFCanon* canon,
|
|||||||
content.writeText("/X Do\n");
|
content.writeText("/X Do\n");
|
||||||
auto proc = sk_make_sp<SkPDFStream>(content.detachAsStream());
|
auto proc = sk_make_sp<SkPDFStream>(content.detachAsStream());
|
||||||
auto d0 = sk_make_sp<SkPDFDict>();
|
auto d0 = sk_make_sp<SkPDFDict>();
|
||||||
d0->insertObjRef("X", SkPDFCreateBitmapObject(std::move(pimg.fImage)));
|
d0->insertRef("X", SkPDFSerializeImage(pimg.fImage.get(), doc));
|
||||||
auto d1 = sk_make_sp<SkPDFDict>();
|
auto d1 = sk_make_sp<SkPDFDict>();
|
||||||
d1->insertObject("XObject", std::move(d0));
|
d1->insertObject("XObject", std::move(d0));
|
||||||
proc->dict()->insertObject("Resources", std::move(d1));
|
proc->dict()->insertObject("Resources", std::move(d1));
|
||||||
@ -796,8 +799,8 @@ SkPDFType3Font::SkPDFType3Font(SkPDFFont::Info info,
|
|||||||
const SkAdvancedTypefaceMetrics& metrics)
|
const SkAdvancedTypefaceMetrics& metrics)
|
||||||
: SkPDFFont(std::move(info)) {}
|
: SkPDFFont(std::move(info)) {}
|
||||||
|
|
||||||
void SkPDFType3Font::getFontSubset(SkPDFCanon* canon) {
|
void SkPDFType3Font::getFontSubset(SkPDFDocument* doc) {
|
||||||
add_type3_font_info(canon, this, this->typeface(), this->glyphUsage(),
|
add_type3_font_info(doc, this, this->typeface(), this->glyphUsage(),
|
||||||
this->firstGlyphID(), this->lastGlyphID());
|
this->firstGlyphID(), this->lastGlyphID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ public:
|
|||||||
/** Subset the font based on current usage.
|
/** Subset the font based on current usage.
|
||||||
* Must be called before emitObject().
|
* Must be called before emitObject().
|
||||||
*/
|
*/
|
||||||
virtual void getFontSubset(SkPDFCanon*) = 0;
|
virtual void getFontSubset(SkPDFDocument*) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return false iff the typeface has its NotEmbeddable flag set.
|
* Return false iff the typeface has its NotEmbeddable flag set.
|
||||||
|
@ -202,6 +202,10 @@ void SkPDFUnion::emitObject(SkWStream* stream) const {
|
|||||||
case Type::kObject:
|
case Type::kObject:
|
||||||
fObject->emitObject(stream);
|
fObject->emitObject(stream);
|
||||||
return;
|
return;
|
||||||
|
case Type::kRef:
|
||||||
|
stream->writeDecAsText(fIntValue);
|
||||||
|
stream->writeText(" 0 R"); // Generation number is always 0.
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
SkDEBUGFAIL("SkPDFUnion::emitObject with bad type");
|
SkDEBUGFAIL("SkPDFUnion::emitObject with bad type");
|
||||||
}
|
}
|
||||||
@ -218,6 +222,7 @@ void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap) const {
|
|||||||
case Type::kString:
|
case Type::kString:
|
||||||
case Type::kNameSkS:
|
case Type::kNameSkS:
|
||||||
case Type::kStringSkS:
|
case Type::kStringSkS:
|
||||||
|
case Type::kRef:
|
||||||
return; // These have no resources.
|
return; // These have no resources.
|
||||||
case Type::kObjRef:
|
case Type::kObjRef:
|
||||||
objNumMap->addObjectRecursively(fObject);
|
objNumMap->addObjectRecursively(fObject);
|
||||||
@ -281,6 +286,10 @@ SkPDFUnion SkPDFUnion::Object(sk_sp<SkPDFObject> objSp) {
|
|||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SkPDFUnion SkPDFUnion::Ref(SkPDFIndirectReference ref) {
|
||||||
|
return SkASSERT(ref.fValue > 0), SkPDFUnion(Type::kRef, (int32_t)ref.fValue);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#if 0 // Enable if needed.
|
#if 0 // Enable if needed.
|
||||||
@ -372,6 +381,10 @@ void SkPDFArray::appendObjRef(sk_sp<SkPDFObject> objSp) {
|
|||||||
this->append(SkPDFUnion::ObjRef(std::move(objSp)));
|
this->append(SkPDFUnion::ObjRef(std::move(objSp)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkPDFArray::appendRef(SkPDFIndirectReference ref) {
|
||||||
|
this->append(SkPDFUnion::Ref(ref));
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
SkPDFDict::~SkPDFDict() { this->drop(); }
|
SkPDFDict::~SkPDFDict() { this->drop(); }
|
||||||
@ -420,6 +433,10 @@ void SkPDFDict::reserve(int n) {
|
|||||||
fRecords.reserve(n);
|
fRecords.reserve(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkPDFDict::insertRef(const char key[], SkPDFIndirectReference ref) {
|
||||||
|
fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::Ref(ref)});
|
||||||
|
}
|
||||||
|
|
||||||
void SkPDFDict::insertObjRef(const char key[], sk_sp<SkPDFObject> objSp) {
|
void SkPDFDict::insertObjRef(const char key[], sk_sp<SkPDFObject> objSp) {
|
||||||
fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::ObjRef(std::move(objSp))});
|
fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::ObjRef(std::move(objSp))});
|
||||||
}
|
}
|
||||||
@ -538,7 +555,7 @@ void SkPDFSharedStream::addResources(
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
SkPDFStream:: SkPDFStream(sk_sp<SkData> data) {
|
SkPDFStream::SkPDFStream(sk_sp<SkData> data) {
|
||||||
this->setData(skstd::make_unique<SkMemoryStream>(std::move(data)));
|
this->setData(skstd::make_unique<SkMemoryStream>(std::move(data)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +145,8 @@ public:
|
|||||||
static SkPDFUnion Object(sk_sp<SkPDFObject>);
|
static SkPDFUnion Object(sk_sp<SkPDFObject>);
|
||||||
static SkPDFUnion ObjRef(sk_sp<SkPDFObject>);
|
static SkPDFUnion ObjRef(sk_sp<SkPDFObject>);
|
||||||
|
|
||||||
|
static SkPDFUnion Ref(SkPDFIndirectReference);
|
||||||
|
|
||||||
/** These two non-virtual methods mirror SkPDFObject's
|
/** These two non-virtual methods mirror SkPDFObject's
|
||||||
corresponding virtuals. */
|
corresponding virtuals. */
|
||||||
void emitObject(SkWStream*) const;
|
void emitObject(SkWStream*) const;
|
||||||
@ -176,6 +178,7 @@ private:
|
|||||||
kStringSkS,
|
kStringSkS,
|
||||||
kObjRef,
|
kObjRef,
|
||||||
kObject,
|
kObject,
|
||||||
|
kRef,
|
||||||
};
|
};
|
||||||
Type fType;
|
Type fType;
|
||||||
|
|
||||||
@ -252,6 +255,7 @@ public:
|
|||||||
void appendString(const SkString&);
|
void appendString(const SkString&);
|
||||||
void appendObject(sk_sp<SkPDFObject>);
|
void appendObject(sk_sp<SkPDFObject>);
|
||||||
void appendObjRef(sk_sp<SkPDFObject>);
|
void appendObjRef(sk_sp<SkPDFObject>);
|
||||||
|
void appendRef(SkPDFIndirectReference);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<SkPDFUnion> fValues;
|
std::vector<SkPDFUnion> fValues;
|
||||||
@ -310,6 +314,7 @@ public:
|
|||||||
void insertObject(const SkString& key, sk_sp<SkPDFObject>);
|
void insertObject(const SkString& key, sk_sp<SkPDFObject>);
|
||||||
void insertObjRef(const char key[], sk_sp<SkPDFObject>);
|
void insertObjRef(const char key[], sk_sp<SkPDFObject>);
|
||||||
void insertObjRef(const SkString& key, sk_sp<SkPDFObject>);
|
void insertObjRef(const SkString& key, sk_sp<SkPDFObject>);
|
||||||
|
void insertRef(const char key[], SkPDFIndirectReference);
|
||||||
|
|
||||||
/** Add the value to the dictionary with the given key.
|
/** Add the value to the dictionary with the given key.
|
||||||
* @param key The text of the key for this dictionary entry.
|
* @param key The text of the key for this dictionary entry.
|
||||||
|
Loading…
Reference in New Issue
Block a user