/*
* 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<unsigned , unsigned > &freq,
unsigned min_w, unsigned max_w,
hb_vector_t<unsigned > &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<unsigned , unsigned > &freq,
unsigned min_w, unsigned max_w,
hb_vector_t<unsigned > &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<unsigned , unsigned > &freq,
unsigned min_w, unsigned max_w,
hb_vector_t<unsigned > &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<unsigned , unsigned > &freq,
unsigned min_w, unsigned max_w,
hb_vector_t<unsigned > &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<unsigned > &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<unsigned > &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<unsigned , unsigned > 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<unsigned > 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 */
Messung V0.5 in Prozent C=92 H=88 G=89
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland