OK, this is starting to make sense to me. If I'm understanding this, the point of your series is that the font has a consistent size based on the unit stored in the font. If it's a physical unit like inches then the font's size in world coordinates changes based on the graphics page unit, so that it maintains the same physical size. Since GdipMeasureString reports world coordinates you see the measurement change while the pixel size should stay the same based on the device dpi.
Points, inches, meters and the like are logical units, pixel is the physical unit measure.
Oh. Then what I mean to say is that if you specify the size in logical units, the size in pixels depends on the dpi of the graphics object. The size in world coordinates depends on the size in pixels, the page unit, page scale, and the world transform.
At least this is the way your tests lead me to believe native behaves. Builtin does not behave that way, so I think you have partially fixed some cases and broken others.
convert_unit() is a very poorly named helper, and its meening and behaviour has changed in time it seems. convert_unit() needs to be removed completely, and conversion between pixels and device/font units should be generalized.
Well, we should mostly be using GdipTransformPoints instead (so that we can account for world transform, page scale, and page unit in a general way) and avoiding convert_unit directly. I see we're essentially duplicating that logic in prepare_dc and transform_and_round_points.
In cases where we need to convert a logical measurement to a device measurement (maybe including in GdipTransformPoints) I think units_to_pixels/pixels_to_units is appropriate, but it should really take a Graphics object rather than a dpi (since the correct dpi cannot be known without a Graphics object). And we need testing to decide whether to account for the page scale in that calculation.
For instance setting device unit to millimeters with GdipSetPageUnit() and then calling GdipDrawImageI() leads to completely wrong results because down in the path there is huge confusion between device units, source coordinates units and device/world transforms applied to both of them using only device X resolution and completely ignoring image resolution. What a mess.
Yes, I'm aware that the DrawImage functions that don't take a destination size should be using the image resolution. I've been putting that off. But I think this only matters for images, fonts, and perhaps texture brushes. All other cases should use world coordinates.