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