5.5. How to generate images with JpGraph library

The two common steps for creating and using a Graph on your Web-page are

  1. Create a script that constructs the graph, by getting the data and specifying how the graph should look, the size, what colors to use, what fonts to use and specifying other augmentations on the graph.

  2. On the HTML page where the graph(s) should be displayed include add one or more <img> tags which links to the PHP graphs script. Of course it is perfectly possible to call the image script directly in the browser to just display the generated image in the browser. This way it is possible to include any number of graphs on the Web-page.

Tip

One further thing to keep in mind is that it is also possible to pass arguments to the image script via the normal HTTP GET/POST arguments.

For example

<img src="showgraph.php?a=1&b=2"> 

This could be used to control the appearance of the image or perhaps send data to the image which will be displayed. Note that this is probably not the best way to send large amount of data to plot. Instead the only practical way, for large data sizes, is to get all the data in the image script directly, perhaps from a DB. Another alternative for large amount of data to be sent to the image script is by creating a POST request to the image script. This is further discussed in ?? (Getting hold of the data to be displayed)

5.5.1. The standard steps of setting up a graph

When it comes to the structure of your imaging script they will generally have the following structure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
// ... Include necessary headers 
require_once 'jpgraph.php';
require_once '....';
 
// Create the graph instance
$graph = new Graph($width,$height, ...); 
 
// Specify what scale should be used in the graph
$graph->SetScale('...');
 
// ... code to construct the graph details and plot objects
 
// Add one or many plot objects to the graph
$graph->Add(..); 
 
// ... and send back the graph to the client
$graph->Stroke();
?>

JpGraph is completely Object oriented so all calls will be action on specific instances of classes. One of the fundamental classes is the Graph() class which represents the entire graph.

After the creation of the Graph() object all the code lines to construct the details of the graph are added. The final method called in an image script will most likely be the Graph::Stroke() method. This will send the constructed image back to the browser. A variation of this is used if the graph are supposed to have image maps (CSIM). In that case the final method will be Graph::StrokeCSIM()

Caution

As discussed in Section 5.1 no text can be returned from an image script. Beware!

In addition to this standard usage pattern you can also choose to:

  1. ... send the graph directly to a file. This is done by specifying a filename as parameter to the final Stroke() method call. See Section 5.5.4 for more detailed information.

  2. ... access the GD image handler for further image processing (also needed to include the image in an PDF file, see Appendix C)

  3. ... make use of the built-in cache system to send back a previously generated image. The cache system, which lessens the burden of the PHP server, works by avoiding running all the code that follows the initial Graph() call by checking if the image has already been created and in that case directly send back the previously created (and stored in a file) image file to the browser. The filename used for the image can be either manually selected or automatically created based on the script name. In addition it is also possible to specify a timeout value in the initial call to the Graph() constructor to indicate how long the image in the cache directory should be considered valid before a new image is generated. A full description of the JpGraph cache system is available in Section 5.6.

    Note

    The cache system by default is disabled and must be enabled by setting the proper define in the file "jpg-config.inc"

  4. ... combine several graphs in the same image using the MGraph() class (Multi-Graph). This is an advanced technique described in ??.

5.5.2. Choosing the image compression format for JpGraph

