1*2d1272b8SAndroid Build Coastguard Worker# Introduction 2*2d1272b8SAndroid Build Coastguard Worker 3*2d1272b8SAndroid Build Coastguard WorkerSeveral tables in the opentype format are formed internally by a graph of subtables. Parent node's 4*2d1272b8SAndroid Build Coastguard Workerreference their children through the use of positive offsets, which are typically 16 bits wide. 5*2d1272b8SAndroid Build Coastguard WorkerSince offsets are always positive this forms a directed acyclic graph. For storage in the font file 6*2d1272b8SAndroid Build Coastguard Workerthe graph must be given a topological ordering and then the subtables packed in serial according to 7*2d1272b8SAndroid Build Coastguard Workerthat ordering. Since 16 bit offsets have a maximum value of 65,535 if the distance between a parent 8*2d1272b8SAndroid Build Coastguard Workersubtable and a child is more then 65,535 bytes then it's not possible for the offset to encode that 9*2d1272b8SAndroid Build Coastguard Workeredge. 10*2d1272b8SAndroid Build Coastguard Worker 11*2d1272b8SAndroid Build Coastguard WorkerFor many fonts with complex layout rules (such as Arabic) it's not unusual for the tables containing 12*2d1272b8SAndroid Build Coastguard Workerlayout rules ([GSUB/GPOS](https://docs.microsoft.com/en-us/typography/opentype/spec/gsub)) to be 13*2d1272b8SAndroid Build Coastguard Workerlarger than 65kb. As a result these types of fonts are susceptible to offset overflows when 14*2d1272b8SAndroid Build Coastguard Workerserializing to the binary font format. 15*2d1272b8SAndroid Build Coastguard Worker 16*2d1272b8SAndroid Build Coastguard WorkerOffset overflows can happen for a variety of reasons and require different strategies to resolve: 17*2d1272b8SAndroid Build Coastguard Worker* Simple overflows can often be resolved with a different topological ordering. 18*2d1272b8SAndroid Build Coastguard Worker* If a subtable has many parents this can result in the link from furthest parent(s) 19*2d1272b8SAndroid Build Coastguard Worker being at risk for overflows. In these cases it's possible to duplicate the shared subtable which 20*2d1272b8SAndroid Build Coastguard Worker allows it to be placed closer to it's parent. 21*2d1272b8SAndroid Build Coastguard Worker* If subtables exist which are themselves larger than 65kb it's not possible for any offsets to point 22*2d1272b8SAndroid Build Coastguard Worker past them. In these cases the subtable can usually be split into two smaller subtables to allow 23*2d1272b8SAndroid Build Coastguard Worker for more flexibility in the ordering. 24*2d1272b8SAndroid Build Coastguard Worker* In GSUB/GPOS overflows from Lookup subtables can be resolved by changing the Lookup to an extension 25*2d1272b8SAndroid Build Coastguard Worker lookup which uses a 32 bit offset instead of 16 bit offset. 26*2d1272b8SAndroid Build Coastguard Worker 27*2d1272b8SAndroid Build Coastguard WorkerIn general there isn't a simple solution to produce an optimal topological ordering for a given graph. 28*2d1272b8SAndroid Build Coastguard WorkerFinding an ordering which doesn't overflow is a NP hard problem. Existing solutions use heuristics 29*2d1272b8SAndroid Build Coastguard Workerwhich attempt a combination of the above strategies to attempt to find a non-overflowing configuration. 30*2d1272b8SAndroid Build Coastguard Worker 31*2d1272b8SAndroid Build Coastguard WorkerThe harfbuzz subsetting library 32*2d1272b8SAndroid Build Coastguard Worker[includes a repacking algorithm](https://github.com/harfbuzz/harfbuzz/blob/main/src/hb-repacker.hh) 33*2d1272b8SAndroid Build Coastguard Workerwhich is used to resolve offset overflows that are present in the subsetted tables it produces. This 34*2d1272b8SAndroid Build Coastguard Workerdocument provides a deep dive into how the harfbuzz repacking algorithm works. 35*2d1272b8SAndroid Build Coastguard Worker 36*2d1272b8SAndroid Build Coastguard WorkerOther implementations exist, such as in 37*2d1272b8SAndroid Build Coastguard Worker[fontTools](https://github.com/fonttools/fonttools/blob/7af43123d49c188fcef4e540fa94796b3b44e858/Lib/fontTools/ttLib/tables/otBase.py#L72), however these are not covered in this document. 38*2d1272b8SAndroid Build Coastguard Worker 39*2d1272b8SAndroid Build Coastguard Worker# Foundations 40*2d1272b8SAndroid Build Coastguard Worker 41*2d1272b8SAndroid Build Coastguard WorkerThere's four key pieces to the harfbuzz approach: 42*2d1272b8SAndroid Build Coastguard Worker 43*2d1272b8SAndroid Build Coastguard Worker* Subtable Graph: a table's internal structure is abstracted out into a lightweight graph 44*2d1272b8SAndroid Build Coastguard Worker representation where each subtable is a node and each offset forms an edge. The nodes only need 45*2d1272b8SAndroid Build Coastguard Worker to know how many bytes the corresponding subtable occupies. This lightweight representation can 46*2d1272b8SAndroid Build Coastguard Worker be easily modified to test new ordering's and strategies as the repacking algorithm iterates. 47*2d1272b8SAndroid Build Coastguard Worker 48*2d1272b8SAndroid Build Coastguard Worker* [Topological sorting algorithm](https://en.wikipedia.org/wiki/Topological_sorting): an algorithm 49*2d1272b8SAndroid Build Coastguard Worker which given a graph gives a linear sorting of the nodes such that all offsets will be positive. 50*2d1272b8SAndroid Build Coastguard Worker 51*2d1272b8SAndroid Build Coastguard Worker* Overflow check: given a graph and a topological sorting it checks if there will be any overflows 52*2d1272b8SAndroid Build Coastguard Worker in any of the offsets. If there are overflows it returns a list of (parent, child) tuples that 53*2d1272b8SAndroid Build Coastguard Worker will overflow. Since the graph has information on the size of each subtable it's straightforward 54*2d1272b8SAndroid Build Coastguard Worker to calculate the final position of each subtable and then check if any offsets to it will 55*2d1272b8SAndroid Build Coastguard Worker overflow. 56*2d1272b8SAndroid Build Coastguard Worker 57*2d1272b8SAndroid Build Coastguard Worker* Content Aware Preprocessing: if the overflow resolver is aware of the format of the underlying 58*2d1272b8SAndroid Build Coastguard Worker tables (eg. GSUB, GPOS) then in some cases preprocessing can be done to increase the chance of 59*2d1272b8SAndroid Build Coastguard Worker successfully packing the graph. For example for GSUB and GPOS we can preprocess the graph and 60*2d1272b8SAndroid Build Coastguard Worker promote lookups to extension lookups (upgrades a 16 bit offset to 32 bits) or split large lookup 61*2d1272b8SAndroid Build Coastguard Worker subtables into two or more pieces. 62*2d1272b8SAndroid Build Coastguard Worker 63*2d1272b8SAndroid Build Coastguard Worker* Offset resolution strategies: given a particular occurrence of an overflow these strategies 64*2d1272b8SAndroid Build Coastguard Worker modify the graph to attempt to resolve the overflow. 65*2d1272b8SAndroid Build Coastguard Worker 66*2d1272b8SAndroid Build Coastguard Worker# High Level Algorithm 67*2d1272b8SAndroid Build Coastguard Worker 68*2d1272b8SAndroid Build Coastguard Worker``` 69*2d1272b8SAndroid Build Coastguard Workerdef repack(graph): 70*2d1272b8SAndroid Build Coastguard Worker graph.topological_sort() 71*2d1272b8SAndroid Build Coastguard Worker 72*2d1272b8SAndroid Build Coastguard Worker if (graph.will_overflow()) 73*2d1272b8SAndroid Build Coastguard Worker preprocess(graph) 74*2d1272b8SAndroid Build Coastguard Worker assign_spaces(graph) 75*2d1272b8SAndroid Build Coastguard Worker graph.topological_sort() 76*2d1272b8SAndroid Build Coastguard Worker 77*2d1272b8SAndroid Build Coastguard Worker while (overflows = graph.will_overflow()): 78*2d1272b8SAndroid Build Coastguard Worker for overflow in overflows: 79*2d1272b8SAndroid Build Coastguard Worker apply_offset_resolution_strategy (overflow, graph) 80*2d1272b8SAndroid Build Coastguard Worker graph.topological_sort() 81*2d1272b8SAndroid Build Coastguard Worker``` 82*2d1272b8SAndroid Build Coastguard Worker 83*2d1272b8SAndroid Build Coastguard WorkerThe actual code for this processing loop can be found in the function hb_resolve_overflows () of 84*2d1272b8SAndroid Build Coastguard Worker[hb-repacker.hh](https://github.com/harfbuzz/harfbuzz/blob/main/src/hb-repacker.hh). 85*2d1272b8SAndroid Build Coastguard Worker 86*2d1272b8SAndroid Build Coastguard Worker# Topological Sorting Algorithms 87*2d1272b8SAndroid Build Coastguard Worker 88*2d1272b8SAndroid Build Coastguard WorkerThe harfbuzz repacker uses two different algorithms for topological sorting: 89*2d1272b8SAndroid Build Coastguard Worker* [Kahn's Algorithm](https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm) 90*2d1272b8SAndroid Build Coastguard Worker* Sorting by shortest distance 91*2d1272b8SAndroid Build Coastguard Worker 92*2d1272b8SAndroid Build Coastguard WorkerKahn's algorithm is approximately twice as fast as the shortest distance sort so that is attempted 93*2d1272b8SAndroid Build Coastguard Workerfirst (only on the first topological sort). If it fails to eliminate overflows then shortest distance 94*2d1272b8SAndroid Build Coastguard Workersort will be used for all subsequent topological sorting operations. 95*2d1272b8SAndroid Build Coastguard Worker 96*2d1272b8SAndroid Build Coastguard Worker## Shortest Distance Sort 97*2d1272b8SAndroid Build Coastguard Worker 98*2d1272b8SAndroid Build Coastguard WorkerThis algorithm orders the nodes based on total distance to each node. Nodes with a shorter distance 99*2d1272b8SAndroid Build Coastguard Workerare ordered first. 100*2d1272b8SAndroid Build Coastguard Worker 101*2d1272b8SAndroid Build Coastguard WorkerThe "weight" of an edge is the sum of the size of the sub-table being pointed to plus 2^16 for a 16 bit 102*2d1272b8SAndroid Build Coastguard Workeroffset and 2^32 for a 32 bit offset. 103*2d1272b8SAndroid Build Coastguard Worker 104*2d1272b8SAndroid Build Coastguard WorkerThe distance of a node is the sum of all weights along the shortest path from the root to that node 105*2d1272b8SAndroid Build Coastguard Workerplus a priority modifier (used to change where nodes are placed by moving increasing or 106*2d1272b8SAndroid Build Coastguard Workerdecreasing the effective distance). Ties between nodes with the same distance are broken based 107*2d1272b8SAndroid Build Coastguard Workeron the order of the offset in the sub table bytes. 108*2d1272b8SAndroid Build Coastguard Worker 109*2d1272b8SAndroid Build Coastguard WorkerThe shortest distance to each node is determined using 110*2d1272b8SAndroid Build Coastguard Worker[Djikstra's algorithm](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm). Then the topological 111*2d1272b8SAndroid Build Coastguard Workerordering is produce by applying a modified version of Kahn's algorithm that uses a priority queue 112*2d1272b8SAndroid Build Coastguard Workerbased on the shortest distance to each node. 113*2d1272b8SAndroid Build Coastguard Worker 114*2d1272b8SAndroid Build Coastguard Worker## Optimizing the Sorting 115*2d1272b8SAndroid Build Coastguard Worker 116*2d1272b8SAndroid Build Coastguard WorkerThe topological sorting operation is the core of the repacker and is run on each iteration so it needs 117*2d1272b8SAndroid Build Coastguard Workerto be as fast as possible. There's a few things that are done to speed up subsequent sorting 118*2d1272b8SAndroid Build Coastguard Workeroperations: 119*2d1272b8SAndroid Build Coastguard Worker 120*2d1272b8SAndroid Build Coastguard Worker* The number of incoming edges to each node is cached. This is required by the Kahn's algorithm 121*2d1272b8SAndroid Build Coastguard Worker portion of both sorts. Where possible when the graph is modified we manually update the cached 122*2d1272b8SAndroid Build Coastguard Worker edge counts of affected nodes. 123*2d1272b8SAndroid Build Coastguard Worker 124*2d1272b8SAndroid Build Coastguard Worker* The distance to each node is cached. Where possible when the graph is modified we manually update 125*2d1272b8SAndroid Build Coastguard Worker the cached distances of any affected nodes. 126*2d1272b8SAndroid Build Coastguard Worker 127*2d1272b8SAndroid Build Coastguard WorkerCaching these values allows the repacker to avoid recalculating them for the full graph on each 128*2d1272b8SAndroid Build Coastguard Workeriteration. 129*2d1272b8SAndroid Build Coastguard Worker 130*2d1272b8SAndroid Build Coastguard WorkerThe other important factor to speed is a fast priority queue which is a core datastructure to 131*2d1272b8SAndroid Build Coastguard Workerthe topological sorting algorithm. Currently a basic heap based queue is used. Heap based queue's 132*2d1272b8SAndroid Build Coastguard Workerdon't support fast priority decreases, but that can be worked around by just adding redundant entries 133*2d1272b8SAndroid Build Coastguard Workerto the priority queue and filtering the older ones out when poppping off entries. This is based 134*2d1272b8SAndroid Build Coastguard Workeron the recommendations in 135*2d1272b8SAndroid Build Coastguard Worker[a study of the practical performance of priority queues in Dijkstra's algorithm](https://www3.cs.stonybrook.edu/~rezaul/papers/TR-07-54.pdf) 136*2d1272b8SAndroid Build Coastguard Worker 137*2d1272b8SAndroid Build Coastguard Worker## Special Handling of 32 bit Offsets 138*2d1272b8SAndroid Build Coastguard Worker 139*2d1272b8SAndroid Build Coastguard WorkerIf a graph contains multiple 32 bit offsets then the shortest distance sorting will be likely be 140*2d1272b8SAndroid Build Coastguard Workersuboptimal. For example consider the case where a graph contains two 32 bit offsets that each point 141*2d1272b8SAndroid Build Coastguard Workerto a subgraph which are not connected to each other. The shortest distance sort will interleave the 142*2d1272b8SAndroid Build Coastguard Workersubtables of the two subgraphs, potentially resulting in overflows. Since each of these subgraphs are 143*2d1272b8SAndroid Build Coastguard Workerindependent of each other, and 32 bit offsets can point extremely long distances a better strategy is 144*2d1272b8SAndroid Build Coastguard Workerto pack the first subgraph in it's entirety and then have the second subgraph packed after with the 32 145*2d1272b8SAndroid Build Coastguard Workerbit offset pointing over the first subgraph. For example given the graph: 146*2d1272b8SAndroid Build Coastguard Worker 147*2d1272b8SAndroid Build Coastguard Worker 148*2d1272b8SAndroid Build Coastguard Worker``` 149*2d1272b8SAndroid Build Coastguard Workera--- b -- d -- f 150*2d1272b8SAndroid Build Coastguard Worker \ 151*2d1272b8SAndroid Build Coastguard Worker \_ c -- e -- g 152*2d1272b8SAndroid Build Coastguard Worker``` 153*2d1272b8SAndroid Build Coastguard Worker 154*2d1272b8SAndroid Build Coastguard WorkerWhere the links from a to b and a to c are 32 bit offsets, the shortest distance sort would be: 155*2d1272b8SAndroid Build Coastguard Worker 156*2d1272b8SAndroid Build Coastguard Worker``` 157*2d1272b8SAndroid Build Coastguard Workera, b, c, d, e, f, g 158*2d1272b8SAndroid Build Coastguard Worker 159*2d1272b8SAndroid Build Coastguard Worker``` 160*2d1272b8SAndroid Build Coastguard Worker 161*2d1272b8SAndroid Build Coastguard WorkerIf nodes d and e have a combined size greater than 65kb then the offset from d to f will overflow. 162*2d1272b8SAndroid Build Coastguard WorkerA better ordering is: 163*2d1272b8SAndroid Build Coastguard Worker 164*2d1272b8SAndroid Build Coastguard Worker``` 165*2d1272b8SAndroid Build Coastguard Workera, b, d, f, c, e, g 166*2d1272b8SAndroid Build Coastguard Worker``` 167*2d1272b8SAndroid Build Coastguard Worker 168*2d1272b8SAndroid Build Coastguard WorkerThe ability for 32 bit offsets to point long distances is utilized to jump over the subgraph of 169*2d1272b8SAndroid Build Coastguard Workerb which gives the remaining 16 bit offsets a better chance of not overflowing. 170*2d1272b8SAndroid Build Coastguard Worker 171*2d1272b8SAndroid Build Coastguard WorkerThe above is an ideal situation where the subgraphs are disconnected from each other, in practice 172*2d1272b8SAndroid Build Coastguard Workerthis is often not this case. So this idea can be generalized as follows: 173*2d1272b8SAndroid Build Coastguard Worker 174*2d1272b8SAndroid Build Coastguard WorkerIf there is a subgraph that is only reachable from one or more 32 bit offsets, then: 175*2d1272b8SAndroid Build Coastguard Worker* That subgraph can be treated as an independent unit and all nodes of the subgraph packed in isolation 176*2d1272b8SAndroid Build Coastguard Worker from the rest of the graph. 177*2d1272b8SAndroid Build Coastguard Worker* In a table that occupies less than 4gb of space (in practice all fonts), that packed independent 178*2d1272b8SAndroid Build Coastguard Worker subgraph can be placed anywhere after the parent nodes without overflowing the 32 bit offsets from 179*2d1272b8SAndroid Build Coastguard Worker the parent nodes. 180*2d1272b8SAndroid Build Coastguard Worker 181*2d1272b8SAndroid Build Coastguard WorkerThe sorting algorithm incorporates this via a "space" modifier that can be applied to nodes in the 182*2d1272b8SAndroid Build Coastguard Workergraph. By default all nodes are treated as being in space zero. If a node is given a non-zero space, n, 183*2d1272b8SAndroid Build Coastguard Workerthen the computed distance to the node will be modified by adding `n * 2^32`. This will cause that 184*2d1272b8SAndroid Build Coastguard Workernode and it's descendants to be packed between all nodes in space n-1 and space n+1. Resulting in a 185*2d1272b8SAndroid Build Coastguard Workertopological sort like: 186*2d1272b8SAndroid Build Coastguard Worker 187*2d1272b8SAndroid Build Coastguard Worker``` 188*2d1272b8SAndroid Build Coastguard Worker| space 0 subtables | space 1 subtables | .... | space n subtables | 189*2d1272b8SAndroid Build Coastguard Worker``` 190*2d1272b8SAndroid Build Coastguard Worker 191*2d1272b8SAndroid Build Coastguard WorkerThe assign_spaces() step in the high level algorithm is responsible for identifying independent 192*2d1272b8SAndroid Build Coastguard Workersubgraphs and assigning unique spaces to each one. More information on the space assignment can be 193*2d1272b8SAndroid Build Coastguard Workerfound in the next section. 194*2d1272b8SAndroid Build Coastguard Worker 195*2d1272b8SAndroid Build Coastguard Worker# Graph Preprocessing 196*2d1272b8SAndroid Build Coastguard Worker 197*2d1272b8SAndroid Build Coastguard WorkerFor certain table types we can preprocess and modify the graph structure to reduce the occurences 198*2d1272b8SAndroid Build Coastguard Workerof overflows. Currently the repacker implements preprocessing only for GPOS and GSUB tables. 199*2d1272b8SAndroid Build Coastguard Worker 200*2d1272b8SAndroid Build Coastguard Worker## GSUB/GPOS Table Splitting 201*2d1272b8SAndroid Build Coastguard Worker 202*2d1272b8SAndroid Build Coastguard WorkerThe GSUB/GPOS preprocessor scans each lookup subtable and determines if the subtable's children are 203*2d1272b8SAndroid Build Coastguard Workerso large that no overflow resolution is possible (for example a single subtable that exceeds 65kb 204*2d1272b8SAndroid Build Coastguard Workercannot be pointed over). When such cases are detected table splitting is invoked: 205*2d1272b8SAndroid Build Coastguard Worker 206*2d1272b8SAndroid Build Coastguard Worker* The subtable is first analyzed to determine the smallest number of split points that will allow 207*2d1272b8SAndroid Build Coastguard Worker for successful offset overflow resolution. 208*2d1272b8SAndroid Build Coastguard Worker 209*2d1272b8SAndroid Build Coastguard Worker* Then the subtable in the graph representation is modified to actually perform the split at the 210*2d1272b8SAndroid Build Coastguard Worker previously computed split points. At a high level splits are done by inserting new subtables 211*2d1272b8SAndroid Build Coastguard Worker which contain a subset of the data of the original subtable and then shrinking the original subtable. 212*2d1272b8SAndroid Build Coastguard Worker 213*2d1272b8SAndroid Build Coastguard WorkerTable splitting must be aware of the underlying format of each subtable type and thus needs custom 214*2d1272b8SAndroid Build Coastguard Workercode for each subtable type. Currently subtable splitting is only supported for GPOS subtable types. 215*2d1272b8SAndroid Build Coastguard Worker 216*2d1272b8SAndroid Build Coastguard Worker## GSUB/GPOS Extension Lookup Promotion 217*2d1272b8SAndroid Build Coastguard Worker 218*2d1272b8SAndroid Build Coastguard WorkerIn GSUB/GPOS tables lookups can be regular lookups which use 16 bit offsets to the children subtables 219*2d1272b8SAndroid Build Coastguard Workeror extension lookups which use 32 bit offsets to the children subtables. If the sub graph of all 220*2d1272b8SAndroid Build Coastguard Workerregular lookups is too large then it can be difficult to find an overflow free configuration. This 221*2d1272b8SAndroid Build Coastguard Workercan be remedied by promoting one or more regular lookups to extension lookups. 222*2d1272b8SAndroid Build Coastguard Worker 223*2d1272b8SAndroid Build Coastguard WorkerDuring preprocessing the graph is scanned to determine the size of the subgraph of regular lookups. 224*2d1272b8SAndroid Build Coastguard WorkerIf the graph is found to be too big then the analysis finds a set of lookups to promote to reduce 225*2d1272b8SAndroid Build Coastguard Workerthe subgraph size. Lastly the graph is modified to convert those lookups to extension lookups. 226*2d1272b8SAndroid Build Coastguard Worker 227*2d1272b8SAndroid Build Coastguard Worker# Offset Resolution Strategies 228*2d1272b8SAndroid Build Coastguard Worker 229*2d1272b8SAndroid Build Coastguard Worker## Space Assignment 230*2d1272b8SAndroid Build Coastguard Worker 231*2d1272b8SAndroid Build Coastguard WorkerThe goal of space assignment is to find connected subgraphs that are only reachable via 32 bit offsets 232*2d1272b8SAndroid Build Coastguard Workerand then assign each such subgraph to a unique non-zero space. The algorithm is roughly: 233*2d1272b8SAndroid Build Coastguard Worker 234*2d1272b8SAndroid Build Coastguard Worker1. Collect the set, `S`, of nodes that are children of 32 bit offsets. 235*2d1272b8SAndroid Build Coastguard Worker 236*2d1272b8SAndroid Build Coastguard Worker2. Do a directed traversal from each node in `S` and collect all encountered nodes into set `T`. 237*2d1272b8SAndroid Build Coastguard Worker Mark all nodes in the graph that are not in `T` as being in space 0. 238*2d1272b8SAndroid Build Coastguard Worker 239*2d1272b8SAndroid Build Coastguard Worker3. Set `next_space = 1`. 240*2d1272b8SAndroid Build Coastguard Worker 241*2d1272b8SAndroid Build Coastguard Worker4. While set `S` is not empty: 242*2d1272b8SAndroid Build Coastguard Worker 243*2d1272b8SAndroid Build Coastguard Worker a. Pick a node `n` in set `S` then perform an undirected graph traversal and find the set `Q` of 244*2d1272b8SAndroid Build Coastguard Worker nodes that are reachable from `n`. 245*2d1272b8SAndroid Build Coastguard Worker 246*2d1272b8SAndroid Build Coastguard Worker b. During traversal if a node, `m`, has a edge to a node in space 0 then `m` must be duplicated 247*2d1272b8SAndroid Build Coastguard Worker to disconnect it from space 0. 248*2d1272b8SAndroid Build Coastguard Worker 249*2d1272b8SAndroid Build Coastguard Worker d. Remove all nodes in `Q` from `S` and assign all nodes in `Q` to `next_space`. 250*2d1272b8SAndroid Build Coastguard Worker 251*2d1272b8SAndroid Build Coastguard Worker 252*2d1272b8SAndroid Build Coastguard Worker c. Increment `next_space` by one. 253*2d1272b8SAndroid Build Coastguard Worker 254*2d1272b8SAndroid Build Coastguard Worker 255*2d1272b8SAndroid Build Coastguard Worker## Manual Iterative Resolutions 256*2d1272b8SAndroid Build Coastguard Worker 257*2d1272b8SAndroid Build Coastguard WorkerFor each overflow in each iteration the algorithm will attempt to apply offset overflow resolution 258*2d1272b8SAndroid Build Coastguard Workerstrategies to eliminate the overflow. The type of strategy applied is dependent on the characteristics 259*2d1272b8SAndroid Build Coastguard Workerof the overflowing link: 260*2d1272b8SAndroid Build Coastguard Worker 261*2d1272b8SAndroid Build Coastguard Worker* If the overflowing offset is inside a space other than space 0 and the subgraph space has more 262*2d1272b8SAndroid Build Coastguard Worker than one 32 bit offset pointing into the subgraph then subdivide the space by moving subgraph 263*2d1272b8SAndroid Build Coastguard Worker from one of the 32 bit offsets into a new space via the duplication of shared nodes. 264*2d1272b8SAndroid Build Coastguard Worker 265*2d1272b8SAndroid Build Coastguard Worker* If the overflowing offset is pointing to a subtable with more than one incoming edge: duplicate 266*2d1272b8SAndroid Build Coastguard Worker the node so that the overflowing offset is pointing at it's own copy of that node. 267*2d1272b8SAndroid Build Coastguard Worker 268*2d1272b8SAndroid Build Coastguard Worker* Otherwise, attempt to move the child subtable closer to it's parent. This is accomplished by 269*2d1272b8SAndroid Build Coastguard Worker raising the priority of all children of the parent. Next time the topological sort is run the 270*2d1272b8SAndroid Build Coastguard Worker children will be ordered closer to the parent. 271*2d1272b8SAndroid Build Coastguard Worker 272*2d1272b8SAndroid Build Coastguard Worker# Test Cases 273*2d1272b8SAndroid Build Coastguard Worker 274*2d1272b8SAndroid Build Coastguard WorkerThe harfbuzz repacker has tests defined using generic graphs: https://github.com/harfbuzz/harfbuzz/blob/main/src/test-repacker.cc 275*2d1272b8SAndroid Build Coastguard Worker 276*2d1272b8SAndroid Build Coastguard Worker# Future Improvements 277*2d1272b8SAndroid Build Coastguard Worker 278*2d1272b8SAndroid Build Coastguard WorkerCurrently for GPOS tables the repacker implementation is sufficient to handle both subsetting and the 279*2d1272b8SAndroid Build Coastguard Workergeneral case of font compilation repacking. However for GSUB the repacker is only sufficient for 280*2d1272b8SAndroid Build Coastguard Workersubsetting related overflows. To enable general case repacking of GSUB, support for splitting of 281*2d1272b8SAndroid Build Coastguard WorkerGSUB subtables will need to be added. Other table types such as COLRv1 shouldn't require table 282*2d1272b8SAndroid Build Coastguard Workersplitting due to the wide use of 24 bit offsets throughout the table. 283*2d1272b8SAndroid Build Coastguard Worker 284*2d1272b8SAndroid Build Coastguard WorkerBeyond subtable splitting there are a couple of "nice to have" improvements, but these are not required 285*2d1272b8SAndroid Build Coastguard Workerto support the general case: 286*2d1272b8SAndroid Build Coastguard Worker 287*2d1272b8SAndroid Build Coastguard Worker* Extension demotion: currently extension promotion is supported but in some cases if the non-extension 288*2d1272b8SAndroid Build Coastguard Worker subgraph is underfilled then packed size can be reduced by demoting extension lookups back to regular 289*2d1272b8SAndroid Build Coastguard Worker lookups. 290*2d1272b8SAndroid Build Coastguard Worker 291*2d1272b8SAndroid Build Coastguard Worker* Currently only children nodes are moved to resolve offsets. However, in many cases moving a parent 292*2d1272b8SAndroid Build Coastguard Worker node closer to it's children will have less impact on the size of other offsets. Thus the algorithm 293*2d1272b8SAndroid Build Coastguard Worker should use a heuristic (based on parent and child subtable sizes) to decide if the children's 294*2d1272b8SAndroid Build Coastguard Worker priority should be increased or the parent's priority decreased. 295