23.3 Storage

Because Paths are such a crucial part of MuPDF, and are used so widely in document content, we take particular care to allow them to be expressed and accessed efficiently.

This means that at path construction time, we spot simple cases where we can optimise the path representation. For example, a move immediately following a move can cause the first move to be dropped. Similarly, a curve with both control points coincident with the endpoints can be expressed as a line.

This means that if you read a path out after construction (see section 23.7 Walking) you cannot rely on the exact representation being the same.

In addition, after constructing a path, there are some simple things that can be done to minimise the memory used.

As paths are constructed, the data buffers within them grow. For efficiency, these grow with some slack in them, so at the end of construction there can be a non-trivial amount of space wasted.

If you intend to simply use the path, and then discard it, this does not matter. If instead you intend to keep the path around for a while, it may be worth calling fz_trim_path to shrink the storage buffers as much as possible.

/* 
   fz_trim_path: Minimise the internal storage 
   used by a path. 
 
   As paths are constructed, the internal buffers 
   grow. To avoid repeated reallocations they 
   grow with some spare space. Once a path has 
   been fully constructed, this call allows the 
   excess space to be trimmed. 
 
   Never throws exceptions. 
*/ 
void fz_trim_path(fz_context *ctx, fz_path *path);

MuPDF automatically calls this function when fz_keep_path is called for the first time as having more than one reference to a path is considered a good indication of it being kept around for a while.

For cases where large numbers of paths are kept around for a long period of time, for example in a fz_display_list (see chapter 11 Display Lists), it can be advantageous to ‘pack’ paths to further minimise the space they use.

To pack a path, first call fz_packed_path_size to obtain the number of bytes required to pack a path:

/* 
   fz_packed_path_size: Return the number of 
   bytes required to pack a path. 
 
   Never throws exceptions. 
*/ 
int fz_packed_path_size(const fz_path *path);

Then, call fz_pack_path with some (suitably aligned) memory of the appropriate size to actually pack the path:

/* 
   fz_pack_path: Pack a path into the given block. 
 
   To minimise the size of paths, this function allows them to be 
   packed into a buffer with other information. 
 
   pack: Pointer to a block of data to pack the path into. Should 
   be aligned by the caller to the same alignment as required for 
   an fz_path pointer. 
 
   max: The number of bytes available in the block. 
   If max < sizeof(fz_path) then an exception will 
   be thrown. If max >= the value returned by 
   fz_packed_path_size, then this call will never 
   fail, except in low memory situations with large 
   paths. 
 
   path: The path to pack. 
 
   Paths can be unpacked’, flat’, or open’. Standard paths, as 
   created are unpacked’. Paths that will pack into less than max 
   bytes will be packed as flat’, unless they are too large (where 
   large indicates that they exceed some private implementation 
   defined limits, currently including having more than 256 
   256 coordinates or commands). 
 
   Large paths are open packed as a header into the given block, 
   plus pointers to other data blocks. Paths can be used 
   interchangably regardless of how they are packed. 
 
   Returns the number of bytes within the block used. Callers can 
   access the packed path data by casting the value of pack on 
   entry to be an fz_path *. 
 
   Throws exceptions on failure to allocate, or if 
   max < sizeof(fz_path). 
*/ 
int fz_pack_path(fz_context *ctx, uint8_t *pack, int max, const fz_path *path);

After a successful call to fz_pack_path , the pointer to the block of memory can be cast to an fz_path * and used as normal.

All the path routines recognise packed paths and will use them interchangeably. Packed paths may not be modified once created, however.