Length unit type based on EMU as standard unit in CO

Hi,

I want to introduce a “Length” unit type as the standard unit type for length used everywhere a logical unit is currently needed. The type always stores values as EMU units internally, is strongly typed, and has integrated conversions to and from other units (hmm, twips, pt, …). Currently the type uses sal_Int64, where the value represents EMUs, which can represent 100th of mm and twips as integers. There are also defined literals for units, which make it possible to express which unit you want directly as a literal: 1_px, 10_emu, 20_hmm. You can also mix them when calculating, e.g. auto width = 1_hmm + 2_twip. Please read more about it in the [1] commit message, LengthBase.hxx, and the tests (how it’s used).

In addition, there are the types Tuple2DL, Size2DL, and Range2DL that use the Length type, but are essentially similar to B2DTuple, B2DSize and B2DRange (they share the same template classes).

Plan to integrate:

I’ve been trying to integrate this into svx and other modules for quite some time, but couldn’t find the right approach to do it step-wise without blowing up the complexity. Now I think I have a plan that can work. The main issue is that in svx, SdrObjects can work with multiple units, but technically they work in two - either 100th mm (Impress, Draw, Calc) or twips (Writer). This is a problem for integration, because if we want to do a bottom-up replacement, we would need to know what the unit-less values represent (hmm or twips), and we don’t know that. I tried it and it started to get too complex.

Another choice is to go top-down and convert Writer first, so we eliminate one possibility. This is the direction I’m going now. It is rather easier to do the conversion in Writer because we already have the SwTwips type there - so we can just replace SwTwips with gfx::Length. One idea was to integrate Length into SwTwips, but that didn’t work as I expected, so we have to convert the usages one by one.

In [2] I started changing this, and in [3] it showed the advantage of this approach by eliminating round-trip rounding errors. The issue is that the properties use 100th mm as the standard unit, and converting it to twips and back (export) produces rounding errors. Using Length instead eliminates that, but we need to introduce new properties (HoriOrientPositionEMU instead of HoriOrientPosition).

Before continuing and start integrating this, I would like some feedback on whether this is an approach we should take, or what you would change.

[1] https://gerrit.collaboraoffice.com/c/online/+/3955

[2] https://gerrit.collaboraoffice.com/c/online/+/3956

[3] https://gerrit.collaboraoffice.com/c/online/+/3957

I like the idea and the proposed implementation plan (top-down).

(I’m not too fond of the name, but I wont bother bikeshedding that :slight_smile:

Given that we now have lots of excess precision (256,000,000 km), possibly we want to store values internally as something like a 56.8 fixed point number? i.e. multiply the unit by 2^8 to give us fractional precision to avoid rounding errors when laying out text. This kind of thing is helpful to avoid fractional errors adding up and generating extra space here and there.

Absolutely great!

As to fixed point number - well, why would we need to have fractions of EMU, which is a fraction of a mm/100? Fixed point will not eliminate rounding, only shift it. OTOH, unfortunately, there is one unit that still can’t be represented in whole EMUs - the PowerPoint’s Master Unit.

I think EMU itself gives you enough cushion so 56.8 is not really needed. For heavy calculations it’s still best to convert to double (can still overflow in certain cases). Anyway the type is designed in such a way that the internal representation is hidden (you still have to_emu and emu construction function), so we should be able to change the internal representation without much problems, so going to 56.8 internally should be easy to do - if that will show itself to be useful. Same is also true if we would want to convert the type to double for some reason.

I think we should be good for text layout too… 1 EMU = 0.00002778mm that should suffice, no?

Fractions of EMU could be useful in certain situations if you want to have a more robust way of rounding to an EMU, without the need to change to double for the calculation. Even with EMU we can represent huge document with that still - so we might just sacrifice some of that (upper 16-24bits will almost all the time be 0) to the lower bits. We can adjust the internal representation without breaking the code using this.

As for master unit - I was aware of that and I think we can just change the internal representation to be 1 = 1/2 EMU to support that, but I didn’t want to change the internal representation before I actually have a problem.

1 Like