Chromium Code Reviews| Index: printing/pdf_metafile_skia.cc |
| diff --git a/printing/pdf_metafile_skia.cc b/printing/pdf_metafile_skia.cc |
| index 36a42d9d135cc875e803c8f01a3c2b5b048d5e8c..30b39a244ef8ec41ee2092a42e0c7119f20092e8 100644 |
| --- a/printing/pdf_metafile_skia.cc |
| +++ b/printing/pdf_metafile_skia.cc |
| @@ -9,15 +9,17 @@ |
| #include "base/metrics/histogram.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/posix/eintr_wrapper.h" |
| +#include "skia/ext/platform_canvas.h" |
| #include "skia/ext/refptr.h" |
| -#include "skia/ext/vector_platform_device_skia.h" |
| -#include "third_party/skia/include/core/SkData.h" |
| +#include "skia/ext/vector_canvas.h" |
| +#include "third_party/skia/include/core/SkCanvas.h" |
| +#include "third_party/skia/include/core/SkDocument.h" |
| +#include "third_party/skia/include/core/SkPictureRecorder.h" |
| +#include "third_party/skia/include/core/SkRect.h" |
| #include "third_party/skia/include/core/SkRefCnt.h" |
| #include "third_party/skia/include/core/SkScalar.h" |
| +#include "third_party/skia/include/core/SkSize.h" |
| #include "third_party/skia/include/core/SkStream.h" |
| -#include "third_party/skia/include/core/SkTypeface.h" |
| -#include "third_party/skia/include/pdf/SkPDFDevice.h" |
| -#include "third_party/skia/include/pdf/SkPDFDocument.h" |
| #include "ui/gfx/point.h" |
| #include "ui/gfx/rect.h" |
| #include "ui/gfx/size.h" |
| @@ -30,115 +32,208 @@ |
| #include "base/file_descriptor_posix.h" |
| #endif |
| +class SkBaseDevice; |
| + |
| +namespace { |
| +// This struct represents all the data we need to draw and redraw this |
| +// page into a SkDocument. |
| +struct Page { |
| + Page(const SkSize& page_size, const SkRect& content_area); |
| + SkSize page_size_; |
| + SkRect content_area_; |
| + skia::RefPtr<SkPicture> content_; |
| +}; |
| + |
| +Page::Page(const SkSize& page_size, const SkRect& content_area) |
| + : page_size_(page_size), content_area_(content_area), content_(/*NULL*/) {} |
| + |
| +SkSize ToSkSize(const gfx::Size& size) { |
| + return SkSize::Make(SkIntToScalar(size.width()), |
| + SkIntToScalar(size.height())); |
| +} |
| + |
| +SkRect ToSkRect(const gfx::Rect& rect) { |
| + return SkRect::MakeLTRB(SkIntToScalar(rect.x()), |
| + SkIntToScalar(rect.y()), |
| + SkIntToScalar(rect.right()), |
| + SkIntToScalar(rect.bottom())); |
| +} |
| + |
| +gfx::Rect ToGfxRect(const SkRect& rect) { |
| + return gfx::Rect(SkScalarTruncToInt(rect.x()), |
| + SkScalarTruncToInt(rect.y()), |
| + SkScalarTruncToInt(rect.width()), |
| + SkScalarTruncToInt(rect.height())); |
| +} |
| + |
| +bool WriteAssetToBuffer(SkStreamAsset* asset, void* buffer, size_t size) { |
| + DCHECK(asset); |
| + DCHECK(asset->getPosition() == 0); |
| + size_t length = asset->getLength(); |
| + if (length > size) |
| + return false; |
| + bool success = (length == asset->read(buffer, length)); |
| + (void)asset->rewind(); |
| + return success; |
| +} |
| + |
| +} // namespace |
| + |
| namespace printing { |
| struct PdfMetafileSkiaData { |
| - skia::RefPtr<SkPDFDevice> current_page_; |
| - SkPDFDocument pdf_doc_; |
| - SkDynamicMemoryWStream pdf_stream_; |
| + scoped_ptr<SkPictureRecorder> recorder_; // Current recording |
| + |
| + std::vector<Page> pages_; |
| + skia::RefPtr<SkStreamAsset> pdf_data_; |
|
Vitaly Buka (NO REVIEWS)
2014/11/04 19:23:55
I guess order shouldbe be:
1. Methods
2. Members
hal.canary
2014/11/16 21:29:28
Acknowledged.
|
| + |
| + // True when the current page is outstanding (and still writable). |
| + bool PageOutstanding() const; |
| + |
| + // True if InitFromData() or FinishDocument has been called. |
| + bool HasData() const; |
| + |
| + size_t PageCount() const; |
| + |
| + // Return a const pointer to the page, or NULL on error. |
| + // The page must be finished (via FinishPage()). |
| + // The pointer is only valid while pages_ is unmodified. |
| + const Page* GetPage(size_t page_number) const; |
| + |
| #if defined(OS_MACOSX) |
| PdfMetafileCg pdf_cg_; |
| #endif |
| }; |
| +bool PdfMetafileSkiaData::PageOutstanding() const { |
| + return recorder_.get() != NULL; |
| +} |
| + |
| +bool PdfMetafileSkiaData::HasData() const { return (pdf_data_.get() != NULL); } |
| + |
| +size_t PdfMetafileSkiaData::PageCount() const { return pages_.size(); } |
| + |
| +const Page* PdfMetafileSkiaData::GetPage(size_t page_number) const { |
| + if (page_number >= PageCount()) |
| + return NULL; |
| + |
| + // Last page and page not finished recording |
| + if ((page_number == PageCount() - 1) && this->PageOutstanding()) |
| + return NULL; |
| + DCHECK(pages_[page_number].content_.get()); |
| + return &(pages_[page_number]); |
| +} |
| + |
| PdfMetafileSkia::~PdfMetafileSkia() {} |
| bool PdfMetafileSkia::Init() { |
| return true; |
| } |
| + |
| +// TODO(halcanary): Create a Metafile class that only stores data. |
| +// Metafile::InitFromData is orthogonal to what the rest of |
| +// PdfMetafileSkia does. |
| bool PdfMetafileSkia::InitFromData(const void* src_buffer, |
| uint32 src_buffer_size) { |
| - return data_->pdf_stream_.write(src_buffer, src_buffer_size); |
| + if (data_->HasData()) |
| + data_->pdf_data_.clear(); // free up RAM first. |
| + SkDynamicMemoryWStream dynamic_memory; |
| + if (!dynamic_memory.write(src_buffer, src_buffer_size)) |
| + return false; |
| + data_->pdf_data_ = skia::AdoptRef(dynamic_memory.detachAsStream()); |
| + return true; |
| +} |
| + |
| +skia::RefPtr<skia::VectorCanvas> PdfMetafileSkia::GetVectorCanvasForNewPage( |
| + const gfx::Size& page_size, const gfx::Rect& content_area, |
| + const float& scale_factor) { |
| + return this->StartPage(page_size, content_area, scale_factor) |
| + ? skia::SharePtr(data_->recorder_->getRecordingCanvas()) |
| + : skia::RefPtr<skia::VectorCanvas>(/*NULL*/); |
| } |
| SkBaseDevice* PdfMetafileSkia::StartPageForVectorCanvas( |
| const gfx::Size& page_size, const gfx::Rect& content_area, |
| const float& scale_factor) { |
| - DCHECK(!page_outstanding_); |
| - page_outstanding_ = true; |
| - |
| - // Adjust for the margins and apply the scale factor. |
| - SkMatrix transform; |
| - transform.setTranslate(SkIntToScalar(content_area.x()), |
| - SkIntToScalar(content_area.y())); |
| - transform.preScale(SkFloatToScalar(scale_factor), |
| - SkFloatToScalar(scale_factor)); |
| - |
| - SkISize pdf_page_size = SkISize::Make(page_size.width(), page_size.height()); |
| - SkISize pdf_content_size = |
| - SkISize::Make(content_area.width(), content_area.height()); |
| - skia::RefPtr<SkPDFDevice> pdf_device = |
| - skia::AdoptRef(new skia::VectorPlatformDeviceSkia( |
| - pdf_page_size, pdf_content_size, transform)); |
| - data_->current_page_ = pdf_device; |
| - return pdf_device.get(); |
| + NOTREACHED(); // Skia has deprecated the use of SkPDFDevice. |
| + return NULL; |
| } |
| + |
| bool PdfMetafileSkia::StartPage(const gfx::Size& page_size, |
| const gfx::Rect& content_area, |
| const float& scale_factor) { |
| - NOTREACHED(); |
| - return false; |
| + if (data_->PageOutstanding()) |
| + this->FinishPage(); |
| + |
| + data_->recorder_.reset(SkNEW(SkPictureRecorder)); |
| + SkSize sk_page_size = ToSkSize(page_size); |
| + data_->pages_.push_back(Page(sk_page_size, ToSkRect(content_area))); |
| + |
| + SkCanvas* recordingCanvas = data_->recorder_->beginRecording( |
| + sk_page_size.width(), sk_page_size.height(), NULL, 0); |
| + // recordingCanvas is owned by the data_->recorder_. No ref() necessary. |
| + if (!recordingCanvas) |
| + return false; |
| + recordingCanvas->scale(scale_factor, scale_factor); |
| + return true; |
| } |
| bool PdfMetafileSkia::FinishPage() { |
| - DCHECK(data_->current_page_.get()); |
| - |
| - data_->pdf_doc_.appendPage(data_->current_page_.get()); |
| - page_outstanding_ = false; |
| + if (!data_->PageOutstanding()) |
| + return false; |
| + DCHECK(!(data_->pages_.back().content_.get())); |
| + data_->pages_.back().content_ = |
| + skia::AdoptRef(data_->recorder_->endRecording()); |
| + data_->recorder_.reset(); |
| return true; |
| } |
| bool PdfMetafileSkia::FinishDocument() { |
| - // Don't do anything if we've already set the data in InitFromData. |
| - if (data_->pdf_stream_.getOffset()) |
| - return true; |
| - |
| - if (page_outstanding_) |
| - FinishPage(); |
| - |
| - data_->current_page_.clear(); |
| - |
| - int font_counts[SkAdvancedTypefaceMetrics::kOther_Font + 2]; |
| - data_->pdf_doc_.getCountOfFontTypes(font_counts); |
| - for (int type = 0; |
| - type <= SkAdvancedTypefaceMetrics::kOther_Font + 1; |
| - type++) { |
| - for (int count = 0; count < font_counts[type]; count++) { |
| - UMA_HISTOGRAM_ENUMERATION( |
| - "PrintPreview.FontType", type, |
| - SkAdvancedTypefaceMetrics::kOther_Font + 2); |
| - } |
| + // If we've already set the data in InitFromData, overwrite it. |
| + if (data_->HasData()) |
| + data_->pdf_data_.clear(); // Free up RAM first. |
| + |
| + if (data_->PageOutstanding()) |
| + this->FinishPage(); |
| + |
| + SkDynamicMemoryWStream pdf_stream; |
| + skia::RefPtr<SkDocument> pdf_doc = |
| + skia::AdoptRef(SkDocument::CreatePDF(&pdf_stream)); |
| + for (const auto& page : data_->pages_) { |
| + SkCanvas* canvas = |
| + pdf_doc->beginPage(page.page_size_.width(), page.page_size_.height(), |
| + &(page.content_area_)); |
|
Vitaly Buka (NO REVIEWS)
2014/11/04 19:23:55
just &page.content_area_
hal.canary
2014/11/16 21:29:28
Done.
|
| + canvas->drawPicture(page.content_.get()); |
| + pdf_doc->endPage(); |
| } |
| + if (!pdf_doc->close()) |
| + return false; |
| - return data_->pdf_doc_.emitPDF(&data_->pdf_stream_); |
| + data_->pdf_data_ = skia::AdoptRef(pdf_stream.detachAsStream()); |
| + return true; |
| } |
| uint32 PdfMetafileSkia::GetDataSize() const { |
| - return base::checked_cast<uint32>(data_->pdf_stream_.getOffset()); |
| + if (!data_->pdf_data_) |
| + return 0; |
| + return base::checked_cast<uint32>(data_->pdf_data_->getLength()); |
| } |
| bool PdfMetafileSkia::GetData(void* dst_buffer, |
| uint32 dst_buffer_size) const { |
| - if (dst_buffer_size < GetDataSize()) |
| + if (!data_->pdf_data_) |
| return false; |
| - |
| - SkAutoDataUnref data(data_->pdf_stream_.copyToData()); |
| - memcpy(dst_buffer, data->bytes(), dst_buffer_size); |
| - return true; |
| + return WriteAssetToBuffer(data_->pdf_data_.get(), dst_buffer, |
| + base::checked_cast<size_t>(dst_buffer_size)); |
| } |
| gfx::Rect PdfMetafileSkia::GetPageBounds(unsigned int page_number) const { |
| - // TODO(vandebo) add a method to get the page size for a given page to |
| - // SkPDFDocument. |
| - NOTIMPLEMENTED(); |
| - return gfx::Rect(); |
| + return ToGfxRect(data_->pages_[page_number].content_area_); |
| } |
| unsigned int PdfMetafileSkia::GetPageCount() const { |
| - // TODO(vandebo) add a method to get the number of pages to SkPDFDocument. |
| - NOTIMPLEMENTED(); |
| - return 0; |
| + return base::checked_cast<int>(data_->PageCount()); |
| } |
| gfx::NativeDrawingContext PdfMetafileSkia::context() const { |
| @@ -170,28 +265,53 @@ bool PdfMetafileSkia::RenderPage(unsigned int page_number, |
| CGContextRef context, |
| const CGRect rect, |
| const MacRenderPageParams& params) const { |
| - DCHECK_GT(data_->pdf_stream_.getOffset(), 0U); |
| + DCHECK_GT(GetDataSize(), 0U); |
| if (data_->pdf_cg_.GetDataSize() == 0) { |
| - SkAutoDataUnref data(data_->pdf_stream_.copyToData()); |
| - data_->pdf_cg_.InitFromData(data->bytes(), data->size()); |
| + if (GetDataSize() == 0) |
| + return false; |
| + size_t length = data_->pdf_data_->getLength(); |
| + scoped_ptr<uint8_t[]> buffer(new uint8_t[length]); |
|
Vitaly Buka (NO REVIEWS)
2014/11/04 19:23:55
just std::vector<uint8_t> buffer(length)?
hal.canary
2014/11/16 21:29:28
Done.
|
| + (void)WriteAssetToBuffer(data_->pdf_data_.get(), buffer.get(), length); |
| + data_->pdf_cg_.InitFromData(buffer.get(), |
| + base::checked_cast<uint32>(length)); |
| } |
| return data_->pdf_cg_.RenderPage(page_number, context, rect, params); |
| } |
| #endif |
| +bool PdfMetafileSkia::SaveTo(base::File* file) const { |
| + DCHECK(file); |
| + if (GetDataSize() == 0U) |
| + return false; |
| + DCHECK(data_->pdf_data_.get()); |
| + SkStreamAsset* asset = data_->pdf_data_.get(); |
| + DCHECK_EQ(asset->getPosition(), 0U); |
| + |
| + char buffer[4096]; |
|
Vitaly Buka (NO REVIEWS)
2014/11/04 19:23:55
maybe std::vector for 1Mb?
hal.canary
2014/11/16 21:29:28
Done.
|
| + do { |
| + size_t read_size = asset->read(buffer, sizeof(buffer)); |
|
Vitaly Buka (NO REVIEWS)
2014/11/04 19:23:55
read_size == 0 ?
hal.canary
2014/11/16 21:29:28
Done.
|
| + DCHECK_GE(sizeof(buffer), read_size); |
| + if (!file->WriteAtCurrentPos(buffer, base::checked_cast<int>(read_size))) { |
| + (void)asset->rewind(); |
| + return false; |
| + } |
| + } while (!asset->isAtEnd()); |
| + |
| + (void)asset->rewind(); |
| + return true; |
| +} |
| + |
| #if defined(OS_CHROMEOS) || defined(OS_ANDROID) |
| + |
| bool PdfMetafileSkia::SaveToFD(const base::FileDescriptor& fd) const { |
| - DCHECK_GT(data_->pdf_stream_.getOffset(), 0U); |
| + DCHECK_GT(GetDataSize(), 0U); |
| if (fd.fd < 0) { |
| DLOG(ERROR) << "Invalid file descriptor!"; |
| return false; |
| } |
| base::File file(fd.fd); |
| - SkAutoDataUnref data(data_->pdf_stream_.copyToData()); |
| - bool result = |
| - file.WriteAtCurrentPos(reinterpret_cast<const char*>(data->data()), |
| - GetDataSize()) == static_cast<int>(GetDataSize()); |
| + bool result = this->SaveTo(&file); |
| DLOG_IF(ERROR, !result) << "Failed to save file with fd " << fd.fd; |
| if (!fd.auto_close) |
| @@ -200,30 +320,30 @@ bool PdfMetafileSkia::SaveToFD(const base::FileDescriptor& fd) const { |
| } |
| #endif |
| -PdfMetafileSkia::PdfMetafileSkia() |
| - : data_(new PdfMetafileSkiaData), |
| - page_outstanding_(false) { |
| +PdfMetafileSkia::PdfMetafileSkia() : data_(new PdfMetafileSkiaData) { |
| + DCHECK(!data_->PageOutstanding()); |
| } |
| scoped_ptr<PdfMetafileSkia> PdfMetafileSkia::GetMetafileForCurrentPage() { |
| - scoped_ptr<PdfMetafileSkia> metafile; |
| - SkPDFDocument pdf_doc(SkPDFDocument::kDraftMode_Flags); |
| - if (!pdf_doc.appendPage(data_->current_page_.get())) |
| + // If we only ever need the metafile for the last page, should we |
| + // only keep a handle on one SkPicture? |
| + DCHECK_NE(data_->PageCount(), 0U); |
| + DCHECK(!data_->PageOutstanding()); |
| + scoped_ptr<PdfMetafileSkia> metafile(new PdfMetafileSkia); |
| + |
| + if (data_->PageCount() == 0) |
| return metafile.Pass(); |
| - SkDynamicMemoryWStream pdf_stream; |
| - if (!pdf_doc.emitPDF(&pdf_stream)) |
| + const Page* page = data_->GetPage(data_->PageCount() - 1); |
| + if (!page) |
| return metafile.Pass(); |
| - SkAutoDataUnref data_copy(pdf_stream.copyToData()); |
| - if (data_copy->size() == 0) |
| - return scoped_ptr<PdfMetafileSkia>(); |
| + metafile->data_->pages_.push_back(*page); // Copy page data; |
| + // Should increment refcnt on page->content_. |
| - metafile.reset(new PdfMetafileSkia); |
| - if (!metafile->InitFromData(data_copy->bytes(), |
| - base::checked_cast<uint32>(data_copy->size()))) { |
| + if (!metafile->FinishDocument()) // Generate PDF. |
| metafile.reset(); |
| - } |
| + |
| return metafile.Pass(); |
| } |