OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 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 <memory> | |
6 #include <string> | |
7 | |
8 #include "base/bind.h" | |
9 #include "base/macros.h" | |
10 #include "base/memory/scoped_ptr.h" | |
11 #include "mojo/application/application_runner_chromium.h" | |
12 #include "mojo/data_pipe_utils/data_pipe_utils.h" | |
13 #include "mojo/public/c/system/main.h" | |
14 #include "mojo/ui/content_viewer_app.h" | |
15 #include "mojo/ui/ganesh_view.h" | |
16 #include "mojo/ui/input_handler.h" | |
17 #include "third_party/pdfium/fpdfsdk/include/fpdf_ext.h" | |
18 #include "third_party/pdfium/fpdfsdk/include/fpdfview.h" | |
19 #include "third_party/skia/include/core/SkCanvas.h" | |
20 #include "third_party/skia/include/core/SkData.h" | |
21 #include "third_party/skia/include/core/SkImage.h" | |
22 #include "third_party/skia/include/core/SkImageInfo.h" | |
23 #include "third_party/skia/include/core/SkSurface.h" | |
24 #include "v8/include/v8.h" | |
25 | |
26 namespace examples { | |
27 | |
28 constexpr uint32_t kContentImageResourceId = 1; | |
29 constexpr uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId; | |
30 | |
31 class PDFLibrary; | |
32 | |
33 class PDFDocument { | |
34 public: | |
35 PDFDocument(const std::shared_ptr<PDFLibrary>& pdf_library, | |
36 FPDF_DOCUMENT doc, | |
37 const std::string& data) | |
38 : pdf_library_(pdf_library), | |
39 doc_(doc), | |
40 data_(data), | |
41 page_count_(FPDF_GetPageCount(doc_)) {} | |
42 | |
43 ~PDFDocument() { FPDF_CloseDocument(doc_); } | |
44 | |
45 uint32_t page_count() { return page_count_; } | |
46 | |
47 skia::RefPtr<SkImage> DrawPage(int page_index) { | |
48 FPDF_PAGE page = FPDF_LoadPage(doc_, page_index); | |
49 int width = static_cast<int>(FPDF_GetPageWidth(page)); | |
50 int height = static_cast<int>(FPDF_GetPageHeight(page)); | |
51 int stride = width * 4; | |
52 | |
53 skia::RefPtr<SkData> pixels = | |
54 skia::AdoptRef(SkData::NewUninitialized(stride * height)); | |
55 DCHECK(pixels); | |
56 | |
57 FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(width, height, FPDFBitmap_BGRA, | |
58 pixels->writable_data(), stride); | |
59 FPDFBitmap_FillRect(bitmap, 0, 0, width, height, 0xFFFFFFFF); | |
60 FPDF_RenderPageBitmap(bitmap, page, 0, 0, width, height, 0, 0); | |
61 FPDFBitmap_Destroy(bitmap); | |
62 | |
63 FPDF_ClosePage(page); | |
64 | |
65 SkImageInfo info = SkImageInfo::Make(width, height, kBGRA_8888_SkColorType, | |
66 kOpaque_SkAlphaType); | |
67 return skia::AdoptRef(SkImage::NewRasterData(info, pixels.get(), stride)); | |
68 } | |
69 | |
70 private: | |
71 std::shared_ptr<PDFLibrary> pdf_library_; | |
72 FPDF_DOCUMENT doc_; | |
73 std::string data_; | |
74 uint32_t page_count_; | |
75 }; | |
76 | |
77 class PDFLibrary : public std::enable_shared_from_this<PDFLibrary> { | |
78 public: | |
79 PDFLibrary() { | |
80 v8::V8::InitializeICU(); | |
81 FPDF_InitLibrary(); | |
82 } | |
83 | |
84 ~PDFLibrary() { FPDF_DestroyLibrary(); } | |
85 | |
86 std::shared_ptr<PDFDocument> Decode(mojo::URLResponsePtr response) { | |
87 std::string data; | |
88 mojo::common::BlockingCopyToString(response->body.Pass(), &data); | |
89 if (data.length() >= static_cast<size_t>(std::numeric_limits<int>::max())) | |
90 return nullptr; | |
91 | |
92 FPDF_DOCUMENT doc = FPDF_LoadMemDocument( | |
93 data.data(), static_cast<int>(data.length()), nullptr); | |
94 if (!doc) | |
95 return nullptr; | |
96 | |
97 return std::make_shared<PDFDocument>(shared_from_this(), doc, data); | |
98 } | |
99 }; | |
100 | |
101 class PDFDocumentView : public mojo::ui::GaneshView, | |
102 public mojo::ui::InputListener { | |
103 public: | |
104 PDFDocumentView( | |
105 mojo::ApplicationImpl* app_impl, | |
106 const std::shared_ptr<PDFDocument>& pdf_document, | |
107 const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback) | |
108 : GaneshView(app_impl, "PDFDocumentViewer", create_view_callback), | |
109 pdf_document_(pdf_document), | |
110 input_handler_(view_service_provider(), this) { | |
111 DCHECK(pdf_document_); | |
112 } | |
113 | |
114 ~PDFDocumentView() override {} | |
115 | |
116 private: | |
117 // |View|: | |
118 void OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params, | |
119 mojo::Array<uint32_t> children_needing_layout, | |
120 const OnLayoutCallback& callback) override { | |
121 size_.width = layout_params->constraints->max_width; | |
122 size_.height = layout_params->constraints->max_height; | |
123 | |
124 auto info = mojo::ui::ViewLayoutResult::New(); | |
125 info->size = size_.Clone(); | |
126 callback.Run(info.Pass()); | |
127 | |
128 UpdateScene(); | |
abarth
2016/01/10 23:09:31
I'm surprised there's no token that I get from lay
jeffbrown
2016/01/26 08:14:17
I'll be redoing layout once this gets checked in.
| |
129 } | |
130 | |
131 // |InputListener|: | |
132 void OnEvent(mojo::EventPtr event, const OnEventCallback& callback) override { | |
133 if (event->key_data && (event->action != mojo::EventType::KEY_PRESSED || | |
134 event->key_data->is_char)) { | |
135 callback.Run(false); | |
136 return; | |
137 } | |
138 | |
139 if ((event->key_data && | |
140 event->key_data->windows_key_code == mojo::KeyboardCode::DOWN) || | |
141 (event->pointer_data && event->pointer_data->vertical_wheel < 0)) { | |
142 GotoNextPage(); | |
143 callback.Run(true); | |
144 return; | |
145 } | |
146 | |
147 if ((event->key_data && | |
148 event->key_data->windows_key_code == mojo::KeyboardCode::UP) || | |
149 (event->pointer_data && event->pointer_data->vertical_wheel > 0)) { | |
150 GotoPreviousPage(); | |
151 callback.Run(true); | |
152 return; | |
153 } | |
154 | |
155 callback.Run(false); | |
156 } | |
157 | |
158 void UpdateScene() { | |
159 mojo::Rect bounds; | |
160 bounds.width = size_.width; | |
161 bounds.height = size_.height; | |
162 | |
163 auto update = mojo::gfx::composition::SceneUpdate::New(); | |
164 mojo::gfx::composition::ResourcePtr content_resource = | |
165 ganesh_renderer()->DrawCanvas( | |
166 size_, | |
167 base::Bind(&PDFDocumentView::DrawContent, base::Unretained(this))); | |
168 DCHECK(content_resource); | |
169 update->resources.insert(kContentImageResourceId, content_resource.Pass()); | |
170 | |
171 auto root_node = mojo::gfx::composition::Node::New(); | |
172 root_node->op = mojo::gfx::composition::NodeOp::New(); | |
173 root_node->op->set_image(mojo::gfx::composition::ImageNodeOp::New()); | |
174 root_node->op->get_image()->content_rect = bounds.Clone(); | |
175 root_node->op->get_image()->image_resource_id = kContentImageResourceId; | |
176 update->nodes.insert(kRootNodeId, root_node.Pass()); | |
177 | |
178 scene()->Update(update.Pass()); | |
179 scene()->Publish(nullptr); | |
180 } | |
181 | |
182 void DrawContent(SkCanvas* canvas) { | |
183 if (!cached_image_) { | |
184 cached_image_ = pdf_document_->DrawPage(page_); | |
185 } | |
186 | |
187 if (cached_image_) { | |
188 canvas->clear(SK_ColorBLACK); | |
189 | |
190 int32_t w, h; | |
191 if (size_.width * cached_image_->height() < | |
192 size_.height * cached_image_->width()) { | |
193 w = size_.width; | |
194 h = cached_image_->height() * size_.width / cached_image_->width(); | |
195 } else { | |
196 w = cached_image_->width() * size_.height / cached_image_->height(); | |
197 h = size_.height; | |
198 } | |
199 canvas->drawImageRect( | |
200 cached_image_.get(), | |
201 SkRect::MakeWH(cached_image_->width(), cached_image_->height()), | |
202 SkRect::MakeXYWH((size_.width - w) / 2, (size_.height - h) / 2, w, h), | |
203 nullptr); | |
204 } else { | |
205 canvas->clear(SK_ColorBLUE); | |
206 } | |
207 | |
208 canvas->flush(); | |
209 } | |
210 | |
211 void GotoNextPage() { | |
212 if (page_ + 1 < pdf_document_->page_count()) { | |
213 page_++; | |
214 Redraw(); | |
215 } | |
216 } | |
217 | |
218 void GotoPreviousPage() { | |
219 if (page_ > 0) { | |
220 page_--; | |
221 Redraw(); | |
222 } | |
223 } | |
224 | |
225 void Redraw() { | |
226 cached_image_.clear(); | |
227 UpdateScene(); | |
abarth
2016/01/10 23:09:31
I was expecting some sort of "invalidate and wait
jeffbrown
2016/01/26 08:14:17
Good point. Let's add that.
| |
228 } | |
229 | |
230 std::shared_ptr<PDFDocument> pdf_document_; | |
231 uint32_t page_ = 0u; | |
232 skia::RefPtr<SkImage> cached_image_; | |
233 | |
234 mojo::Size size_; | |
235 mojo::ui::InputHandler input_handler_; | |
236 | |
237 DISALLOW_COPY_AND_ASSIGN(PDFDocumentView); | |
238 }; | |
239 | |
240 class PDFContentViewProviderApp : public mojo::ui::ViewProviderApp { | |
241 public: | |
242 PDFContentViewProviderApp(const std::shared_ptr<PDFLibrary>& pdf_library, | |
243 const std::shared_ptr<PDFDocument>& pdf_document) | |
244 : pdf_library_(pdf_library), pdf_document_(pdf_document) { | |
245 DCHECK(pdf_library_); | |
246 DCHECK(pdf_document_); | |
247 } | |
248 | |
249 ~PDFContentViewProviderApp() override {} | |
250 | |
251 bool CreateView( | |
252 const std::string& connection_url, | |
253 mojo::InterfaceRequest<mojo::ServiceProvider> services, | |
254 mojo::ServiceProviderPtr exposed_services, | |
255 const mojo::ui::ViewProvider::CreateViewCallback& callback) override { | |
256 new PDFDocumentView(app_impl(), pdf_document_, callback); | |
257 return true; | |
258 } | |
259 | |
260 private: | |
261 std::shared_ptr<PDFLibrary> pdf_library_; | |
262 std::shared_ptr<PDFDocument> pdf_document_; | |
263 | |
264 DISALLOW_COPY_AND_ASSIGN(PDFContentViewProviderApp); | |
265 }; | |
266 | |
267 class PDFContentViewerApp : public mojo::ui::ContentViewerApp { | |
268 public: | |
269 PDFContentViewerApp() : pdf_library_(std::make_shared<PDFLibrary>()) {} | |
270 | |
271 ~PDFContentViewerApp() override {} | |
272 | |
273 mojo::ui::ViewProviderApp* LoadContent( | |
274 const std::string& content_handler_url, | |
275 mojo::URLResponsePtr response) override { | |
276 std::shared_ptr<PDFDocument> pdf_document = | |
277 pdf_library_->Decode(response.Pass()); | |
278 if (!pdf_document) { | |
279 LOG(ERROR) << "Could not decode PDFDocument."; | |
280 return nullptr; | |
281 } | |
282 | |
283 return new PDFContentViewProviderApp(pdf_library_, pdf_document); | |
284 } | |
285 | |
286 private: | |
287 std::shared_ptr<PDFLibrary> pdf_library_; | |
288 | |
289 DISALLOW_COPY_AND_ASSIGN(PDFContentViewerApp); | |
290 }; | |
291 | |
292 } // namespace examples | |
293 | |
294 MojoResult MojoMain(MojoHandle application_request) { | |
295 mojo::ApplicationRunnerChromium runner(new examples::PDFContentViewerApp()); | |
296 return runner.Run(application_request); | |
297 } | |
OLD | NEW |