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 |