ZIM file format
The ZIM file format is based on the Zeno File Format. It starts with a header, which is described here:
Header
A ZIM file starts with a header. This starts always at offset 0.
Length in byte, all types are littlendian.
Field Name | Type | Offset | Length | Description |
---|---|---|---|---|
magicNumber | integer | 0 | 4 | Magic number to recognise the file format, must be 72173914 |
version | integer | 4 | 4 | ZIM=5, bytes 1-2: major, bytes 3-4: minor version of the ZIM file format |
uuid | integer | 8 | 16 | unique id of this zim file |
articleCount | integer | 24 | 4 | total number of articles |
clusterCount | integer | 28 | 4 | total number of clusters |
urlPtrPos | integer | 32 | 8 | position of the directory pointerlist orderes by URL |
titlePtrPos | integer | 40 | 8 | position of the directory pointerlist ordered by Title |
clusterPtrPos | integer | 48 | 8 | position of the cluster pointer list |
mimeListPos | integer | 56 | 8 | position of the MIME type list (also header size) |
mainPage | integer | 64 | 4 | main page or 0xffffffff if no main page |
layoutPage | integer | 68 | 4 | layout page or 0xffffffffff if no layout page |
Each article in the ZIM file has a directory entry. Since the directory entry has a variable size we have an index pointerlist which is a list of 4-byte offsets. The pointers points to the directory entries.
MIME Type List (mimeListPos)
The MIME type list always follows directly after the header, so the mimeListPos also defines the end and size of the ZIM file header.
The MIME types in this list are zero terminated strings. An empty string marks the end of the MIME type list.
Field Name | Type | Offset | Length | Description |
---|---|---|---|---|
<1st MIME Type> | string | 0 | n/a | declaration of the <1st MIME Type> - zero terminated |
<2nd MIME Type> | string | n/a | n/a | declaration of the <2nd MIME Type> - zero terminated |
... | string | ... | ... | ... |
<last entry / end> | string | n/a | 0 | empty string - zero terminated |
URL Pointer List (urlPtrPos)
The URL pointer list is a list of 8 byte offsets to the directory entries.
The directory entries are always ordered by URL. Ordering is simply done by comparing the URL strings.
Since directory entries have variable sizes this is needed for random access.
Field Name | Type | Offset | Length | Description |
---|---|---|---|---|
<1st URL> | integer | 0 | 8 | pointer to the directory entry of <1st URL> |
<2nd URL> | integer | 8 | 8 | pointer to the directory entry of <2nd URL> |
<nth URL> | integer | (n-1)*8 | 8 | pointer to the directory entry of <nth URL> |
... | integer | ... | 8 | ... |
Zimlib caches directory entries and references the cached entries via the URL pointers.
Title Pointer List (titlePtrPos)
The title pointer list is a list of article indeces ordered by title. The title pointer list actually points to entries in the URL pointer list. Note that the title pointers are only 4 bytes. They are not offsets in the file but article numbers. To get the offset of an article from the title pointer list, you have to look it up in the URL pointer list.
Field Name | Type | Offset | Length | Description |
---|---|---|---|---|
<1st Title> | integer | 0 | 4 | Pointer to the URL pointer of <1st Title> |
<2nd Title> | integer | 4 | 4 | Pointer to the URL pointer of <2nd Title> |
<nth Title> | integer | (n-1)*4 | 4 | Pointer to the URL pointer of <nth Title> |
... | integer | ... | 4 | ... |
The indirection from titles via URLs to directory entries has two reasons:
- the pointer list is only half in size as 4 bytes are enough for each entry
- accessing directory entries by title also makes use of cached directory entries which are referenced by the URL pointers, as implemented in zimlib
Cluster pointer list (clusterPtrPos)
The cluster pointer list is a global list of 8 byte offsets which point to all data clusters in a ZIM file.
Field Name | Type | Offset | Length | Description |
---|---|---|---|---|
<1st Cluster> | integer | 0 | 8 | Pointer to the <1st Cluster> |
<1st Cluster> | integer | 8 | 8 | Pointer to the <2nd Cluster> |
<nth Cluster> | integer | (n-1)*8 | 8 | Pointer to the <nth Cluster> |
... | integer | ... | 8 | ... |
Directory entries
length in byte, all data is little endian. There are 2 types of directory entries: article entries and redirect entries. If the first two bytes are 0xffff the directory entrie is a redirect.
article entry
Field Name | Type | Offset | Length | Description |
---|---|---|---|---|
mime | integer | 0 | 2 | mime type number - points to the mime type list |
parameter len | 2 | 1 | length of extra paramters (which are currently unused an hence this is always 0) | |
namespace | char | 3 | 1 | |
version | integer | 4 | 4 | |
cluster number | integer | 8 | 4 | |
blob number | integer | 12 | 4 | |
url | string | 16 | zero terminated | string with the url |
title | string | zero terminated | string with title or empty; in case it is empty, the url is used as title | |
parameter | data | see extra len | extra parameters |
redirect entry
Field Name | Type | Offset | Length | Description |
---|---|---|---|---|
mime | integer | 0 | 2 | 0xffff for redirect |
parameter len | 2 | 1 | length of extra paramters (which are currently unused an hence this is always 0) | |
namespace | char | 3 | 1 | |
version | integer | 4 | 4 | |
redirect index | integer | 8 | 4 | |
url | string | 12 | zero terminated | string with the url |
title | string | zero terminated | string with title or empty; in case it is empty, the url is used as title | |
parameter | data | see extra len | extra parameters |
Clusters
The clusters contain the actual article data. This file section contain a list of clusters, which contain a list of blobs each. The blob is the data of one specific article. So this blob is adressed by the cluster number and the blob number in this cluster. The cluster number is used to look up the file offset in the cluster pointer list.
The cluster has a starting byte, which indicated, which compresion is used. After this byte, all other data is compressed. Possible values are:
- 0 default (no compression)
- 1 none also no compression (inherited from zeno)
- 2 zlib
- 3 bzip2
- 4 lzma2 (default compression format)
Support for zlib and bzip2 is deprecated. By default it is not compiled into the library any more. Only lzma2 is used. The zimlib uses xz-utils as a C++ implementation of lzma2, for Java see LZMA2-java.
The data area has a list of 4 byte offsets to the blobs counting from the first offset. The offset addresses uncompressed data. The last pointer points to the end of the data area. So there is always one more offset than blobs. Since the first offset points to the start of the first data, the number of offsets can be determined by dividing this offset by 4. The size of one blob is calculated by the difference of two consecutive offsets.
Namespaces
Namespaces seperate different types of data stored in the ZIM File Format.
They can be distinguished by prepending the article namespace before the article name in the URL path, eg. http://localhost/A/Articlename.
Namespace | Description |
---|---|
- | layout, eg. the LayoutPage, CSS, JavaScript and images not related to the articles |
A | articles - see Article Format |
B | article meta data - see Article Format |
I | images, files - see Image Handling |
J | images, text - see Image Handling |
M | ZIM metadata - see Metadata |
U | categories, text - see Category Handling |
V | categories, article list - see Category Handling |
W | categories per article, category list - see Category Handling |
X | fulltext index - see ZIM Index Format |
URLs
ZIM contents are addressed using URLs: /<namespace>/<article_url>.
The references in Article Text (<a href=""></a>, <img src=""> etc.) are URL-encoded (RFC 1738).
The URLs in the UrlPointerlist are not encoded. Some readers process the requests that already do the decoding internally whereas most readers will handle the URLs directly. In this case you have to do the decoding before you pass the parameter to zimlib, but zimlib already provides a method to do so.