LibPDF: Try harder to use a RAII object to restore state

Follow-up to #21489. There, I made us use a RAII object.

That's great, but if the embedded instruction stream pushes
its own graphics state, then an early return would cause us to
not process graphics state pop instructions in the embedded stream.

To fix this, remember the graphics stack depth before entering
the nested instruction stream, and explicitly shrink the stack back
to that size upon exit.

Enables us to render all pages of
https://devstreaming-cdn.apple.com/videos/wwdc/2017/821kjtggolzxsv/821/821_get_started_with_display_p3.pdf
without crashing.
This commit is contained in:
Nico Weber 2023-10-19 07:54:55 -04:00 committed by Tim Flynn
parent c5a4c22fa7
commit 609e640530
Notes: sideshowbarker 2024-07-18 05:01:22 +09:00

View File

@ -646,20 +646,26 @@ RENDERER_HANDLER(paint_xobject)
// Use a RAII object to restore the graphics state, to make sure it gets restored even if
// a TRY(handle_operator()) causes us to exit the operators loop early.
// Explicitly resize stack size at the end so that if the recursive document contains
// `q q unsupportedop Q Q`, we undo the stack pushes from the inner `q q` even if
// `unsupportedop` terminates processing the inner instruction stream before `Q Q`
// would normally pop state.
class ScopedState {
public:
ScopedState(Renderer& renderer)
: m_renderer(renderer)
, m_starting_stack_depth(m_renderer.m_graphics_state_stack.size())
{
MUST(m_renderer.handle_save_state({}));
}
~ScopedState()
{
MUST(m_renderer.handle_restore_state({}));
m_renderer.m_graphics_state_stack.shrink(m_starting_stack_depth);
}
private:
Renderer& m_renderer;
size_t m_starting_stack_depth;
};
ScopedState scoped_state { *this };