[vector] Restore reset defaults and clear PDF draw resources (#6355) Vector reset omitted draw colors, paint background, SVG id prefixes, and custom palette overrides. Restore these defaults while preserving configuration across clear. The PDF draw context also kept its opacity resource dictionary and counter after clear, including automatic clearing after render. Clear both with the accumulated output so later documents do not carry resources from earlier renders. Add regressions for SVG/PDF reset, configuration preservation, custom palette overrides, and fresh-context PDF equivalence after explicit clear, reset, and repeated rendering. All three regression groups fail against the original implementation. Tested with meson test -C build: 288 passed, 1 skipped, 0 failed. The vector suite and the issue reproducer pass. The regression also compiles and runs as C99 with -Wall -Wextra -Werror. Fixes: https://github.com/harfbuzz/harfbuzz/issues/6342 Assisted-by: OpenAI Codex
diff --git a/src/hb-vector-draw.cc b/src/hb-vector-draw.cc index 4884d13..21ae06b 100644 --- a/src/hb-vector-draw.cc +++ b/src/hb-vector-draw.cc
@@ -955,8 +955,8 @@ * * Discards accumulated draw output so @draw can be reused for * another render. User configuration (transform, scale factors, - * precision) is preserved. Call hb_vector_draw_reset() to - * also reset user configuration to defaults. + * precision, foreground, background, and budget) is preserved. Call + * hb_vector_draw_reset() to also reset user configuration to defaults. * * Since: 14.2.0 */ @@ -968,6 +968,8 @@ draw->defs.clear (); draw->body.clear (); draw->path.clear (); + draw->pdf_extgstate_dict.clear (); + draw->pdf_extgstate_count = 0; draw->recharge_budget (); } @@ -975,7 +977,8 @@ * hb_vector_draw_reset: * @draw: a draw context. * - * Resets @draw state and clears accumulated content. + * Restores user configuration to defaults and clears accumulated content. + * The output format and attached user data are preserved. * * Since: 13.0.0 */ @@ -985,6 +988,8 @@ draw->transform = {1, 0, 0, 1, 0, 0}; draw->x_scale_factor = 1.f; draw->y_scale_factor = 1.f; + draw->foreground = HB_COLOR (0, 0, 0, 255); + draw->background = HB_COLOR (0, 0, 0, 0); draw->set_precision (2); draw->budget = HB_BUDGET_DEFAULT; hb_vector_draw_clear (draw);
diff --git a/src/hb-vector-paint.cc b/src/hb-vector-paint.cc index 227a329..fc5e3c6 100644 --- a/src/hb-vector-paint.cc +++ b/src/hb-vector-paint.cc
@@ -722,9 +722,9 @@ * * Discards accumulated paint output so @paint can be reused for * another render. User configuration (transform, scale factors, - * precision, foreground, palette, custom palette colors) - * is preserved. Call hb_vector_paint_reset() to also reset - * user configuration to defaults. + * precision, foreground, background, palette, custom palette colors, + * SVG id prefix, and budget) is preserved. Call hb_vector_paint_reset() + * to also reset user configuration to defaults. * * Since: 14.2.0 */ @@ -784,7 +784,8 @@ * hb_vector_paint_reset: * @paint: a paint context. * - * Resets @paint state and clears accumulated content. + * Restores user configuration to defaults and clears accumulated content. + * The output format and attached user data are preserved. * * Since: 13.0.0 */ @@ -795,7 +796,10 @@ paint->x_scale_factor = 1.f; paint->y_scale_factor = 1.f; paint->foreground = HB_COLOR (0, 0, 0, 255); + paint->background = HB_COLOR (0, 0, 0, 0); paint->palette = 0; + hb_vector_paint_set_svg_prefix (paint, nullptr); + hb_vector_paint_clear_custom_palette_colors (paint); paint->set_precision (2); paint->budget = HB_BUDGET_DEFAULT; hb_vector_paint_clear (paint);
diff --git a/test/vector/meson.build b/test/vector/meson.build index d839f1d..d391907 100644 --- a/test/vector/meson.build +++ b/test/vector/meson.build
@@ -13,6 +13,14 @@ suite: 'vector', ) +test_vector_reset = executable('test-vector-reset', 'test-vector-reset.c', + dependencies: [libharfbuzz_vector_dep], +) + +test('reset', test_vector_reset, + suite: 'vector', +) + tests = [ 'basic', 'varc',
diff --git a/test/vector/test-vector-reset.c b/test/vector/test-vector-reset.c new file mode 100644 index 0000000..5605d8b --- /dev/null +++ b/test/vector/test-vector-reset.c
@@ -0,0 +1,170 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifdef NDEBUG +#undef NDEBUG +#endif +#include <assert.h> +#include <string.h> + +#include <hb.h> +#include <hb-vector.h> + +static void +test_draw_reset (hb_vector_format_t format) +{ + hb_vector_draw_t *fresh = hb_vector_draw_create_or_fail (format); + hb_vector_draw_t *draw = hb_vector_draw_create_or_fail (format); + assert (fresh && draw); + hb_color_t foreground = HB_COLOR (0, 0, 255, 128); + hb_color_t background = HB_COLOR (0, 255, 0, 255); + + hb_vector_draw_set_foreground (draw, foreground); + hb_vector_draw_set_background (draw, background); + hb_vector_draw_clear (draw); + assert (hb_vector_draw_get_foreground (draw) == foreground); + assert (hb_vector_draw_get_background (draw) == background); + + hb_vector_draw_reset (draw); + assert (hb_vector_draw_get_format (draw) == format); + assert (hb_vector_draw_get_foreground (draw) == hb_vector_draw_get_foreground (fresh)); + assert (hb_vector_draw_get_background (draw) == hb_vector_draw_get_background (fresh)); + hb_vector_draw_destroy (draw); + hb_vector_draw_destroy (fresh); +} + +static void +test_paint_reset (hb_vector_format_t format) +{ + hb_vector_paint_t *fresh = hb_vector_paint_create_or_fail (format); + hb_vector_paint_t *paint = hb_vector_paint_create_or_fail (format); + assert (fresh && paint); + hb_color_t foreground = HB_COLOR (0, 0, 255, 128); + hb_color_t background = HB_COLOR (0, 255, 0, 255); + hb_color_t custom = HB_COLOR (255, 0, 0, 255); + hb_color_t color; + hb_paint_funcs_t *funcs = hb_vector_paint_get_funcs (paint); + + hb_vector_paint_set_foreground (paint, foreground); + hb_vector_paint_set_background (paint, background); + hb_vector_paint_set_palette (paint, 2); + hb_vector_paint_set_svg_prefix (paint, "old-"); + hb_vector_paint_set_custom_palette_color (paint, 7, custom); + hb_vector_paint_clear (paint); + assert (hb_vector_paint_get_foreground (paint) == foreground); + assert (hb_vector_paint_get_background (paint) == background); + assert (hb_vector_paint_get_palette (paint) == 2); + assert (!strcmp (hb_vector_paint_get_svg_prefix (paint), "old-")); + if (format == HB_VECTOR_FORMAT_SVG) + { + assert (hb_paint_custom_palette_color (funcs, paint, 7, &color)); + assert (color == custom); + } + + hb_vector_paint_reset (paint); + assert (hb_vector_paint_get_format (paint) == format); + assert (hb_vector_paint_get_foreground (paint) == hb_vector_paint_get_foreground (fresh)); + assert (hb_vector_paint_get_background (paint) == hb_vector_paint_get_background (fresh)); + assert (hb_vector_paint_get_palette (paint) == hb_vector_paint_get_palette (fresh)); + assert (!strcmp (hb_vector_paint_get_svg_prefix (paint), hb_vector_paint_get_svg_prefix (fresh))); + if (format == HB_VECTOR_FORMAT_SVG) + assert (!hb_paint_custom_palette_color (funcs, paint, 7, &color)); + + hb_vector_paint_destroy (paint); + hb_vector_paint_destroy (fresh); +} + +static void +draw_triangle (hb_vector_draw_t *draw) +{ + hb_vector_extents_t extents = {0, 0, 10, 10}; + hb_vector_draw_set_extents (draw, &extents); + hb_draw_funcs_t *funcs = hb_vector_draw_get_funcs (draw); + hb_draw_state_t state = HB_DRAW_STATE_DEFAULT; + hb_draw_move_to (funcs, draw, &state, 0, 0); + hb_draw_line_to (funcs, draw, &state, 10, 0); + hb_draw_line_to (funcs, draw, &state, 0, 10); + hb_draw_close_path (funcs, draw, &state); + hb_vector_draw_new_path (draw); +} + +static void +test_pdf_clear (void) +{ + hb_vector_draw_t *fresh = hb_vector_draw_create_or_fail (HB_VECTOR_FORMAT_PDF); + hb_vector_draw_t *draw = hb_vector_draw_create_or_fail (HB_VECTOR_FORMAT_PDF); + assert (fresh && draw); + hb_color_t foreground = HB_COLOR (0, 0, 0, 128); + hb_vector_draw_set_foreground (fresh, foreground); + hb_vector_draw_set_foreground (draw, foreground); + draw_triangle (fresh); + hb_blob_t *expected = hb_vector_draw_render (fresh); + assert (expected); + unsigned int expected_length; + const char *expected_data = hb_blob_get_data (expected, &expected_length); + assert (expected_length); + + /* Discard a path whose opacity resource has already been emitted. */ + draw_triangle (draw); + hb_vector_draw_clear (draw); + for (unsigned int i = 0; i < 3; i++) + { + draw_triangle (draw); + hb_blob_t *output = hb_vector_draw_render (draw); + assert (output); + unsigned int length; + const char *data = hb_blob_get_data (output, &length); + assert (length == expected_length); + assert (!memcmp (data, expected_data, length)); + hb_blob_destroy (output); + /* Subsequent iterations rely on render's automatic clear. */ + } + + /* Reset must discard PDF resources too, while restoring the colors. */ + draw_triangle (draw); + hb_vector_draw_reset (draw); + hb_vector_draw_set_foreground (draw, foreground); + draw_triangle (draw); + hb_blob_t *output = hb_vector_draw_render (draw); + assert (output); + unsigned int length; + const char *data = hb_blob_get_data (output, &length); + assert (length == expected_length); + assert (!memcmp (data, expected_data, length)); + hb_blob_destroy (output); + hb_blob_destroy (expected); + hb_vector_draw_destroy (draw); + hb_vector_draw_destroy (fresh); +} + +int +main (void) +{ + test_draw_reset (HB_VECTOR_FORMAT_SVG); + test_draw_reset (HB_VECTOR_FORMAT_PDF); + test_paint_reset (HB_VECTOR_FORMAT_SVG); + test_paint_reset (HB_VECTOR_FORMAT_PDF); + test_pdf_clear (); + return 0; +}