While lookup into https://github.com/zufuliu/notepad4/issues/1118, I found some issues regarding checkMonospaced and aveCharWidth.
The mentioned bug is because I use aveCharWidth instead of monospaceCharacterWidth inside PositionCache::MeasureWidths(), unifont has an unusual tmAveCharWidth value (other than width of x):
1200:Unifont max=8.000000, min=8.000000, avg=16.000000, variance=0.000000
minWidth to calculate scaledVariance is better, the rational is that when (maxWidth - minWidth)/minWidth < Epsilon, rendering ASCII graphic characters with minWidth will have no visual differences, the calculation has nothing related to aveCharWidth. The calculation can be optimized to use multiplication:const XYPOSITION variance = maxWidth - minWidth;
constexpr XYPOSITION monospaceWidthEpsilon = 0.000001;
const XYPOSITION scaledVariance = monospaceWidthEpsilon * minWidth;
measurements.monospaceASCII = variance < scaledVariance;
aveCharWidth can be unified to average character with of ASCII graphic characters (same as PlatCocoa), this would reduce platform differences (e.g. between GDI and DirectWrite). currently, platforms other GDI and Qt given AverageCharWidth() with measured string width, they can be unified as follow:XYPOSITION aveCharWidth = positions.back() / allASCIIGraphic.length();
if (!surface.SupportsFeature(Supports::FractionalStrokeWidth)) {
aveCharWidth = std::round(aveCharWidth);
}
measurements.aveCharWidth = aveCharWidth;
aveCharWidth changed to average character with of ASCII graphic characters, using it inside PositionCache::MeasureWidths() is better than monospaceCharacterWidth.ListBoxX::GetDesiredRect(), the local averageCharWidth can be replaced with aveCharWidth field (set by ac.lb->SetAverageCharWidth(aveCharWidth); inside ScintillaBase::AutoCompleteStart()).
std::max_element()+std::min_element()can be merged into singlestd::minmax_element()as we don't care which one is min/max.both are slow than a plain loop like following:
Including punctuation in the calculation of average character width will decrease the value and may lead to unexpected changes that are visible to users. Any change for Unifont should be narrow to avoid the possibility of regressions.
That's a little strange. Are you trying to handle bi-width fonts where the Chinese characters should be exactly double the width of Roman characters?
Using
std::minmax_elementwould be fine although there will have to be anstd::absto avoid a negative value. Its easier to determine the intent ofstd::max_element/std::min_elementthan an explicit loop so can afford a tiny speed decrease.Only happens for proportional font, so does
SurfaceImpl::AverageCharWidth()in PlatCocoa.mm.Unifont is ASCII monospaced, but GDI
SurfaceGDI::AverageCharWidth()value is 2x wider than ASCII letter, use it (instead ofmonospaceCharacterWidth) insidePositionCache::MeasureWidths()to compute positions for ASCII characters gives wrong caret position.positions are increased only (
current >= prev).it also saves code size for MSVC, see
_Minmax_element_implinhttps://github.com/microsoft/STL/blob/main/stl/src/vector_algorithms.cpp#L2270
Since it is GDI that is providing poor data, a change to just that platform layer will have less potential to cause unexpected results. Perhaps just limit the return of
SurfaceGDI::AverageCharWidthto a maximum oftmMaxCharWidth.It looks a bug in the Unifont font itself, it's
tmAveCharWidthis same astmMaxCharWidth.If APIs or embedded font metadata are producing bad results then it is hard to fix sensibly. A bug report could be sent to Unifont or a particular version could be recommended if this is present only in some versions.
https://savannah.gnu.org/projects/unifont/