serenity/Userland/Applications/PDFViewer/PDFViewer.h
Nico Weber 21917e7b1e LibPDF+PDFViewer+MacPDF: Don't draw hidden text by default
Text can be rendered in various ways in PDFs: Filled, stroked,
both filled and stroked, set as clipping path, hidden, or
some combinations thereof.

We don't implement any of this at the moment except "filled".

Hidden text is used in scanned documents: The image of the scan is
drawn in the background, and then OCRd text is "drawn" as hidden
on top of the scanned bitmap. That way, the (hidden) text can be
selected and copied, and it looks like you're selecting text from
the scanned bitmap. Find-in-page also works similarly. (We currently
have neither text selection nor find-in-page, but one day we will.)

Now that we have pretty good support for CCITT and are growing some
support for JBIG2, we now draw both the scanned background image
as well as the foreground text. They're not always perfectly aligned.

This change makes it so that we don't render text that's marked as
hidden. (We still do most of the coordinate math, which will probably
come in handy at some point when we implement text selection.)

This makes these scanned documents appear as they're supposed to
appear (at least in documents where we manage to decode the background
bitmap).

This also adds a debug option to force rendering of hidden text.
2024-03-16 13:10:48 -04:00

114 lines
3.6 KiB
C++

/*
* Copyright (c) 2021-2022, Matthew Olsson <mattco@serenityos.org>
* Copyright (c) 2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/HashMap.h>
#include <LibGUI/AbstractScrollableWidget.h>
#include <LibGfx/Bitmap.h>
#include <LibPDF/Document.h>
#include <LibPDF/Renderer.h>
static constexpr size_t initial_zoom_level = 8;
struct PageDimensionCache {
// Fixed for a given document
struct PageInfo {
Gfx::FloatSize size;
int rotation;
};
// Based on PageInfo, also depends on some dynamic factors like
// zoom level and app size
struct RenderInfo {
Gfx::FloatSize size;
float total_height_before_this_page;
};
Vector<PageInfo> page_info;
Vector<RenderInfo> render_info;
float max_width;
float total_height;
};
class PDFViewer : public GUI::AbstractScrollableWidget {
C_OBJECT(PDFViewer)
public:
enum class PageViewMode {
Single,
Multiple,
};
virtual ~PDFViewer() override = default;
ALWAYS_INLINE u32 current_page() const { return m_current_page_index; }
void set_current_page(u32 current_page);
ALWAYS_INLINE RefPtr<PDF::Document> const& document() const { return m_document; }
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();
void reset_zoom();
void rotate(int degrees);
PageViewMode page_view_mode() const { return m_page_view_mode; }
void set_page_view_mode(PageViewMode);
bool show_rendering_diagnostics() const { return m_rendering_preferences.show_diagnostics; }
void set_show_rendering_diagnostics(bool);
bool show_clipping_paths() const { return m_rendering_preferences.show_clipping_paths; }
void set_show_clipping_paths(bool);
bool show_images() const { return m_rendering_preferences.show_images; }
void set_show_images(bool);
bool show_hidden_text() const { return m_rendering_preferences.show_hidden_text; }
void set_show_hidden_text(bool);
bool clip_images() const { return m_rendering_preferences.clip_images; }
void set_clip_images(bool);
bool clip_paths() const { return m_rendering_preferences.clip_paths; }
void set_clip_paths(bool);
bool clip_text() const { return m_rendering_preferences.clip_text; }
void set_clip_text(bool);
protected:
PDFViewer();
virtual void paint_event(GUI::PaintEvent&) override;
virtual void resize_event(GUI::ResizeEvent&) override;
virtual void mousewheel_event(GUI::MouseEvent&) override;
virtual void mousedown_event(GUI::MouseEvent&) override;
virtual void mouseup_event(GUI::MouseEvent&) override;
virtual void mousemove_event(GUI::MouseEvent&) override;
virtual void timer_event(Core::TimerEvent&) override;
private:
struct RenderedPage {
NonnullRefPtr<Gfx::Bitmap> bitmap;
int rotation;
};
PDF::PDFErrorOr<NonnullRefPtr<Gfx::Bitmap>> get_rendered_page(u32 index);
PDF::PDFErrorOr<NonnullRefPtr<Gfx::Bitmap>> render_page(u32 page_index);
PDF::PDFErrorOr<void> cache_page_dimensions(bool recalculate_fixed_info = false);
void change_page(u32 new_page);
RefPtr<PDF::Document> m_document;
u32 m_current_page_index { 0 };
Vector<HashMap<u32, RenderedPage>> m_rendered_page_list;
u8 m_zoom_level { initial_zoom_level };
PageDimensionCache m_page_dimension_cache;
PageViewMode m_page_view_mode;
PDF::RenderingPreferences m_rendering_preferences;
Gfx::IntPoint m_pan_starting_position;
int m_rotations { 0 };
};