Add PDF viewer example. R=ben@chromium.org Review URL: https://codereview.chromium.org/690583002
diff --git a/DEPS b/DEPS index b92a671..c471a15 100644 --- a/DEPS +++ b/DEPS
@@ -28,6 +28,7 @@ 'libcxxabi_revision': '4ad1009ab3a59fa7a6896d74d5e4de5885697f95', 'chromium_git': 'https://chromium.googlesource.com', 'skia_git': 'https://skia.googlesource.com', + 'pdfium_git': 'https://pdfium.googlesource.com', 'boringssl_git': 'https://boringssl.googlesource.com', 'sfntly_revision': '1bdaae8fc788a5ac8936d68bf24f37d977a13dac', 'skia_revision': '32501a1bd4964576264795ada7e9c3bf572285b3', @@ -45,6 +46,10 @@ # and whatever else without interference from each other. 'buildtools_revision': '56bc51aff4175d3fa27dcd0faa2c345ab046c8a5', # Three lines of non-changing comments so that + # the commit queue can handle CLs rolling PDFium + # and whatever else without interference from each other. + 'pdfium_revision': '767aebbef641a89498deebc29369a078207b4dcc', + # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. 'boringssl_revision': '751e889b1d791d2b91c1437248454fa1d4e101a5', @@ -123,6 +128,9 @@ 'src/third_party/mesa/src': Var('chromium_git') + '/chromium/deps/mesa.git' + '@' + '9731cb962963bca8a05f3d0f6ea40c3a781f0537', + 'src/third_party/pdfium': + 'https://pdfium.googlesource.com/pdfium.git' + '@' + Var('pdfium_revision'), + 'src/third_party/boringssl/src': 'https://boringssl.googlesource.com/boringssl.git' + '@' + Var('boringssl_revision'), }
diff --git a/examples/BUILD.gn b/examples/BUILD.gn index 8bdb389..0f03011 100644 --- a/examples/BUILD.gn +++ b/examples/BUILD.gn
@@ -12,6 +12,7 @@ "//examples/content_handler_demo", "//examples/echo", "//examples/http_server", + "//examples/pdf_viewer", "//examples/png_viewer", "//examples/sample_app", "//examples/surfaces_app",
diff --git a/examples/bitmap_uploader/BUILD.gn b/examples/bitmap_uploader/BUILD.gn index e7cdeb5..5ecd768 100644 --- a/examples/bitmap_uploader/BUILD.gn +++ b/examples/bitmap_uploader/BUILD.gn
@@ -11,6 +11,7 @@ public_deps = [ "//base", "//mojo/public/c/gles2", + "//mojo/public/gles2:for_shared_library", "//mojo/services/public/interfaces/gpu", "//mojo/services/public/interfaces/surfaces", ]
diff --git a/examples/pdf_viewer/BUILD.gn b/examples/pdf_viewer/BUILD.gn new file mode 100644 index 0000000..6ba591d --- /dev/null +++ b/examples/pdf_viewer/BUILD.gn
@@ -0,0 +1,21 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +shared_library("pdf_viewer") { + sources = [ + "pdf_viewer.cc" + ] + + deps = [ + "//examples/bitmap_uploader", + "//mojo/application", + "//mojo/public/c/system:for_shared_library", + "//mojo/public/cpp/bindings", + "//mojo/public/cpp/utility", + "//mojo/services/public/cpp/view_manager", + "//mojo/services/public/interfaces/content_handler", + "//mojo/services/public/interfaces/network", + "//third_party/pdfium", + ] +}
diff --git a/examples/pdf_viewer/DEPS b/examples/pdf_viewer/DEPS new file mode 100644 index 0000000..51c59e2 --- /dev/null +++ b/examples/pdf_viewer/DEPS
@@ -0,0 +1,4 @@ +include_rules = [ + "+third_party/pdfium", + "+v8", +]
diff --git a/examples/pdf_viewer/pdf_viewer.cc b/examples/pdf_viewer/pdf_viewer.cc new file mode 100644 index 0000000..c4f0c04 --- /dev/null +++ b/examples/pdf_viewer/pdf_viewer.cc
@@ -0,0 +1,261 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/strings/string_tokenizer.h" +#include "examples/bitmap_uploader/bitmap_uploader.h" +#include "mojo/application/application_runner_chromium.h" +#include "mojo/public/c/system/main.h" +#include "mojo/public/cpp/application/application_connection.h" +#include "mojo/public/cpp/application/application_delegate.h" +#include "mojo/public/cpp/application/application_impl.h" +#include "mojo/public/cpp/application/interface_factory_impl.h" +#include "mojo/public/cpp/application/service_provider_impl.h" +#include "mojo/services/public/cpp/view_manager/types.h" +#include "mojo/services/public/cpp/view_manager/view.h" +#include "mojo/services/public/cpp/view_manager/view_manager.h" +#include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h" +#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h" +#include "mojo/services/public/cpp/view_manager/view_observer.h" +#include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h" +#include "mojo/services/public/interfaces/input_events/input_events.mojom.h" +#include "mojo/services/public/interfaces/input_events/input_key_codes.mojom.h" +#include "third_party/pdfium/fpdfsdk/include/fpdf_ext.h" +#include "third_party/pdfium/fpdfsdk/include/fpdfformfill.h" +#include "third_party/pdfium/fpdfsdk/include/fpdftext.h" +#include "third_party/pdfium/fpdfsdk/include/fpdfview.h" +#include "v8/include/v8.h" + +#define BACKGROUND_COLOR 0xFF888888 + +namespace mojo { +namespace examples { + +class PDFViewer; + +class PDFView : public ViewManagerDelegate, public ViewObserver { + public: + static void Spawn(URLResponsePtr response, + ServiceProviderImpl* exported_services, + scoped_ptr<ServiceProvider> imported_services, + Shell* shell) { + // PDFView deletes itself when its View is destroyed. + new PDFView( + response.Pass(), exported_services, imported_services.Pass(), shell); + } + + private: + PDFView(URLResponsePtr response, + ServiceProviderImpl* exported_services, + scoped_ptr<ServiceProvider> imported_services, + Shell* shell) + : current_page_(0), + page_count_(0), + doc_(NULL), + imported_services_(imported_services.Pass()), + shell_(shell), + root_(nullptr), + view_manager_client_factory_(shell, this) { + exported_services->AddService(&view_manager_client_factory_); + FetchPDF(response.Pass()); + } + + virtual ~PDFView() { + if (doc_) + FPDF_CloseDocument(doc_); + if (root_) + root_->RemoveObserver(this); + } + + // Overridden from ViewManagerDelegate: + virtual void OnEmbed(ViewManager* view_manager, + View* root, + ServiceProviderImpl* exported_services, + scoped_ptr<ServiceProvider> imported_services) override { + root_ = root; + root_->AddObserver(this); + bitmap_uploader_.reset(new BitmapUploader(root_)); + bitmap_uploader_->Init(shell_); + bitmap_uploader_->SetColor(BACKGROUND_COLOR); + DrawBitmap(); + } + + virtual void OnViewManagerDisconnected(ViewManager* view_manager) override { + } + + // Overridden from ViewObserver: + virtual void OnViewBoundsChanged(View* view, + const Rect& old_bounds, + const Rect& new_bounds) override { + DCHECK_EQ(view, root_); + DrawBitmap(); + } + + virtual void OnViewInputEvent(View* view, const EventPtr& event) override { + if (event->key_data && + (event->action != EVENT_TYPE_KEY_PRESSED || + event->key_data->is_char)) { + return; + } + if ((event->key_data && + event->key_data->windows_key_code == KEYBOARD_CODE_DOWN) || + (event->wheel_data && event->wheel_data->y_offset < 0)) { + if (current_page_ < (page_count_ - 1)) { + current_page_++; + DrawBitmap(); + } + } else if ((event->key_data && + event->key_data->windows_key_code == KEYBOARD_CODE_UP) || + (event->wheel_data && event->wheel_data->y_offset > 0)) { + if (current_page_ > 0) { + current_page_--; + DrawBitmap(); + } + } + } + + virtual void OnViewDestroyed(View* view) override { + DCHECK_EQ(view, root_); + delete this; + } + + void DrawBitmap() { + if (!root_ || !doc_) + return; + + FPDF_PAGE page = FPDF_LoadPage(doc_, current_page_); + FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page); + int width = static_cast<int>(FPDF_GetPageWidth(page)); + int height = static_cast<int>(FPDF_GetPageHeight(page)); + + scoped_ptr<std::vector<unsigned char>> bitmap; + bitmap.reset(new std::vector<unsigned char>); + bitmap->resize(width * height * 4); + + FPDF_BITMAP f_bitmap = FPDFBitmap_CreateEx( + width, height, FPDFBitmap_BGRA, &(*bitmap)[0], width * 4); + FPDFBitmap_FillRect(f_bitmap, 0, 0, width, height, 0xFFFFFFFF); + FPDF_RenderPageBitmap(f_bitmap, page, 0, 0, width, height, 0, 0); + FPDFBitmap_Destroy(f_bitmap); + + FPDFText_ClosePage(text_page); + FPDF_ClosePage(page); + + bitmap_uploader_->SetBitmap(width, height, bitmap.Pass(), + BitmapUploader::BGRA); + } + + void FetchPDF(URLResponsePtr response) { + int content_length = GetContentLength(response->headers); + scoped_ptr<unsigned char[]> data; + data_.reset(new unsigned char[content_length]); + unsigned char* buf = data_.get(); + uint32_t bytes_remaining = content_length; + uint32_t num_bytes = bytes_remaining; + while (bytes_remaining > 0) { + MojoResult result = ReadDataRaw( + response->body.get(), buf, &num_bytes, MOJO_READ_DATA_FLAG_NONE); + if (result == MOJO_RESULT_SHOULD_WAIT) { + Wait(response->body.get(), + MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE); + } else if (result == MOJO_RESULT_OK) { + buf += num_bytes; + num_bytes = bytes_remaining -= num_bytes; + } else { + break; + } + } + + doc_ = FPDF_LoadMemDocument(data_.get(), content_length, NULL); + page_count_ = FPDF_GetPageCount(doc_); + } + + int GetContentLength(const Array<String>& headers) { + for (size_t i = 0; i < headers.size(); ++i) { + base::StringTokenizer t(headers[i], ": ;="); + while (t.GetNext()) { + if (!t.token_is_delim() && t.token() == "Content-Length") { + while (t.GetNext()) { + if (!t.token_is_delim()) + return atoi(t.token().c_str()); + } + } + } + } + return 0; + } + + scoped_ptr<unsigned char[]> data_; + int current_page_; + int page_count_; + FPDF_DOCUMENT doc_; + scoped_ptr<ServiceProvider> imported_services_; + Shell* shell_; + View* root_; + ViewManagerClientFactory view_manager_client_factory_; + scoped_ptr<BitmapUploader> bitmap_uploader_; + + DISALLOW_COPY_AND_ASSIGN(PDFView); +}; + +class ContentHandlerImpl : public InterfaceImpl<ContentHandler> { + public: + explicit ContentHandlerImpl(Shell* shell) : shell_(shell) {} + virtual ~ContentHandlerImpl() {} + + private: + // Overridden from ContentHandler: + virtual void OnConnect( + const mojo::String& requestor_url, + URLResponsePtr response, + InterfaceRequest<ServiceProvider> service_provider) override { + ServiceProviderImpl* exported_services = new ServiceProviderImpl(); + BindToRequest(exported_services, &service_provider); + scoped_ptr<ServiceProvider> remote( + exported_services->CreateRemoteServiceProvider()); + PDFView::Spawn(response.Pass(), exported_services, remote.Pass(), shell_); + } + + Shell* shell_; + + DISALLOW_COPY_AND_ASSIGN(ContentHandlerImpl); +}; + +class PDFViewer : public ApplicationDelegate { + public: + PDFViewer() {} + virtual ~PDFViewer() { + FPDF_DestroyLibrary(); + } + private: + // Overridden from ApplicationDelegate: + virtual void Initialize(ApplicationImpl* app) override { + content_handler_factory_.reset( + new InterfaceFactoryImplWithContext<ContentHandlerImpl, Shell>( + app->shell())); + + v8::V8::InitializeICU(); + FPDF_InitLibrary(NULL); + } + + // Overridden from ApplicationDelegate: + virtual bool ConfigureIncomingConnection( + ApplicationConnection* connection) override { + connection->AddService(content_handler_factory_.get()); + return true; + } + + scoped_ptr<InterfaceFactoryImplWithContext<ContentHandlerImpl, Shell> > + content_handler_factory_; + + DISALLOW_COPY_AND_ASSIGN(PDFViewer); +}; + +} // namespace examples +} // namespace mojo + +MojoResult MojoMain(MojoHandle shell_handle) { + mojo::ApplicationRunnerChromium runner(new mojo::examples::PDFViewer); + return runner.Run(shell_handle); +}
diff --git a/examples/png_viewer/BUILD.gn b/examples/png_viewer/BUILD.gn index 85910cb..4dce21a 100644 --- a/examples/png_viewer/BUILD.gn +++ b/examples/png_viewer/BUILD.gn
@@ -14,7 +14,6 @@ "//mojo/public/c/system:for_shared_library", "//mojo/public/cpp/bindings", "//mojo/public/cpp/utility", - "//mojo/public/gles2:for_shared_library", "//mojo/services/public/cpp/view_manager", "//mojo/services/public/interfaces/content_handler", "//mojo/services/public/interfaces/network",
diff --git a/mojo/shell/context.cc b/mojo/shell/context.cc index 215ed7b..d7f8e30 100644 --- a/mojo/shell/context.cc +++ b/mojo/shell/context.cc
@@ -69,6 +69,7 @@ void InitContentHandlers(DynamicApplicationLoader* loader, base::CommandLine* command_line) { // Default content handlers. + loader->RegisterContentHandler("application/pdf", GURL("mojo://pdf_viewer/")); loader->RegisterContentHandler("image/png", GURL("mojo://png_viewer/")); loader->RegisterContentHandler("text/html", GURL("mojo://html_viewer/"));