 Yeah, good morning or good afternoon when I look at the time. I'm Regina Henshel, and I'm going to introduce you how LibreOffice implements what is called a limo stretch for shapes. The slide has a lot of code snippets. Please don't try to read them in detail, but focus on the bold or colored parts. I have uploaded a version of the presentation with has additional commands and has additional slides so that when you later look at the presentation again, you will find your way through the code. Our first example of what is a limo stretch at all. When you have an octet and drag it wider and it is simple scaled, then you see that the cuts here change their slope. When you have an octet where a limo stretch is enabled, then it looks as if it is here cut and you have a direct right part here. To understand what happened, we look at the markup in the file. And there you find information about the outer size of a shape, the width and the height. And you have a view box which defines an inner coordinate system that is the left and top of the origin. And this is the width and that is the height of the inner coordinate system. And the shape has a description of the path. That means of this line here. Here, for example, it has a command M for move to. We start at coordinate 0, 2, that's here. And L means a line tool. We draw a line from here to 0 and so on. When you have a shape where a limo stretch is enabled, the markup is nearly the same. The only difference is that you have an additional attribute which is a path stretch point. You might wonder where the name limo stretch comes from. That's history. This feature already exists in Microsoft binary formats, which means Microsoft from 98. And it exists in RTF and it exists in VML. Here an example for the markup in VML. There you have two inner coordinate system is the size. You have a path and you have an additional information which says that this shape has to be a limo stretched. Okay, that's an example. To understand what LibreOffice does, it is necessary that we first look how normally shapes are rendered. It starts with a method render. Custom shapes are not rendered directly, but they need an object to be rendered. So we have a create object. When it is not a rectangle, this object is always a path object. So we have a create path object. A custom shape can have several sub-paths. For example, a smiley has a path for the outer line, has path for the eyes and for the mouth. And therefore, we need to create a sub-path. What does this sub-path do? It has not a switch over the commands. Here, for example, the case for the command, we have a case line two. And there you find that it gets a point from the internal description, append it to the polygon so that we can get a real path. So next we look what does get point. Get point itself is only a converter from a double to integer. So real work is done in a get point as mb3d point. It's distinguished horizontal and vertical. In my presentation, I will look only at horizontal. The vertical case is nearly the same. And here you see a part get parameter and an x scale. And we cook what they do. Get parameter, at the first glance, it converts the description in the path, may have references to adjustment values and equations and resolves these references to real number and returns this number in the parameter of value x. We will later have a deeper look on that method. And this value is then multiplied with a scale. What is the scale? The scale is a member in the enhanced shape 2D. Unfortunately, this code is quite old. So some members are not indicated in their name and some other parts exist which are not good readable. And therefore, this talk. This member is set in the constructor. The constructor called method setPathSize. And in this method, you find a calculation of a ratio. A logic rectangle in the code is, in most cases, the outer size of the shape. In our case, the width of 30.5 centimeter. And code width is held the number 6 of the view books, which is the internal coordinate system. So we get this ratio. And when we look at an example, we look at a point 2.0, that is here in the inner coordinate system. We multiply it with this ratio and get the value 4.5. So the outer coordinate is here. And the point is drawn at this place. That is a normal way the inner path description is transformed to an outer coordinate. So the first question is, how does Librovis prevent the scaling? We have seen at the left side that it keeps the same shape. When we look a bit further down in this setPathSize, we find this. And this is not quickly to understand what here happens. And therefore, I have removed all the type conversions and guards against division by 0. And simplified version looks this way. And here you see an FX ratio is calculated. This ratio is a ratio from shape width to shape height. FX ratio is, unfortunately, it's a member again. And you do not see it here that it is a member. And what you then do is this K-value, which we had on the last slides, is divided by this FX ratio. In the case, this FX ratio is larger than 1, which means when this ratio is larger than 1, then height is the smaller and width is the larger one. When that is not the case, our scale remains the same. It remains with the width at the nominator. So when we combine that, we can say our scale is a minimum of width and height divided by the width. What does it mean when we use this transformed scale? When we now look at the value 2 and we multiply it with a changed scale value, we get 1.5. That is this point here. It is no longer displayed here, but it is displayed here. And we have the original shape. When we do that with all points, we get really this shape. But that is the inner geometry. The shape itself has still this width and this height. So there must be something else in the code. This is not a total solution. There must be something which moves this part to that edge. So let us look what it is. Now we need to look a little bit closer to the get parameter. Get parameter decides what to do on the type of parameter. We have parameters which are in the past description which are poor numbers. We have references to handles. Handles are that one where you can grip with a mouse and change the shape. And we have references to equations which are internal of the shape. For a number, it uses the case normal. And when we look at what is done in this case, we see a condition here. And this condition is the value which is in the past description is compared with the coordinate widths. So when we have a 6 here, which was the width of the inner coordinate system, then it does something special. It multiplies the return value with this FX ratio. It tweaks it. So we now get a return value from get parameter. We get no longer a 6, but we get this here. And so previously we had a 6 multiplied with the scale. And we get 4.5. Now we get 6 tweaked multiplied with the scale. And when we shorten it and calculate, we get 30.5, which is at the right edge of the shape. My first attempt over the shape was I write all the eight points. I list them here with the coordinates. Simple to get this octagon. I add the special attribute for the stretch that it makes a limo stretch. But then I get this. Indeed, the points which has the coordinate 6 moved to the right. But the points which have the coordinate 4 remains at their place. That did not move. So there must happen something in addition. When we compare what if I had made a first try and what is actually their markup, then you see instead of the value 4, there is used a reference. That is here the question mark is a reference to a formula. The formula is below. Its name is F0. And the formula itself is here, right minus 2. So we have to understand what is going on here. We have to look what happens with this right minus 2. This right minus 2 is an equation. That means that our get parameter no longer uses a normal case, but it uses the case equation. This equation uses a method. Let's look at it. And that is really hard, this part. I start here, step by step. F0 means it is a vector, a vector of no chest pointers. It's a vector of pointers to derive classes of expression nodes. And to make it harder, this expression node has an operand which is written as a bracket pair. And this operand results the value. It's strange. It's not a method as one would expect, but it's an operand. This value is assigned two times. This F number is the number which will be later returned. But in addition, it is assigned to a struct result. This struct takes the value and it takes a flag to indicate whether this value is valid or not. In this case, of course, it is valid, but it's true. This struct is put into a vector equation results. And this vector is aligned with this vector and means that whatever I calculate here, whatever I calculate here is cached in this vector so that you only need to calculate it once. OK. So we have to look now at the expression nodes. This is a minus. So we get the expression node binary function expression. Its operator calls a method getValue and that is a binary minus. OK. It has two arguments. The second one is a two. That is a constant value expression that simply returns a value. The first one is an enum. You have right and left and top and such enums. So we get an enum value expression. And that calls a function which has a switch over these enums and in one case we have the enum right. And there you find the solution. This value is multiplied with a fixed ratio. That is the same tweak as you have seen in the beginning with a value six. And so we have, we'll skip that, we have the situation. What has to be done to get this lemo stretch? Lemo office has code to not scale it so that we get this. Lemo office has code to move this point at the edge to the right. But to get this point here, the author of the shape has to use the enum value right in his markup. You can, I have some slides about the relationship to ODF. I will not show you here. But when you look at the definition in the standard, you will find that this way lemo office does it is not ODF conform. Oops. You can imagine it in a second indicator is the markup has two coordinates. And these coordinates are nowhere used in the code. That is the question was whether this is implemented. Yes, it is the current state of the implementation. And it shows that in this area, there is a lot of things to do. Some things could become easy hacks. For example, the prefix, the members can become in. There are some values which should be an enum. But there is a lot of things to do to get this part of the code conform to ODF. Given shape, which points are lemo stretch points and which are? No, there is no user interface. The values come in from the predefined shapes of MS binary format and from the predefined shape if we have a VML filter of the shapes in VML, they have these points and there comes these values in. Users see it only when there is an error. Error is, for example, you have a call out with rounded corners. And when you drag it wider, these corners are no longer a circle because it is simple, wrongly implemented. Some more questions? What are you interested in? I could not show you all, but when you. What about more complex shapes which are defined using bezier curves or splines? Is this something that you've thought about that is supported, that might be supported? I was on the custom shapes because that shapes are used in Microsoft Office and interoperability with Microsoft Office is for those who are paid for their work first aid. In the beginning, there was plan to implement a spline tool. We have some enums with half the spline, but it doesn't exist. You have only, besides the custom shapes and the path definition, you have only the curves which use bezier curves. To what I understood is that you have to define the thing in the description of the shape, which is a bit annoying because don't you want it always to look similarly? I wonder if the functionalities that we have to equally stretch an image and shape could be used here so that when you press shift and you scale horizontally, the limo function is always applied at the middle of the shape. If you don't press shift, it is not applied. Same for vertical size. Does it make sense? That would mean you have to change the definition of the shape. That is problematic because LibreOffice does a lot of default values only if it is the original shape definition as you find it in the MS binary format. When you change it, other things of that shape, for example, the handle position also is not maintained. You would make a real custom shape and that will not work. Yes. You have really to go into the markup and set this stretch point attribute when your own shape should get this. The predefined shapes already have this. You have to look further. This is, for example, definition of binary shape as we have it in code to get the correspondence of the Microsoft binary shapes. And there we have the code width. And these two values here define the stretch point. In this, when there is no stretch point, then there will be a special value which indicates we have no stretch point. And when there is a stretch point, then we'll be here the number of the coordinates. This such definition exists for all preset shapes of the binary format. The O-A-O-X-M-L needs not this way because it always uses the way that the shape itself is defined using the value right. So the predefined shapes of O-X-M-L always are so defined that they use the value right already to get this lemur stretch.