Peter B. West wrote: > > What about font-size="12pt+2%+0.8*from-parent(height div 32)" ? > > Good question. Make it > font-size="12pt+2%+0.8*(from-parent(height) div 32)" though. Even > nastier is > font-size="12pt+2%+0.8*(from-nearest-specified(height) div 32)" > because in markers and in static-content, we have to keep track of where > *all* property specifications occur in the ancestry FO tree to resolve > it. In general, the functions will be resolvable as the FO tree is > built. The tree is "static", in the sense that the tree relationships
Correct. Neither of the examples given has anything to do with the interface proposed, because all of the computations are done on the FOTree side of the house. > are maintained in spite of any to-ing and fro-ing with the Area Tree. > Markers are an exception, and because marker properties are resolved in > the context of the static-content into which they are eventually placed, > all the information required for from-nearest-specified() must be > available in the static-content FO subtrees. Yes, this is the real issue. Since an fo:marker's content can be used more than one place, this requires that its contents be "grafted" into the tree where needed. I think the only trick here is to pass the static content context back to the "get" method so that it knows how to get the information it needs. Sec 6.11.4 says that fo:retrieve-marker "is (conceptually) replaced by the children of the fo:marker that it retrieves." The most general way that I can think of to implement this is to force the passage of a parent fop.fo.flow.RetrieveMarker in the "get" method's signature. This tells the "get" method: "One of your ancestors is an fo:marker object, and, for purposes of this "get", consider that ancestor grafted into the tree at this fo:retrieve-marker's location." Of course, if there is no ancestor fo:marker, pass a null. Now, this raises another issue. FONode has a getParent() method. This method may need to be expanded to include this concept. Any child could then ask for its parent either with null (go up the tree through fo:marker, i.e. the way the input specifies, and the way it works now), or with a "grafting point" specified, so that if a grafting point is specified, it will go up the tree in that direction instead. In fact, it may be good to create a GraftingPoint interface that RetrieveMarker implements, in case there are additional similar items now or in the future. class Marker { ... getParent(GraftingPoint gp) { if (gp == null) { return this.parent; } return gp.getParent(null); } ... } So, lets use: font-size="12pt+2%+0.8*(from-nearest-specified(height) div 32) as an example. Lets assume an FOTree fragment that looks like this: fo:marker fo:block fo:inline For both the block and the inline, the "get" will need to research its ancestry to resolve the expression. If we pass the grafting point to the "get", and the "get" directly or indirectly uses the getParent(GraftingPoint gp) method to find that ancestry, it seems to me that everybody has everything they need. The key insight for me here is that *none* of this is actually dependent on the Area Tree at all, that what we are really doing is grafting. I had originally thought that some Area Tree information would need to be passed in, but I really think the above is much more elegant, and more clearly follows the concepts that are in play. Of cource, I rely on the rest of you guys to tell me if I have missed something (a real possibility). > Because this is not required in the fo:flows, a good deal of property > storage efficiency is realizable. > > This is why I was talking some time ago about a PropertyValue type which > is an RPN-style expression, which can be rapidly resolved without > recourse to the general parser. Without it, we have to carry at least > some expressions around in the raw, after having first parsed them in > order to determine that we can't resolve them, and then throw them to > the parser again whenever a) we have sufficient context, or b) the page > is re-laid. The idea of performing a full parse on a given expression > more than once makes me nauseous. Again, this is an implementation detail, and doesn't affect the interface. However, on the implementation side, it seems that the tradeoff will be between doing a full parse each time, or creating lots of objects. John Austin's inquiry about the huge number of objects created is what got me started down this line of thinking. I suppose that the best way would be to have your cake and eat it too -- store integers where possible, and create objects where not possible, and teach everything how to tell the difference. (Here is a half-baked idea that I don't want to even think about pursuing for a while -- PropertyStrategy. With the API I have proposed, one could conceivably store the Properties one of several ways, and have the user select which one they want based on performance needs). > The approach I am thinking about with such expressions is to associate > the expression, and therefore the FO node, with the *area* which will > provide the context for the resolution of the percentage component(s) of > the expression. (It may be enough to use the parent area of the areas > that the node generates, and to work back to the appropriate reference > area or other dimension when the parent dimensions are resolved.) There are two issues: 1) getting the ancestry right, and 2) getting the expression stored and resolved. In general, #1 goes with the interface, and #2 goes with the implementation. ATM, I am less interested in the implementation, except to make sure that it can be done. Do you think the "grafting" idea works for issue #1? > When the dimensions of such an area are resolved, the list of attached > FO property expressions can also be resolved. Exactly how to do this I > am not yet sure, but I am thinking of something along the lines of a > co-routine, implemented by message-passing queues, similar to the > existing structure of the parser/FOtree-builder interaction. Because a marker can be grafted into numerous places (retrieve-markers) in the FOTree, the properties of its children can *never* be resolved. They will always have to be computed on-the-fly, in real time, in the context of the grafting point. Hence, use the "get" interface when the information is needed. Victor Mote