| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "printing/pdf_metafile_cairo_linux.h" | |
| 6 | |
| 7 #include <stdio.h> | |
| 8 | |
| 9 #include <cairo.h> | |
| 10 #include <cairo-pdf.h> | |
| 11 | |
| 12 #include "base/eintr_wrapper.h" | |
| 13 #include "base/file_descriptor_posix.h" | |
| 14 #include "base/file_util.h" | |
| 15 #include "base/logging.h" | |
| 16 #include "printing/units.h" | |
| 17 #include "skia/ext/vector_platform_device_cairo_linux.h" | |
| 18 #include "ui/gfx/rect.h" | |
| 19 #include "ui/gfx/size.h" | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 // Tests if |surface| is valid. | |
| 24 bool IsSurfaceValid(cairo_surface_t* surface) { | |
| 25 return cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS; | |
| 26 } | |
| 27 | |
| 28 // Tests if |context| is valid. | |
| 29 bool IsContextValid(cairo_t* context) { | |
| 30 return cairo_status(context) == CAIRO_STATUS_SUCCESS; | |
| 31 } | |
| 32 | |
| 33 // Destroys and resets |surface|. | |
| 34 void CleanUpSurface(cairo_surface_t** surface) { | |
| 35 if (*surface) { | |
| 36 cairo_surface_destroy(*surface); | |
| 37 *surface = NULL; | |
| 38 } | |
| 39 } | |
| 40 | |
| 41 // Destroys and resets |context|. | |
| 42 void CleanUpContext(cairo_t** context) { | |
| 43 if (*context) { | |
| 44 cairo_destroy(*context); | |
| 45 *context = NULL; | |
| 46 } | |
| 47 } | |
| 48 | |
| 49 // Callback function for Cairo to write PDF stream. | |
| 50 // |dst_buffer| is actually a pointer of type `std::string*`. | |
| 51 cairo_status_t WriteCairoStream(void* dst_buffer, | |
| 52 const unsigned char* src_data, | |
| 53 unsigned int src_data_length) { | |
| 54 DCHECK(dst_buffer); | |
| 55 DCHECK(src_data); | |
| 56 DCHECK_GT(src_data_length, 0u); | |
| 57 | |
| 58 std::string* buffer = reinterpret_cast<std::string*>(dst_buffer); | |
| 59 buffer->append(reinterpret_cast<const char*>(src_data), src_data_length); | |
| 60 | |
| 61 return CAIRO_STATUS_SUCCESS; | |
| 62 } | |
| 63 | |
| 64 } // namespace | |
| 65 | |
| 66 namespace printing { | |
| 67 | |
| 68 PdfMetafileCairo::PdfMetafileCairo() | |
| 69 : surface_(NULL), | |
| 70 context_(NULL), | |
| 71 current_data_(NULL) { | |
| 72 } | |
| 73 | |
| 74 PdfMetafileCairo::~PdfMetafileCairo() { | |
| 75 // Releases all resources if we forgot to do so. | |
| 76 CleanUpAll(); | |
| 77 } | |
| 78 | |
| 79 bool PdfMetafileCairo::Init() { | |
| 80 // We need to check |current_data_| to ensure Init/InitFromData has not been | |
| 81 // called before. | |
| 82 DCHECK(!current_data_); | |
| 83 | |
| 84 current_data_ = &cairo_data_; | |
| 85 // Creates an 1 by 1 Cairo surface for the entire PDF file. | |
| 86 // The size for each page will be overwritten later in StartPage(). | |
| 87 surface_ = cairo_pdf_surface_create_for_stream(WriteCairoStream, | |
| 88 current_data_, 1, 1); | |
| 89 | |
| 90 // Cairo always returns a valid pointer. | |
| 91 // Hence, we have to check if it points to a "nil" object. | |
| 92 if (!IsSurfaceValid(surface_)) { | |
| 93 DLOG(ERROR) << "Cannot create Cairo surface for PdfMetafileCairo!"; | |
| 94 CleanUpSurface(&surface_); | |
| 95 return false; | |
| 96 } | |
| 97 | |
| 98 // Creates a context. | |
| 99 context_ = cairo_create(surface_); | |
| 100 if (!IsContextValid(context_)) { | |
| 101 DLOG(ERROR) << "Cannot create Cairo context for PdfMetafileCairo!"; | |
| 102 CleanUpContext(&context_); | |
| 103 CleanUpSurface(&surface_); | |
| 104 return false; | |
| 105 } | |
| 106 | |
| 107 return true; | |
| 108 } | |
| 109 | |
| 110 bool PdfMetafileCairo::InitFromData(const void* src_buffer, | |
| 111 uint32 src_buffer_size) { | |
| 112 if (src_buffer == NULL || src_buffer_size == 0) | |
| 113 return false; | |
| 114 | |
| 115 raw_data_ = std::string(reinterpret_cast<const char*>(src_buffer), | |
| 116 src_buffer_size); | |
| 117 current_data_ = &raw_data_; | |
| 118 return true; | |
| 119 } | |
| 120 | |
| 121 SkDevice* PdfMetafileCairo::StartPageForVectorCanvas( | |
| 122 const gfx::Size& page_size, const gfx::Rect& content_area, | |
| 123 const float& scale_factor) { | |
| 124 if (!StartPage(page_size, content_area, scale_factor)) | |
| 125 return NULL; | |
| 126 | |
| 127 return skia::VectorPlatformDeviceCairo::CreateDevice( | |
| 128 context_, page_size.width(), page_size.height(), true); | |
| 129 } | |
| 130 | |
| 131 bool PdfMetafileCairo::StartPage(const gfx::Size& page_size, | |
| 132 const gfx::Rect& content_area, | |
| 133 const float& scale_factor) { | |
| 134 DCHECK(IsSurfaceValid(surface_)); | |
| 135 DCHECK(IsContextValid(context_)); | |
| 136 // Passing this check implies page_surface_ is NULL, and current_page_ is | |
| 137 // empty. | |
| 138 DCHECK_GT(page_size.width(), 0); | |
| 139 DCHECK_GT(page_size.height(), 0); | |
| 140 // |scale_factor| is not supported yet. | |
| 141 DCHECK_EQ(scale_factor, 1); | |
| 142 | |
| 143 // Don't let WebKit draw over the margins. | |
| 144 cairo_surface_set_device_offset(surface_, | |
| 145 content_area.x(), | |
| 146 content_area.y()); | |
| 147 | |
| 148 cairo_pdf_surface_set_size(surface_, page_size.width(), page_size.height()); | |
| 149 return context_ != NULL; | |
| 150 } | |
| 151 | |
| 152 bool PdfMetafileCairo::FinishPage() { | |
| 153 DCHECK(IsSurfaceValid(surface_)); | |
| 154 DCHECK(IsContextValid(context_)); | |
| 155 | |
| 156 // Flushes all rendering for current page. | |
| 157 cairo_surface_flush(surface_); | |
| 158 cairo_show_page(context_); | |
| 159 return true; | |
| 160 } | |
| 161 | |
| 162 bool PdfMetafileCairo::FinishDocument() { | |
| 163 DCHECK(IsSurfaceValid(surface_)); | |
| 164 DCHECK(IsContextValid(context_)); | |
| 165 | |
| 166 cairo_surface_finish(surface_); | |
| 167 | |
| 168 DCHECK(!cairo_data_.empty()); // Make sure we did get something. | |
| 169 | |
| 170 CleanUpContext(&context_); | |
| 171 CleanUpSurface(&surface_); | |
| 172 return true; | |
| 173 } | |
| 174 | |
| 175 uint32 PdfMetafileCairo::GetDataSize() const { | |
| 176 // We need to check at least these two members to ensure that either Init() | |
| 177 // has been called to initialize |data_|, or metafile has been closed. | |
| 178 DCHECK(!context_); | |
| 179 DCHECK(!current_data_->empty()); | |
| 180 | |
| 181 return current_data_->size(); | |
| 182 } | |
| 183 | |
| 184 bool PdfMetafileCairo::GetData(void* dst_buffer, uint32 dst_buffer_size) const { | |
| 185 DCHECK(dst_buffer); | |
| 186 DCHECK_GT(dst_buffer_size, 0u); | |
| 187 memcpy(dst_buffer, current_data_->data(), dst_buffer_size); | |
| 188 | |
| 189 return true; | |
| 190 } | |
| 191 | |
| 192 cairo_t* PdfMetafileCairo::context() const { | |
| 193 return context_; | |
| 194 } | |
| 195 | |
| 196 bool PdfMetafileCairo::SaveTo(const FilePath& file_path) const { | |
| 197 // We need to check at least these two members to ensure that either Init() | |
| 198 // has been called to initialize |data_|, or metafile has been closed. | |
| 199 DCHECK(!context_); | |
| 200 DCHECK(!current_data_->empty()); | |
| 201 | |
| 202 bool success = true; | |
| 203 if (file_util::WriteFile(file_path, current_data_->data(), GetDataSize()) | |
| 204 != static_cast<int>(GetDataSize())) { | |
| 205 DLOG(ERROR) << "Failed to save file " << file_path.value().c_str(); | |
| 206 success = false; | |
| 207 } | |
| 208 return success; | |
| 209 } | |
| 210 | |
| 211 gfx::Rect PdfMetafileCairo::GetPageBounds(unsigned int page_number) const { | |
| 212 NOTIMPLEMENTED(); | |
| 213 return gfx::Rect(); | |
| 214 } | |
| 215 | |
| 216 unsigned int PdfMetafileCairo::GetPageCount() const { | |
| 217 NOTIMPLEMENTED(); | |
| 218 return 1; | |
| 219 } | |
| 220 | |
| 221 #if defined(OS_CHROMEOS) | |
| 222 bool PdfMetafileCairo::SaveToFD(const base::FileDescriptor& fd) const { | |
| 223 // We need to check at least these two members to ensure that either Init() | |
| 224 // has been called to initialize |data_|, or metafile has been closed. | |
| 225 DCHECK(!context_); | |
| 226 DCHECK(!current_data_->empty()); | |
| 227 | |
| 228 if (fd.fd < 0) { | |
| 229 DLOG(ERROR) << "Invalid file descriptor!"; | |
| 230 return false; | |
| 231 } | |
| 232 | |
| 233 bool success = true; | |
| 234 if (file_util::WriteFileDescriptor(fd.fd, current_data_->data(), | |
| 235 GetDataSize()) < 0) { | |
| 236 DLOG(ERROR) << "Failed to save file with fd " << fd.fd; | |
| 237 success = false; | |
| 238 } | |
| 239 | |
| 240 if (fd.auto_close) { | |
| 241 if (HANDLE_EINTR(close(fd.fd)) < 0) { | |
| 242 DPLOG(WARNING) << "close"; | |
| 243 success = false; | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 return success; | |
| 248 } | |
| 249 #endif // if defined(OS_CHROMEOS) | |
| 250 | |
| 251 void PdfMetafileCairo::CleanUpAll() { | |
| 252 CleanUpContext(&context_); | |
| 253 CleanUpSurface(&surface_); | |
| 254 cairo_data_.clear(); | |
| 255 raw_data_.clear(); | |
| 256 skia::VectorPlatformDeviceCairo::ClearFontCache(); | |
| 257 } | |
| 258 | |
| 259 } // namespace printing | |
| OLD | NEW |