mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 17:12:43 +00:00 
			
		
		
		
	LibPDF: Propagate errors in Renderer/PDFViewer
This commit is contained in:
		
							parent
							
								
									d82bd885ce
								
							
						
					
					
						commit
						b240d23a87
					
				
					 4 changed files with 140 additions and 72 deletions
				
			
		|  | @ -1,5 +1,5 @@ | ||||||
| /*
 | /*
 | ||||||
|  * Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org> |  * Copyright (c) 2021-2022, Matthew Olsson <mattco@serenityos.org> | ||||||
|  * Copyright (c) 2022, the SerenityOS developers. |  * Copyright (c) 2022, the SerenityOS developers. | ||||||
|  * |  * | ||||||
|  * SPDX-License-Identifier: BSD-2-Clause |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  | @ -8,6 +8,7 @@ | ||||||
| #include "PDFViewer.h" | #include "PDFViewer.h" | ||||||
| #include <AK/Array.h> | #include <AK/Array.h> | ||||||
| #include <LibGUI/Action.h> | #include <LibGUI/Action.h> | ||||||
|  | #include <LibGUI/MessageBox.h> | ||||||
| #include <LibGUI/Painter.h> | #include <LibGUI/Painter.h> | ||||||
| #include <LibPDF/Renderer.h> | #include <LibPDF/Renderer.h> | ||||||
| 
 | 
 | ||||||
|  | @ -56,15 +57,15 @@ void PDFViewer::set_document(RefPtr<PDF::Document> document) | ||||||
|     update(); |     update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RefPtr<Gfx::Bitmap> PDFViewer::get_rendered_page(u32 index) | PDF::PDFErrorOr<NonnullRefPtr<Gfx::Bitmap>> PDFViewer::get_rendered_page(u32 index) | ||||||
| { | { | ||||||
|     auto& rendered_page_map = m_rendered_page_list[index]; |     auto& rendered_page_map = m_rendered_page_list[index]; | ||||||
|     auto existing_rendered_page = rendered_page_map.get(m_zoom_level); |     auto existing_rendered_page = rendered_page_map.get(m_zoom_level); | ||||||
|     if (existing_rendered_page.has_value() && existing_rendered_page.value().rotation == m_rotations) |     if (existing_rendered_page.has_value() && existing_rendered_page.value().rotation == m_rotations) | ||||||
|         return existing_rendered_page.value().bitmap; |         return existing_rendered_page.value().bitmap; | ||||||
| 
 | 
 | ||||||
|     // FIXME: Propogate errors in the Renderer
 |     auto page = TRY(m_document->get_page(index)); | ||||||
|     auto rendered_page = render_page(MUST(m_document->get_page(index))); |     auto rendered_page = TRY(render_page(page)); | ||||||
|     rendered_page_map.set(m_zoom_level, { rendered_page, m_rotations }); |     rendered_page_map.set(m_zoom_level, { rendered_page, m_rotations }); | ||||||
|     return rendered_page; |     return rendered_page; | ||||||
| } | } | ||||||
|  | @ -81,7 +82,14 @@ void PDFViewer::paint_event(GUI::PaintEvent& event) | ||||||
|     if (!m_document) |     if (!m_document) | ||||||
|         return; |         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()); |     set_content_size(page->size()); | ||||||
| 
 | 
 | ||||||
|     painter.translate(frame_thickness(), frame_thickness()); |     painter.translate(frame_thickness(), frame_thickness()); | ||||||
|  | @ -196,7 +204,7 @@ void PDFViewer::rotate(int degrees) | ||||||
|     update(); |     update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RefPtr<Gfx::Bitmap> PDFViewer::render_page(const PDF::Page& page) | PDF::PDFErrorOr<NonnullRefPtr<Gfx::Bitmap>> PDFViewer::render_page(const PDF::Page& page) | ||||||
| { | { | ||||||
|     auto zoom_scale_factor = static_cast<float>(zoom_levels[m_zoom_level]) / 100.0f; |     auto zoom_scale_factor = static_cast<float>(zoom_levels[m_zoom_level]) / 100.0f; | ||||||
| 
 | 
 | ||||||
|  | @ -208,7 +216,7 @@ RefPtr<Gfx::Bitmap> PDFViewer::render_page(const PDF::Page& page) | ||||||
|     auto width = height / page_scale_factor; |     auto width = height / page_scale_factor; | ||||||
|     auto bitmap = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, { width, height }).release_value_but_fixme_should_propagate_errors(); |     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) { |     if (page.rotate + m_rotations != 0) { | ||||||
|         int rotation_count = ((page.rotate + m_rotations) / 90) % 4; |         int rotation_count = ((page.rotate + m_rotations) / 90) % 4; | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| /*
 | /*
 | ||||||
|  * Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org> |  * Copyright (c) 2021-2022, Matthew Olsson <mattco@serenityos.org> | ||||||
|  * Copyright (c) 2022, the SerenityOS developers. |  * Copyright (c) 2022, the SerenityOS developers. | ||||||
|  * |  * | ||||||
|  * SPDX-License-Identifier: BSD-2-Clause |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  | @ -45,12 +45,12 @@ protected: | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     struct RenderedPage { |     struct RenderedPage { | ||||||
|         RefPtr<Gfx::Bitmap> bitmap; |         NonnullRefPtr<Gfx::Bitmap> bitmap; | ||||||
|         int rotation; |         int rotation; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     RefPtr<Gfx::Bitmap> get_rendered_page(u32 index); |     PDF::PDFErrorOr<NonnullRefPtr<Gfx::Bitmap>> get_rendered_page(u32 index); | ||||||
|     RefPtr<Gfx::Bitmap> render_page(const PDF::Page&); |     PDF::PDFErrorOr<NonnullRefPtr<Gfx::Bitmap>> render_page(const PDF::Page&); | ||||||
| 
 | 
 | ||||||
|     RefPtr<PDF::Document> m_document; |     RefPtr<PDF::Document> m_document; | ||||||
|     u32 m_current_page_index { 0 }; |     u32 m_current_page_index { 0 }; | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ | ||||||
| #include <LibPDF/Renderer.h> | #include <LibPDF/Renderer.h> | ||||||
| 
 | 
 | ||||||
| #define RENDERER_HANDLER(name) \ | #define RENDERER_HANDLER(name) \ | ||||||
|     void Renderer::handle_##name([[maybe_unused]] Vector<Value> const& args) |     PDFErrorOr<void> Renderer::handle_##name([[maybe_unused]] Vector<Value> const& args) | ||||||
| 
 | 
 | ||||||
| #define RENDERER_TODO(name)                                         \ | #define RENDERER_TODO(name)                                         \ | ||||||
|     RENDERER_HANDLER(name)                                          \ |     RENDERER_HANDLER(name)                                          \ | ||||||
|  | @ -20,9 +20,9 @@ | ||||||
| 
 | 
 | ||||||
| namespace PDF { | namespace PDF { | ||||||
| 
 | 
 | ||||||
| void Renderer::render(Document& document, Page const& page, RefPtr<Gfx::Bitmap> bitmap) | PDFErrorOr<void> Renderer::render(Document& document, Page const& page, RefPtr<Gfx::Bitmap> bitmap) | ||||||
| { | { | ||||||
|     Renderer(document, page, bitmap).render(); |     return Renderer(document, page, bitmap).render(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Renderer::Renderer(RefPtr<Document> document, Page const& page, RefPtr<Gfx::Bitmap> bitmap) | Renderer::Renderer(RefPtr<Document> document, Page const& page, RefPtr<Gfx::Bitmap> bitmap) | ||||||
|  | @ -56,7 +56,7 @@ Renderer::Renderer(RefPtr<Document> document, Page const& page, RefPtr<Gfx::Bitm | ||||||
|     m_bitmap->fill(Gfx::Color::NamedColor::White); |     m_bitmap->fill(Gfx::Color::NamedColor::White); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Renderer::render() | PDFErrorOr<void> Renderer::render() | ||||||
| { | { | ||||||
|     // Use our own vector, as the /Content can be an array with multiple
 |     // Use our own vector, as the /Content can be an array with multiple
 | ||||||
|     // streams which gets concatenated
 |     // streams which gets concatenated
 | ||||||
|  | @ -68,7 +68,7 @@ void Renderer::render() | ||||||
|     if (m_page.contents->is<ArrayObject>()) { |     if (m_page.contents->is<ArrayObject>()) { | ||||||
|         auto contents = m_page.contents->cast<ArrayObject>(); |         auto contents = m_page.contents->cast<ArrayObject>(); | ||||||
|         for (auto& ref : *contents) { |         for (auto& ref : *contents) { | ||||||
|             auto bytes = MUST(m_document->resolve_to<StreamObject>(ref))->bytes(); |             auto bytes = TRY(m_document->resolve_to<StreamObject>(ref))->bytes(); | ||||||
|             byte_buffer.append(bytes.data(), bytes.size()); |             byte_buffer.append(bytes.data(), bytes.size()); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|  | @ -76,38 +76,44 @@ void Renderer::render() | ||||||
|         byte_buffer.append(bytes.data(), bytes.size()); |         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) |     for (auto& command : commands) | ||||||
|         handle_command(command); |         TRY(handle_command(command)); | ||||||
|  | 
 | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Renderer::handle_command(Command const& command) | PDFErrorOr<void> Renderer::handle_command(Command const& command) | ||||||
| { | { | ||||||
|     switch (command.command_type()) { |     switch (command.command_type()) { | ||||||
| #define V(name, snake_name, symbol)               \ | #define V(name, snake_name, symbol)                    \ | ||||||
|     case CommandType::name:                       \ |     case CommandType::name:                            \ | ||||||
|         handle_##snake_name(command.arguments()); \ |         TRY(handle_##snake_name(command.arguments())); \ | ||||||
|         break; |         break; | ||||||
|         ENUMERATE_COMMANDS(V) |         ENUMERATE_COMMANDS(V) | ||||||
| #undef V | #undef V | ||||||
|     case CommandType::TextNextLineShowString: |     case CommandType::TextNextLineShowString: | ||||||
|         handle_text_next_line_show_string(command.arguments()); |         TRY(handle_text_next_line_show_string(command.arguments())); | ||||||
|         break; |         break; | ||||||
|     case CommandType::TextNextLineShowStringSetSpacing: |     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; |         break; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(save_state) | RENDERER_HANDLER(save_state) | ||||||
| { | { | ||||||
|     m_graphics_state_stack.append(state()); |     m_graphics_state_stack.append(state()); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(restore_state) | RENDERER_HANDLER(restore_state) | ||||||
| { | { | ||||||
|     m_graphics_state_stack.take_last(); |     m_graphics_state_stack.take_last(); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(concatenate_matrix) | RENDERER_HANDLER(concatenate_matrix) | ||||||
|  | @ -122,26 +128,31 @@ RENDERER_HANDLER(concatenate_matrix) | ||||||
| 
 | 
 | ||||||
|     state().ctm.multiply(new_transform); |     state().ctm.multiply(new_transform); | ||||||
|     m_text_rendering_matrix_is_dirty = true; |     m_text_rendering_matrix_is_dirty = true; | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(set_line_width) | RENDERER_HANDLER(set_line_width) | ||||||
| { | { | ||||||
|     state().line_width = args[0].to_float(); |     state().line_width = args[0].to_float(); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(set_line_cap) | RENDERER_HANDLER(set_line_cap) | ||||||
| { | { | ||||||
|     state().line_cap_style = static_cast<LineCapStyle>(args[0].get<int>()); |     state().line_cap_style = static_cast<LineCapStyle>(args[0].get<int>()); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(set_line_join) | RENDERER_HANDLER(set_line_join) | ||||||
| { | { | ||||||
|     state().line_join_style = static_cast<LineJoinStyle>(args[0].get<int>()); |     state().line_join_style = static_cast<LineJoinStyle>(args[0].get<int>()); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(set_miter_limit) | RENDERER_HANDLER(set_miter_limit) | ||||||
| { | { | ||||||
|     state().miter_limit = args[0].to_float(); |     state().miter_limit = args[0].to_float(); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(set_dash_pattern) | RENDERER_HANDLER(set_dash_pattern) | ||||||
|  | @ -151,10 +162,11 @@ RENDERER_HANDLER(set_dash_pattern) | ||||||
|     for (auto& element : *dash_array) |     for (auto& element : *dash_array) | ||||||
|         pattern.append(element.get<int>()); |         pattern.append(element.get<int>()); | ||||||
|     state().line_dash_pattern = LineDashPattern { pattern, args[1].get<int>() }; |     state().line_dash_pattern = LineDashPattern { pattern, args[1].get<int>() }; | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_TODO(set_color_rendering_intent); | RENDERER_TODO(set_color_rendering_intent) | ||||||
| RENDERER_TODO(set_flatness_tolerance); | RENDERER_TODO(set_flatness_tolerance) | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(set_graphics_state_from_dict) | 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<NameObject>(args[0]))->name(); |     auto dict_name = MUST(m_document->resolve_to<NameObject>(args[0]))->name(); | ||||||
|     auto ext_gstate_dict = MUST(m_page.resources->get_dict(m_document, CommonNames::ExtGState)); |     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)); |     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) | RENDERER_HANDLER(path_move) | ||||||
| { | { | ||||||
|     m_current_path.move_to(map(args[0].to_float(), args[1].to_float())); |     m_current_path.move_to(map(args[0].to_float(), args[1].to_float())); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(path_line) | RENDERER_HANDLER(path_line) | ||||||
| { | { | ||||||
|     VERIFY(!m_current_path.segments().is_empty()); |     VERIFY(!m_current_path.segments().is_empty()); | ||||||
|     m_current_path.line_to(map(args[0].to_float(), args[1].to_float())); |     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) | ||||||
| RENDERER_TODO(path_cubic_bezier_curve_no_first_control); | RENDERER_TODO(path_cubic_bezier_curve_no_first_control) | ||||||
| RENDERER_TODO(path_cubic_bezier_curve_no_second_control); | RENDERER_TODO(path_cubic_bezier_curve_no_second_control) | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(path_close) | RENDERER_HANDLER(path_close) | ||||||
| { | { | ||||||
|     m_current_path.close(); |     m_current_path.close(); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(path_append_rect) | 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() + size.width(), pos.y() + size.height() }); | ||||||
|     m_current_path.line_to({ pos.x(), pos.y() + size.height() }); |     m_current_path.line_to({ pos.x(), pos.y() + size.height() }); | ||||||
|     m_current_path.close(); |     m_current_path.close(); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(path_stroke) | RENDERER_HANDLER(path_stroke) | ||||||
| { | { | ||||||
|     m_painter.stroke_path(m_current_path, state().stroke_color, state().line_width); |     m_painter.stroke_path(m_current_path, state().stroke_color, state().line_width); | ||||||
|     m_current_path.clear(); |     m_current_path.clear(); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(path_close_and_stroke) | RENDERER_HANDLER(path_close_and_stroke) | ||||||
| { | { | ||||||
|     m_current_path.close(); |     m_current_path.close(); | ||||||
|     handle_path_stroke(args); |     TRY(handle_path_stroke(args)); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(path_fill_nonzero) | RENDERER_HANDLER(path_fill_nonzero) | ||||||
| { | { | ||||||
|     m_painter.fill_path(m_current_path, state().paint_color, Gfx::Painter::WindingRule::Nonzero); |     m_painter.fill_path(m_current_path, state().paint_color, Gfx::Painter::WindingRule::Nonzero); | ||||||
|     m_current_path.clear(); |     m_current_path.clear(); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(path_fill_nonzero_deprecated) | RENDERER_HANDLER(path_fill_nonzero_deprecated) | ||||||
| { | { | ||||||
|     handle_path_fill_nonzero(args); |     TRY(handle_path_fill_nonzero(args)); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(path_fill_evenodd) | RENDERER_HANDLER(path_fill_evenodd) | ||||||
| { | { | ||||||
|     m_painter.fill_path(m_current_path, state().paint_color, Gfx::Painter::WindingRule::EvenOdd); |     m_painter.fill_path(m_current_path, state().paint_color, Gfx::Painter::WindingRule::EvenOdd); | ||||||
|     m_current_path.clear(); |     m_current_path.clear(); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(path_fill_stroke_nonzero) | RENDERER_HANDLER(path_fill_stroke_nonzero) | ||||||
| { | { | ||||||
|     m_painter.stroke_path(m_current_path, state().stroke_color, state().line_width); |     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) | RENDERER_HANDLER(path_fill_stroke_evenodd) | ||||||
| { | { | ||||||
|     m_painter.stroke_path(m_current_path, state().stroke_color, state().line_width); |     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) | RENDERER_HANDLER(path_close_fill_stroke_nonzero) | ||||||
| { | { | ||||||
|     m_current_path.close(); |     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) | RENDERER_HANDLER(path_close_fill_stroke_evenodd) | ||||||
| { | { | ||||||
|     m_current_path.close(); |     m_current_path.close(); | ||||||
|     handle_path_fill_stroke_evenodd(args); |     TRY(handle_path_fill_stroke_evenodd(args)); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(path_end) | RENDERER_HANDLER(path_end) | ||||||
| { | { | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(path_intersect_clip_nonzero) | 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
 |     // FIXME: Support arbitrary path clipping in the painter and utilize that here
 | ||||||
|     auto bounding_box = map(m_current_path.bounding_box()); |     auto bounding_box = map(m_current_path.bounding_box()); | ||||||
|     m_painter.add_clip_rect(bounding_box.to_type<int>()); |     m_painter.add_clip_rect(bounding_box.to_type<int>()); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(path_intersect_clip_evenodd) | 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
 |     // FIXME: Support arbitrary path clipping in the painter and utilize that here
 | ||||||
|     auto bounding_box = map(m_current_path.bounding_box()); |     auto bounding_box = map(m_current_path.bounding_box()); | ||||||
|     m_painter.add_clip_rect(bounding_box.to_type<int>()); |     m_painter.add_clip_rect(bounding_box.to_type<int>()); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(text_begin) | RENDERER_HANDLER(text_begin) | ||||||
| { | { | ||||||
|     m_text_matrix = Gfx::AffineTransform(); |     m_text_matrix = Gfx::AffineTransform(); | ||||||
|     m_text_line_matrix = Gfx::AffineTransform(); |     m_text_line_matrix = Gfx::AffineTransform(); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(text_end) | RENDERER_HANDLER(text_end) | ||||||
| { | { | ||||||
|     // FIXME: Do we need to do anything here?
 |     // FIXME: Do we need to do anything here?
 | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(text_set_char_space) | RENDERER_HANDLER(text_set_char_space) | ||||||
| { | { | ||||||
|     text_state().character_spacing = args[0].to_float(); |     text_state().character_spacing = args[0].to_float(); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(text_set_word_space) | RENDERER_HANDLER(text_set_word_space) | ||||||
| { | { | ||||||
|     text_state().word_spacing = args[0].to_float(); |     text_state().word_spacing = args[0].to_float(); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(text_set_horizontal_scale) | RENDERER_HANDLER(text_set_horizontal_scale) | ||||||
| { | { | ||||||
|     m_text_rendering_matrix_is_dirty = true; |     m_text_rendering_matrix_is_dirty = true; | ||||||
|     text_state().horizontal_scaling = args[0].to_float() / 100.0f; |     text_state().horizontal_scaling = args[0].to_float() / 100.0f; | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(text_set_leading) | RENDERER_HANDLER(text_set_leading) | ||||||
| { | { | ||||||
|     text_state().leading = args[0].to_float(); |     text_state().leading = args[0].to_float(); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(text_set_font) | RENDERER_HANDLER(text_set_font) | ||||||
|  | @ -330,17 +365,20 @@ RENDERER_HANDLER(text_set_font) | ||||||
|     text_state().font_variant = font_variant; |     text_state().font_variant = font_variant; | ||||||
| 
 | 
 | ||||||
|     m_text_rendering_matrix_is_dirty = true; |     m_text_rendering_matrix_is_dirty = true; | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(text_set_rendering_mode) | RENDERER_HANDLER(text_set_rendering_mode) | ||||||
| { | { | ||||||
|     text_state().rendering_mode = static_cast<TextRenderingMode>(args[0].get<int>()); |     text_state().rendering_mode = static_cast<TextRenderingMode>(args[0].get<int>()); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(text_set_rise) | RENDERER_HANDLER(text_set_rise) | ||||||
| { | { | ||||||
|     m_text_rendering_matrix_is_dirty = true; |     m_text_rendering_matrix_is_dirty = true; | ||||||
|     text_state().rise = args[0].to_float(); |     text_state().rise = args[0].to_float(); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(text_next_line_offset) | RENDERER_HANDLER(text_next_line_offset) | ||||||
|  | @ -350,12 +388,14 @@ RENDERER_HANDLER(text_next_line_offset) | ||||||
|     m_text_matrix = transform; |     m_text_matrix = transform; | ||||||
|     m_text_line_matrix = transform; |     m_text_line_matrix = transform; | ||||||
|     m_text_rendering_matrix_is_dirty = true; |     m_text_rendering_matrix_is_dirty = true; | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(text_next_line_and_set_leading) | RENDERER_HANDLER(text_next_line_and_set_leading) | ||||||
| { | { | ||||||
|     text_state().leading = -args[1].to_float(); |     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) | 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_line_matrix = new_transform; | ||||||
|     m_text_matrix = new_transform; |     m_text_matrix = new_transform; | ||||||
|     m_text_rendering_matrix_is_dirty = true; |     m_text_rendering_matrix_is_dirty = true; | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(text_next_line) | 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) | RENDERER_HANDLER(text_show_string) | ||||||
| { | { | ||||||
|     auto text = MUST(m_document->resolve_to<StringObject>(args[0]))->string(); |     auto text = MUST(m_document->resolve_to<StringObject>(args[0]))->string(); | ||||||
|     show_text(text); |     show_text(text); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(text_next_line_show_string) | RENDERER_HANDLER(text_next_line_show_string) | ||||||
| { | { | ||||||
|     handle_text_next_line(args); |     TRY(handle_text_next_line(args)); | ||||||
|     handle_text_show_string(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) | RENDERER_HANDLER(text_show_string_array) | ||||||
| { | { | ||||||
|  | @ -406,85 +450,97 @@ RENDERER_HANDLER(text_show_string_array) | ||||||
|             show_text(str, next_shift); |             show_text(str, next_shift); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_TODO(type3_font_set_glyph_width); | RENDERER_TODO(type3_font_set_glyph_width) | ||||||
| RENDERER_TODO(type3_font_set_glyph_width_and_bbox); | RENDERER_TODO(type3_font_set_glyph_width_and_bbox) | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(set_stroking_space) | RENDERER_HANDLER(set_stroking_space) | ||||||
| { | { | ||||||
|     state().stroke_color_space = MUST(get_color_space(args[0])); |     state().stroke_color_space = MUST(get_color_space(args[0])); | ||||||
|     VERIFY(state().stroke_color_space); |     VERIFY(state().stroke_color_space); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(set_painting_space) | RENDERER_HANDLER(set_painting_space) | ||||||
| { | { | ||||||
|     state().paint_color_space = MUST(get_color_space(args[0])); |     state().paint_color_space = MUST(get_color_space(args[0])); | ||||||
|     VERIFY(state().paint_color_space); |     VERIFY(state().paint_color_space); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(set_stroking_color) | RENDERER_HANDLER(set_stroking_color) | ||||||
| { | { | ||||||
|     state().stroke_color = state().stroke_color_space->color(args); |     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) | RENDERER_HANDLER(set_painting_color) | ||||||
| { | { | ||||||
|     state().paint_color = state().paint_color_space->color(args); |     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) | RENDERER_HANDLER(set_stroking_color_and_space_to_gray) | ||||||
| { | { | ||||||
|     state().stroke_color_space = DeviceGrayColorSpace::the(); |     state().stroke_color_space = DeviceGrayColorSpace::the(); | ||||||
|     state().stroke_color = state().stroke_color_space->color(args); |     state().stroke_color = state().stroke_color_space->color(args); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(set_painting_color_and_space_to_gray) | RENDERER_HANDLER(set_painting_color_and_space_to_gray) | ||||||
| { | { | ||||||
|     state().paint_color_space = DeviceGrayColorSpace::the(); |     state().paint_color_space = DeviceGrayColorSpace::the(); | ||||||
|     state().paint_color = state().paint_color_space->color(args); |     state().paint_color = state().paint_color_space->color(args); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(set_stroking_color_and_space_to_rgb) | RENDERER_HANDLER(set_stroking_color_and_space_to_rgb) | ||||||
| { | { | ||||||
|     state().stroke_color_space = DeviceRGBColorSpace::the(); |     state().stroke_color_space = DeviceRGBColorSpace::the(); | ||||||
|     state().stroke_color = state().stroke_color_space->color(args); |     state().stroke_color = state().stroke_color_space->color(args); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(set_painting_color_and_space_to_rgb) | RENDERER_HANDLER(set_painting_color_and_space_to_rgb) | ||||||
| { | { | ||||||
|     state().paint_color_space = DeviceRGBColorSpace::the(); |     state().paint_color_space = DeviceRGBColorSpace::the(); | ||||||
|     state().paint_color = state().paint_color_space->color(args); |     state().paint_color = state().paint_color_space->color(args); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(set_stroking_color_and_space_to_cmyk) | RENDERER_HANDLER(set_stroking_color_and_space_to_cmyk) | ||||||
| { | { | ||||||
|     state().stroke_color_space = DeviceCMYKColorSpace::the(); |     state().stroke_color_space = DeviceCMYKColorSpace::the(); | ||||||
|     state().stroke_color = state().stroke_color_space->color(args); |     state().stroke_color = state().stroke_color_space->color(args); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_HANDLER(set_painting_color_and_space_to_cmyk) | RENDERER_HANDLER(set_painting_color_and_space_to_cmyk) | ||||||
| { | { | ||||||
|     state().paint_color_space = DeviceCMYKColorSpace::the(); |     state().paint_color_space = DeviceCMYKColorSpace::the(); | ||||||
|     state().paint_color = state().paint_color_space->color(args); |     state().paint_color = state().paint_color_space->color(args); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RENDERER_TODO(shade); | RENDERER_TODO(shade) | ||||||
| RENDERER_TODO(inline_image_begin); | RENDERER_TODO(inline_image_begin) | ||||||
| RENDERER_TODO(inline_image_begin_data); | RENDERER_TODO(inline_image_begin_data) | ||||||
| RENDERER_TODO(inline_image_end); | RENDERER_TODO(inline_image_end) | ||||||
| RENDERER_TODO(paint_xobject); | RENDERER_TODO(paint_xobject) | ||||||
| RENDERER_TODO(marked_content_point); | RENDERER_TODO(marked_content_point) | ||||||
| RENDERER_TODO(marked_content_designate); | RENDERER_TODO(marked_content_designate) | ||||||
| RENDERER_TODO(marked_content_begin); | RENDERER_TODO(marked_content_begin) | ||||||
| RENDERER_TODO(marked_content_begin_with_property_list); | RENDERER_TODO(marked_content_begin_with_property_list) | ||||||
| RENDERER_TODO(marked_content_end); | RENDERER_TODO(marked_content_end) | ||||||
| RENDERER_TODO(compatibility_begin); | RENDERER_TODO(compatibility_begin) | ||||||
| RENDERER_TODO(compatibility_end); | RENDERER_TODO(compatibility_end) | ||||||
| 
 | 
 | ||||||
| template<typename T> | template<typename T> | ||||||
| Gfx::Point<T> Renderer::map(T x, T y) const | Gfx::Point<T> Renderer::map(T x, T y) const | ||||||
|  | @ -505,25 +561,29 @@ Gfx::Rect<T> Renderer::map(Gfx::Rect<T> rect) const | ||||||
|     return state().ctm.map(rect); |     return state().ctm.map(rect); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Renderer::set_graphics_state_from_dict(NonnullRefPtr<DictObject> dict) | PDFErrorOr<void> Renderer::set_graphics_state_from_dict(NonnullRefPtr<DictObject> dict) | ||||||
| { | { | ||||||
|     if (dict->contains(CommonNames::LW)) |     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)) |     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)) |     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)) |     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)) |     if (dict->contains(CommonNames::D)) { | ||||||
|         handle_set_dash_pattern(MUST(dict->get_array(m_document, CommonNames::D))->elements()); |         auto array = MUST(dict->get_array(m_document, CommonNames::D)); | ||||||
|  |         TRY(handle_set_dash_pattern(array->elements())); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if (dict->contains(CommonNames::FL)) |     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) | void Renderer::show_text(String const& string, float shift) | ||||||
|  |  | ||||||
|  | @ -79,22 +79,22 @@ struct GraphicsState { | ||||||
| 
 | 
 | ||||||
| class Renderer { | class Renderer { | ||||||
| public: | public: | ||||||
|     static void render(Document&, Page const&, RefPtr<Gfx::Bitmap>); |     static PDFErrorOr<void> render(Document&, Page const&, RefPtr<Gfx::Bitmap>); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     Renderer(RefPtr<Document>, Page const&, RefPtr<Gfx::Bitmap>); |     Renderer(RefPtr<Document>, Page const&, RefPtr<Gfx::Bitmap>); | ||||||
| 
 | 
 | ||||||
|     void render(); |     PDFErrorOr<void> render(); | ||||||
| 
 | 
 | ||||||
|     void handle_command(Command const&); |     PDFErrorOr<void> handle_command(Command const&); | ||||||
| #define V(name, snake_name, symbol) \ | #define V(name, snake_name, symbol) \ | ||||||
|     void handle_##snake_name(Vector<Value> const& args); |     PDFErrorOr<void> handle_##snake_name(Vector<Value> const& args); | ||||||
|     ENUMERATE_COMMANDS(V) |     ENUMERATE_COMMANDS(V) | ||||||
| #undef V | #undef V | ||||||
|     void handle_text_next_line_show_string(Vector<Value> const& args); |     PDFErrorOr<void> handle_text_next_line_show_string(Vector<Value> const& args); | ||||||
|     void handle_text_next_line_show_string_set_spacing(Vector<Value> const& args); |     PDFErrorOr<void> handle_text_next_line_show_string_set_spacing(Vector<Value> const& args); | ||||||
| 
 | 
 | ||||||
|     void set_graphics_state_from_dict(NonnullRefPtr<DictObject>); |     PDFErrorOr<void> set_graphics_state_from_dict(NonnullRefPtr<DictObject>); | ||||||
|     // shift is the manual advance given in the TJ command array
 |     // shift is the manual advance given in the TJ command array
 | ||||||
|     void show_text(String const&, float shift = 0.0f); |     void show_text(String const&, float shift = 0.0f); | ||||||
|     PDFErrorOr<NonnullRefPtr<ColorSpace>> get_color_space(Value const&); |     PDFErrorOr<NonnullRefPtr<ColorSpace>> get_color_space(Value const&); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Matthew Olsson
						Matthew Olsson