Mupdf Explored
Mupdf Explored
Robin Watts
This is the beginnings of a book on MuPDF. It is far from complete, but offers
useful information as far as it goes. We offer it with the latest release of MuPDF
(currently 1.11) in the hopes it will be useful.
Any feedback, corrections or suggestions are welcome. Please visit bugs.
ghostscript.com and open a bug with “MuPDF” as the product, and “Docu-
mentation” as the component.
We will endeavour to make new versions available from the Documentation
section of mupdf.com as they appear.
i
Contents
Preface i
1 Introduction 1
1.1 What is MuPDF? . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 License . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.3 Dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2 Quick Start 4
2.1 How to open a document and render some pages . . . . . . . . . 4
3 The Context 5
3.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
3.2 Creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3.3 Custom Allocators . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.4 Multi-threading . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.5 Cloning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.6 Destruction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.7 Tuning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.8 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4 Error handling 14
4.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.2 Throwing exceptions . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.3 Handling exceptions . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.4 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
ii
CONTENTS iii
5.4.3 Hashing . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
5.4.4 Key storable items . . . . . . . . . . . . . . . . . . . . . . 25
5.4.5 Reap passes . . . . . . . . . . . . . . . . . . . . . . . . . . 26
5.5 Scavenging memory allocator . . . . . . . . . . . . . . . . . . . . 26
5.6 Reacting to Out of Memory events . . . . . . . . . . . . . . . . . 27
8 Building Blocks 53
8.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
8.2 Colorspaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
8.2.1 Basic Colorspaces . . . . . . . . . . . . . . . . . . . . . . 53
8.3 Pixmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
CONTENTS iv
8.3.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
8.3.2 Saving . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
8.4 Bitmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
8.5 Halftones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
8.6 Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
8.6.1 Compressed Images . . . . . . . . . . . . . . . . . . . . . 59
8.6.2 Pixmap Images . . . . . . . . . . . . . . . . . . . . . . . . 59
8.7 Buffers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
8.8 Transforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
8.9 Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
8.9.1 Creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
8.9.2 Reference counting . . . . . . . . . . . . . . . . . . . . . . 70
8.9.3 Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
8.9.4 Transformation . . . . . . . . . . . . . . . . . . . . . . . . 73
8.9.5 Bounding . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
8.9.6 Stroking . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
8.9.7 Walking . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
8.10 Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
8.10.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
8.10.2 Creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
8.10.3 Population . . . . . . . . . . . . . . . . . . . . . . . . . . 80
8.10.4 Measurement . . . . . . . . . . . . . . . . . . . . . . . . . 82
8.10.5 Cloning . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
8.10.6 Language . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
8.10.7 Implementation . . . . . . . . . . . . . . . . . . . . . . . . 83
8.11 Shadings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
8.11.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
8.11.2 Creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
8.11.3 Bounding . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
8.11.4 Painting . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
8.11.5 Decomposition . . . . . . . . . . . . . . . . . . . . . . . . 88
9 Display Lists 90
9.0.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
9.0.2 Creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
9.0.3 Playback . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
9.0.4 Reference counting . . . . . . . . . . . . . . . . . . . . . . 92
9.0.5 Miscellaneous operations . . . . . . . . . . . . . . . . . . . 93
Introduction
1.2 License
MuPDF is released under two licenses.
Firstly, it is available under the GNU Afferro General Purpose License (hence-
forth the GNU AGPL). This is a complex license worthy of careful study and
more words than we have space for here. Some key points, however are:
1
CHAPTER 1. INTRODUCTION 2
• You are free to use MuPDF within a piece of software written entirely
for your own use with no problems. The moment you pass that software
to any other person, or make it available to any other person as part of
a “Software as a service” installation, you must abide by the following
terms.
• If you link MuPDF into your own software, then the entirety of that
software must be licensed under the GNU AGPL.
• If you use MuPDF as part of a “Software as a service” installation, then
you must license the entirety of that installation under the GNU AGPL.
• Releasing a piece of software under the GNU AGPL requires you to be
prepared to give full source code to any user that receives a copy of the
software. No charge (other than nominal media costs) may be made for
this.
• You must ensure that all end users of that system have the ability to
update the software with an updated version of MuPDF. This includes
embedded systems.
• Using MuPDF under the GNU AGPL, you receive no warranty and no
support.
There are other terms too, and we strongly recommend that you read the license
in full and understand your obligations under it before developing code based
upon MuPDF.
If you find that you can abide by all the terms of the GNU AGPL, you can use
MuPDF in your own projects without any license fee.
These terms, however, are generally stringent enough that they are inappro-
priate for people producing commercial products - giving the source code to a
commercial product away is generally unacceptable, and the ‘relinking’ require-
ments of the GNU AGPL are too cumbersome for embedded users.
It is for this reason that Artifex (the developers of MuPDF) offer commercial
licenses. Contact [email protected] for a quote tailored to your exact needs.
The Artifex commercial license removes all the onerous terms of the GNU
AGPL, including the need to license your entire app, to give away source, and
to ensure relinking capabilities.
If you find yourself unable to accept and comply with the terms of the GNU
AGPL, and unwilling to obtain a Commercial license from Artifex, you cannot
legally use MuPDF in any software that you distribute.
CHAPTER 1. INTRODUCTION 3
1.3 Dependencies
The core MuPDF library makes use of various software libraries.
Freetype Renderer for various font types.
Harfbuzz OpenType Font shaper built upon freetype, required for e-pub files.
JBig2dec Image decoder for JBIG2 images.
JpegLib Image decoder for JPEG images.
MuJS Javascript engine used for PDF files.
OpenJPEG Image decoder for JPEG2000 images.
ZLib Compression library.
In addition, the MuPDF library can optionally make use of:
OpenSSL Encryption library, required for Digital Signatures support.
Finally, the MuPDF viewer for Linux and Windows can optionally make use of:
Curl An http fetcher used for displaying files as they download.
These libraries are packaged with MuPDF, either in the distribution archives
or as git submodules. From time to time, these libraries may include bug fixes
that have not been accepted back into the upstream repositories. We therefore
strongly recommend using the versions of the libraries that we ship, rather than
any other versions you may find on your system.
Chapter 2
Quick Start
4
Chapter 3
The Context
3.1 Overview
The core MuPDF library is designed for simplicity, portability, and ease of
integration. For all these reasons, it has no global variables, has no thread
library dependencies, and has a well defined exception system to handle runtime
errors. Nonetheless, in order to be as useful as possible, clearly the library
must have some state and needs to be able to take advantage of multi-threaded
environments.
The solution to these seemingly conflicting requirements is the Context
(fz context).
Every caller to MuPDF should create a Context at the start of its use of the
library, and destroy it at the end. This Context (or one ‘cloned’ from it) will
then be passed in to every MuPDF API call.
Global State At its simplest, the Context contains global settings for the li-
brary. For instance, the levels of Antialiasing used by the text and line
art rendering routines are set in the Context, as is the default style sheet
for Epub or FB2 files. In addition, the library stores its own private
information there too.
Error handling All error handling within MuPDF is done using the fz try/
fz catch constructs; see chapter 4 Error handling for more details.
These constructs can be nested, so rely on an exception stack maintained
within the context. As such it is vitally important that no two threads
use the same context at the same time. See section 3.4 Multi-threading
for more information.
Allocation When embedding MuPDF into a system it is often desirable to
5
CHAPTER 3. THE CONTEXT 6
3.2 Creation
To create a context, use fz new context:
/*
fz_new_context: Allocate context containing global state.
For example, a simple, single threaded program using the standard allocator
can just use:
fz_context *ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
The malloc, realloc and free function pointers have essentially the same
semantics as the standard malloc, realloc and free standard functions, with
the exceptions that they take an additional initial argument - that of the user
value specified in the fz alloc context.
3.4 Multi-threading
MuPDF itself does not rely on a thread system, but it will make use of one if one
is present. This is crucial to ensure that MuPDF can be called from multiple
threads at once.
A typical example of this might be in a multi-core processor on a printer. We
can interpret the PDF file to a display list, and then render ‘bands’ from that
display list to send to the printer. By using multiple threads we can render
multiple bands at once, thus vastly improving processing times.
In this example, although each thread will be rendering different things, they
will probably share some information - for instance the same font is likely to be
CHAPTER 3. THE CONTEXT 8
used in multiple bands. Rather than have every thread render all the glyphs that
it needs from the font independently, it would be nice if they could collaborate
and share results.
We therefore arrange that data structures such as the font cache can be shared
between the different threads. This, however, brings dangers; what if two
threads try to write to the same data structure at once?
To save this being a problem, we rely on the user providing some locking func-
tions for us.
/*
Locking functions
typedef struct
{
void *user;
void (*lock)(void *user, int lock);
void (*unlock)(void *user, int lock);
} fz_locks_context;
enum {
...
FZ_LOCK_MAX
};
lightweight).
These locks are not assumed to be recursive (though recursive locks will work
just fine).
To avoid deadlocks, MuPDF guarantees never to take lock n if that thread
already holds lock m (for n ¿ m).
There are 3 simple rules to follow when using MuPDF in a multi threaded
environment:
1. No simultaneous calls to MuPDF in different threads are allowed
to use the same context.
Most of time it is simplest just to use a different context for every thread;
just create a new context at the same time as you create the thread. See
section 3.5 Cloning for more information.
2. No simultaneous calls to MuPDF in different threads are allowed
to use the same document.
Only one thread can be accessing an document at a time. Once display
lists are created from that document, multiple threads can operate on
them safely.
The document can safely be used from several different threads as long as
there are safeguards in place to prevent the usages being simultaneous.
3. No simultaneous calls to MuPDF in different threads are allowed
to use the same device.
Calling a device simultaneously from different threads will cause it to get
confused and may crash. Calling a device from several different threads
is perfectly acceptable as long as there are safeguards in place to prevent
the calls being simultaneous.
3.5 Cloning
The context contains the exception stack for the fz try/fz catch constructs.
As such trying to use the same context from multiple threads at the same time
will lead to crashes.
The solution to this is to ‘clone’ the context. Each clone will share the same
underlying store (and will inherit the same settings, such as allocators, locks
etc), but will have its own exception handle. Other settings, such as anti-alias
levels, will be inherited from the original at the time of cloning, but can be
changed to be different if required.
For example, in a viewer application, we might want to have a background
process that runs through the file generating page thumbnails. In order for this
CHAPTER 3. THE CONTEXT 10
not to interfere with the foreground process, we would clone the context, and
use the cloned context in the thumbnailing thread. We might choose to disable
anti-aliasing for the thumbnailing thread to trade quality for speed.
Any images decoded for the thumbnailing thread would live on in the store
though, and would hence be available should the viewers normal render opera-
tions need them.
To clone a context, use fz clone context:
/*
fz_clone_context: Make a clone of an existing context.
For example:
fz_context *worker_ctx = fz_clone_context(ctx);
In order for cloned contexts to work safely, they rely on being able to
take locks around certain operations to make them atomic. Accordingly,
fz clone context will return NULL (to indicate failure) if the base context
did not have locking functions defined.
3.6 Destruction
Once you have finished with an fz context (either your original one, or a
‘cloned’ one) you can destroy it using fz drop context.
/*
fz_drop_context: Free a context and its global state.
The context and all of its global state is freed, and any
buffered warnings are flushed (see fz_flush_warnings). If NULL
is passed in nothing will happen.
CHAPTER 3. THE CONTEXT 11
For example:
fz_drop_context(ctx);
3.7 Tuning
Some of MuPDF’s functionality relies on heuristics to make decisions. Rather
than hard code these decisions in the library code, the tuning context allows
callers to override the defaults with their own ‘tuned’ versions.
Currently, we have just 2 calls defined here, both to do with image handling,
but this may expand in future.
The first tuning function enables fine control over how much of an image MuPDF
should decode if it only requires a subarea:
/*
fz_tune_image_decode_fn: Given the width and height of an image,
the subsample factor, and the subarea of the image actually
required, the caller can decide whether to decode the whole image
or just a subarea.
Having defined a function of this type to implement the desired strategy, it can
be set into the context using:
/*
fz_tune_image_decode: Set the tuning function to use for
image decode.
CHAPTER 3. THE CONTEXT 12
The second function allows fine control over the scaling used when images are
scaled:
/*
fz_tune_image_scale_fn: Given the source width and height of
image, together with the actual required width and height,
decide whether we should use mitchell scaling.
Having defined a function of this type to implement the desired strategy, it can
be set into the context using:
/*
fz_tune_image_scale: Set the tuning function to use for
image scaling.
3.8 Summary
The basic usage of Contexts is as follows:
1. Call fz new context to create a context. Pass in any custom allocators
CHAPTER 3. THE CONTEXT 13
required. If you wish to use MuPDF from multiple threads at the same
time, you must also pass in locking functions. Set the store size appropri-
ately.
2. Call fz clone context to clone the context as many times as you need;
typically once for each ‘worker’ thread.
3. Perform the operations required using MuPDF within fz try/fz catch
constructs.
4. Call fz drop context with each cloned context.
5. Call fz drop context with the original context.
Things to remember:
1. A fz context can only be used in 1 thread at a time.
2. A fz document can only be used in 1 thread at a time.
3. A fz device can only be used in 1 thread at a time.
4. A fz context shares the store with all the fz contexts cloned from it.
Chapter 4
Error handling
4.1 Overview
MuPDF handles all its errors using an exception system. This is superficially
similar to C++ exceptions, but (as MuPDF is written in C) it is implemented
using macros that wrap the setjmp/longjmp standard C functions.
It is probably best not to peek behind the curtain, and just to think of these
constructs as being extensions to the language. Indeed, we have worked very
hard to ensure that the complexities involved are minimised.
Unless otherwise specified, all MuPDF API functions can throw exceptions, and
should therefore be called within an fz try/fz always/fz catch construct.
The general anatomy of such a construct is as follows:
fz_try(ctx)
{
/* Do stuff in here that might throw an exception.
* NEVER return from here. ’break’ can be used to
* continue execution (either in the always block or
* after the catch block). */
}
fz_always(ctx)
{
/* Anything in here will always be executed, regardless
* of whether the fz_try clause exited normally, or an
* exception was thrown. */
}
fz_catch(ctx)
{
/* This block will execute if (and only if) anything in
* the fz_try block calls fz_throw. We should clean up
14
CHAPTER 4. ERROR HANDLING 15
In an ideal world, that would be all there is to it. Unfortunately, there are 2
wrinkles.
The first one, relatively simple, is that you must not return from within a fz try
block. To do so will corrupt the exception stack and cause problems and crashes.
To mitigate this, you can safely break out of the fz try, and execution will pass
into the fz always block (if there is one, or continue after the fz catch block
if not).
The second one, is more convoluted. If you do not wish to understand the long
and complex reasons behind this, skip forward to the summary and just follow
the rules there.
As stated before fz try/ fz catch are implemented using setjmp/longjmp,
and these can ‘lose’ changes to variables.
For example:
house_t *build_house(fz_context *ctx)
{
walls_t *w = NULL;
roof_t *r = NULL;
house_t *h = NULL;
fz_try(ctx)
{
w = make_walls();
r = make_roof();
h = combine(w, r); /* Note, NOT: return combine(w,r); */
}
fz_always(ctx)
{
drop_walls(w);
drop_roof(r);
CHAPTER 4. ERROR HANDLING 16
}
fz_catch(ctx)
{
return NULL; /* Or fz_rethrow if we’re nested */
}
return h;
}
In the above code (as well as throughout MuPDF), we follow the convention
that destructors always accept NULL. This makes cleanup code much simpler.
Reading through this code, it is fairly obvious what will happen if everything
works correctly. First we’ll make some walls, w, and a roof, r. Then we combine
the walls and the roof, to get our house, h. Next we tidy up the walls and the
roof, and we return the completed house to our caller.
It’s more interesting to consider what will happen if we have failures.
First let’s consider what happens if the make walls fails. This will fz throw an
exception, and control will jump immediately to the fz always. This will drop
w and r (both of which are still NULL). The fz catch can then handle the error,
either by returning NULL, to indicate failure, or perhaps by fz rethrowing the
error to an enclosing fz try/ fz catch construct. No problems there.
So what happens when the failure occurs in make roof fails? Let’s run through
the code again.
This time, make walls succeeds, and w is set to this new value. Then make roof
fails, fz throwing an exception, and control will jump immediately to the
fz always. This will then try to drop w (now a valid value) and r (which is still
NULL). The fz catch can then handle the error, either by returning NULL, to
indicate failure, or perhaps by fz rethrowing the error to an enclosing fz try/
fz catch construct. All sounds quite plausible.
Unfortunately, if you try it, on some systems you will find that you have a
memory leak (or worse). When drop walls is called, sometimes you will find
that w has ‘lost’ its value.
This is due to an obscure part of the C specification that states that any changes
to local variables made between a setjmp and a longjmp can be lost.
In fz try/ fz catch terms, this means that any local variables set within the
fz try block can be ‘lost’ when either fz always or fz catch are reached.
Fortunately, there is a fix for this, fz var. By calling fz var(w); we can
‘protect’ variable w from such unwanted behaviour.
It’s not really necessary to know how this works, but for those interested, a quick
explanation. The ‘loss’ of the value occurs because the compiler can postpone
writing the value back into the storage location for the variable (or can choose to
just hold it in a register). The call to fz var passes the address of the variable
CHAPTER 4. ERROR HANDLING 17
out of scope; this forces the compiler not to hold it in a register. Further, the
compiler has no way of knowing whether any functions it call might access that
location, so it needs to make sure that the variable value is written back on every
function call - such as longjmp. Hence the variable is magically protected.
Calls to fz var are very low cost (but are not NOPs), so erring on the side of
caution and calling fz var on more than you need to will probably not hurt.
A corrected version of the above example is therefore:
house_t *build_house(fz_context *ctx)
{
walls_t *w = NULL;
roof_t *r = NULL;
house_t *h = NULL;
fz_var(w);
fz_var(r);
fz_try(ctx)
{
w = make_walls();
r = make_roof();
h = combine(w, r); /* Note, NOT: return combine(w,r); */
}
fz_always(ctx)
{
drop_walls(w);
drop_roof(r);
}
fz_catch(ctx)
{
return NULL; /* Or fz_rethrow if we’re nested */
}
return h;
}
{
FZ_ERROR_NONE = 0,
FZ_ERROR_GENERIC = 1,
FZ_ERROR_SYNTAX = 2,
FZ_ERROR_TRYLATER = 3,
FZ_ERROR_ABORT = 4,
FZ_ERROR_COUNT
};
In almost all cases, you should be using FZ ERROR GENERIC, for example:
fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to open file ’%s’", filename);
The error will remain readable in this way until the next use of fz try/fz catch
on that same context.
Some code may choose to swallow the error and retry the same code again in a
different manner. To facilitate this, we can find out the type of error using:
int fz_caught(fz_context *ctx);
See section 4.2 Throwing exceptions for a list of the possible exception types.
To simplify the job of deciding whether to pass on exceptions of a given type,
we have a convenience function that with rethrow just a particular type.:
void fz_rethrow_if(fz_context *ctx, int errcode);
CHAPTER 4. ERROR HANDLING 19
4.4 Summary
The basic exception handling rules are as follows:
1. All MuPDF functions except those that explicitly state otherwise, throw
exceptions on errors, and must therefore be called from within an fz try/
fz catch construct.
2. A fz try block must be paired with an fz catch block, and optionally
an fz always block can appear between them.
3. Never return from an fz try block.
4. An fz try block will terminate when control reaches the end of the block,
or when break is called.
5. Any variable that is changed within an fz try block may lose its value if
an exception occurs, unless protected by fz var call.
6. The contents of the fz always block will always be executed (after the
fz try block and before the fz catch block, if appropriate).
7. If an exception is thrown during the fz try block, control will jump to
the fz always block (if there is one) and then continue to the fz catch
block.
Chapter 5
Reference Counting,
Memory Management and
The Store
5.1 Overview
While MuPDF is running, it holds various objects in memory, and passes them
between its various components. For instance, MuPDF might read a path defi-
nition in in the PDF interpreter, and pass it first into the display list and then
on into the renderer.
To avoid needless copying of data, a reference counting scheme is used. Each
significant object has a reference count, so that when one area of the code
retains a reference to something (perhaps the display list), the data need not
be copied wholesale. In the above example, the PDF interpreter might hold
one reference, and first the display list and then the renderer might take others.
Some references are held just for a short length of time, but others can persist
for a much longer period.
During the course of displaying files, MuPDF loads various resources into mem-
ory, such as fonts and images. By holding these resources in memory throughout
the processing of the file we can avoid reloading them each time they are re-
quired.
As the document is rendered, more memory is needed to hold rendered versions
of glyphs from the font, or decoded versions of images. By keeping these decoded
versions around in memory, we can avoid the need to redecode them the next
time we need the same glyph, or the same image.
20
CHAPTER 5. REFERENCE COUNTING, MEMORY MANAGEMENT AND THE STORE21
Keeping all this data around can end up using a large amount of memory, which
may be unfeasible for some systems. Equally, not keeping any of it around will
result in a drastic performance drop.
The solution is to keep as much around as can conveniently fit in memory.
MuPDF achieves this using a mechanism known as “The Store”.
The Store is a mechanism for holding blocks of data likely to be reusable. When-
ever MuPDF needs such a block of data, it checks the Store to see if the data
is there already - if it is, it can be instantly reused. If not the code forms the
data itself, and then puts it into the store.
The MuPDF allocation code is tied into the store, so that if an allocation ever
fails, objects are evicted from the store, and the allocation retried. This ‘scav-
enging’ of memory means that we can safely keep lots of cached data around
without ever worrying that it will cause us to run out of memory.
5.2.1 Implementation
These keep and drop calls for simple objects are generally implemented by using
one of a set of standard functions. There are a range of these, depending on
the expected size of the reference counts, and all handle the locking required to
ensure thread safety:
CHAPTER 5. REFERENCE COUNTING, MEMORY MANAGEMENT AND THE STORE22
and thus appropriate keep and drop functions can be defined simply:
fz_path *fz_keep_path(fz_context *ctx, fz_path *path)
{
return fz_keep_imp8(ctx, &path->refs);
}
More complex variations of these functions are available to cope with ‘storable’
objects, and still more complex versions to cope with ‘key storable’ objects -
these are explained in the following sections.
However they are implemented, these objects all look basically the same to most
users - they can simply be ‘kept’ and ‘dropped’ as required.
5.4.1 Overview
As discussed above, the Store is basically a set of key/value pairs. While the
values are always fz storables, the keys can be of many different types, due
to coming from many disparate parts of the system.
Accordingly, we need a mechanism to allow us to safely know what ‘type’ a
given key is, and to compare 2 keys of identical type.
CHAPTER 5. REFERENCE COUNTING, MEMORY MANAGEMENT AND THE STORE24
We will have just one instance of this for each type - normally a static const
structure defined in the code. Whenever we insert (or lookup) something in the
store, we pass the address of that ‘types’ structure.
We only compare items if they have the same type pointer, and any comparison
is done using the cmp key function pointer therein. In common with normal C
idioms, 0 means match, non zero means different.
The keep key and drop key entries are used to implement reference counting
of keys. Because keys can be an amalgam of several reference counted objects,
the keep and drop functions provided here can take and drop these in sync.
The print function is purely for debugging purposes as part of calls to
fz print store - it should generate a human readable summary of the key
to the given fz output stream.
The make hash key and needs reap functions are explained in the following
subsections.
5.4.3 Hashing
In order to ensure the Store performs well, we must ensure that certain processes
run efficiently - notably searching for an existing entry, insertion and deletion.
Accordingly, the Store is implemented based on a hash table. For every ‘key’,
we need to be able to form a hash, but this process is complicated slightly by
the fact that every different fz storable has a different type for the key.
We solve this by having the make hash key member of the fz store type struc-
ture convert whatever its key data is into a common structure:
typedef struct fz_store_hash_s
{
fz_store_drop_fn *drop;
union
{
struct
CHAPTER 5. REFERENCE COUNTING, MEMORY MANAGEMENT AND THE STORE25
{
const void *ptr;
int i;
} pi;
struct
{
const void *ptr;
int i;
fz_irect r;
} pir;
struct
{
int id;
float m[4];
} im;
} u;
} fz_store_hash;
The caller will always arrange for this structure to be zero filled on entry to
the make hash key call. On exit, it should have been updated with the key
details. Implementers may extend the union found in this structure as required,
though ideally the size of the overall structure should be minimised to avoid
unnecessary work.
Once the Store has formed a fz store hash it can then generate the required
hash for the hashtable as required.
Some objects can be used both as values within the Store, and as a component
of keys within the Store. We refer to these objects as “key storable” objects.
In this case, we need to take additional care to ensure that we do not end
up keeping an item within the store, purely because its value is referred to by
another key in the store.
An example of this are fz images in PDF files. Each fz image is placed into
the Store to enable it to be easily reused. When the image is rendered, a pixmap
is generated from the image, and the pixmap is placed into the Store so it can be
reused on subsequent renders. The image forms part of the key for the pixmap.
When we close the pdf document (and any associated pages/display lists etc),
we drop the images from the Store. This may leave us in the position of the
images having non-zero reference counts purely because they are used as part
of the keys for the pixmaps.
We therefore use special reference counting functions to implement these
fz key storable items, fz keep key storable and fz drop key storable
rather than the more usual fz keep storable and fz drop storable.
CHAPTER 5. REFERENCE COUNTING, MEMORY MANAGEMENT AND THE STORE26
The sole difference is that these enable us to store the number of references
to these items that are used in keys. This is achieved by callers taking
and dropping references for use in keys with fz keep key storable key and
fz drop key storable key.
This means that key storable items need to provide two sets of keep and drop
functions, one for ‘normal’ callers, and one for use during key handling. For
example:
fz_image *fz_keep_image(fz_context *ctx, fz_image *image);
void fz_drop_image(fz_context *ctx, fz_image *image);
The purpose of this extra work is to allow us to spot when we may need to
check the Store for ‘dead’ entries - those that can never be ‘found’ by looking
in the store.
When the number of references to a key storable object equals the number of
references to an object from keys in the Store, we know that we can remove all
the items which have that object as part of the key. This is done by running a
pass over the store, ‘reaping’ those items.
If a key does not consist of any storable objects, then the needs reap entry in its
fz store type can safely be left as NULL. If it does, however, it must provide
an implementation to check whether a reap pass is required. Essentially this
needs to check if any of its constituent fz key storable objects need reaping,
which can be done by a call to:
int fz_key_storable_needs_reaping(fz_context *ctx, const fz_key_storable
*ks);
Reap passes are slower than we would like as they touch every item in the
store. We therefore provide a way to ‘batch’ such reap passes together, us-
ing fz defer reap start and fz defer reap end to bracket a region in which
many may be triggered.
into the fz new context call (or to malloc and family if no custom allocators
were supplied). (See chapter 3 The Context for details).
If a call to the underlying custom allocator fails, MuPDF will automatically seek
to evict the least recently used objects from the store that are not currently being
used, and then will retry the allocation. This can happen several times, with
more and more objects being freed between each attempt.
Allocation failures are therefore only fatal to MuPDF if there are no remaining
objects to be freed in the store.
This ‘just in time’ scavenging of memory means that the store limit can safely
be set to a high level (or to be unlimited), and MuPDF will still operate within
safe bounds.
6.1 Overview
Although MuPDF handles multiple different file formats, it offers a unified API
for dealing with them. The fz document API allows all the common operations
to be performed on a document, hiding the implementation specifics away from
the caller.
Not all functions are available on all document types (for instance, JPEG files
do not support annotations), but the API returns sane values.
Open a document file and read its basic structure so pages and
objects can be located. MuPDF will try to repair broken
documents (without actually changing the file contents).
28
CHAPTER 6. THE DOCUMENT INTERFACE 29
For embedded systems, or secure applications, the use of a local filing system
may be inappropriate, so an alternative is available whereby documents can
be opened from an fz stream. See chapter 10 The Stream interface for more
details on fz streams.
/*
fz_open_document_with_stream: Open a PDF, XPS or CBZ document.
/*
fz_drop_document: Release an open document.
Once the last reference to the document is dropped, all resources used by that
document will be released, including those in the Store.
CHAPTER 6. THE DOCUMENT INTERFACE 30
Any non-reflowable document types (such as PDF) will ignore this layout re-
quest. The results of the layout will depend both upon a target width and
CHAPTER 6. THE DOCUMENT INTERFACE 31
height, a given font size, the CSS styles in effect. Documents can be laid out
multiple times to allow changes in these properties to take effect.
MuPDF provides its own default CSS style sheet, but this can be overridden by
the user CSS style sheet in the context:
/*
fz_user_css: Get the user stylesheet source text.
*/
const char *fz_user_css(fz_context *ctx);
/*
fz_set_user_css: Set the user stylesheet source text for use with
HTML and EPUB.
*/
void fz_set_user_css(fz_context *ctx, const char *text);
/*
Find a bookmark and return its page number.
*/
int fz_lookup_bookmark(fz_context *ctx, fz_document *doc, fz_bookmark
mark);
For document types like images, they appear as a single page. If you forget to
lay out a reflowable document, this will trigger a layout for a default size and
return the required number of pages.
Once you know how many pages there are, you can fetch the fz page object for
each page required:
/*
fz_load_page: Load a page.
/*
fz_drop_page: Free a loaded page.
Once the last reference to a page is dropped, the resources it consumes are all
released automatically.
Page Contents (or just Contents) are typically the ordinary printed matter that
you would get on a page; the text, illustrations, any headers or footers, and
maybe some printers marks.
Annotations are normally extra information that is overlaid on the top of these
page contents. Examples include freehand scribbles on the page, highlights/
underlines/strikeouts overlaid on the text, sticky notes etc. Annotations are
typically added to a document by people reading the document after it has
been published rather than by the original author.
Annotations can be enumerated from the page one at a time, by first calling
fz first annot, and then fz next annot:
/*
fz_first_annot: Return a pointer to the first annotation on a page.
/*
fz_next_annot: Return a pointer to the next annotation on a page.
Annotations are reference counted and can be kept and dropped as usual. They
can also be bounded, by passing a rectangle to fz bound annot:
/*
fz_bound_annot: Return the bounding rectangle of the annotation.
On return, the rectangle is populated with the bounding box of the annotation.
Links describe ‘active’ regions on the page; if the user ‘clicks’ within such a
region typically the viewer should respond. Some links move to other places in
the document, others launch external clients such as mail or web sites.
The links on a page can be read by calling fz load links:
/*
fz_load_links: Load the list of links for a page.
Returns a linked list of all the links on the page, each with
its clickable region and link destination. Each link is
CHAPTER 6. THE DOCUMENT INTERFACE 34
This returns a linked list of fz link structures. link->next gives the next one
in the chain.
rendering.
*/
void fz_run_page(fz_context *ctx, fz_page *page, fz_device *dev, const
fz_matrix *transform, fz_cookie *cookie);
This will cause each graphical object from the page contents and annotations
in turn to be transformed, and fed to the device.
For finer control, you may wish to run the page contents, and the annotations
separately:
/*
fz_run_page_contents: Run a page through a device. Just the main
page content, without the annotations, if any.
/*
fz_run_annot: Run an annotation through a device.
annot: an annotation.
All three of these functions (fz run page, fz run page contents,
fz run annot) take an fz cookie pointer. The Cookie is a lightweight
way of controlling the processing of the page. For more details, see section 7.3
Cookie. For most simple cases this can be NULL.
Chapter 7
7.1 Overview
In many ways, the Device interface is the heart of MuPDF.
When any given document handler is told to run the page (fz run page) the ap-
propriate document interpreter serialises the page contents as a series of graph-
ical operations, and calls the device interface to perform these actions.
Many different implementations of the device interface exist within MuPDF.
The most obvious one is the Draw device. When this is called, it renders the
graphical objects in turn into a Pixmap.
Alternatively we have the Structured Text device that captures the text out-
put and forms it into an easily processable structure (for searching, or text
extraction).
Some devices, such as the SVG Output device, repackage the graphical objects
into a different format. The end product of these devices is a new document
with (as much as possible) the same overall appearance as the initial page.
Finally, devices such as the Display List device manage to be both implementers
of the interface, and callers of it. Callers can run page contents to the Display
List device just once, and then replay it quickly many times over to other devices;
ideal for rendering pages in bands, or repeatedly redrawing as a viewer pans and
zooms around a document.
By implementing new devices callers can tap the power of MuPDF in new and
interesting ways, perhaps to harness specific hardware facilities of a device.
37
CHAPTER 7. THE DEVICE INTERFACE 38
Line Art is handled by the device functions to plot paths. See section 8.9 Paths
for more information.
void fz_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path,
int even_odd, const fz_matrix *ctm, fz_colorspace *colorspace,
const float *color, float alpha);
void fz_stroke_path(fz_context *ctx, fz_device *dev, const fz_path
*path, const fz_stroke_state *stroke, const fz_matrix *ctm,
fz_colorspace *colorspace, const float *color, float alpha);
void fz_clip_path(fz_context *ctx, fz_device *dev, const fz_path *path,
int even_odd, const fz_matrix *ctm, const fz_rect *scissor);
void fz_clip_stroke_path(fz_context *ctx, fz_device *dev, const fz_path
*path, const fz_stroke_state *stroke, const fz_matrix *ctm, const
fz_rect *scissor);
7.2.2 Text
Text is handled by the device functions to plot text. See section 8.10 Text for
more information.
void fz_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text,
const fz_matrix *ctm, fz_colorspace *colorspace, const float
*color, float alpha);
CHAPTER 7. THE DEVICE INTERFACE 39
The fz clip text and fz clip stroke text functions are used to start a clip.
Subsequent operations will be clipped through the areas delimited by these,
until an fz pop clip is seen. See subsection 7.2.5 Clipping and Masking for
more details.
7.2.3 Images
Images are handled by the device functions to plot images. See section 8.6
Images for more information.
void fz_fill_image(fz_context *ctx, fz_device *dev, fz_image *image,
const fz_matrix *ctm, float alpha);
void fz_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image
*image, const fz_matrix *ctm, fz_colorspace *colorspace, const
float *color, float alpha);
void fz_clip_image_mask(fz_context *ctx, fz_device *dev, fz_image
*image, const fz_matrix *ctm, const fz_rect *scissor);
The fz clip image mask function is used to start a clip. Subsequent operations
will be clipped through the area delimited by this, until an fz pop clip is seen.
See subsection 7.2.5 Clipping and Masking for more details.
7.2.4 Shadings
Shaded areas (such as radial, linear and mesh based shadings) are rendered by
filling a (normally) clipped region with a shade. This is achieved by calling
fz fill shade. See section 8.11 Shadings for more details.
void fz_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade,
const fz_matrix *ctm, float alpha);
CHAPTER 7. THE DEVICE INTERFACE 40
Graphical objects can be restricted to a given area using Clipping. The area to
clip to can be specified as paths, text, or images as explained in subsection 7.2.1
Line Art, subsection 7.2.2 Text, and subsection 7.2.3 Images.
Each call to such a function starts a clipping group, which will be terminated
by calling:
void fz_pop_clip(fz_context *ctx, fz_device *dev);
Some document formats (such as PDF) offer a rich transparency model that
allows graphical objects to be ’Grouped’ together and imposed upon the page
as if they have a given opacity, using a variety of different blend modes.
MuPDF implements this by using the fz begin group and fz end group calls.
void fz_begin_group(fz_context *ctx, fz_device *dev, const fz_rect
*area, int isolated, int knockout, int blendmode, float alpha);
void fz_end_group(fz_context *ctx, fz_device *dev);
CHAPTER 7. THE DEVICE INTERFACE 41
The exact details of PDF transparency are too complex to explain here; for a
full explanation see The PDF Reference Manual.
7.2.7 Tiling
Every device has a set of render flags (a simple int, in which bits can be set or
cleared).
CHAPTER 7. THE DEVICE INTERFACE 42
That is to say, the bits given in set are set, and then the bits given in clear
are cleared.
The current only documented use of this is for the GProof device to request the
Draw device to grid fit its tiled images.
The reason for using Render Flags rather than Device Hints (see section 7.4
Device Hints) is that Render Flags can be carried forward though display lists.
7.3 Cookie
The cookie is a lightweight mechanism for controlling and detecting the be-
haviour of a given interpretation call (i.e. fz run page, fz run page contents,
fz run annot, fz run display list etc).
To use the cookie, a caller should simply define:
fz_cookie *cookie = { 0 };
and then pass &cookie as the last parameter to the interpretation call, for
example:
fz_run_page(ctx, page, dev, transform, &cookie);
The contents and definition of fz cookie are even more subject to change than
other structures, so it is important to always initialise all the subfields to zero.
The safest way to do this is as given above. If new fields are added to the
structure, callers code should not need to change, and the default behaviour of
zero-valued new fields will always remain the same.
CHAPTER 7. THE DEVICE INTERFACE 43
Content interpretations can take a (relatively) long time. Once one has been
started, it can be useful a) to know how far through processing we are, and b)
to be able to abort processing should the results of a run no longer be required.
As a run progresses, 2 fields in the cookie are updated. Firstly, progress will
be set to a number that increases as progress is made. Think of this informally
as being the number of objects that have been processed so far. In some cases
(notably when processing a display list) we can know an upper bound for this
value, and this value will be given as progress max. In cases where no upper
bound is known, progress max will be set to -1. It is possible that the upper
bound may start as -1, and then change to a known value later.
These values are intended to enable user feedback to be given, and should not
be taken as guarantees of performance.
While running content, the interpreter periodically checks the abort field of the
cookie. If it is discovered to be non zero, the rest of the content is ignored.
If the caller decides that it does not need the results of a run once it has been
started (perhaps the user changes the page, or closes the file), then it should
therefore set the abort field of the cookie to 1.
No guarantees are made about how often the cookie is checked, nor about how
fast an interpreter will respond to the abort field once it is set. Setting the abort
flag will never hurt, and will frequently help, however. Once the flag has been
set to 1, it should never be reset to 0, as the results will be unpredictable.
Resources used by a run cannot be released until the end of a run, regardless of
the setting of abort. Callers still need to wait for the fz run page (or other)
call to complete before the page etc can be safely dropped.
CHAPTER 7. THE DEVICE INTERFACE 44
The cookie also has a role to play when working in Progressive Mode. The
incomplete ok and incomplete fields are used for this. See chapter 16 Pro-
gressive Mode) for more details.
For example: By default the draw device renders shadings. For some
purposes (perhaps rendering fast low quality thumbnails) you may want
to tell it to ignore shadings. For this you would enable the
FZ_IGNORE_SHADE hint.
*/
void fz_enable_device_hints(fz_context *ctx, fz_device *dev, int hints);
/*
fz_disable_device_hints : Disable hints in a device.
If, however, you wish to extract the page content to an html file, you might
want to include images in this output. So for this, you would disable the
FZ IGNORE IMAGE hint before running the extraction, and the text extraction
device would know to include them in its output structures.
The set of hints is subject to expansion in future, but is currently defined to be:
enum
{
/* Hints */
FZ_IGNORE_IMAGE = 1,
FZ_IGNORE_SHADE = 2,
FZ_DONT_INTERPOLATE_IMAGES = 4,
FZ_MAINTAIN_CONTAINER_STACK = 8,
FZ_NO_CACHE = 16,
};
FZ IGNORE IMAGE being enabled implies that a device will (or should) make no
effort to handle images. For example, when playing back a display list (with
fz run display list), if the target device sets FZ IGNORE IMAGE, no image
related calls will be made.
FZ IGNORE SHADE being enabled implies that a device will (or should) make no
effort to handle shadings. For instance, if you are doing a quick pass across files
trying to generate low quality thumbnail images, you may choose to disable
shadings for speed.
FZ DONT INTERPOLAGE IMAGES being enabled prevents the draw device perform-
ing interpolation. MuTool Draw uses this to inhibit interpolation when anti-
aliasing is disabled. Finer control over this can now be given using the Tuning
Context (see section 3.7 Tuning).
FZ MAINTAIN CONTAINER STACK being enabled helps devices by causing MuPDF
to maintain a stack of containers. This effectively moves some logic that would
have to be in several devices into a place where it can be easily reused. Currently
the only device that makes use of this is the SVG device, but it is hoped that
more will use it in future.
FZ NO CACHE being enabled tells the interpreter to try to avoid caching any
objects after the end of the content run. This can be used, for example, when
searching a PDF for a text string to avoid pulling all the images, shadings, fonts
etc and other resources for pages into memory at the expense of those that are
used on the current page.
CHAPTER 7. THE DEVICE INTERFACE 46
The BBox device is a simple device that calculates the bbox of all the marking
operations on a page.
/*
fz_new_bbox_device: Create a device to compute the bounding
box of all marks on a page.
The fz rect passed to the fz new bbox device must obviously stay in scope
for the duration of the life of the device as it will be updated on exit with the
bounding box for the contents.
The Draw device is the core renderer for MuPDF. Every draw device instance
is constructed with a destination Pixmap (see section 8.3 Pixmaps for more
details), and each graphical object passed to the device is rendered into that
pixmap.
/*
fz_new_draw_device: Create a device to draw on a pixmap.
Most of the time we render complete pixmaps, but a mechanism exists to allow
us to render a given bbox within a pixmap:
CHAPTER 7. THE DEVICE INTERFACE 47
/*
fz_new_draw_device_with_bbox: Create a device to draw on a pixmap.
This can be useful for updating particular areas of a page (for instance when an
annotation has been edited or moved) without redrawing the whole thing.
During the course of rendering, the draw device may create new temporary
internal pixmaps to cope with transparency and grouping. This is invisible to
the caller, and can safely be considered an implementation detail, but should
be considered when estimating the memory use for a given rendering operation.
The exact number and size of internal pixmaps required depends on the exact
complexity and makeup of the graphical objects being displayed.
To limit memory use, a typical strategy is to render pages in bands; rather than
creating a single pixmap the size of the page and rendering that, create pixmaps
for ’slices’ across the page, and render them one at a time. The memory savings
are not just seen in the cost of the basic pixmap, but also serve to limit the sizes
of the internal pixmaps used during rendering.
The cost for this is that the page contents do need to be run through repeatedly.
This can be achieved by reinterpreting directly from the file, but that can be
expensive. The next device provides a route to help with this.
The Display list device simply records all the calls made to it in a list. This
list can then be played back later, potentially multiple times and with different
transforms, to other devices.
/*
fz_new_list_device: Create a rendering device for a display list.
list: A display list that the list device takes ownership of.
*/
fz_device *fz_new_list_device(fz_context *ctx, fz_display_list *list);
For more details of the uses of Display Lists, see chapter 9 Display Lists.
The PDF Output device is still a work in progress, as its handling of fonts is
incomplete. Nonetheless for certain classes of files it can be useful.
End users will probably prefer to use the document writer interface (see /r-
jwrefDocumentWriter) which wraps this class up, rather than call it directly.
Nonetheless this can be useful in specific circumstances when generating partic-
ular sections of a PDF file (such as appearance streams for annotations).
The PDF Output device takes the sequence of graphical operations it is called
with, and forms it back into a sequence of PDF operations, together with a set
of required resources. These can then be formed into a completely new PDF
page (or a PDF annotation) which can then be inserted into a document.
/*
pdf_page_write: Create a device that will record the
graphical operations given to it into a sequence of
pdf operations, together with a set of resources. This
sequence/set pair can then be used as the basis for
adding a page to the document (see pdf_add_page).
The Structured Text device is used to extract the text from a given graphical
stream, together with the position it inhabits on the output page. It can also
optionally include details of images and their positions within its output.
/*
fz_new_stext_device: Create a device to extract the text on a page.
Gather and sort the text on a page into spans of uniform style,
arranged into lines and blocks by reading order. The reading order
is determined by various heuristics, so may not be accurate.
sheet: The text sheet to which styles should be added. This can
either be a newly created (empty) text sheet, or one containing
styles from a previous text device. The same sheet cannot be used
in multiple threads simultaneously.
page: The text page to which content should be added. This will
usually be a newly created (empty) text page, but it can be one
containing data already (for example when merging multiple pages, or
watermarking).
*/
fz_device *fz_new_stext_device(fz_context *ctx, fz_stext_sheet *sheet,
fz_stext_page *page);
This can be used as the basis for searching (including highlighting the text as
matches are found), for exporting text files (or text and image based files such
as HTML), or even to do more complex page analysis (such as spotting what
regions of the page are text, what are graphics etc).
An (initially empty) fz stext sheet should be created using
fz new stext sheet, and an empty fz stext page created using
fz new stext page. These are used in the call to fz new stext device.
After the contents have been run to that device, the sheet will be populated
with the common styles used by the page, and the page will be populated with
details of the text extracted and its position.
The SVG output device is used to generate SVG pages from arbitrary input.
End users will probably prefer to use the document writer interface (see /rjwref-
DocumentWriter) which wraps this class up, rather than call it directly.
/*
fz_new_svg_device: Create a device that outputs (single page)
SVG files to the given output stream.
CHAPTER 7. THE DEVICE INTERFACE 50
The device currently generates SVG 1.1 compliant files. SVG Fonts are NOT
used due to poor client support. Instead glyphs are sent as reusable sym-
bols. Shadings are sent as rasterised images. JPEGs will be passed through
unchanged, and all other images will be converted to PNG.
The Test device, as its name suggests, tests a given set of page contents for
which features are used. Currently this is restricted to testing for whether the
graphical objects used are greyscale or colour. Testing for additional features
may be added in future.
/*
fz_new_test_device: Create a device to test for features.
Standalone use
Passthrough use
As discussed above, the envisaged use case for this device is to detect whether
page contents require colour or not to allow printers to decide whether to ras-
terise for colour inks or a faster/cheaper greyscale pass.
CHAPTER 7. THE DEVICE INTERFACE 52
Such printers will normally be operating in banded mode, which requires (or
at least greatly benefits from) the use of a display list. By using the device in
passthrough mode, the testing can be performed at the same time as the list
is built.
Simply create the display list device as you would normally, and pass it into
fz new test device as passthrough. Then run the page contents through
the returned test device. The test device will pass each call through to the
underlying list device and so the display list be built as normal.
When run in this mode, the device can no longer use the ‘early-exit’ optimisation
of throwing a FZ ABORT error.
The Trace device is a simple debugging device that allows an XML-like repre-
sentation of the device calls made to be output.
/*
fz_new_trace_device: Create a device to print a debug trace of all
device calls.
*/
fz_device *fz_new_trace_device(fz_context *ctx, fz_output *out);
Building Blocks
8.1 Overview
MuPDF uses many constructs and concepts that, while not deserving of chapters
in their own rights, do deserve mention.
8.2 Colorspaces
In order to represent a given color for a graphical object, we need both the
color component values and details of the colorspace that the color is specified
in. Color values are defined simply as floats (between 0 and 1 inclusive), and
colorspaces are defined using the fz colorspace structure.
As with many other such structures in MuPDF, these are reference counted
objects (see section 5.2 Reference Counting).
MuPDF contains a set of inbuilt colorspaces that cover most simple require-
ments. These can
53
CHAPTER 8. BUILDING BLOCKS 54
8.3 Pixmaps
8.3.1 Overview
8.3.2 Saving
This has the effect of setting the size and format of the data for the complete
image. The caller then proceeds to render the page in horizontal strips from the
top to the bottom, and pass them in to fz write band:
/*
fz_write_band: Cause a band writer to write the next band
of data for an image.
stride: The byte offset from the first byte of the data
for a pixel to the first byte of the data for the same pixel
on the row below.
The band writer keeps track of how much data has been written, and when an
entire page has been sent, it writes out any image trailer required.
For formats that can accommodate multiple pages, a new call to
fz write header will start the process again. Otherwise (or after the final
image), the band writer can be neatly discarded by calling:
void fz_drop_band_writer(fz_context *ctx, fz_band_writer *writer);
8.4 Bitmaps
The fz bitmap structure is used to represent a 2 dimensional array of
monochrome pixels. They are the 1 bit per component equivalent of the
fz pixmap structure.
The core rendering engine of MuPDF does not currently make use of
fz bitmaps, but rather they are used as a step along the way for outputting
rendered information.
Functions exist within MuPDF to create fz bitmaps from fz pixmaps by
halftoning. See section 8.5 Halftones.
/*
fz_new_bitmap_from_pixmap: Make a bitmap from a pixmap and a
halftone.
8.5 Halftones
The fz halftone structure represents a set of tiles, one per component, each of a
potentially different size. Each of these tiles is a 2-dimensional array of threshold
values (actually implemented as a single component fz pixmap). During the
halftoning (bitmap creation) process, if the contone value is smaller than the
threshold value, then it remains unset in the output. If it is larger or equal then
it is set in the output.
For convenience, a NULL pointer can be used to signify the default halftone. The
default halftone can also be fetched by using:
/*
fz_default_halftone: Create a ’default’ halftone structure
for the given number of components.
The creation of halftones is a specialised field upon which much research has
been done. The mechanisms in MuPDF are designed to allow people the freedom
to create and tune the halftones for their particular application.
The usual reference counting behaviour applies to fz halftones, with
fz keep halftone and fz drop halftone claiming and releasing references re-
spectively.
CHAPTER 8. BUILDING BLOCKS 58
8.6 Images
The fz image structure is used to represent a generic Image object in MuPDF.
It can be viewed as an encapsulation from which both a rendering of an image
(as an fz pixmap) and (often) the original source data can be retrieved.
The primary use of an fz image is to allow a rendered pixmap to be retrieved.
This is done by calling:
/*
fz_get_pixmap_from_image: Called to get a handle to a pixmap from
an image.
subarea: The subarea of the image that we actually care about (or
NULL to indicate the whole image).
Frequently this will involve decoding the image from its source data, so should
be considered a potentially expensive call, both in terms of CPU time, and
memory usage.
To minimise the impact of such decodes, fz images make use of the Store (see
chapter 5 Reference Counting, Memory Management and The Store) to cache
decoded versions in. This means that (subject to enough memory being avail-
able) repeated calls to get a fz pixmap from the same fz image (with the same
parameters) will return the same fz pixmap each time, with no further decode
being required.
The usual reference counting behaviour applies to fz images, with
fz keep image and fz drop image claiming and releasing references respec-
tively.
CHAPTER 8. BUILDING BLOCKS 59
8.7 Buffers
The fz buffer structure is used to represent arbitrary buffers of data. Es-
sentially they are a representation for arbitrary blocks of bytes (in whatever
encoding required), with simple functions for extending, concatenating, and
writing in byte, char, utf8 and bitwise fashion.
CHAPTER 8. BUILDING BLOCKS 60
Both the internals and API level functions of MuPDF use fz buffers exten-
sively.
The usual reference counting behaviour applies to fz buffers, with
fz keep buffer and fz drop buffer claiming and releasing references respec-
tively.
8.8 Transforms
The fz matrix structure is used to represent 2 dimensional matrices used for
transforming points, shapes and other geometry.
The six fields of the fz matrix structure correspond to a matrix of the form:
a b 0
c d 0
e f 1
Returns m.
/*
fz_shear: Create a shearing matrix.
Returns m.
/*
fz_rotate: Create a rotation matrix.
Returns m.
/*
fz_translate: Create a translation matrix.
Returns m.
a b 0
x0 y0
1 = x y 1 c d 0
e f 1
Rectangles can be transformed using the following function, which allows for
the fact that the image of a rectangle may be flipped:
/*
fz_transform_rect: Apply a transform to a rectangle.
Returns result.
Returns m (updated).
/*
fz_post_scale: Scale a matrix by postmultiplication.
Returns m (updated).
/*
fz_pre_shear: Premultiply a matrix with a shearing matrix.
Returns m (updated).
/*
fz_pre_rotate: Rotate a transformation by premultiplying.
Returns m (updated).
/*
fz_pre_translate: Translate a matrix by premultiplication.
Returns m.
Finally, sometimes it is useful to find the matrix that would represent the reverse
of a given transformation. This can be achieved by ‘inverting’ the matrix.
This is not possible in all cases, but can be achieved for most ‘well-behaved’
transformations.
/*
fz_invert_matrix: Create an inverse matrix.
Returns inverse.
/*
fz_try_invert_matrix: Attempt to create an inverse matrix.
8.9 Paths
Postscript (or PDF) style paths are represented using the fz path structure. A
postscript path consists of a sequence of instructions describing the movement
of a ‘pen’ around a given path.
The first instruction is always a ‘move’ to a specified location. Subsequent
instructions move the pen position onwards to new positions on the page, ei-
ther via straight lines, or via curves described by given control points. Such
instructions can either be made with the pen up or down.
Once created paths can then be rendered by MuPDF either by being filled, or
by being stroked. The path itself has no knowledge of how it will be used - the
details of the fill or the stroke attributes are supplied externally to this structure.
A description of the exact rules used for filling and stroking are beyond the scope
of this document. For more information see “The PDF Reference Manual” or
“The Postscript Language Reference Manual”.
Paths are reference counted objects, with the implicit understanding that once
more than one reference exists to a path, it will no longer be modified.
8.9.1 Creation
Once a path exists, commands can be added to it. The first command must
always be a ‘move’.
CHAPTER 8. BUILDING BLOCKS 67
/*
fz_moveto: Append a ’moveto’ command to a path.
/*
fz_quadto: Append a ’quadto’ command to a path. (For a
quadratic bezier).
/*
fz_curveto: Append a ’curveto’ command to a path. (For a
cubic bezier).
x0, y0: The coordinates of the first control point for the
curve.
CHAPTER 8. BUILDING BLOCKS 68
x1, y1: The coordinates of the second control point for the
curve.
In addition, we have 2 functions for adding curves (cubic beziers) where one
of the control points is coincident with the neighbouring endpoints. These
functions mirror the usage in PDF, but offer no benefits other than convenience
as such curves are detected automatically as part of an fz curveto call.
/*
fz_curvetov: Append a ’curvetov’ command to a path. (For a
cubic bezier with the first control coordinate equal to
the start point).
x1, y1: The coordinates of the second control point for the
curve.
/*
fz_curvetoy: Append a ’curvetoy’ command to a path. (For a
cubic bezier with the second control coordinate equal to
the end point).
x0, y0: The coordinates of the first control point for the
curve.
x2, y2: The end coordinates for the curve (and the second
control coordinate).
At any point after the initial move, we can close the path using fz closepath:
/*
fz_closepath: Close the current subpath.
After a path has been closed, the only acceptable next command is a move. A
path need not be closed before a second or subsequent move is sent.
Finally, we have one additional path construction function, fz rectto. This
appends a rectangle to the current path. This rectangle is equivalent to a move,
3 lines and a closepath, and so is the one exception to the rule that paths must
begin with a move (as one is implicit within the rectangle command).
/*
fz_rectto: Append a ’rectto’ command to a path.
Finally, during path construction, the coordinate at which the notional path
cursor has reached can be read using the fz currentpoint function.
/*
fz_currentpoint: Return the current point that a path has
reached or (0,0) if empty.
*/
fz_point fz_currentpoint(fz_context *ctx, fz_path *path);
As stated before, fz paths are reference counted objects. Once one has been
created, references can be created/destroyed using the standard keep/drop con-
ventions:
/*
fz_keep_path: Take an additional reference to
a path.
/*
fz_drop_path: Drop a reference to a path,
destroying the path if it is the last
reference.
8.9.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 subsection 8.9.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.
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.
CHAPTER 8. BUILDING BLOCKS 72
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 9 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.
Then, call fz pack path with some (suitably aligned) memory of the appropri-
ate size to actually pack the path:
/*
fz_pack_path: Pack a path into the given block.
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
CHAPTER 8. BUILDING BLOCKS 73
entry to be an fz_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.
8.9.4 Transformation
This counts as modifying a path of course, so ensure that you are the only
reference holder, or fz clone path it first.
8.9.5 Bounding
8.9.6 Stroking
Where filling a path simply requires details of the fill to be used, stroking a
path can radically alter its appearance. The details of the stroke attributes are
passed in a fz stroke state structure.
Stroke states are created and managed with reference counting using the func-
tions described below, but unlike other structures, the definition of the structure
itself is public. Callers are expected to alter the different fields in the struc-
ture themselves. The sole exception to this is the refs field, that should only
be altered using the usual fz keep stroke state and fz drop stroke state
mechanisms.
typedef struct fz_stroke_state_s fz_stroke_state;
struct fz_stroke_state_s
{
int refs;
CHAPTER 8. BUILDING BLOCKS 75
It is hoped that the meaning of the individual fields within a fz stroke state
structure are self evident to anyone working in this field. If you are unfamil-
iar with any of the concepts here, see “The PDF Reference Manual” or “The
Postscript Language Reference Manual” for more details.
Most simply a reference to a stroke state structure can be obtained by calling
fz new stroke state:
/*
fz_new_stroke_state: Create a new (empty) stroke state
structure (with no dash data) and return a reference to it.
/*
fz_drop_stroke_state: Drop a reference to a stroke
state structure, destroying the structure if it is
the last reference.
Once more than one reference is held to a stroke state, it should be considered
‘frozen’ or ‘immutable’ as other reference holders may be confused by changes to
it. Accordingly, we provide functions to ensure that we are holding a reference
to an ‘unshared’ stroke state:
/*
fz_unshare_stroke_state: Given a reference to a
(possibly) shared stroke_state structure, return
a reference to an equivalent stroke_state structure
that is guaranteed to be unshared (i.e. one that can
safely be modified).
/*
fz_unshare_stroke_state_with_dash_len: Given a reference to a
(possibly) shared stroke_state structure, return a reference
to a stroke_state structure (with room for a given amount of
dash data) that is guaranteed to be unshared (i.e. one that
can safely be modified).
Finally, we have a simple function to clone a stroke state and return a new
reference to it:
/*
fz_clone_stroke_state: Create an identical stroke_state
structure and return a reference to it.
8.9.7 Walking
Given a path, it can be useful to be able to read it out again. MuPDF uses
this internally in a output devices such as the PDF or SVG devices (see subsec-
tion 7.5.4 PDF Output Device or subsection 7.5.6 SVG Output Device) to con-
vert paths to a new representation, and in the draw device (see subsection 7.5.2
Draw Device) for rendering.
To isolate callers from the implementation specifics of paths, MuPDF offers a
mechanism to ‘walk’ an fz path, getting a callback for each command in the
path.
typedef struct
{
/* Compulsory ones */
void (*moveto)(fz_context *ctx, void *arg, float x, float y);
void (*lineto)(fz_context *ctx, void *arg, float x, float y);
void (*curveto)(fz_context *ctx, void *arg, float x1, float y1,
float x2, float y2, float x3, float y3);
void (*closepath)(fz_context *ctx, void *arg);
/* Optional ones */
void (*quadto)(fz_context *ctx, void *arg, float x1, float y1, float
x2, float y2);
void (*curvetov)(fz_context *ctx, void *arg, float x2, float y2,
float x3, float y3);
CHAPTER 8. BUILDING BLOCKS 78
/*
fz_walk_path: Walk the segments of a path, calling the
appropriate callback function from a given set for each
segment of the path.
8.10 Text
8.10.1 Overview
MuPDFs central text type is an fz text structure. The exact definition of this
structure has evolved considerably in the past to accommodate the needs of dif-
ferent input formats, and it is possible this will continue in future. Accordingly
we have hidden the implementation behind an interface.
Nonetheless, it is worthwhile mentioning some of the design goals that have
influenced the development of this area of the code.
As fz text objects are the only text objects passed across the device interface,
they need to encode several layers of information. For simple rendering devices,
they need to be expressive enough to allow us to exactly render the exact speci-
fied glyphs. For text output devices, they need to be expressive enough to allow
the unicode values to be extracted.
Ideally, given any input format we would like to be able to output any output
format from it (including the same format) with no loss of data. This means
that our fz text objects need to be expressive enough to represent the super-set
of functionality of all input formats out there, even if we do not currently make
use of all the information.
Some input formats only specify glyph ids, whereas some only specify unicode
values. In order to allow us to transmute one format into another, we require
fz text objects to encapsulate both forms of data at the same time.
Some formats, that make use of glyph shaping, require us to cope with unicode
and glyph id equivalents that have no direct 1-1 correspondence.
For bidirectional text, the order in which text may be displayed on the page
may not be trivially related to the order in which it is specified in the source
file. We both need to render efficiently, and to maintain the ability to recreate
the initial logical order of the text.
Accordingly, our fz text object represents a block of text in logical order,
including font style and position, together with both unicode and glyph data
(subject to the availability of the information in the original file).
If more information is required, then details of the current implementation are
included in subsection 8.10.7 Implementation, otherwise just use it as a simple
black box.
typedef struct fz_text_s fz_text;
Text objects are reference, with the implicit understanding that once more than
one reference exists to an object, it will no longer be modified.
CHAPTER 8. BUILDING BLOCKS 80
8.10.2 Creation
Empty fz text objects can be created using the fz new text call:
/*
fz_new_text: Create a new empty fz_text object.
/*
fz_drop_text: Drop a reference to the object, freeing
if if is the last one.
8.10.3 Population
Once created, characters can be added to the fz text object either singly:
/*
fz_show_glyph: Add a glyph/unicode value to a text object.
8.10.4 Measurement
Once a fz text object has been created we can measure the area it will cover
on the page:
/*
fz_bound_text: Find the bounds of a given text object.
8.10.5 Cloning
8.10.6 Language
Some formats include a declaration of which language is being used for a given
piece of text. This can be used to influence aspects of the text layout, includ-
ing the exact choice of glyphs used in a given font. While we make relatively
CHAPTER 8. BUILDING BLOCKS 83
little use of this at present, we try to preserve the information as part of our
philosophy of not losing any information unnecessarily.
Accordingly, we use ISO 639 language specification strings, for example:
typedef enum fz_text_language_e
{
FZ_LANG_UNSET = 0,
FZ_LANG_ur = FZ_LANG_TAG2(’u’,’r’),
FZ_LANG_urd = FZ_LANG_TAG3(’u’,’r’,’d’),
FZ_LANG_ko = FZ_LANG_TAG2(’k’,’o’),
FZ_LANG_ja = FZ_LANG_TAG2(’j’,’a’),
FZ_LANG_zh = FZ_LANG_TAG2(’z’,’h’),
FZ_LANG_zh_Hans = FZ_LANG_TAG3(’z’,’h’,’s’),
FZ_LANG_zh_Hant = FZ_LANG_TAG3(’z’,’h’,’t’),
} fz_text_language;
/*
Recover ISO 639 (639-{1,2,3,5}) language specification
strings losslessly from a 15 bit fz_text_language code.
8.10.7 Implementation
A fz text structure represents a block of text. At the lowest level the con-
stituents of a block are fz text items.
typedef struct fz_text_item_s fz_text_item;
struct fz_text_item_s
CHAPTER 8. BUILDING BLOCKS 84
{
float x, y;
int gid; /* -1 for one gid to many ucs mappings */
int ucs; /* -1 for one ucs to many gid mappings */
};
The items can be thought of as the individual ‘characters’ that make up the
display, together with their position. Where possible, we attempt to give both
the glyph id (gid) and the unicode value (ucs) for the character, but there are
various cases where a 1-1 mapping is not possible.
Some unicode characters can result in a string of glyphs. The glyph ids will be
sent in a series of fz text items, in which the first ucs value will be the source
unicode character, and subsequent ones will be -1.
Some sequences of unicode characters can result in a single glyph. Again, a
sequence of fz text items will be sent listing the unicode values, but all but
the first item will have the gid value set to -1.
In more complex cases, sequences of unicode characters can be transformed into
a sequence of glyphs, with no direct correspondence between the source text
and the output characters. In this case as many fz text items as are required
are used, with either the gid or ucs values padded out by -1s as necessary.
Different input formats offer the text in different forms. With PDF, the data
within the file is (typically) in the form of glyph ids, and mechanisms are op-
tionally provided to infer unicode values from them. Glyphs are sent in any
order, and absolutely positioned on the page.
With XPS the input can be either in the form of unicode or glyph ids, and
directionality information is encoded in the file. This means that the logical
ordering of the glyphs is well defined.
Some formats, such as EPub and HTML, send unicode text with even less
positioning information, and rely on the interpreter to perform layout. Part
of this process involves inferring directional information from the source text,
and then using shaping mechanisms embedded within the font to do complex
conversions to give the final positioned glyph sequences.
In all such cases MuPDF will preserve the logical ordering of the unicode entries,
at the cost of drawing glyphs non-monotonically onto the page.
Sequences of fz text items that share the same characteristics are gathered
together into fz text spans:
struct fz_text_span_s
{
fz_font *font;
fz_matrix trm;
unsigned wmode : 1; /* 0 horizontal, 1 vertical */
CHAPTER 8. BUILDING BLOCKS 85
Sequences of these spans are then gathered up into a linked list rooted in a
fz text.
struct fz_text_s
{
int refs;
fz_text_span *head, *tail;
};
8.11 Shadings
8.11.1 Overview
One of the most powerful graphical effects within PDF and other input formats
is that of Shadings. Our central type representing shadings, fz shade is all that
we have to pass details of shadings across the fz device interface.
Consequently, we need fz shade to be expressive enough to cope with shadings
from all possible sources, and yet we would like to avoid having to reproduce
the shade handling code in all devices.
Accordingly, fz shade is defined to be expressive enough to encapsulate all
the different shading representations found in PDF with the data essentially
unchanged. PDF is currently the super-set of shadings found in other formats.
If this changes, fz shade will be extended as required.
typedef struct fz_shade_s
{
fz_storable storable;
int use_function;
float function[256][FZ_MAX_COLORS + 1];
fz_compressed_buffer *buffer;
} fz_shade;
8.11.2 Creation
Currently, there is no defined API for creating a shading due to the public nature
of the structure. Just call fz malloc struct(ctx, fz shade) and initialise the
fields accordingly.
We may look to add convenience functions in the future, as this is likely to be
desirable for the JNI (and other) bindings.
Shading objects are reference counted, with the implicit understanding that once
more than one reference exists to a fz shade, it will no longer be modified.
CHAPTER 8. BUILDING BLOCKS 87
Returns shade.
*/
fz_shade *fz_keep_shade(fz_context *ctx, fz_shade *shade);
/*
fz_drop_shade: Drop a reference to an fz_shade.
8.11.3 Bounding
Once created, we can ask for the bounds of a given shade under a given trans-
formation. This can sometimes be infinite.
/*
fz_bound_shade: Bound a given shading.
8.11.4 Painting
8.11.5 Decomposition
For devices that wish to get access to a higher level representation of a shading,
but do not wish to access the internals of a shading directly, we provide a
function to decompose a shading to a mesh.
This is called with functions to ‘prepare’ and ‘fill’ vertices respectively. The
mesh is decomposed to triangles internally, each vertex is ‘prepared’ and each
triangle ‘filled’ in turn.
The ordering of these calls is not guaranteed, other than the fact that a vertex
will always be prepared before it is used as part of a triangle to be filled.
typedef struct fz_vertex_s fz_vertex;
struct fz_vertex_s
{
fz_point p;
float c[FZ_MAX_COLORS];
};
/*
fz_shade_prepare_fn: Callback function type for use with
fz_process_shade.
/*
fz_shade_process_fn: Callback function type for use with
fz_process_shade.
/*
fz_process_shade: Process a shade, using supplied callback
functions. This decomposes the shading to a mesh (even ones
that are not natively meshes, such as linear or radial
shadings), and processes triangles from those meshes.
Display Lists
9.0.1 Overview
9.0.2 Creation
An empty display list can be created by the fz new display list call.
/*
fz_new_display_list: Create an empty display list.
90
CHAPTER 9. DISPLAY LISTS 91
Once created it can be populated by creating a display list device instance that
writes to it.
/*
fz_new_list_device: Create a rendering device for a display list.
list: A display list that the list device takes ownership of.
*/
fz_device *fz_new_list_device(fz_context *ctx, fz_display_list *list);
Once you have created such a display list device, any calls made to that device
(such as by calling fz run page or similar) will be recorded into the display list.
When you have finished writing to the display list (remembering to
call fz close device), you dispose of the device as normal (by calling
fz drop device). This leaves you holding the sole reference to the display
list itself.
Writing to a display list is not thread safe. That is to say, do not attempt to
write to a display list from more than one thread at a time. Similarly, do not
attempt to read from display lists while write operations are ongoing.
9.0.3 Playback
In common with most other objects in MuPDF, fz display lists are reference
counted. This means that once you have finished with a reference to a display
list, it can safely be disposed of by calling fz drop display list.
/*
fz_drop_display_list: Drop a reference to a display list, freeing it
if the reference count reaches zero.
Should you wish to keep a new reference to a display list, you can generate one
using fz keep display list.
/*
fz_keep_display_list: Keep a reference to a display list.
In general, it is rare for you to want to make a new reference to a display list
until write operations on one have finished. It is good form to avoid this.
There are a few other operations that can be performed efficiently on a display
list. Firstly, one can request the bounds of a list.
/*
fz_bound_display_list: Return the bounding box of the page recorded
in a display list.
*/
fz_rect *fz_bound_display_list(fz_context *ctx, fz_display_list *list,
fz_rect *bounds);
Secondly, one can create a new fz image from a display list. This is useful
for creating scalable content to embed in other document types; for instance
MuPDF makes use of this to turn SVG files embedded within epub files (for
illustrations and cover pages etc) into convenient objects for adding into the
flow of text.
/*
Create a new image from a display list.
Finally, it is possible to very quickly check if a given display list is empty or not.
/*
Check for a display list being empty
10.1 Overview
MuPDF is designed to run in a variety of different environments. As such,
this means input can come from many different sources. On desktop computers
input may come as files on backing store. For web served files, input may be
streamed over a network. For systems with DRM embedded, the data may need
to be decoded on the fly.
Similarly, data can be encapsulated within different formats in different ways,
with multiple layers of encoding.
Accordingly, MuPDF abstracts the idea of an ‘input stream’ to a reusable class,
fz stream. Many implementations of fz streams are given by default in the
core library, but the abstract nature of this class allows callers to provide im-
plementations of their own to seamlessly extend the systems capabilities as
required.
10.2 Creation
The exact mechanism for creating a stream depends upon the source for that
particular stream, but typically it will involve a call to a creation function, such
as fz open file.
/*
fz_open_file: Open the named file and wrap it in a stream.
94
CHAPTER 10. THE STREAM INTERFACE 95
Alternative functions exist to allow creating streams from C level FILE pointers:
/*
fz_open_file: Wrap an open file descriptor in a stream.
There are too many other options for creating streams to list them all here, but
CHAPTER 10. THE STREAM INTERFACE 96
their use should be self evident from the header file definitions. Once created,
all streams can be used in the same ways.
10.3 Usage
The simplest way to read bytes from a stream is to call fz read byte to read
the next byte from a file. Akin to the standard fgetc, this returns -1 for end
of data, or the next byte available.
/*
fz_read_byte: Read the next byte from a stream.
To read more than 1 byte at a time, there are two different options.
Firstly, and most efficiently, bytes can be read directly from the streams under-
lying buffer. For a given fz stream *stm, the current position in the stream is
pointed to by stm->rp. Bytes can simply be read out, and the pointer incre-
mented by the number read.
To do this, you must first know how many bytes there are available to be read
out. This is achieved by calling fz available. If there are no bytes already
decoded and awaiting reading, this call will trigger a refill of the underlying
buffer, which may take noticeable time.
/*
fz_available: Ask how many bytes are available immediately from
a given stream.
*/
size_t fz_available(fz_context *ctx, fz_stream *stm, size_t max);
To avoid needless work, a ‘max’ value can be supplied as a hint, telling any
buffer refill operation that is triggered how many bytes are actually required.
Specifying a max value does not guarantee you anything about the number of
bytes actually made available.
Some callers may find this awkward - the need to potentially repeatedly call
until you get enough bytes to fill a buffer of the required length may be tedious.
Therefore as an alternative, we provide a simpler call, fz read.
Designed to be similar to the standard fread call, this attempts to read as
many bytes as possible into a supplied data block, returning the actual number
of bytes successfully read.
/*
fz_read: Read from a stream into a given data block.
Typically the only reason that fz read will not return the requested number of
bytes is if we hit the end of the stream. This implies that calls to fz read will
block until such data is ready. For streams based on ‘fast’ sources like files or
memory, this is an unimportant distinction.
For streams based on (say) an http download, this might result in significant
delays, and an unacceptable user experience. To alleviate this problem we have
a mechanism whereby such streams can signal a temporary end of data by
throwing the FZ ERROR TRYLATER error. See chapter 16 Progressive Mode for
more details.
To facilitate reading without blocking (or using buffers larger than required),
fz available can be called to find out the number of bytes that can safely be
requested.
If data within a stream is not required, it can be skipped over using fz skip:
/*
fz_skip: Read from a stream discarding data.
CHAPTER 10. THE STREAM INTERFACE 98
As a special case, after a single byte is read, it can be pushed back into the
stream, using fz unread byte:
/*
fz_unread_byte: Unread the single last byte successfully
read from a stream. Do not call this without having
successfully read a byte.
*/
void fz_unread_byte(fz_context *ctx FZ_UNUSED, fz_stream *stm);
The act of reading a byte, and then, if successful pushing it back again is
encapsulated in a convenience function, fz peek byte:
/*
fz_peek_byte: Peek at the next byte in a stream.
Often, when parsing different document formats, it can be useful to read specific
objects from streams, so convenience functions exist for this too. Firstly, integers
of different size and endianness are catered for:
/*
fz_read_[u]int(16|24|32|64)(_le)?
/*
fz_read_line: Read a line from stream into the buffer until either a
terminating newline or EOF, which it replaces with a null byte
(’\0’).
Returns buf on success, and NULL when end of file occurs while no
characters
have been read.
*/
char *fz_read_line(fz_context *ctx, fz_stream *stm, char *buf, size_t
max);
Streams (or sections of streams) can be treated as a string of bits, packed either
most significant or least significant bits first.
To read from an msb packed stream, use fz read bits:
/*
fz_read_bits: Read the next n bits from a stream (assumed to
CHAPTER 10. THE STREAM INTERFACE 100
;
Whichever of these is used, reading n bits will return the results in the lowest
n bits of the returned value.
After reading bits using these functions, if a return to reading bytewise (or
objectwise) is required, then fz sync bits must be called.
/*
fz_sync_bits: Called after reading bits to tell the stream
that we are about to return to reading bytewise. Resyncs
the stream to whole byte boundaries.
*/
void fz_sync_bits(fz_context *ctx FZ_UNUSED, fz_stream *stm);
Returns a buffer created from reading from the stream. May throw
exceptions on failure to allocate.
*/
fz_buffer *fz_read_all(fz_context *ctx, fz_stream *stm, size_t initial);
This will throw an error (and hence not return any data) if an error is encoun-
tered during the decode of the stream. Sometimes it can be preferable to ‘do
the best we can’ and tolerate problematic data. For such cases, we provide
fz read best:
/*
fz_read_best: Attempt to read a stream into a buffer. If truncated
is NULL behaves as fz_read_all, otherwise does not throw exceptions
in the case of failure, but instead sets a truncated flag.
10.3.5 Seeking
Most stream operations simply advance the stream pointer as the stream is read.
The current stream position can always be obtained using fz tell (deliberately
similar to the standard ftell call):
/*
fz_tell: return the current reading position within a stream
*/
fz_off_t fz_tell(fz_context *ctx, fz_stream *stm);
Some streams allow you to seek within them, that is, to change the current
stream pointer to a given offset. To do this, use fz seek (deliberately similar
to fseek):
/*
CHAPTER 10. THE STREAM INTERFACE 102
In the event that a stream does not support seeking, an error will be thrown.
10.3.7 Destruction
In common with most other MuPDF objects, fz streams are reference counted.
As such additional references can be taken using fz keep stream and they can
be destroyed using fz drop stream.
Note that care must be taken not to use fz stream objects simultaneously in
more than one thread. Not only does the act of reading in one thread upset
the point at which the next read will happen in another thread, no protection
is provided to make operations atomic - thus the internal data can become
corrupted and cause crashes.
Note that some fz try/fz catch-ery may be required as part of the setup for
state.
The hard work for this function is done using fz new stream, and two ‘foo’
specific functions, foo next and foo close. First let’s look at fz new stream:
/*
fz_new_stream: Create a new stream object with the given
internal state and function pointers.
next: Should provide the next set of bytes (up to max) of stream
data. Return the number of bytes read, or EOF when there is no
more data.
CHAPTER 10. THE STREAM INTERFACE 104
close: Should clean up and free the internal state. May not
throw exceptions.
*/
fz_stream *fz_new_stream(fz_context *ctx, void *state, fz_stream_next_fn
*next, fz_stream_close_fn *close);
This creates the main fz stream structure, populates it with the given pointers
(state, foo next and foo close) and sets the internal buffer pointers up to
indicate an empty buffer.
As soon as anyone tries to read from the buffer (or to find out how many bytes
are available), the MuPDF stream functions will cause foo next to be called.
This is a function of the following type:
/*
fz_stream_next_fn: A function type for use when implementing
fz_streams. The supplied function of this type is called
whenever data is required, and the current buffer is empty.
When the stream is closed, the foo close function will be called. This should
be a function of type fz stream close fn:
/*
fz_stream_close_fn: A function type for use when implementing
fz_streams. The supplied function of this type is called
when the stream is closed, to release the stream specific
state information.
/*
fz_stream_meta_fn: A function type for use when implementing
fz_streams. The supplied function of this type is called when
fz_meta is requested, and the arguments are as defined for
fz_meta.
11.1 Overview
In the same way as fz streams abstracts input streams, MuPDF uses a reusable
class, fz output, to abstract output streams.
11.2 Creation
The exact function to call to create an output stream depends on the specific
stream required, but they generally follow a similar format. Some common
examples are:
/*
fz_new_output_with_file: Open an output stream that writes to a
FILE *.
/*
fz_new_output_with_path: Open an output stream that writes to a
given path.
106
CHAPTER 11. THE OUTPUT INTERFACE 107
/*
fz_new_output_with_buffer: Open an output stream that appends
to a buffer.
One of the most common use cases is to get an output stream that goes to stdout
or stderr, and we provide convenience functions for exactly this. In addition we
allow the streams for stdout and stderr to be replaced by other fz outputs,
thus allowing redirection to be changed simply for any of our existing tools:
/*
fz_stdout: The standard out output stream. By default
this stream writes to stdout. This may be overridden
using fz_set_stdout.
*/
fz_output *fz_stdout(fz_context *ctx);
/*
fz_stderr: The standard error output stream. By default
this stream writes to stderr. This may be overridden
using fz_set_stderr.
*/
fz_output *fz_stderr(fz_context *ctx);
/*
fz_set_stdout: Replace default standard output stream
with a given stream.
/*
fz_set_stderr: Replace default standard error stream
with a given stream.
11.3 Usage
x: value to write
*/
void fz_write_byte(fz_context *ctx, fz_output *out, unsigned char x);
We have convenience functions for outputting 16 and 32bit integers in both big
and little endian forms:
/*
fz_write_int32_be: Write a big-endian 32-bit binary integer.
*/
void fz_write_int32_be(fz_context *ctx, fz_output *out, int x);
/*
fz_write_int32_le: Write a little-endian 32-bit binary integer.
*/
void fz_write_int32_le(fz_context *ctx, fz_output *out, int x);
/*
fz_write_int16_be: Write a big-endian 16-bit binary integer.
CHAPTER 11. THE OUTPUT INTERFACE 109
*/
void fz_write_int16_be(fz_context *ctx, fz_output *out, int x);
/*
fz_write_int16_le: Write a little-endian 16-bit binary integer.
*/
void fz_write_int16_le(fz_context *ctx, fz_output *out, int x);
To output printable strings, we have the simple fputc, fputs and fputrune
equivalents:
/*
fz_putc: fputc equivalent for output streams.
*/
#define fz_putc(C,O,B) fz_write_byte(C, O, B)
/*
fz_puts: fputs equivalent for output streams.
*/
#define fz_puts(C,O,S) fz_write(C, O, (S), strlen(S))
/*
fz_putrune: fputrune equivalent for output streams.
*/
#define fz_putrune(C,O,R) fz_write_rune(C, O, R)
%P outputs a fz_point*.
%q and %( output escaped strings in C/PDF syntax.
%ll{d,u,x} indicates that the values are 64bit.
%z{d,u,x} indicates that the value is a size_t.
%Z{d,u,x} indicates that the value is a fz_off_t.
*/
size_t fz_vsnprintf(char *buffer, size_t space, const char *fmt, va_list
args);
/*
fz_snprintf: The non va_list equivalent of fz_vsnprintf.
*/
size_t fz_snprintf(char *buffer, size_t space, const char *fmt, ...);
/*
fz_printf: fprintf equivalent for output streams. See fz_snprintf.
*/
void fz_printf(fz_context *ctx, fz_output *out, const char *fmt, ...);
/*
fz_vprintf: vfprintf equivalent for output streams. See fz_vsnprintf.
*/
void fz_vprintf(fz_context *ctx, fz_output *out, const char *fmt,
va_list ap);
11.3.4 Seeking
Unlike fz streams, which support fz tell in all cases, fz outputs can only
fz tell output if they are seekable:
/*
fz_tell_output: Return the current file position. Throw an error
on unseekable outputs.
*/
fz_off_t fz_tell_output(fz_context *ctx, fz_output *out);
CHAPTER 11. THE OUTPUT INTERFACE 111
This has parallels with the implementation of fz streams, but is not quite
identical.
If ¡state¿ needs no destruction, then we can use NULL in place of foo close.
Otherwise foo close should be a function of type:
/*
fz_output_close_fn: A function type for use when implementing
fz_outputs. The supplied function of this type is called
when the output stream is closed, to release the stream specific
state information.
*/
typedef void (fz_output_write_fn)(fz_context *ctx, void *state, const
void *data, size_t n);
Optionally we can choose to have our output stream support fz seek output
and fz tell output. To do that we must implement foo seek and foo tell
respectively, and assign them out->seek and out->tell during creation.
/*
fz_output_seek_fn: A function type for use when implementing
fz_outputs. The supplied function of this type is called when
fz_seek_output is requested.
/*
fz_output_tell_fn: A function type for use when implementing
fz_outputs. The supplied function of this type is called when
fz_tell_output is requested.
12.1 Overview
MuPDFs built in renderer (see /rjwrefDrawDevice) produces in-memory arrays
of contone values for areas of document pages. The MuPDF library includes
routines to be able to output these areas to a number of different output formats.
Typically these devices all follow a similar pattern, enabling either full page or
banded rendering to be performed according to the requirements of the partic-
ular application.
For a given format XXX, there tend to be 3 functions defined:
void fz_save_pixmap_as_XXX(fz_context *ctx, fz_pixmap *pixmap, char
*filename);
113
CHAPTER 12. RENDERED OUTPUT FORMATS 114
Next, the caller should render bands of the page in turn, and pass them in to
fz write band.
/*
fz_write_band: Cause a band writer to write the next band
of data for an image.
stride: The byte offset from the first byte of the data
for a pixel to the first byte of the data for the same pixel
on the row below.
Once enough data has been sent, the band writer automatically writes any
trailer for the file.
At this point, the caller can either call fz write header to start a new page,
or they can call fz drop band writer to clean up.
void fz_drop_band_writer(fz_context *ctx, fz_band_writer *writer);
12.3 PNM
The simplest output format supported is that of PNM. The pixmap can be
greyscale, or RGB, with or without alpha (though the alpha plane is always
ignored on writing).
/*
fz_save_pixmap_as_pnm: Save a pixmap as a PNM image file.
*/
void fz_save_pixmap_as_pnm(fz_context *ctx, fz_pixmap *pixmap, char
*filename);
12.4 PAM
Related to PNM we have PAM. The pixmap formats here can be greyscale, RGB
or CMYK, with or without alpha (and the alpha plane is written to the file).
The TUPLTYPE in the image header reflects the color and alpha configuration,
though not all readers support all variants.
/*
fz_save_pixmap_as_pam: Save a pixmap as a PAM image file.
*/
void fz_save_pixmap_as_pam(fz_context *ctx, fz_pixmap *pixmap, char
*filename);
12.5 PBM
Bitmaps suitable for output to the PBM format are generated by drawing to
greyscale contone (with no alpha), and then halftoning down to monochrome.
/*
fz_save_bitmap_as_pbm: Save a bitmap as a PBM image file.
*/
void fz_save_bitmap_as_pbm(fz_context *ctx, fz_bitmap *bitmap, char
*filename);
12.6 PKM
Bitmaps suitable for output to the PKM format are generated by drawing to
CMYK contone (with no alpha), and then halftoning down to give 1bpc cmyk.
/*
fz_save_bitmap_as_pkm: Save a 4bpp cmyk bitmap as a PAM image file.
*/
void fz_save_bitmap_as_pkm(fz_context *ctx, fz_bitmap *bitmap, char
*filename);
12.7 PNG
The PNG format will accept either greyscale or RGB pixmaps, with or with-
out alpha. As a special case, alpha only pixmaps are accepted and written as
greyscale.
/*
CHAPTER 12. RENDERED OUTPUT FORMATS 117
/*
Write a pixmap to an output stream in PNG format.
*/
void fz_write_pixmap_as_png(fz_context *ctx, fz_output *out, const
fz_pixmap *pixmap);
/*
fz_new_png_band_writer: Obtain a fz_band_writer instance
for producing PNG output.
*/
fz_band_writer *fz_new_png_band_writer(fz_context *ctx, fz_output *out);
Because PNG is such a useful and widely used format, we have another couple
of functions. These take either an fz image or an fz pixmap and produce
an fz buffer containing a PNG encoded version. This is very useful when
converting between document formats as we can frequently use a PNG version
of an image as a replacement for other image formats that may not be supported.
/*
Create a new buffer containing the image/pixmap in PNG format.
*/
fz_buffer *fz_new_buffer_from_image_as_png(fz_context *ctx, fz_image
*image);
fz_buffer *fz_new_buffer_from_pixmap_as_png(fz_context *ctx, fz_pixmap
*pixmap);
12.8 PWG/CUPS
The PWG format is intended to encapsulate output for printers. As such there
are many values that can be set in the headers. To allow for this, we expose
these fields as an options structure that can be fed into the output functions.
typedef struct fz_pwg_options_s fz_pwg_options;
struct fz_pwg_options_s
{
/* These are not interpreted as CStrings by the writing code, but
* are rather copied directly out. */
char media_class[64];
char media_color[64];
char media_type[64];
CHAPTER 12. RENDERED OUTPUT FORMATS 118
char output_type[64];
int media_type_num;
int compression;
unsigned int row_count;
unsigned int row_feed;
unsigned int row_step;
No documentation for these fields is given here - for more information see the
PWG specification.
There are 2 sets of output functions available for PWG, those that take
fz pixmaps (for contone output) and those that take f bitmaps (for halftoned
output).
PWG files are structured as a header (to identify the format), followed by a
stream of pages (images). Those functions that save (or write) a complete file
include the file header as part of their output. If the option is used to append
to a file, then the header is not added, as we presume we are appending new
page information to the end of an existing file.
In circumstances when the header is not output automatically (such as when
using the band writer) the header output must be triggered manually, by calling:
CHAPTER 12. RENDERED OUTPUT FORMATS 119
/*
Output the file header to a pwg stream, ready for pages to follow it.
*/
void fz_write_pwg_file_header(fz_context *ctx, fz_output *out);
12.8.1 Contone
The PWG writer can accept pixmaps in greyscale, RGB and CMYK format,
with no alpha planes.
PWG files can be saved to a file using:
/*
fz_save_pixmap_as_pwg: Save a pixmap as a pwg
The file header will only be sent in the case where we are not appending to an
existing file.
Alternatively, pages may be sent to an output stream. Two functions exist to
do this. The first always sends a complete PWG file (including header):
/*
Output a pixmap to an output stream as a pwg raster.
*/
void fz_write_pixmap_as_pwg(fz_context *ctx, fz_output *out, const
fz_pixmap *pixmap, const fz_pwg_options *pwg);
The second sends just the page data, and is therefore suitable for sending the
second or subsequent pages in a file. Alternatively, the header can be sent
manually, and then this function can be used for all the pages in a file.
/*
Output a page to a pwg stream to follow a header, or other pages.
*/
void fz_write_pixmap_as_pwg_page(fz_context *ctx, fz_output *out, const
fz_pixmap *pixmap, const fz_pwg_options *pwg);
CHAPTER 12. RENDERED OUTPUT FORMATS 120
In all cases, a NULL value can be sent for the fz pwg options field, in which
case default values will be used.
12.8.2 Mono
The monochrome version of the PWG writer parallels the contone one. It can
accept monochrome bitmaps only.
PWG files can be saved to a file using:
/*
fz_save_bitmap_as_pwg: Save a bitmap as a pwg
The file header will only be sent in the case where we are not appending to an
existing file.
Alternatively, pages may be sent to an output stream. Two functions exist to
do this. The first always sends a complete PWG file (including header):
/*
Output a bitmap to an output stream as a pwg raster.
*/
void fz_write_bitmap_as_pwg(fz_context *ctx, fz_output *out, const
fz_bitmap *bitmap, const fz_pwg_options *pwg);
The second sends just the page data, and is therefore suitable for sending the
second or subsequent pages in a file. Alternatively, the header can be sent
manually, and then this function can be used for all the pages in a file.
CHAPTER 12. RENDERED OUTPUT FORMATS 121
/*
Output a bitmap page to a pwg stream to follow a header, or other
pages.
*/
void fz_write_bitmap_as_pwg_page(fz_context *ctx, fz_output *out, const
fz_bitmap *bitmap, const fz_pwg_options *pwg);
In all cases, a NULL value can be sent for the fz pwg options field, in which
case default values will be used.
12.9 TGA
The TGA writer can accept pixmaps in greyscale, RGB and BGR formats, with
and without alpha.
/*
fz_save_pixmap_as_tga: Save a pixmap as a TGA image file.
Can accept RGB, BGR or Grayscale pixmaps, with or without
alpha.
*/
void fz_save_pixmap_as_tga(fz_context *ctx, fz_pixmap *pixmap, const
char *filename);
/*
Write a pixmap to an output stream in TGA format.
Can accept RGB, BGR or Grayscale pixmaps, with or without
alpha.
*/
void fz_write_pixmap_as_tga(fz_context *ctx, fz_output *out, fz_pixmap
*pixmap);
/*
fz_new_tga_band_writer: Generate a new band writer for TGA
format images. Note that image must be generated vertically
flipped for use with this writer!
12.10 PCL
PCL is not a standard image format, rather it is a page description language for
printers. Unfortunately, the exact implementation of PCL varies from printer
to printer, so it can be necessary to tweak the output according to the exact
intended destination.
Accordingly, we have a pcl options structure to allow this to happen. To use
this, you simply define a pcl options structure on the stack:
pcl_options options = { 0 };
Next you populate those options. Typically this is done by requesting a preset
from our current defined set.
/*
fz_pcl_preset: Retrieve a set of fz_pcl_options suitable for a given
preset.
val: The value that the option should be set to. Acceptable ranges of
values depend on the option in question.
12.10.1 Color
Color PCL output can be generated from RGB pixmaps with alpha (though the
alpha is ignored) using:
void fz_save_pixmap_as_pcl(fz_context *ctx, fz_pixmap *pixmap, char
*filename, int append, const fz_pcl_options *pcl);
CHAPTER 12. RENDERED OUTPUT FORMATS 124
This is 24bpp RGB output, relying on the printers ability to dither. Blank lines
are skipped, repeated lines are coded efficiently, and other lines are coded using
deltas. Nonetheless file sizes can still be large with this output method.
12.10.2 Mono
12.11 Postscript
Postscript output is currently done as image output rather than high-level ob-
jects.
Pixmaps suitable for PS image output are greyscale, RGB or CMYK with no
alpha.
void fz_write_pixmap_as_ps(fz_context *ctx, fz_output *out, const
fz_pixmap *pixmap);
Postscript requires file level headers and trailers, over and above that produced
by the band writer itself. These can be generated using the following functions:
void fz_write_ps_file_header(fz_context *ctx, fz_output *out);
CHAPTER 12. RENDERED OUTPUT FORMATS 125
13.1 Overview
Images are ubiquitous in document formats, and come in a huge variety of
formats, ranging from full colour to monochrome, compressed to uncompressed,
large to small. The ability to efficiently represent and decode 2d arrays of pixels
is vital.
MuPDF represents images using an abstract type, fz image. This takes the
form of a base class, upon which different implementations can be built. All
fz images are reference counted, using the standard fz keep and fz drop con-
ventions:
/*
fz_drop_image: Drop a reference to an image.
/*
fz_keep_image: Increment the reference count of an image.
126
CHAPTER 13. THE IMAGE INTERFACE 127
size:
/*
fz_get_pixmap_from_image: Called to get a handle to a pixmap from an
image.
subarea: The subarea of the image that we actually care about (or
NULL
to indicate the whole image).
Many images have a resolution encoded within them. This may or may not be
honoured in the way they are positioned on the page, and it will certainly not
be honoured when zooming is taken into account, but for some operations it is
useful to be able to request it.
/*
fz_image_resolution: Request the natural resolution
of an image.
when decoded - repeated requests for pixmaps from the same image will (not
necessarily) require the image to be decoded again and again.
13.2.1 Compressed
The most common type of fz image is fz compressed image - that is, an image
based upon a fz buffer of data in a standard compressed format, such as JPEG,
PNG, TIFF, and others.
With such images, the data is held in an fz compressed buffer:
typedef struct fz_compressed_buffer_s
{
fz_compression_params params;
fz_buffer *buffer;
} fz_compressed_buffer;
The data is held in the buffer field, and the details of the compression used are
given in the params field, of type fz compression params:
struct fz_compression_params_s
{
int type;
union {
struct {
int color_transform; /* Use -1 for unset */
} jpeg;
struct {
int smask_in_data;
} jpx;
struct {
int columns;
int rows;
int k;
int end_of_line;
int encoded_byte_align;
int end_of_block;
int black_is_1;
int damaged_rows_before_error;
} fax;
struct
{
int columns;
int colors;
int predictor;
CHAPTER 13. THE IMAGE INTERFACE 129
int bpc;
}
flate;
struct
{
int columns;
int colors;
int predictor;
int bpc;
int early_change;
} lzw;
} u;
};
The choice of which of the union clauses is used is made by the type field:
enum
{
FZ_IMAGE_UNKNOWN = 0,
/* Uncompressed samples */
FZ_IMAGE_RAW,
/* Compressed samples */
FZ_IMAGE_FAX,
FZ_IMAGE_FLATE,
FZ_IMAGE_LZW,
FZ_IMAGE_RLD,
13.2.2 Decoded
The next most common type of image is based upon a decoded fz pixmap.
These are generally only used if the pixmap takes less storage than the com-
pressed data would.
/*
fz_pixmap_image_tile: Retried the underlying fz_pixmap
for an image.
The easiest way to tell if an image is a decoded image is to request its underlying
tile. If it returns NULL, you know it is not this sort of image.
The final standard sort of image in MuPDF (though more types may of course
be added in future) is that based upon a display list.
we use this to easily embed one file format within another. For example, epub
files frequently contain SVG images for title pages. We open the SVG image as
a separate document, run it to a display list, and close the document. We can
then create an image from the display list, and use this in the HTML flow of
the epub document.
These images maintain the properties of the original (vector-based) document
in that they remain scalable even after conversion to an image.
CHAPTER 13. THE IMAGE INTERFACE 131
This loads the data into memory, and calls fz new image from buffer inter-
nally.
If the data cannot be recognised from its header, and more information is re-
quired, then the data can be formed in an fz compressed buffer, and an image
created with:
/*
fz_new_image_from_compressed_buffer: Create an image based on
the data in the supplied compressed buffer.
Finally, if we have a decoded fz pixmap, we can form a new image from it:
/*
fz_new_image_from_pixmap: Create an image from the given
pixmap.
Then we’d define a new image creation function, fz new image from foo, of the
form:
CHAPTER 13. THE IMAGE INTERFACE 133
return &foo->super;
}
The key call here is the call to fz new image. This is a macro which wraps a
call to fz new image of size:
/*
fz_new_image_of_size: Internal function to make a new fz_image
structure for a derived class.
#define fz_new_image(CTX,W,H,B,CS,X,Y,I,IM,D,C,M,T,G,S,Z) \
((T*)Memento_label(fz_new_image_of_size(CTX,W,H,B,CS,X,Y,I,IM,D,C,M,sizeof(T),G,S,Z),#T))
The macro takes identical parameters to the function other than passing the
structure type in place of the structure type saved, and performing a typecast
to simplify the typical enclosing code.
Both function and macro take pointers to 3 functions that need to be defined
for the new format. Firstly, foo get is of the following type:
/*
fz_get_pixmap_fn: Function type to get a decoded pixmap
for an image.
w, h: The actual width and height that the whole image would
need to be decoded to.
*/
typedef fz_pixmap *(fz_image_get_pixmap_fn)(fz_context *ctx, fz_image
*im, fz_irect *subarea, int w, int h, int *l2factor);
The actual deallocation of the fz image block and its associated resources will
be done on return from this function. The fz drop image fn is responsible
just for deallocating its implementation specific resources (i.e. the contents of
foo image rather than fz image).
The decoded and subsampled image is then placed into the store so that it will
(hopefully) be found the next time a decode of the image is requested.
Chapter 14
14.1 Overview
MuPDF is written as an extensible framework for handling different document
types. Each different document format provides an fz document handler struc-
ture that provides the required callbacks to recognise and open files of its sup-
ported type. For example:
extern fz_document_handler pdf_document_handler;
extern fz_document_handler xps_document_handler;
extern fz_document_handler svg_document_handler;
...
At startup, the calling program must register the required document han-
dlers. It can either register them each individually, by repeatedly calling
fz register document handler:
/*
fz_register_document_handler: Register a handler
for a document type.
For example:
137
CHAPTER 14. THE DOCUMENT HANDLER INTERFACE 138
fz_register_document_handler(ctx, &pdf_document_handler);
fz_register_document_handler(ctx, &xps_document_handler);
fz_register_document_handler(ctx, &svg_document_handler);
...
or, it can use a convenience function to register all the standard handlers enabled
in a given build:
/*
fz_register_document_handler: Register handlers
for all the standard document types supported in
this build.
*/
void fz_register_document_handlers(fz_context *ctx);
This would then be created using a call to fz new document, such as:
foo_document *foo = fz_new_document(ctx, foo_document);
This returns an empty document structure with super populated with default
values, and the foo specific fields initialized to 0. The document handler then
needs to fill in the document level functions.
CHAPTER 14. THE DOCUMENT HANDLER INTERFACE 140
Implementations must fill in the drop document field, with a pointer to a func-
tion called to free any resources help by the document when the reference count
drops to 0. In the unlikely event that your implementation has no resources,
this field can be left NULL.
/*
fz_document_drop_fn: Called when the reference count for
the fz_document drops to 0. The implementation should
release any resources held by the document. The actual
document pointer will be freed by the caller.
*/
typedef void (fz_document_drop_fn)(fz_context *ctx, fz_document *doc);
Certain document types encode permissions within them to say what users are
allowed to do with them (printing, extracting etc). If your document handler’s
format has this concept, then you must fill in the has permission field with a
pointer to a function called to attempt to query such permissions:
/*
fz_document_has_permission_fn: Type for a function to be
called to see if a document grants a certain permission. See
fz_document_has_permission for more information.
*/
typedef int (fz_document_has_permission_fn)(fz_context *ctx, fz_document
*doc, fz_permission permission);
Certain document types can optionally include outline (table of contents) infor-
mation within them. If your document handler’s format has this concept, then
you must fill in the load outline field with a pointer to a function called to
attempt to load such information if it is there:
/*
fz_document_load_outline_fn: Type for a function to be called to
load the outlines for a document. See fz_document_load_outline
for more information.
*/
typedef fz_outline *(fz_document_load_outline_fn)(fz_context *ctx,
fz_document *doc);
If your document format requires a layout pass before it can be viewed, then
you must fill in the layout field with a pointer to a function called to perform
such a layout:
/*
fz_document_layout_fn: Type for a function to be called to lay
out a document. See fz_layout_document for more information.
*/
typedef void (fz_document_layout_fn)(fz_context *ctx, fz_document *doc,
float w, float h, float em);
CHAPTER 14. THE DOCUMENT HANDLER INTERFACE 142
If your document requires a layout pass, you should provide functions to both
make and resolve bookmarks to enable reader positions to be kept over layout
changes. Accordingly the make bookmark and lookup bookmark fields should
be filled out:
/*
fz_document_make_bookmark_fn: Type for a function to make
a bookmark. See fz_make_bookmark for more information.
*/
typedef fz_bookmark (fz_document_make_bookmark_fn)(fz_context *ctx,
fz_document *doc, int page);
/*
fz_document_lookup_bookmark_fn: Type for a function to lookup
a bookmark. See fz_lookup_bookmark for more information.
*/
typedef int (fz_document_lookup_bookmark_fn)(fz_context *ctx,
fz_document *doc, fz_bookmark mark);
Some document formats can encode internal links that point to another page
in the document. If your document supports this concept, then you must fill in
the resolve link field with a pointer to a function called to resolve a textual
link to a page number, and location on that page:
/*
fz_document_resolve_link_fn: Type for a function to be called to
resolve an internal link to a page number. See fz_resolve_link
for more information.
*/
typedef int (fz_document_resolve_link_fn)(fz_context *ctx, fz_document
*doc, const char *uri, float *xp, float *yp);
All document formats must fill in the count pages field with a pointer to a
function called to return the number of pages in a document:
/*
fz_document_count_pages_fn: Type for a function to be called to
count the number of pages in a document. See fz_count_pages for
more information.
*/
typedef int (fz_document_count_pages_fn)(fz_context *ctx, fz_document
*doc);
/*
fz_document_lookup_metadata_fn: Type for a function to query
a documents metadata. See fz_lookup_metadata for more
information.
*/
typedef int (fz_document_lookup_metadata_fn)(fz_context *ctx,
fz_document *doc, const char *key, char *buf, int size);
All document formats must fill in the load page field with a pointer to a function
called to return a reference to a fz page structure:
/*
fz_document_load_page_fn: Type for a function to load a given
page from a document. See fz_load_page for more information.
*/
typedef fz_page *(fz_document_load_page_fn)(fz_context *ctx, fz_document
*doc, int number);
To create a fz page use the fz new page macro. For a document of type foo,
typically a foo page structure would be defined as below:
typedef struct
{
fz_page super;
<foo specific fields>
} foo_page;
This would then be created using a call to fz new page, such as:
foo_page *foo = fz_new_page(ctx, foo_page);
This returns an empty document structure with super populated with default
values, and the foo specific fields initialized to 0. The document handler imple-
mentation then needs to fill in the page level functions.
The fz page structure contains a list of functions used to implement the page
level calls:
typedef struct fz_page_s
{
int refs;
fz_page_drop_page_fn *drop_page;
fz_page_bound_page_fn *bound_page;
fz_page_run_page_contents_fn *run_page_contents;
fz_page_load_links_fn *load_links;
CHAPTER 14. THE DOCUMENT HANDLER INTERFACE 144
fz_page_first_annot_fn *first_annot;
fz_page_page_presentation_fn *page_presentation;
fz_page_control_separation_fn *control_separation;
fz_page_separation_disabled_fn *separation_disabled;
fz_page_count_separations_fn *count_separations;
fz_page_get_separation_fn *get_separation;
} fz_page;
The fz page (and hence derived foo page) structures are reference counted.
The refs field is used to keep the reference count in. All the reference counting
is handled by the core library, and all that is required of the implementation is
that it should supply a drop page function that will be called when the reference
count reaches zero. This is of type:
/*
fz_page_drop_page_fn: Type for a function to release all the
resources held by a page. Called automatically when the
reference count for that page reaches zero.
*/
typedef void (fz_page_drop_page_fn)(fz_context *ctx, fz_page *page);
Implementations must fill in the bound page field with the address of a function
to return the pages bounding box, of type:
/*
fz_page_bound_page_fn: Type for a function to return the
bounding box of a page. See fz_bound_page for more
information.
*/
typedef fz_rect *(fz_page_bound_page_fn)(fz_context *ctx, fz_page *page,
fz_rect *);
Implementations must fill in the run page contents field with the address of a
function to interpret the contents of a page, of type:
/*
fz_page_run_page_contents_fn: Type for a function to run the
contents of a page. See fz_run_page_contents for more
information.
*/
typedef void (fz_page_run_page_contents_fn)(fz_context *ctx, fz_page
*page, fz_device *dev, const fz_matrix *transform, fz_cookie
*cookie);
/*
fz_page_load_links_fn: Type for a function to load the links
from a page. See fz_load_links for more information.
*/
typedef fz_link *(fz_page_load_links_fn)(fz_context *ctx, fz_page *page);
Some document formats can encode information that specifies how pages should
be presented to the user as a slideshow - how long they should be displayed, and
which transition to use when moving to the next page etc. In implementations of
document handlers for such formats, they should fill in the page presentation
field with the address of a function to obtain this information, of type:
/*
fz_page_page_presentation_fn: Type for a function to
obtain the details of how this page should be presented when
in presentation mode. See fz_page_presentation for more
information.
*/
typedef fz_transition *(fz_page_page_presentation_fn)(fz_context *ctx,
fz_page *page, fz_transition *transition, float *duration);
/*
fz_page_separation_disabled_fn: Type for a function to detect
whether a given separation is enabled or disabled on a page.
See fz_separation_disabled for more information.
*/
typedef int (fz_page_separation_disabled_fn)(fz_context *ctx, fz_page
*page, int separation);
/*
fz_page_count_separations_fn: Type for a function to count
the number of separations on a page. See fz_count_separations
for more information.
*/
typedef int (fz_page_count_separations_fn)(fz_context *ctx, fz_page
*page);
/*
fz_page_get_separation_fn: Type for a function to retrieve
details of a separation on a page. See fz_get_separation
for more information.
*/
typedef const char *(fz_page_get_separation_fn)(fz_context *ctx, fz_page
*page, int separation, uint32_t *rgb, uint32_t *cmyk);
14.3.1 PDF
14.3.2 XPS
14.3.3 EPUB
Support for EPub v2 is provided by epub document handler. Tables are not
currently supported, but is planned. Support for v3 is not planned.
The same document handler supports the FB2 (Fiction Book 2) electronic book
format.
14.3.4 HTML
14.3.5 SVG
14.3.6 Image
Support for a range of common image types (including PNG, JPEG, TIFF,
JPEG2000, BMP and GIF) is provided by image document handler.
14.3.7 CBZ
15.1 Usage
As well as opening existing documents, MuPDF contains functions to allow the
easy creation of new documents. The most general form of this functionality
takes the form of the fz document writer interface.
A document writer is obtained by calling a generation function. The most
general purpose one is:
/*
fz_new_document_writer: Create a new fz_document_writer, for a
file of the given type.
148
CHAPTER 15. THE DOCUMENT WRITER INTERFACE 149
Once a fz document writer has been created, pages can be written to the
document one at a time. The process is started by calling fz begin page:
/*
fz_begin_page: Called to start the process of writing a page to
a document.
This function returns a fz device pointer that should be used to write the
page contents to. This can be done by making a sequence of normal device calls
(see chapter 7 The Device interface) to paint the page with its content. One
of the most common ways of doing this is by calling fz run page contents on
another open document. This therefore offers a quick mechanism for converting
documents from one format to another.
Once the page contents have all been written, the page is finalized by calling
fz end page:
/*
fz_end_page: Called to end the process of writing a page to a
document.
CHAPTER 15. THE DOCUMENT WRITER INTERFACE 150
*/
void fz_end_page(fz_context *ctx, fz_document_writer *wri);
At this point, many formats will allow more pages to be written, simply by
repeating the fz begin page, output, fz end page loop.
When all the pages have been written, the produced document can be finalized
by calling fz close document writer:
/*
fz_close_document_writer: Called to end the process of writing
pages to a document.
Finally, the document writer itself can be freed in the usual fashion by calling
fz drop document writer:
/*
fz_drop_document_writer: Called to discard a fz_document_writer.
This may be called at any time during the process to release all
the resources owned by the writer.
15.2 Implementation
Support for a new type of document writer requires a new structure, derived
from fz document writer:
typedef struct
{
fz_document_writer_begin_page_fn *begin_page;
fz_document_writer_end_page_fn *end_page;
fz_document_writer_close_writer_fn *close_writer;
fz_document_writer_drop_writer_fn *drop_writer;
fz_device *dev;
} fz_document_writer;
For instance:
CHAPTER 15. THE DOCUMENT WRITER INTERFACE 151
typedef struct
{
fz_document_writer super;
<foo specific fields>
} foo_document_writer;
return &foo->super;
}
This uses a friendly macro that allocates a structure of the required size, ini-
tialises the function pointers as required, and zeroes the extra values in the
structure.
/*
fz_new_document_writer_of_size: Internal function to allocate a
block for a derived document_writer structure, with the base
structure’s function pointers populated correctly, and the extra
space zero initialised.
*/
fz_document_writer *fz_new_document_writer_of_size(fz_context *ctx,
size_t size, fz_document_writer_begin_page_fn *begin_page,
fz_document_writer_end_page_fn *end_page,
fz_document_writer_close_writer_fn *close,
fz_document_writer_drop_writer_fn *drop);
#define
fz_new_derived_document_writer(CTX,TYPE,BEGIN_PAGE,END_PAGE,CLOSE,DROP)
\
((TYPE
*)Memento_label(fz_new_document_writer_of_size(CTX,sizeof(TYPE),BEGIN_PAGE,END_PAGE,CLOSE,DROP
The actual work for the document writer is done in the functions that are
passed to fz new derived document writer. In the example above these were
foo begin page, foo end page, foo close, and foo drop. These have the fol-
lowing 4 types respectively.
/*
fz_document_writer_begin_page_fn: Function type to start
the process of writing a page to a document.
CHAPTER 15. THE DOCUMENT WRITER INTERFACE 152
/*
fz_document_writer_end_page_fn: Function type to end the
process of writing a page to a document.
/*
fz_document_writer_close_writer_fn: Function type to end
the process of writing pages to a document.
/*
fz_document_writer_drop_writer_fn: Function type to discard
an fz_document_writer. This may be called at any time during
the process to release all the resources owned by the writer.
Progressive Mode
16.1 Overview
When used in the normal way, MuPDF requires the entirety of a file to be
present before it can be opened. For some applications, this can be a significant
restriction - for instance, when downloading a PDF file over a slow internet link,
being able to view just the first page or two may be enough to know whether it
is the correct file or not.
Normal PDF files require the end of the file to be present before file reading can
begin, as this is where the ‘trailer’ lives (effectively the index for the entire file).
In an effort to allow early display of the first page, Adobe (the originators of the
PDF format) introduced the concept of a ‘linearized’ PDF file. This is a PDF
file that, while constructed in accordance with the original specification, also
has some extra information contained within the file to allow fast access to the
first page. This information is known as the ‘hint stream’. In addition, extra
constraints are placed upon the ordering of data within the file in an effort to
ensure that the first page will download quickly.
Unfortunately, Linearized PDF files are far from a panacea. The specification
is overly-complex, unclear and consequently poorly supported in both readers
and writers of the format. Even when implemented correctly, it is of limited use
for pages other than the first one.
MuPDF therefore attempts to solve the problem using a combination of mecha-
nisms, known together as “progressive mode”. When run in this mode, MuPDF
can not only take advantage of the linearization information (if present) in a
file, but is also capable of directing the actual download mechanism used by a
file. By controlling the order in which sections of a file are fetched, any page
required can be viewed before the whole fetch is complete.
153
CHAPTER 16. PROGRESSIVE MODE 154
For optimum performance a file should be both linearized and be available over
a byte-range supporting link, but benefits can still be had with either one of
these alone.
Coupled with the ability to render pages ignoring (and detecting) errors, this
means that ‘rough renderings’ of pages can be given even before all the content
(such as images and fonts) for a page have been downloaded.
16.2 Implementation
MuPDF has made various extensions to its mechanisms for handling progressive
loading. They rely on some special properties built into a type of fz stream
known as a ‘progressive’ stream.
At its lowest level MuPDF reads file data from a fz stream, us-
ing the fz open document with stream call. The alternative entrypoint
fz open document is implemented by calling this.
The PDF interpreter uses the fz lookup metadata call to check for its stream
being progressive or not. Any non-progressive stream will be read as normal,
with the system assuming that the entire file is present immediately.
If it is found to be progressive, another fz lookup metadata call is made to
find out what the length of the stream will be once the entire file is fetched. An
HTTP fetcher can know this by consulting the Content-Length header before
any data has been fetched.
With this information MuPDF can decide whether a file is linearized or not.
(Technically, knowing the length enables us to check with the length value given
in a linearized object - if these differ, the assumption is that an incremental save
has taken place, thus the file is no longer linearized.)
Other than supporting the required metadata responses, the key thing that
marks a stream as being progressive, is that it will not block when attempting
to read data it does not have. Instead, it will throw a FZ ERROR TRYLATER error.
This particular error code will be interpreted by the caller as an indication that
it should retry the parsing of the current objects at a later time.
When a MuPDF call is made on a progressive stream, such as
fz open document with stream, or fz load page, the caller should be pre-
pared to handle a FZ ERROR TRYLATER error as meaning that more data is re-
quired before it can continue. No indication is directly given as to exactly how
CHAPTER 16. PROGRESSIVE MODE 155
much more data is required, but as the caller will be implementing the progres-
sive fz stream that it has passed into MuPDF to start with, it can reasonably
be expected to figure out an estimate for itself.
With these mechanisms in place, a caller can repeatedly try to render each page
in turn until it gets a successful result.
Once a page has been loaded, if its contents are to be ‘run’ as normal (using
e.g. fz run page) any error (such as failing to read a font, or an image, or
even a content stream belonging to the page) will result in a rendering that
aborts with an FZ ERROR TRYLATER error. The caller can catch this and display
a placeholder instead.
If each pages data was entirely self-contained and sent in sequence this would
perhaps be acceptable, with each page appearing one after the other. Unfortu-
nately, the linearization procedure as laid down by Adobe does NOT do this:
objects shared between multiple pages (other than the first) are not sent with
the pages themselves, but rather AFTER all the pages have been sent.
This means that a document that has a title page, then contents that share a
font used on pages 2 onwards, will not be able to correctly display page 2 until
after the font has arrived in the file, which will not be until all the page data
has been sent.
To mitigate against this, MuPDF provides a way whereby callers can indicate
that they are prepared to accept an ‘incomplete’ rendering of the file (perhaps
with missing images, or with substitute fonts).
Callers prepared to tolerate such renderings should set the ‘incomplete ok’ flag
in the cookie, then call fz run page etc as normal. If a FZ ERROR TRYLATER error
is thrown at any point during the page rendering, the error will be swallowed,
the ‘incomplete’ field in the cookie will become non-zero and rendering will
continue. When control returns to the caller the caller can check the value of the
‘incomplete’ field and know that the rendering it received is not authoritative.
If the caller has control over the fetch of the file (be it http or some other
protocol), then it is possible to use byte range requests to fetch the document
‘out of order’. This enables non-linearized files to be progressively displayed
as they download, and fetches complete renderings of pages earlier than would
otherwise be the case. This process requires no changes within MuPDF itself,
but rather in the way the progressive stream learns from the attempts MuPDF
makes to fetch data.
CHAPTER 16. PROGRESSIVE MODE 156
for these pages will remain incomplete until much more of the file
has arrived.
[Typically therefore when we jump to a page in a linear file on a non
byte request capable link, we will see a rough rendering for that page
as soon as data arrives for it (which will typically take much longer
than would be the case with byte range capable downloads), and that
will improve much more slowly as images and fonts may not appear
until almost the whole file has arrived.]
– When the whole file has arrived, then we will attempt to read the
outlines for the file.
For a non-linearized PDF on a byte request capable stream:
– MuPDF will immediately seek to the end of the file to attempt to
read the trailer. This will fail with a FZ ERROR TRYLATER due to the
data not being here yet, but the stream code should remember that
this data is required and it should be prioritized in the background
fetch process.
– Repeated attempts to open the stream should eventually succeed
therefore. As MuPDF jumps through the file trying to read first the
xrefs, then the page tree objects, then the page contents themselves
etc, the background fetching process will be driven by the attempts
to read the file in the foreground.
[Typically therefore the opening of a non-linearized file will be slower than
a linearized one, as the xrefs/page trees for a non-linear file can be 20%+
of the file data. Once past this initial point however, pages and data can
be pulled from the file almost as fast as with a linearized file.]
For a non-linearized PDF on a non-byte request capable stream:
– MuPDF will immediately seek to the end of the file to attempt to
read the trailer. This will fail with a FZ ERROR TRYLATER due to the
data not being here yet. Subsequent retries will continue to fail until
the whole file has arrived, whereupon the whole file will be instantly
available.
[This is the worst case situation - nothing at all can be displayed until the
entire file has downloaded.]