Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "printing/pdf_ps_metafile_linux.h" | 5 #include "printing/pdf_ps_metafile_linux.h" |
| 6 | 6 |
| 7 #include <stdio.h> | 7 #include <stdio.h> |
| 8 | 8 |
| 9 #include <cairo.h> | |
| 9 #include <cairo-pdf.h> | 10 #include <cairo-pdf.h> |
| 10 #include <cairo-ps.h> | 11 #include <cairo-ps.h> |
| 11 | 12 |
| 12 #include "base/file_util.h" | 13 #include "base/file_util.h" |
| 13 #include "base/logging.h" | 14 #include "base/logging.h" |
| 14 | 15 |
| 16 namespace { | |
| 17 | |
| 18 // Tests if |surface| is valid. | |
| 19 bool IsSurfaceValid(cairo_surface_t* surface) { | |
| 20 return cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS; | |
| 21 } | |
| 22 | |
| 23 // Tests if |context| is valid. | |
| 24 bool IsContextValid(cairo_t* context) { | |
| 25 return cairo_status(context) == CAIRO_STATUS_SUCCESS; | |
| 26 } | |
| 27 | |
| 28 // Destroys and resets |surface|. | |
| 29 void CleanUpSurface(cairo_surface_t** surface) { | |
| 30 if (*surface) { | |
| 31 cairo_surface_destroy(*surface); | |
| 32 *surface = NULL; | |
| 33 } | |
| 34 } | |
| 35 | |
| 36 // Destroys and resets |context|. | |
| 37 void CleanUpContext(cairo_t** context) { | |
| 38 if (*context) { | |
| 39 cairo_destroy(*context); | |
| 40 *context = NULL; | |
| 41 } | |
| 42 } | |
| 43 | |
| 44 // Callback function for Cairo to write PDF/PS stream. | |
| 45 // |dst_buffer| is actually a pointer of type `std::string*`. | |
| 46 cairo_status_t WriteCairoStream(void* dst_buffer, | |
| 47 const unsigned char* src_data, | |
| 48 unsigned int src_data_length) { | |
| 49 DCHECK(dst_buffer); | |
| 50 DCHECK(src_data); | |
| 51 DCHECK(src_data_length > 0); | |
| 52 | |
| 53 std::string* buffer = reinterpret_cast<std::string*>(dst_buffer); | |
| 54 buffer->append(reinterpret_cast<const char*>(src_data), src_data_length); | |
| 55 | |
| 56 return CAIRO_STATUS_SUCCESS; | |
| 57 } | |
| 58 | |
| 59 } // namespace | |
| 60 | |
| 15 namespace printing { | 61 namespace printing { |
| 16 | 62 |
| 17 PdfPsMetafile::PdfPsMetafile(const FileFormat& format) | 63 PdfPsMetafile::PdfPsMetafile(const FileFormat& format) |
| 18 : format_(format), | 64 : format_(format), |
| 19 surface_(NULL), context_(NULL), | 65 surface_(NULL), context_(NULL), |
| 20 page_surface_(NULL), page_context_(NULL) { | 66 page_surface_(NULL), page_context_(NULL) { |
| 67 } | |
| 68 | |
| 69 PdfPsMetafile::~PdfPsMetafile() { | |
| 70 // Releases resources if we forgot to do so. | |
| 71 CleanUp(); | |
| 72 } | |
| 73 | |
| 74 bool PdfPsMetafile::Init() { | |
| 75 // We need to check at least these two members to ensure Init() has not been | |
| 76 // called before. Passing these two checks also implies that surface_, | |
| 77 // page_surface_, and page_context_ are NULL, and current_page_ is empty. | |
| 78 DCHECK(!context_); | |
| 79 DCHECK(all_pages_.empty()); | |
| 21 | 80 |
| 22 // Create an 1 by 1 Cairo surface for entire PDF/PS file. | 81 // Create an 1 by 1 Cairo surface for entire PDF/PS file. |
| 23 // The size for each page will be overwritten later in StartPage(). | 82 // The size for each page will be overwritten later in StartPage(). |
| 24 switch (format_) { | 83 switch (format_) { |
| 25 case PDF: { | 84 case PDF: { |
| 26 surface_ = cairo_pdf_surface_create_for_stream(WriteCairoStream, | 85 surface_ = cairo_pdf_surface_create_for_stream(WriteCairoStream, |
| 27 &all_pages_, 1, 1); | 86 &all_pages_, 1, 1); |
| 28 } | 87 } |
| 29 break; | 88 break; |
| 30 | 89 |
| 31 case PS: { | 90 case PS: { |
| 32 surface_ = cairo_ps_surface_create_for_stream(WriteCairoStream, | 91 surface_ = cairo_ps_surface_create_for_stream(WriteCairoStream, |
| 33 &all_pages_, 1, 1); | 92 &all_pages_, 1, 1); |
| 34 } | 93 } |
| 35 break; | 94 break; |
| 36 | 95 |
| 37 default: | 96 default: |
| 38 NOTREACHED(); | 97 NOTREACHED(); |
| 39 return; | 98 return false; |
| 40 } | 99 } |
| 41 | 100 |
| 42 // Cairo always returns a valid pointer. | 101 // Cairo always returns a valid pointer. |
| 43 // Hence, we have to check if it points to a "nil" object. | 102 // Hence, we have to check if it points to a "nil" object. |
| 44 if (!IsSurfaceValid(surface_)) { | 103 if (!IsSurfaceValid(surface_)) { |
| 45 DLOG(ERROR) << "Cannot create Cairo surface for PdfPsMetafile!"; | 104 DLOG(ERROR) << "Cannot create Cairo surface for PdfPsMetafile!"; |
| 46 cairo_surface_destroy(surface_); | 105 CleanUpSurface(&surface_); |
| 47 return; | 106 return false; |
| 48 } | 107 } |
| 49 | 108 |
| 50 // Create a context. | 109 // Create a context. |
| 51 context_ = cairo_create(surface_); | 110 context_ = cairo_create(surface_); |
| 52 if (!IsContextValid(context_)) { | 111 if (!IsContextValid(context_)) { |
| 53 DLOG(ERROR) << "Cannot create Cairo context for PdfPsMetafile!"; | 112 DLOG(ERROR) << "Cannot create Cairo context for PdfPsMetafile!"; |
| 54 cairo_destroy(context_); | 113 CleanUpContext(&context_); |
| 55 cairo_surface_destroy(surface_); | 114 CleanUpSurface(&surface_); |
| 56 return; | 115 return false; |
| 57 } | 116 } |
| 58 | 117 |
| 59 // Since |context_| will keep a reference of |surface_|, we can decreace | 118 return true; |
| 60 // surface's reference count by one safely. | |
| 61 cairo_surface_destroy(surface_); | |
| 62 } | 119 } |
| 63 | 120 |
| 64 PdfPsMetafile::PdfPsMetafile(const FileFormat& format, | 121 bool PdfPsMetafile::Init(const void* src_buffer, size_t src_buffer_size) { |
| 65 const void* src_buffer, | 122 // We need to check at least these two members to ensure Init() has not been |
| 66 size_t src_buffer_size) | 123 // called before. Passing these two checks also implies that surface_, |
| 67 : format_(format), | 124 // page_surface_, and page_context_ are NULL, and current_page_ is empty. |
| 68 surface_(NULL), context_(NULL), | 125 DCHECK(!context_); |
| 69 page_surface_(NULL), page_context_(NULL) { | 126 DCHECK(all_pages_.empty()); |
| 127 | |
| 128 if (src_buffer == NULL || src_buffer_size == 0) { | |
| 129 return false; | |
| 130 } | |
| 70 | 131 |
| 71 all_pages_ = std::string(reinterpret_cast<const char*>(src_buffer), | 132 all_pages_ = std::string(reinterpret_cast<const char*>(src_buffer), |
| 72 src_buffer_size); | 133 src_buffer_size); |
| 134 | |
| 135 return true; | |
| 73 } | 136 } |
| 74 | 137 |
| 75 PdfPsMetafile::~PdfPsMetafile() { | 138 cairo_t* PdfPsMetafile::StartPage(double width_in_points, |
| 76 // Releases resources if we forgot to do so. | 139 double height_in_points) { |
| 77 Close(); | |
| 78 } | |
| 79 | |
| 80 bool PdfPsMetafile::StartPage(double width_in_points, double height_in_points) { | |
| 81 DCHECK(IsSurfaceValid(surface_)); | 140 DCHECK(IsSurfaceValid(surface_)); |
| 82 DCHECK(IsContextValid(context_)); | 141 DCHECK(IsContextValid(context_)); |
| 83 DCHECK(!page_surface_ && !page_context_); | 142 // Passing this check implies page_surface_ is NULL, and current_page_ is |
| 143 // empty. | |
| 144 DCHECK(!page_context_); | |
| 84 DCHECK_GT(width_in_points, 0.); | 145 DCHECK_GT(width_in_points, 0.); |
| 85 DCHECK_GT(height_in_points, 0.); | 146 DCHECK_GT(height_in_points, 0.); |
| 86 | 147 |
| 87 // Create a target surface for the new page. | 148 // Create a target surface for the new page. |
| 88 // Cairo 1.6.0 does NOT allow the first argument be NULL, | 149 // Cairo 1.6.0 does NOT allow the first argument be NULL, |
| 89 // but some newer versions do support NULL pointer. | 150 // but some newer versions do support NULL pointer. |
| 90 switch (format_) { | 151 switch (format_) { |
| 91 case PDF: { | 152 case PDF: { |
| 92 page_surface_ = cairo_pdf_surface_create_for_stream(WriteCairoStream, | 153 page_surface_ = cairo_pdf_surface_create_for_stream(WriteCairoStream, |
| 93 ¤t_page_, | 154 ¤t_page_, |
| 94 width_in_points, | 155 width_in_points, |
| 95 height_in_points); | 156 height_in_points); |
| 96 } | 157 } |
| 97 break; | 158 break; |
| 98 | 159 |
| 99 case PS: { | 160 case PS: { |
| 100 page_surface_ = cairo_ps_surface_create_for_stream(WriteCairoStream, | 161 page_surface_ = cairo_ps_surface_create_for_stream(WriteCairoStream, |
| 101 ¤t_page_, | 162 ¤t_page_, |
| 102 width_in_points, | 163 width_in_points, |
| 103 height_in_points); | 164 height_in_points); |
| 104 } | 165 } |
| 105 break; | 166 break; |
| 106 | 167 |
| 107 default: | 168 default: |
| 108 NOTREACHED(); | 169 NOTREACHED(); |
| 109 return false; | 170 CleanUp(); |
| 171 return NULL; | |
| 110 } | 172 } |
| 111 | 173 |
| 112 // Cairo always returns a valid pointer. | 174 // Cairo always returns a valid pointer. |
| 113 // Hence, we have to check if it points to a "nil" object. | 175 // Hence, we have to check if it points to a "nil" object. |
| 114 if (!IsSurfaceValid(page_surface_)) { | 176 if (!IsSurfaceValid(page_surface_)) { |
| 115 DLOG(ERROR) << "Cannot create Cairo surface for PdfPsMetafile!"; | 177 DLOG(ERROR) << "Cannot create Cairo surface for PdfPsMetafile!"; |
| 116 cairo_surface_destroy(page_surface_); | 178 CleanUp(); |
| 117 return false; | 179 return NULL; |
| 118 } | 180 } |
| 119 | 181 |
| 120 // Create a context. | 182 // Create a context. |
| 121 page_context_ = cairo_create(page_surface_); | 183 page_context_ = cairo_create(page_surface_); |
| 122 if (!IsContextValid(page_context_)) { | 184 if (!IsContextValid(page_context_)) { |
| 123 DLOG(ERROR) << "Cannot create Cairo context for PdfPsMetafile!"; | 185 DLOG(ERROR) << "Cannot create Cairo context for PdfPsMetafile!"; |
| 124 cairo_destroy(page_context_); | 186 CleanUp(); |
| 125 cairo_surface_destroy(page_surface_); | 187 return NULL; |
| 126 return false; | |
| 127 } | 188 } |
| 128 | 189 |
| 129 // Since |page_context_| will keep a reference of |page_surface_|, we can | 190 return page_context_; |
| 130 // decreace surface's reference count by one safely. | |
| 131 cairo_surface_destroy(page_surface_); | |
| 132 | |
| 133 return true; | |
| 134 } | 191 } |
| 135 | 192 |
| 136 void PdfPsMetafile::FinishPage(float shrink) { | 193 bool PdfPsMetafile::FinishPage(float shrink) { |
| 137 DCHECK(IsSurfaceValid(surface_)); | 194 DCHECK(IsSurfaceValid(surface_)); |
| 138 DCHECK(IsContextValid(context_)); | 195 DCHECK(IsContextValid(context_)); |
| 139 DCHECK(IsSurfaceValid(page_surface_)); | 196 DCHECK(IsSurfaceValid(page_surface_)); |
| 140 DCHECK(IsContextValid(page_context_)); | 197 DCHECK(IsContextValid(page_context_)); |
| 198 DCHECK(shrink > 0); | |
| 141 | 199 |
| 142 // Flush all rendering for current page. | 200 // Flush all rendering for current page. |
| 143 cairo_surface_flush(page_surface_); | 201 cairo_surface_flush(page_surface_); |
| 144 | 202 |
| 145 // TODO(myhuang): Use real page settings. | 203 // TODO(myhuang): Use real page settings. |
| 146 // We hard-coded page settings here for testing purpose. | 204 // We hard-coded page settings here for testing purpose. |
| 147 // The paper size is US Letter (8.5 in. by 11 in.). | 205 // The paper size is US Letter (8.5 in. by 11 in.). |
| 148 // The default margins are: | 206 // The default margins are: |
| 149 // Left = 0.25 in. | 207 // Left = 0.25 in. |
| 150 // Right = 0.25 in. | 208 // Right = 0.25 in. |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 161 } | 219 } |
| 162 break; | 220 break; |
| 163 | 221 |
| 164 case PS: { | 222 case PS: { |
| 165 cairo_ps_surface_set_size(surface_, kWidthInPoint, kHeightInPoint); | 223 cairo_ps_surface_set_size(surface_, kWidthInPoint, kHeightInPoint); |
| 166 } | 224 } |
| 167 break; | 225 break; |
| 168 | 226 |
| 169 default: | 227 default: |
| 170 NOTREACHED(); | 228 NOTREACHED(); |
| 171 return; | 229 CleanUp(); |
| 230 return false; | |
| 231 } | |
| 232 | |
| 233 // Check if our surface is still valid after resizing. | |
| 234 if (!IsSurfaceValid(surface_)) { | |
| 235 DLOG(ERROR) << "Cannot resize Cairo surface for PdfPsMetafile!"; | |
| 236 CleanUp(); | |
| 237 return false; | |
| 172 } | 238 } |
| 173 | 239 |
| 174 // Save context's states. | 240 // Save context's states. |
| 175 cairo_save(context_); | 241 cairo_save(context_); |
| 176 // Copy current page onto the surface of final result. | 242 // Copy current page onto the surface of final result. |
| 177 // Margins are done by coordinates transformation. | 243 // Margins are done by coordinates transformation. |
| 178 // Please NOTE that we have to call cairo_scale() before we call | 244 // Please NOTE that we have to call cairo_scale() before we call |
| 179 // cairo_set_source_surface(). | 245 // cairo_set_source_surface(). |
| 180 const double scale_factor = 1. / shrink; | 246 const double scale_factor = 1. / shrink; |
| 181 cairo_scale(context_, scale_factor, scale_factor); | 247 cairo_scale(context_, scale_factor, scale_factor); |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 207 kScaledTopMarginInPoint, | 273 kScaledTopMarginInPoint, |
| 208 kScaledPrintableWidthInPoint, | 274 kScaledPrintableWidthInPoint, |
| 209 kScaledPrintableHeightInPoint); | 275 kScaledPrintableHeightInPoint); |
| 210 cairo_fill(context_); | 276 cairo_fill(context_); |
| 211 | 277 |
| 212 // Finishing the duplication of current page. | 278 // Finishing the duplication of current page. |
| 213 cairo_show_page(context_); | 279 cairo_show_page(context_); |
| 214 cairo_surface_flush(surface_); | 280 cairo_surface_flush(surface_); |
| 215 | 281 |
| 216 // Destroy resoreces for current page. | 282 // Destroy resoreces for current page. |
| 217 cairo_destroy(page_context_); | 283 CleanUpContext(&page_context_); |
| 218 page_context_ = NULL; | 284 CleanUpSurface(&page_surface_); |
|
myhuang
2009/08/26 20:50:36
I forgot to do line#284 here in rev#24474
| |
| 219 page_surface_ = NULL; | |
| 220 current_page_.clear(); | 285 current_page_.clear(); |
| 221 | 286 |
| 222 // Restore context's states. | 287 // Restore context's states. |
| 223 cairo_restore(context_); | 288 cairo_restore(context_); |
| 289 | |
| 290 return true; | |
| 224 } | 291 } |
| 225 | 292 |
| 226 void PdfPsMetafile::Close() { | 293 void PdfPsMetafile::Close() { |
| 227 if (surface_ != NULL && IsSurfaceValid(surface_)) { | 294 DCHECK(IsSurfaceValid(surface_)); |
| 228 cairo_surface_finish(surface_); | 295 DCHECK(IsContextValid(context_)); |
| 229 surface_ = NULL; | 296 // Passing this check implies page_surface_ is NULL, and current_page_ is |
| 230 } | 297 // empty. |
| 231 if (context_ != NULL && IsContextValid(context_)) { | 298 DCHECK(!page_context_); |
| 232 cairo_destroy(context_); | 299 |
| 233 context_ = NULL; | 300 cairo_surface_finish(surface_); |
| 234 } | 301 DCHECK(!all_pages_.empty()); // Make sure we did get something. |
| 302 | |
| 303 CleanUpContext(&context_); | |
| 304 CleanUpSurface(&surface_); | |
| 235 } | 305 } |
| 236 | 306 |
| 237 unsigned int PdfPsMetafile::GetDataSize() const { | 307 unsigned int PdfPsMetafile::GetDataSize() const { |
| 238 DCHECK(!surface_ && !context_); | 308 // We need to check at least these two members to ensure that either Init() |
| 309 // has been called to initialize |all_pages_|, or metafile has been closed. | |
| 310 // Passing these two checks also implies that surface_, page_surface_, and | |
| 311 // page_context_ are NULL, and current_page_ is empty. | |
| 312 DCHECK(!context_); | |
| 313 DCHECK(!all_pages_.empty()); | |
| 239 | 314 |
| 240 return all_pages_.size(); | 315 return all_pages_.size(); |
| 241 } | 316 } |
| 242 | 317 |
| 243 void PdfPsMetafile::GetData(void* dst_buffer, size_t dst_buffer_size) const { | 318 bool PdfPsMetafile::GetData(void* dst_buffer, size_t dst_buffer_size) const { |
| 244 DCHECK(!surface_ && !context_); | |
| 245 DCHECK(dst_buffer); | 319 DCHECK(dst_buffer); |
| 320 DCHECK(dst_buffer_size > 0); | |
| 321 // We need to check at least these two members to ensure that either Init() | |
| 322 // has been called to initialize |all_pages_|, or metafile has been closed. | |
| 323 // Passing these two checks also implies that surface_, page_surface_, and | |
| 324 // page_context_ are NULL, and current_page_ is empty. | |
| 325 DCHECK(!context_); | |
| 326 DCHECK(!all_pages_.empty()); | |
| 246 | 327 |
| 247 size_t data_size = GetDataSize(); | 328 size_t data_size = GetDataSize(); |
| 248 if (data_size < dst_buffer_size) | 329 if (dst_buffer_size > data_size) { |
| 249 dst_buffer_size = data_size; | 330 return false; |
| 331 } | |
| 332 | |
| 250 memcpy(dst_buffer, all_pages_.data(), dst_buffer_size); | 333 memcpy(dst_buffer, all_pages_.data(), dst_buffer_size); |
| 334 | |
| 335 return true; | |
| 251 } | 336 } |
| 252 | 337 |
| 253 bool PdfPsMetafile::SaveTo(const FilePath& filename) const { | 338 bool PdfPsMetafile::SaveTo(const FilePath& filename) const { |
| 254 DCHECK(!surface_ && !context_); | 339 // We need to check at least these two members to ensure that either Init() |
| 340 // has been called to initialize |all_pages_|, or metafile has been closed. | |
| 341 // Passing these two checks also implies that surface_, page_surface_, and | |
| 342 // page_context_ are NULL, and current_page_ is empty. | |
| 343 DCHECK(!context_); | |
| 344 DCHECK(!all_pages_.empty()); | |
| 255 | 345 |
| 256 const unsigned int data_size = GetDataSize(); | 346 const unsigned int data_size = GetDataSize(); |
| 257 const unsigned int bytes_written = | 347 const unsigned int bytes_written = |
| 258 file_util::WriteFile(filename, all_pages_.data(), data_size); | 348 file_util::WriteFile(filename, all_pages_.data(), data_size); |
| 259 if (bytes_written != data_size) { | 349 if (bytes_written != data_size) { |
| 260 DLOG(ERROR) << "Failed to save file: " << filename.value(); | 350 DLOG(ERROR) << "Failed to save file: " << filename.value(); |
| 261 return false; | 351 return false; |
| 262 } | 352 } |
| 263 | 353 |
| 264 return true; | 354 return true; |
| 265 } | 355 } |
| 266 | 356 |
| 267 cairo_status_t PdfPsMetafile::WriteCairoStream(void* dst_buffer, | 357 void PdfPsMetafile::CleanUp() { |
| 268 const unsigned char* src_data, | 358 CleanUpContext(&context_); |
| 269 unsigned int src_data_length) { | 359 CleanUpSurface(&surface_); |
| 270 DCHECK(dst_buffer); | 360 CleanUpContext(&page_context_); |
| 271 DCHECK(src_data); | 361 CleanUpSurface(&page_surface_); |
| 272 | 362 current_page_.clear(); |
| 273 std::string* buffer = reinterpret_cast<std::string* >(dst_buffer); | 363 all_pages_.clear(); |
| 274 buffer->append(reinterpret_cast<const char*>(src_data), src_data_length); | |
| 275 | |
| 276 return CAIRO_STATUS_SUCCESS; | |
| 277 } | |
| 278 | |
| 279 bool PdfPsMetafile::IsSurfaceValid(cairo_surface_t* surface) const { | |
| 280 return cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS; | |
| 281 } | |
| 282 | |
| 283 bool PdfPsMetafile::IsContextValid(cairo_t* context) const { | |
| 284 return cairo_status(context) == CAIRO_STATUS_SUCCESS; | |
| 285 } | 364 } |
| 286 | 365 |
| 287 } // namespace printing | 366 } // namespace printing |
| 288 | 367 |
| OLD | NEW |