By default JpGraph automatically chooses the image format to use in the order PNG, JPEG and GIF. The exact format depends on what is available on the system the library is installed on. There are two ways you can influence the way the graphic format is chosen:

  1. Change the default graphic format by changing the DEFINE (in jpg-config.inc.php)

    DEFINE('DEFAULT_GFORMAT','auto')

    For example; if you by default want all your images to be generated with JPG encodation the define should be changed to

    DEFINE('DEFAULT_GFORMAT','jpg')

  2. By dynamically (in your script) select the wanted compression format with a call to

    Image::SetImgFormat()

    For example; if you want your image to use the JPEG format

    $graph->img->SetImgFormat('jpeg')

    (The above line assume that you have called your variable that holds the instantiated Graph() object "$graph"

5.5.3. Sending back the image to the browser

The very last statement in almost all graph scripts is the line

$graph->Stroke();

Note

Actually there are some valid exceptions to this when you do some more advanced graph generation involving caching together with the CSIM functionality.

This line starts the actual graph creation. All method calls up to this stage has just been to set the scene for the library and specify all necessary parameters. It is first when you make the call to the Stroke() method the library actually starts to build the image. Assuming there are no errors detected when the image is generated the library will now take the following steps in principle:

  1. Start building the image in memory. This is done by analyzing the specified parameters and making use of the supplied data in order to create the various plots that have been specified.

  2. Check what headers are needed, i.e. what image compression are used for the graph, and send that header back to the client.

    The library also have to check how the library was called since if it was called from the command line no MIME headers should be sent back at all, just the raw image data. Running the command line version of PHP will allow you to dynamically create images without using a HTTP server.

  3. Send the actual image data representing the built up image back to the client

The dreaded: Headers has already been sent error

This is an error that everyone, and we really mean everyone, will see one time or the other when producing dynamic images with PHP.

First, this is not a problem with JpGraph per se. What has happened is that your PHP script which produces the image has already returned some data to the client before the image header has been sent.

This is most often caused by one or more spaces before the first "<?php" statement. What happens is that the server normally sends back all data it finds in the files it reads. Since the server no sees a space, a perfectly valid character, it will send that data back to the client. However, before it does that it will automatically generate a header. Since it has seen a normal character data it will generate a header telling the client to expect a data stream of characters.

When later JpGraph tries to send its image header the server will detect that a header has already been sent and since each HTTP data stream can only have one type (and hence only one header) it will generate an error message which is sent back to the client.

To correct this error check your files for any output (even a single space) before the call to Graph::Graph() (or Graph::Stroke()) If you are running on older version of a Windows server this problem could also be caused by blank line at the end of the files. On some older Windows versions together with PHP4 it might also be called by a file ending in a newline (which all the JpGraph library files does) Remove the newline so that the file ends just after the final "?>" Also remember that when you include external file using include/include_once and so on PHP includes the whole content of the file; this content of the file also includes any potential carriage return/line feed or "blank" space before "<?php" and after "?>" These "dirty characters" will cause the problem just described.

5.5.4. Writing the image directly to a file

In addition to just streaming the file back to the browser it is also possible to write the file directly to a named file. The file name is given as an argument to the Graph::Stroke() method. For example as

$graph->Stroke('/tmp/myimage.png');

There are three important things to note here

  1. The PHP process must have write permission on the directory you are trying to write the image file on. If you are running PHP through your browser this means that the HTTP server process must have write permission on that directory.

  2. The file suffix (e.g. '.png') should match the image compression type used.

  3. If the image is streamed directly to a file and not back to the browser the script can of course return ordinary text.

Writing the image to both a file and stream it back to the browser

In this case you should instead use the method Graph::StrokeStore($aFileName) which was introduced in version 2.5 of the library. If you are on a previous version and for various reasons cannot upgrade then you can use the following "trick" to achieve this.

The idea is to use the _IMG_HANDLER option that forces the Graph::Stroke() to just return the image handler and then stop. We can then manually first send the image to the chosen file and then stream it back to the browser using some internal methods in the library. The following code snippet shows how this is done.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
 
// ... necessary includes ...
 
$graph = new Graph(400,300);
 
// ... code to generate a graph ...
 
// Get the handler to prevent the library from sending the
// image to the browser
$gdImgHandler = $graph->Stroke(_IMG_HANDLER);
 
// Stroke image to a file and browser
 
// Default is PNG so use ".png" as suffix
$fileName = "/tmp/imagefile.png";
$graph->img->Stream($fileName);
 
// Send it back to browser
$graph->img->Headers();
$graph->img->Stream();
?>

5.5.5. Alternatives to streaming or storing the image

There are also two predefined filenames which have special meaning when supplied as argument ot the Stoke() method.

_IMG_AUTO

This will create a file in the same directory as the script with the same name as the script but with the correct image extension.

_IMG_HANDLER

Specifying this filename will not create a an image to file nor stream it back to the browser. Instead it will instruct the Stroke() method to just return the handle for the GD image. This is useful if you later want to manipulate the image in ways that are not yet supported by JpGraph. For example include the image in a dynamically generated PDF file. See Appendix C for a detailed example how to include an image in a PDF generated with the "fpdf" library.

5.5.6. Forcing the browser to update your graph

Some browser may not send back a request to the HTTP server unless the user presses "Refresh" (F5 - in most browsers). This can lead to problems that the user is seeing old data since the file stamp of the script might not change but the data the script is using to create the image/graph is. A simple trick is to add a dummy time argument which is not used in the script.

For example

<img src="myimagescript.php?dummy=\'.now().">

Since the dummy argument will be a new number whenever the browser checks it the browser understands that it must re-fetch the script and force the image to be reloaded and redisplayed.

It is also important to be aware of any internal caching the browser might do. The general problem with dynamically generated images is that the image generating script (file) remains the same. This makes the browser believe that the data hasn\'t changed (since the script is the same) and if the browser already has issued a previous GET request and has the data cached it will not send a new GET if the time stamp on the file is the same since it then believes it should and can use the old browser cached version.

5.5.7. Printing the generated image

Some browsers, most notable IE (< v7) can have issues printing a dynamic image. This is because the designers of IE assumed that all images are traditional images that are available as static image files. Not that they could be dynamically generated. This unfortunately have some implications.

  1. IE will often (always?) re-fetch the page when preparing to print. This means that a new image will be generated and is perhaps very different from what the user thinks he is printing (if the data is changing rapidly).

  2. Some older versions of IE simply refuses to print dynamic images if they are not available as a static "*.png", "*.jpg" etc. file. The only known workaround is to make sure to use static images.

There is one final reported problem to be aware of. Normally most browsers will support "right-clicking" on an image to download the image locally. However, some older versions of IE will become very confused when dynamic images are used. This could manifest itself as that the file type is not the wanted, for example, trying to download a "*.png" image could cause the file to be saved as a "*.bmp" file instead.

Newer versions of IE seems to be able to handle dynamic images much better.

It should also be mentioned that some older versions of FireFox (< v3) could in some circumstances fetch a dynamic image twice causing unnecessary load on the server (See FireFox Bugzilla). However, there are no known issues with dynamic images in current versions of FireFox and IE (i.e. IE v8).