/* * CFF Width Optimizer * * Determines optimal defaultWidthX and nominalWidthX values * to minimize CharString byte cost. * * Based on fontTools.cffLib.width */ #ifndef HB_CFF_WIDTH_OPTIMIZER_HH #define HB_CFF_WIDTH_OPTIMIZER_HH #include "hb.hh" namespace CFF { /* Calculate byte cost for encoding a width delta */ static inline unsigned width_delta_cost (int delta) { delta = abs (delta); if (delta <= 107) return 1; if (delta <= 1131) return 2; return 5; } /* Cumulative sum forward */ static void cumsum_forward (const hb_hashmap_t &freq, unsigned min_w, unsigned max_w, hb_vector_t &cumsum) { cumsum.resize (max_w - min_w + 1); unsigned v = 0; for (unsigned x = min_w; x <= max_w; x++) { v += freq.get (x); cumsum[x - min_w] = v; } } /* Cumulative max forward */ static void cummax_forward (const hb_hashmap_t &freq, unsigned min_w, unsigned max_w, hb_vector_t &cummax) { cummax.resize (max_w - min_w + 1); unsigned v = 0; for (unsigned x = min_w; x <= max_w; x++) { v = hb_max (v, freq.get (x)); cummax[x - min_w] = v; } } /* Cumulative sum backward */ static void cumsum_backward (const hb_hashmap_t &freq, unsigned min_w, unsigned max_w, hb_vector_t &cumsum) { cumsum.resize (max_w - min_w + 1); unsigned v = 0; for (int x = (int) max_w; x >= (int) min_w; x--) { v += freq.get ((unsigned) x); cumsum[x - min_w] = v; } } /* Cumulative max backward */ static void cummax_backward (const hb_hashmap_t &freq, unsigned min_w, unsigned max_w, hb_vector_t &cummax) { cummax.resize (max_w - min_w + 1); unsigned v = 0; for (int x = (int) max_w; x >= (int) min_w; x--) { v = hb_max (v, freq.get ((unsigned) x)); cummax[x - min_w] = v; } } /* Helper to safely get cumulative value with bounds checking */ static inline unsigned safe_get (const hb_vector_t &vec, int x, unsigned min_w, unsigned max_w) { if (x < (int) min_w || x > (int) max_w) return 0; return vec[x - min_w]; } /* Optimize defaultWidthX and nominalWidthX from a list of widths * O(UPEM+numGlyphs) algorithm from fontTools.cffLib.width */ static void optimize_widths (const hb_vector_t &width_list, unsigned &default_width, unsigned &nominal_width) { if (width_list.length == 0) { default_width = nominal_width = 0; return; } /* Build frequency map */ hb_hashmap_t widths; unsigned min_w = width_list[0]; unsigned max_w = width_list[0]; for (unsigned w : width_list) { widths.set (w, widths.get (w) + 1); min_w = hb_min (min_w, w); max_w = hb_max (max_w, w); } /* Cumulative sum/max forward/backward */ hb_vector_t cumFrqU, cumMaxU, cumFrqD, cumMaxD; cumsum_forward (widths, min_w, max_w, cumFrqU); cummax_forward (widths, min_w, max_w, cumMaxU); cumsum_backward (widths, min_w, max_w, cumFrqD); cummax_backward (widths, min_w, max_w, cumMaxD); /* Cost per nominal choice, without default consideration */ auto nomnCost = [&] (unsigned x) -> unsigned { return safe_get (cumFrqU, x, min_w, max_w) + safe_get (cumFrqU, x - 108, min_w, max_w) + safe_get (cumFrqU, x - 1132, min_w, max_w) * 3 + safe_get (cumFrqD, x, min_w, max_w) + safe_get (cumFrqD, x + 108, min_w, max_w) + safe_get (cumFrqD, x + 1132, min_w, max_w) * 3 - widths.get (x); }; /* Cost-saving per nominal choice, by best default choice */ auto dfltCost = [&] (unsigned x) -> unsigned { unsigned u = hb_max (hb_max (safe_get (cumMaxU, x, min_w, max_w), safe_get (cumMaxU, x - 108, min_w, max_w) * 2), safe_get (cumMaxU, x - 1132, min_w, max_w) * 5); unsigned d = hb_max (hb_max (safe_get (cumMaxD, x, min_w, max_w), safe_get (cumMaxD, x + 108, min_w, max_w) * 2), safe_get (cumMaxD, x + 1132, min_w, max_w) * 5); return hb_max (u, d); }; /* Find best nominal */ unsigned best_nominal = min_w; unsigned best_cost = nomnCost (min_w) - dfltCost (min_w); for (unsigned x = min_w + 1; x <= max_w; x++) { unsigned cost = nomnCost (x) - dfltCost (x); if (cost < best_cost) { best_cost = cost; best_nominal = x; } } /* Work back the best default */ unsigned best_default = best_nominal; unsigned best_default_cost = (unsigned) -1; /* Check candidates around best_nominal */ int candidates[] = { (int) best_nominal, (int) best_nominal - 108, (int) best_nominal - 1132, (int) best_nominal + 108, (int) best_nominal + 1132 }; for (int candidate : candidates) { if (candidate < (int) min_w || candidate > (int) max_w) continue; /* Compute actual cost with this default */ unsigned cost = 0; for (auto kv : widths.iter ()) { unsigned w = kv.first; unsigned freq = kv.second; if (w == (unsigned) candidate) continue; cost += freq * width_delta_cost ((int) w - (int) best_nominal); } if (cost < best_default_cost) { best_default_cost = cost; best_default = (unsigned) candidate; } } default_width = best_default; nominal_width = best_nominal; } } /* namespace CFF */ #endif /* HB_CFF_WIDTH_OPTIMIZER_HH */