Prechádzať zdrojové kódy

LibPDF: Switch to best-effort PDF rendering

The current rendering routine aborts as soon as an error is found during
rendering, which potentially severely limits the contents we show on
screen. Moreover, whenever an error happens the PDFViewer widget shows
an error dialog, and doesn't display the bitmap that has been painted so
far.

This commit improves the situation in both fronts, implementing
rendering now with a best-effort approach. Firstly, execution of
operations isn't halted after an operand results in an error, but
instead execution of all operations is always attempted, and all
collected errors are returned in bulk. Secondly, PDFViewer now always
displays the resulting bitmap, regardless of error being produced or
not. To communicate errors, an on_render_errors callback has been added
so clients can subscribe to these events and handle them as appropriate.
Rodrigo Tobar 2 rokov pred
rodič
commit
e87fecf710

+ 6 - 1
Userland/Applications/PDFViewer/PDFViewer.cpp

@@ -319,7 +319,12 @@ PDF::PDFErrorOr<NonnullRefPtr<Gfx::Bitmap>> PDFViewer::render_page(u32 page_inde
     auto& page_size = m_page_dimension_cache.render_info[page_index].size;
     auto bitmap = TRY(Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, page_size.to_type<int>()));
 
-    TRY(PDF::Renderer::render(*m_document, page, bitmap, m_rendering_preferences));
+    auto maybe_errors = PDF::Renderer::render(*m_document, page, bitmap, m_rendering_preferences);
+    if (maybe_errors.is_error()) {
+        auto errors = maybe_errors.release_error();
+        on_render_errors(page_index, errors);
+        return bitmap;
+    }
 
     if (page.rotate + m_rotations != 0) {
         int rotation_count = ((page.rotate + m_rotations) / 90) % 4;

+ 1 - 0
Userland/Applications/PDFViewer/PDFViewer.h

@@ -53,6 +53,7 @@ public:
     PDF::PDFErrorOr<void> set_document(RefPtr<PDF::Document>);
 
     Function<void(u32 new_page)> on_page_change;
+    Function<void(u32 page, PDF::Errors const& errors)> on_render_errors;
 
     void zoom_in();
     void zoom_out();

+ 16 - 10
Userland/Libraries/LibPDF/Renderer.cpp

@@ -22,7 +22,7 @@
 
 namespace PDF {
 
-PDFErrorOr<void> Renderer::render(Document& document, Page const& page, RefPtr<Gfx::Bitmap> bitmap, RenderingPreferences rendering_preferences)
+PDFErrorsOr<void> Renderer::render(Document& document, Page const& page, RefPtr<Gfx::Bitmap> bitmap, RenderingPreferences rendering_preferences)
 {
     return Renderer(document, page, bitmap, rendering_preferences).render();
 }
@@ -91,7 +91,7 @@ Renderer::Renderer(RefPtr<Document> document, Page const& page, RefPtr<Gfx::Bitm
     m_bitmap->fill(Gfx::Color::NamedColor::White);
 }
 
-PDFErrorOr<void> Renderer::render()
+PDFErrorsOr<void> Renderer::render()
 {
     // Use our own vector, as the /Content can be an array with multiple
     // streams which gets concatenated
@@ -113,26 +113,32 @@ PDFErrorOr<void> Renderer::render()
 
     auto operators = TRY(Parser::parse_operators(m_document, byte_buffer));
 
-    for (auto& op : operators)
-        TRY(handle_operator(op));
-
+    Errors errors;
+    for (auto& op : operators) {
+        auto maybe_error = handle_operator(op);
+        if (maybe_error.is_error()) {
+            errors.add_error(maybe_error.release_error());
+        }
+    }
+    if (!errors.errors().is_empty())
+        return errors;
     return {};
 }
 
 PDFErrorOr<void> Renderer::handle_operator(Operator const& op, Optional<NonnullRefPtr<DictObject>> extra_resources)
 {
     switch (op.type()) {
-#define V(name, snake_name, symbol)                                 \
-    case OperatorType::name:                                        \
-        MUST(handle_##snake_name(op.arguments(), extra_resources)); \
+#define V(name, snake_name, symbol)                                \
+    case OperatorType::name:                                       \
+        TRY(handle_##snake_name(op.arguments(), extra_resources)); \
         break;
         ENUMERATE_OPERATORS(V)
 #undef V
     case OperatorType::TextNextLineShowString:
-        MUST(handle_text_next_line_show_string(op.arguments()));
+        TRY(handle_text_next_line_show_string(op.arguments()));
         break;
     case OperatorType::TextNextLineShowStringSetSpacing:
-        MUST(handle_text_next_line_show_string_set_spacing(op.arguments()));
+        TRY(handle_text_next_line_show_string_set_spacing(op.arguments()));
         break;
     }
 

+ 2 - 2
Userland/Libraries/LibPDF/Renderer.h

@@ -96,12 +96,12 @@ struct RenderingPreferences {
 
 class Renderer {
 public:
-    static PDFErrorOr<void> render(Document&, Page const&, RefPtr<Gfx::Bitmap>, RenderingPreferences preferences);
+    static PDFErrorsOr<void> render(Document&, Page const&, RefPtr<Gfx::Bitmap>, RenderingPreferences preferences);
 
 private:
     Renderer(RefPtr<Document>, Page const&, RefPtr<Gfx::Bitmap>, RenderingPreferences);
 
-    PDFErrorOr<void> render();
+    PDFErrorsOr<void> render();
 
     PDFErrorOr<void> handle_operator(Operator const&, Optional<NonnullRefPtr<DictObject>> = {});
 #define V(name, snake_name, symbol) \