diff --git a/Userland/Applications/PDFViewer/PDFViewer.cpp b/Userland/Applications/PDFViewer/PDFViewer.cpp index b7b6b5308f..b756ea3dd8 100644 --- a/Userland/Applications/PDFViewer/PDFViewer.cpp +++ b/Userland/Applications/PDFViewer/PDFViewer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Matthew Olsson + * Copyright (c) 2021-2022, Matthew Olsson * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause @@ -8,6 +8,7 @@ #include "PDFViewer.h" #include #include +#include #include #include @@ -56,15 +57,15 @@ void PDFViewer::set_document(RefPtr document) update(); } -RefPtr PDFViewer::get_rendered_page(u32 index) +PDF::PDFErrorOr> PDFViewer::get_rendered_page(u32 index) { auto& rendered_page_map = m_rendered_page_list[index]; auto existing_rendered_page = rendered_page_map.get(m_zoom_level); if (existing_rendered_page.has_value() && existing_rendered_page.value().rotation == m_rotations) return existing_rendered_page.value().bitmap; - // FIXME: Propogate errors in the Renderer - auto rendered_page = render_page(MUST(m_document->get_page(index))); + auto page = TRY(m_document->get_page(index)); + auto rendered_page = TRY(render_page(page)); rendered_page_map.set(m_zoom_level, { rendered_page, m_rotations }); return rendered_page; } @@ -81,7 +82,14 @@ void PDFViewer::paint_event(GUI::PaintEvent& event) if (!m_document) return; - auto page = get_rendered_page(m_current_page_index); + auto maybe_page = get_rendered_page(m_current_page_index); + if (maybe_page.is_error()) { + auto error = maybe_page.release_error(); + GUI::MessageBox::show_error(nullptr, String::formatted("Error rendering page:\n{}", error.message())); + return; + } + + auto page = maybe_page.release_value(); set_content_size(page->size()); painter.translate(frame_thickness(), frame_thickness()); @@ -196,7 +204,7 @@ void PDFViewer::rotate(int degrees) update(); } -RefPtr PDFViewer::render_page(const PDF::Page& page) +PDF::PDFErrorOr> PDFViewer::render_page(const PDF::Page& page) { auto zoom_scale_factor = static_cast(zoom_levels[m_zoom_level]) / 100.0f; @@ -208,7 +216,7 @@ RefPtr PDFViewer::render_page(const PDF::Page& page) auto width = height / page_scale_factor; auto bitmap = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, { width, height }).release_value_but_fixme_should_propagate_errors(); - PDF::Renderer::render(*m_document, page, bitmap); + TRY(PDF::Renderer::render(*m_document, page, bitmap)); if (page.rotate + m_rotations != 0) { int rotation_count = ((page.rotate + m_rotations) / 90) % 4; diff --git a/Userland/Applications/PDFViewer/PDFViewer.h b/Userland/Applications/PDFViewer/PDFViewer.h index 180a4b952e..12f4a17cb2 100644 --- a/Userland/Applications/PDFViewer/PDFViewer.h +++ b/Userland/Applications/PDFViewer/PDFViewer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Matthew Olsson + * Copyright (c) 2021-2022, Matthew Olsson * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause @@ -45,12 +45,12 @@ protected: private: struct RenderedPage { - RefPtr bitmap; + NonnullRefPtr bitmap; int rotation; }; - RefPtr get_rendered_page(u32 index); - RefPtr render_page(const PDF::Page&); + PDF::PDFErrorOr> get_rendered_page(u32 index); + PDF::PDFErrorOr> render_page(const PDF::Page&); RefPtr m_document; u32 m_current_page_index { 0 }; diff --git a/Userland/Libraries/LibPDF/Renderer.cpp b/Userland/Libraries/LibPDF/Renderer.cpp index 87f00ea7dc..87207b1396 100644 --- a/Userland/Libraries/LibPDF/Renderer.cpp +++ b/Userland/Libraries/LibPDF/Renderer.cpp @@ -9,7 +9,7 @@ #include #define RENDERER_HANDLER(name) \ - void Renderer::handle_##name([[maybe_unused]] Vector const& args) + PDFErrorOr Renderer::handle_##name([[maybe_unused]] Vector const& args) #define RENDERER_TODO(name) \ RENDERER_HANDLER(name) \ @@ -20,9 +20,9 @@ namespace PDF { -void Renderer::render(Document& document, Page const& page, RefPtr bitmap) +PDFErrorOr Renderer::render(Document& document, Page const& page, RefPtr bitmap) { - Renderer(document, page, bitmap).render(); + return Renderer(document, page, bitmap).render(); } Renderer::Renderer(RefPtr document, Page const& page, RefPtr bitmap) @@ -56,7 +56,7 @@ Renderer::Renderer(RefPtr document, Page const& page, RefPtrfill(Gfx::Color::NamedColor::White); } -void Renderer::render() +PDFErrorOr Renderer::render() { // Use our own vector, as the /Content can be an array with multiple // streams which gets concatenated @@ -68,7 +68,7 @@ void Renderer::render() if (m_page.contents->is()) { auto contents = m_page.contents->cast(); for (auto& ref : *contents) { - auto bytes = MUST(m_document->resolve_to(ref))->bytes(); + auto bytes = TRY(m_document->resolve_to(ref))->bytes(); byte_buffer.append(bytes.data(), bytes.size()); } } else { @@ -76,38 +76,44 @@ void Renderer::render() byte_buffer.append(bytes.data(), bytes.size()); } - auto commands = MUST(Parser::parse_graphics_commands(byte_buffer)); + auto commands = TRY(Parser::parse_graphics_commands(byte_buffer)); for (auto& command : commands) - handle_command(command); + TRY(handle_command(command)); + + return {}; } -void Renderer::handle_command(Command const& command) +PDFErrorOr Renderer::handle_command(Command const& command) { switch (command.command_type()) { -#define V(name, snake_name, symbol) \ - case CommandType::name: \ - handle_##snake_name(command.arguments()); \ +#define V(name, snake_name, symbol) \ + case CommandType::name: \ + TRY(handle_##snake_name(command.arguments())); \ break; ENUMERATE_COMMANDS(V) #undef V case CommandType::TextNextLineShowString: - handle_text_next_line_show_string(command.arguments()); + TRY(handle_text_next_line_show_string(command.arguments())); break; case CommandType::TextNextLineShowStringSetSpacing: - handle_text_next_line_show_string_set_spacing(command.arguments()); + TRY(handle_text_next_line_show_string_set_spacing(command.arguments())); break; } + + return {}; } RENDERER_HANDLER(save_state) { m_graphics_state_stack.append(state()); + return {}; } RENDERER_HANDLER(restore_state) { m_graphics_state_stack.take_last(); + return {}; } RENDERER_HANDLER(concatenate_matrix) @@ -122,26 +128,31 @@ RENDERER_HANDLER(concatenate_matrix) state().ctm.multiply(new_transform); m_text_rendering_matrix_is_dirty = true; + return {}; } RENDERER_HANDLER(set_line_width) { state().line_width = args[0].to_float(); + return {}; } RENDERER_HANDLER(set_line_cap) { state().line_cap_style = static_cast(args[0].get()); + return {}; } RENDERER_HANDLER(set_line_join) { state().line_join_style = static_cast(args[0].get()); + return {}; } RENDERER_HANDLER(set_miter_limit) { state().miter_limit = args[0].to_float(); + return {}; } RENDERER_HANDLER(set_dash_pattern) @@ -151,10 +162,11 @@ RENDERER_HANDLER(set_dash_pattern) for (auto& element : *dash_array) pattern.append(element.get()); state().line_dash_pattern = LineDashPattern { pattern, args[1].get() }; + return {}; } -RENDERER_TODO(set_color_rendering_intent); -RENDERER_TODO(set_flatness_tolerance); +RENDERER_TODO(set_color_rendering_intent) +RENDERER_TODO(set_flatness_tolerance) RENDERER_HANDLER(set_graphics_state_from_dict) { @@ -162,27 +174,31 @@ RENDERER_HANDLER(set_graphics_state_from_dict) auto dict_name = MUST(m_document->resolve_to(args[0]))->name(); auto ext_gstate_dict = MUST(m_page.resources->get_dict(m_document, CommonNames::ExtGState)); auto target_dict = MUST(ext_gstate_dict->get_dict(m_document, dict_name)); - set_graphics_state_from_dict(target_dict); + TRY(set_graphics_state_from_dict(target_dict)); + return {}; } RENDERER_HANDLER(path_move) { m_current_path.move_to(map(args[0].to_float(), args[1].to_float())); + return {}; } RENDERER_HANDLER(path_line) { VERIFY(!m_current_path.segments().is_empty()); m_current_path.line_to(map(args[0].to_float(), args[1].to_float())); + return {}; } -RENDERER_TODO(path_cubic_bezier_curve); -RENDERER_TODO(path_cubic_bezier_curve_no_first_control); -RENDERER_TODO(path_cubic_bezier_curve_no_second_control); +RENDERER_TODO(path_cubic_bezier_curve) +RENDERER_TODO(path_cubic_bezier_curve_no_first_control) +RENDERER_TODO(path_cubic_bezier_curve_no_second_control) RENDERER_HANDLER(path_close) { m_current_path.close(); + return {}; } RENDERER_HANDLER(path_append_rect) @@ -195,63 +211,74 @@ RENDERER_HANDLER(path_append_rect) m_current_path.line_to({ pos.x() + size.width(), pos.y() + size.height() }); m_current_path.line_to({ pos.x(), pos.y() + size.height() }); m_current_path.close(); + return {}; } RENDERER_HANDLER(path_stroke) { m_painter.stroke_path(m_current_path, state().stroke_color, state().line_width); m_current_path.clear(); + return {}; } RENDERER_HANDLER(path_close_and_stroke) { m_current_path.close(); - handle_path_stroke(args); + TRY(handle_path_stroke(args)); + return {}; } RENDERER_HANDLER(path_fill_nonzero) { m_painter.fill_path(m_current_path, state().paint_color, Gfx::Painter::WindingRule::Nonzero); m_current_path.clear(); + return {}; } RENDERER_HANDLER(path_fill_nonzero_deprecated) { - handle_path_fill_nonzero(args); + TRY(handle_path_fill_nonzero(args)); + return {}; } RENDERER_HANDLER(path_fill_evenodd) { m_painter.fill_path(m_current_path, state().paint_color, Gfx::Painter::WindingRule::EvenOdd); m_current_path.clear(); + return {}; } RENDERER_HANDLER(path_fill_stroke_nonzero) { m_painter.stroke_path(m_current_path, state().stroke_color, state().line_width); - handle_path_fill_nonzero(args); + TRY(handle_path_fill_nonzero(args)); + return {}; } RENDERER_HANDLER(path_fill_stroke_evenodd) { m_painter.stroke_path(m_current_path, state().stroke_color, state().line_width); - handle_path_fill_evenodd(args); + TRY(handle_path_fill_evenodd(args)); + return {}; } RENDERER_HANDLER(path_close_fill_stroke_nonzero) { m_current_path.close(); - handle_path_fill_stroke_nonzero(args); + TRY(handle_path_fill_stroke_nonzero(args)); + return {}; } RENDERER_HANDLER(path_close_fill_stroke_evenodd) { m_current_path.close(); - handle_path_fill_stroke_evenodd(args); + TRY(handle_path_fill_stroke_evenodd(args)); + return {}; } RENDERER_HANDLER(path_end) { + return {}; } RENDERER_HANDLER(path_intersect_clip_nonzero) @@ -259,6 +286,7 @@ RENDERER_HANDLER(path_intersect_clip_nonzero) // FIXME: Support arbitrary path clipping in the painter and utilize that here auto bounding_box = map(m_current_path.bounding_box()); m_painter.add_clip_rect(bounding_box.to_type()); + return {}; } RENDERER_HANDLER(path_intersect_clip_evenodd) @@ -266,38 +294,45 @@ RENDERER_HANDLER(path_intersect_clip_evenodd) // FIXME: Support arbitrary path clipping in the painter and utilize that here auto bounding_box = map(m_current_path.bounding_box()); m_painter.add_clip_rect(bounding_box.to_type()); + return {}; } RENDERER_HANDLER(text_begin) { m_text_matrix = Gfx::AffineTransform(); m_text_line_matrix = Gfx::AffineTransform(); + return {}; } RENDERER_HANDLER(text_end) { // FIXME: Do we need to do anything here? + return {}; } RENDERER_HANDLER(text_set_char_space) { text_state().character_spacing = args[0].to_float(); + return {}; } RENDERER_HANDLER(text_set_word_space) { text_state().word_spacing = args[0].to_float(); + return {}; } RENDERER_HANDLER(text_set_horizontal_scale) { m_text_rendering_matrix_is_dirty = true; text_state().horizontal_scaling = args[0].to_float() / 100.0f; + return {}; } RENDERER_HANDLER(text_set_leading) { text_state().leading = args[0].to_float(); + return {}; } RENDERER_HANDLER(text_set_font) @@ -330,17 +365,20 @@ RENDERER_HANDLER(text_set_font) text_state().font_variant = font_variant; m_text_rendering_matrix_is_dirty = true; + return {}; } RENDERER_HANDLER(text_set_rendering_mode) { text_state().rendering_mode = static_cast(args[0].get()); + return {}; } RENDERER_HANDLER(text_set_rise) { m_text_rendering_matrix_is_dirty = true; text_state().rise = args[0].to_float(); + return {}; } RENDERER_HANDLER(text_next_line_offset) @@ -350,12 +388,14 @@ RENDERER_HANDLER(text_next_line_offset) m_text_matrix = transform; m_text_line_matrix = transform; m_text_rendering_matrix_is_dirty = true; + return {}; } RENDERER_HANDLER(text_next_line_and_set_leading) { text_state().leading = -args[1].to_float(); - handle_text_next_line_offset(args); + TRY(handle_text_next_line_offset(args)); + return {}; } RENDERER_HANDLER(text_set_matrix_and_line_matrix) @@ -370,26 +410,30 @@ RENDERER_HANDLER(text_set_matrix_and_line_matrix) m_text_line_matrix = new_transform; m_text_matrix = new_transform; m_text_rendering_matrix_is_dirty = true; + return {}; } RENDERER_HANDLER(text_next_line) { - handle_text_next_line_offset({ 0.0f, -text_state().leading }); + TRY(handle_text_next_line_offset({ 0.0f, -text_state().leading })); + return {}; } RENDERER_HANDLER(text_show_string) { auto text = MUST(m_document->resolve_to(args[0]))->string(); show_text(text); + return {}; } RENDERER_HANDLER(text_next_line_show_string) { - handle_text_next_line(args); - handle_text_show_string(args); + TRY(handle_text_next_line(args)); + TRY(handle_text_show_string(args)); + return {}; } -RENDERER_TODO(text_next_line_show_string_set_spacing); +RENDERER_TODO(text_next_line_show_string_set_spacing) RENDERER_HANDLER(text_show_string_array) { @@ -406,85 +450,97 @@ RENDERER_HANDLER(text_show_string_array) show_text(str, next_shift); } } + + return {}; } -RENDERER_TODO(type3_font_set_glyph_width); -RENDERER_TODO(type3_font_set_glyph_width_and_bbox); +RENDERER_TODO(type3_font_set_glyph_width) +RENDERER_TODO(type3_font_set_glyph_width_and_bbox) RENDERER_HANDLER(set_stroking_space) { state().stroke_color_space = MUST(get_color_space(args[0])); VERIFY(state().stroke_color_space); + return {}; } RENDERER_HANDLER(set_painting_space) { state().paint_color_space = MUST(get_color_space(args[0])); VERIFY(state().paint_color_space); + return {}; } RENDERER_HANDLER(set_stroking_color) { state().stroke_color = state().stroke_color_space->color(args); + return {}; } -RENDERER_TODO(set_stroking_color_extended); +RENDERER_TODO(set_stroking_color_extended) RENDERER_HANDLER(set_painting_color) { state().paint_color = state().paint_color_space->color(args); + return {}; } -RENDERER_TODO(set_painting_color_extended); +RENDERER_TODO(set_painting_color_extended) RENDERER_HANDLER(set_stroking_color_and_space_to_gray) { state().stroke_color_space = DeviceGrayColorSpace::the(); state().stroke_color = state().stroke_color_space->color(args); + return {}; } RENDERER_HANDLER(set_painting_color_and_space_to_gray) { state().paint_color_space = DeviceGrayColorSpace::the(); state().paint_color = state().paint_color_space->color(args); + return {}; } RENDERER_HANDLER(set_stroking_color_and_space_to_rgb) { state().stroke_color_space = DeviceRGBColorSpace::the(); state().stroke_color = state().stroke_color_space->color(args); + return {}; } RENDERER_HANDLER(set_painting_color_and_space_to_rgb) { state().paint_color_space = DeviceRGBColorSpace::the(); state().paint_color = state().paint_color_space->color(args); + return {}; } RENDERER_HANDLER(set_stroking_color_and_space_to_cmyk) { state().stroke_color_space = DeviceCMYKColorSpace::the(); state().stroke_color = state().stroke_color_space->color(args); + return {}; } RENDERER_HANDLER(set_painting_color_and_space_to_cmyk) { state().paint_color_space = DeviceCMYKColorSpace::the(); state().paint_color = state().paint_color_space->color(args); + return {}; } -RENDERER_TODO(shade); -RENDERER_TODO(inline_image_begin); -RENDERER_TODO(inline_image_begin_data); -RENDERER_TODO(inline_image_end); -RENDERER_TODO(paint_xobject); -RENDERER_TODO(marked_content_point); -RENDERER_TODO(marked_content_designate); -RENDERER_TODO(marked_content_begin); -RENDERER_TODO(marked_content_begin_with_property_list); -RENDERER_TODO(marked_content_end); -RENDERER_TODO(compatibility_begin); -RENDERER_TODO(compatibility_end); +RENDERER_TODO(shade) +RENDERER_TODO(inline_image_begin) +RENDERER_TODO(inline_image_begin_data) +RENDERER_TODO(inline_image_end) +RENDERER_TODO(paint_xobject) +RENDERER_TODO(marked_content_point) +RENDERER_TODO(marked_content_designate) +RENDERER_TODO(marked_content_begin) +RENDERER_TODO(marked_content_begin_with_property_list) +RENDERER_TODO(marked_content_end) +RENDERER_TODO(compatibility_begin) +RENDERER_TODO(compatibility_end) template Gfx::Point Renderer::map(T x, T y) const @@ -505,25 +561,29 @@ Gfx::Rect Renderer::map(Gfx::Rect rect) const return state().ctm.map(rect); } -void Renderer::set_graphics_state_from_dict(NonnullRefPtr dict) +PDFErrorOr Renderer::set_graphics_state_from_dict(NonnullRefPtr dict) { if (dict->contains(CommonNames::LW)) - handle_set_line_width({ dict->get_value(CommonNames::LW) }); + TRY(handle_set_line_width({ dict->get_value(CommonNames::LW) })); if (dict->contains(CommonNames::LC)) - handle_set_line_cap({ dict->get_value(CommonNames::LC) }); + TRY(handle_set_line_cap({ dict->get_value(CommonNames::LC) })); if (dict->contains(CommonNames::LJ)) - handle_set_line_join({ dict->get_value(CommonNames::LJ) }); + TRY(handle_set_line_join({ dict->get_value(CommonNames::LJ) })); if (dict->contains(CommonNames::ML)) - handle_set_miter_limit({ dict->get_value(CommonNames::ML) }); + TRY(handle_set_miter_limit({ dict->get_value(CommonNames::ML) })); - if (dict->contains(CommonNames::D)) - handle_set_dash_pattern(MUST(dict->get_array(m_document, CommonNames::D))->elements()); + if (dict->contains(CommonNames::D)) { + auto array = MUST(dict->get_array(m_document, CommonNames::D)); + TRY(handle_set_dash_pattern(array->elements())); + } if (dict->contains(CommonNames::FL)) - handle_set_flatness_tolerance({ dict->get_value(CommonNames::FL) }); + TRY(handle_set_flatness_tolerance({ dict->get_value(CommonNames::FL) })); + + return {}; } void Renderer::show_text(String const& string, float shift) diff --git a/Userland/Libraries/LibPDF/Renderer.h b/Userland/Libraries/LibPDF/Renderer.h index d35c5a57a7..fe0b4f27d8 100644 --- a/Userland/Libraries/LibPDF/Renderer.h +++ b/Userland/Libraries/LibPDF/Renderer.h @@ -79,22 +79,22 @@ struct GraphicsState { class Renderer { public: - static void render(Document&, Page const&, RefPtr); + static PDFErrorOr render(Document&, Page const&, RefPtr); private: Renderer(RefPtr, Page const&, RefPtr); - void render(); + PDFErrorOr render(); - void handle_command(Command const&); + PDFErrorOr handle_command(Command const&); #define V(name, snake_name, symbol) \ - void handle_##snake_name(Vector const& args); + PDFErrorOr handle_##snake_name(Vector const& args); ENUMERATE_COMMANDS(V) #undef V - void handle_text_next_line_show_string(Vector const& args); - void handle_text_next_line_show_string_set_spacing(Vector const& args); + PDFErrorOr handle_text_next_line_show_string(Vector const& args); + PDFErrorOr handle_text_next_line_show_string_set_spacing(Vector const& args); - void set_graphics_state_from_dict(NonnullRefPtr); + PDFErrorOr set_graphics_state_from_dict(NonnullRefPtr); // shift is the manual advance given in the TJ command array void show_text(String const&, float shift = 0.0f); PDFErrorOr> get_color_space(Value const&);