Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(236)

Side by Side Diff: printing/pdf_ps_metafile_linux.cc

Issue 173516: Fix memory leak problem in PdfPsMetafile.... (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: '' Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « printing/pdf_ps_metafile_linux.h ('k') | printing/pdf_ps_metafile_linux_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 &current_page_, 154 &current_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 &current_page_, 162 &current_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
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
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
OLDNEW
« no previous file with comments | « printing/pdf_ps_metafile_linux.h ('k') | printing/pdf_ps_metafile_linux_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698