Difference between revisions of "ZIM file format"

Jump to navigation Jump to search
1,086 bytes removed ,  07:44, 23 April 2021
→‎Clusters: Clarification around the cluster compression types
(→‎Clusters: Add 5 for Zstandard compression)
(→‎Clusters: Clarification around the cluster compression types)
(3 intermediate revisions by one other user not shown)
Line 1: Line 1:
[[Image:Schema File Format.png|500px|right]]
Beginning 2021, we change the way we handle namespaces in ZIM file format.
The '''ZIM file format''' is based on the old and deprecated [[Zeno File Format]]. See also a walk through example at [[ZIM File Example]].
It starts with a header, which is described here:


This is major change in the way we handle entries in libzim and the semantics around but it is not a break in the binary zim format. Old library/readers are compatible with new zim files.
This page describe the new format. The old format can be found here : [[ZIM file format old namespace]].[[Image:Schema File Format.png|500px|right]]
== Header ==
== Header ==
A ZIM file starts with a header. This is offset 0.
A ZIM archive starts with a header :
 
Length in bytes, all types are little-endian.
 
All integers are unsigned integers (uint_16, uint_32, uint_64).


{| class="sortable" style="border-width:1px; border-style:solid; border-color:#888888; background-color:#eeeeee; border-collapse:collapse; empty-cells:show" cellspacing="0" cellpadding="4" {{Prettytable}}
{| class="sortable" style="border-width:1px; border-style:solid; border-color:#888888; background-color:#eeeeee; border-collapse:collapse; empty-cells:show" cellspacing="0" cellpadding="4" {{Prettytable}}
Line 19: Line 16:
|4
|4
|2
|2
|Major version of the ZIM file format (5 or 6)
|Major version of the ZIM archive format (6)
|-
|-
| minorVersion || integer || 6 || 2 || Minor version of the ZIM file format                         
| minorVersion || integer || 6 || 2 || Minor version of the ZIM archive format (1 for new namespace usage, 0 for old namespace usage)                        
|-
|-
| uuid || integer || 8 || 16 || unique id of this zim file                          
| uuid || integer || 8 || 16 || unique id of this zim archive                          
|-
|-
| articleCount || integer || 24 || 4 || total number of articles                  
| entryCount || integer || 24 || 4 || total number of entries                  
|-
|-
| clusterCount || integer || 28 || 4 || total number of clusters                   
| clusterCount || integer || 28 || 4 || total number of clusters                   
Line 31: Line 28:
| urlPtrPos || integer || 32 || 8 || position of the directory pointerlist ordered by URL                     
| urlPtrPos || integer || 32 || 8 || position of the directory pointerlist ordered by URL                     
|-
|-
| titlePtrPos || integer || 40 || 8 || position of the directory pointerlist ordered by Title                  
| titlePtrPos || integer || 40 || 8 || position of the directory pointerlist ordered by Title
This is considered as obsolete, readers should use <code>[[Search indexes#Title index v0|X/listing/titleordered/v0]]</code> instead and fallback to <code>titlePtrPos</code> if entry is not present.
|-
|-
| clusterPtrPos || integer || 48 || 8 || position of the cluster pointer list                 
| clusterPtrPos || integer || 48 || 8 || position of the cluster pointer list                 
Line 39: Line 37:
| mainPage || integer || 64 || 4 || main page or 0xffffffff if no main page                       
| mainPage || integer || 64 || 4 || main page or 0xffffffff if no main page                       
|-
|-
| layoutPage || integer || 68 || 4 || [[Layout Page|layout page]] or 0xffffffffff if no layout page                     
| layoutPage || integer || 68 || 4 || [[Layout Page|layout page]] or 0xffffffffff if no layout page (deprecated, always 0xffffffffff)                    
|-
|-
| checksumPos || integer || 72 || 8 || pointer to the md5checksum of this file without the checksum itself. This points always 16 bytes before the end of the file.
| checksumPos || integer || 72 || 8 || pointer to the md5checksum of this archive without the checksum itself. This points always 16 bytes before the end of the archive.
|}
|}


Line 48: Line 46:
Minor version is updated when an compatible change is integrated (a lib made for a minor version n will be able to read a version n+1)
Minor version is updated when an compatible change is integrated (a lib made for a minor version n will be able to read a version n+1)


There are currently 2 major versions :
The current major version is 6. You may found old zim archives with major version 5. They are the same than 6 less extended cluster, so you can read a 5 major version as if it was a 6.
* The version 5
 
* The version 6 (the same that version 5 + potential extended cluster)
The minor version can be :
* 0 : We use the old namespace usage (see [[ZIM file format old namespace]])
* 1 : We use the new namespace usage (describe here).
A zim archive may be embedded in another file at a specific offset. In the context of zim format, the start of the zim header is the offset 0. Readers allowing to read an embedded archive must adapt offset accordingly.


== MIME Type List (mimeListPos) ==
== MIME Type List (mimeListPos) ==
Line 72: Line 73:
The URL pointer list is a list of 8 byte offsets to the directory entries.
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.
The directory entries are always ordered by "full" URL (<code><namespace><path></code>). Ordering is simply done by comparing the URL strings.


Since directory entries have variable sizes this is needed for random access.
Since directory entries have variable sizes this is needed for random access.
Line 91: Line 92:


== Title Pointer List (titlePtrPos) ==
== Title Pointer List (titlePtrPos) ==
The title pointer list is a list of article indices ordered by title. The title pointer list actually points to entries
The title pointer list is a list of entry indices ordered by title (<code><namespace><title></code>). The title pointer list actually points to entries in the URL pointer list.
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.
Note that the title pointers are only 4 bytes. They are not offsets in the file but entry numbers.
 
To get the offset of an entry from the title pointer list, you have to look it up in the URL pointer list.


{| class="sortable" style="border-width:1px; border-style:solid; border-color:#888888; background-color:#eeeeee; border-collapse:collapse; empty-cells:show" cellspacing="0" cellpadding="4" {{Prettytable}}
{| class="sortable" style="border-width:1px; border-style:solid; border-color:#888888; background-color:#eeeeee; border-collapse:collapse; empty-cells:show" cellspacing="0" cellpadding="4" {{Prettytable}}
Line 112: Line 115:


== Directory Entries ==
== Directory Entries ==
Directory entries hold the meta information about all articles, images and other objects in a ZIM file.
Directory entries hold the meta information about all entries, images and other objects in a ZIM archive.


There are many types of directory entries:
There are different types of directory entries:


=== Article Entry ===
=== Content Entry ===
{| class="sortable" style="border-width:1px; border-style:solid; border-color:#888888; background-color:#eeeeee; border-collapse:collapse; empty-cells:show" cellspacing="0" cellpadding="4" {{Prettytable}}
{| class="sortable" style="border-width:1px; border-style:solid; border-color:#888888; background-color:#eeeeee; border-collapse:collapse; empty-cells:show" cellspacing="0" cellpadding="4" {{Prettytable}}
! Field Name !! Type !!Offset!!Length!! Description                 
! Field Name !! Type !!Offset!!Length!! Description                 
Line 122: Line 125:
| mimetype || integer || 0 || 2 || MIME type number as defined in the MIME type list                       
| mimetype || integer || 0 || 2 || MIME type number as defined in the MIME type list                       
|-
|-
| parameter len || byte || 2 || 1 || (not used) length of extra paramters                       
| parameter len || byte || 2 || 1 || (not used) length of extra paramters (must be 0)                      
|-
|-
| namespace || char || 3 || 1 || defines to which namespace this directory entry belongs                           
| namespace || char || 3 || 1 || defines to which namespace this directory entry belongs                           
|-
|-
| revision || integer || 4 || 4 || (optional) identifies a revision of the contents of this directory entry, needed to identify updates or revisions in the original history                       
| revision || integer || 4 || 4 || (not used) identifies a revision of the contents of this directory entry, needed to identify updates or revisions in the original history (must be 0)                        
|-
|-
| cluster number || integer || 8 || 4 || cluster number in which the data of this directory entry is stored                 
| cluster number || integer || 8 || 4 || cluster number in which the data of this directory entry is stored                 
Line 149: Line 152:
| namespace || char || 3 || 1 || defines to which namespace this directory entry belongs                           
| namespace || char || 3 || 1 || defines to which namespace this directory entry belongs                           
|-
|-
| revision || integer || 4 || 4 || (optional) identifies a revision of the contents of this directory entry, needed to identify updates or revisions in the original history                       
| revision || integer || 4 || 4 || (not used) identifies a revision of the contents of this directory entry, needed to identify updates or revisions in the original history (must be 0)                        
|-
|-
| redirect index || integer || 8 || 4 || pointer to the directory entry of the redirect target                 
| redirect index || integer || 8 || 4 || pointer to the directory entry of the redirect target                 
Line 160: Line 163:
|}
|}


=== Linktarget or deleted Entry ===
=== Linktarget or deleted Entry (DEPRECATED) ===
{| class="sortable" style="border-width:1px; border-style:solid; border-color:#888888; background-color:#eeeeee; border-collapse:collapse; empty-cells:show" cellspacing="0" cellpadding="4" {{Prettytable}}
There is two kinds of deprecated entry that could be found in pretty old zim files (I, main develloper of libzim, never saw it).
! Field Name !! Type !!Offset!!Length!! Description               
 
|-
They have mimetype equal to 0xfffe or 0xfffd. Reader implementation may check for those value and ignore the whole dirent.
| mimetype || integer || 0 || 2 || 0xfffe for linktarget, 0xfffd for deleted entry
|-
| parameter len || byte || 2 || 1 || (not used) length of extra paramters                     
|-
| namespace || char || 3 || 1 || defines to which namespace this directory entry belongs                         
|-
| revision || integer || 4 || 4 || (optional) identifies a revision of the contents of this directory entry, needed to identify updates or revisions in the original history                     
|-
| url || string || 16 ||zero terminated|| string with the URL as refered in the URL pointer list                       
|-
| title || string || n/a ||zero terminated|| string with an title as refered in the Title pointer list or empty; in case it is empty, the URL is used as title                     
|-
| parameter || data || ||see parameter len|| (not used) extra parameters                       
|}


== Cluster Pointer List (clusterPtrPos) ==
== Cluster Pointer List (clusterPtrPos) ==
The cluster pointer list is a list of 8 byte offsets which point to all data clusters in a ZIM file.
The cluster pointer list is a list of 8 byte offsets which point to all data clusters in a ZIM archive.


{| class="sortable" style="border-width:1px; border-style:solid; border-color:#888888; background-color:#eeeeee; border-collapse:collapse; empty-cells:show" cellspacing="0" cellpadding="4" {{Prettytable}}
{| class="sortable" style="border-width:1px; border-style:solid; border-color:#888888; background-color:#eeeeee; border-collapse:collapse; empty-cells:show" cellspacing="0" cellpadding="4" {{Prettytable}}
Line 199: Line 188:
The first byte of the cluster identifies some information about the cluster.
The first byte of the cluster identifies some information about the cluster.


The first fourth low bits identifies if the cluster is compressed (4) or not (0). The default is uncompressed indicated by a value of 0 or 1 (obsoleted, inherited by Zeno) while compressed clusters are indicated by a value of 4 which indicates [[LZMA2 compression]] (or more precisely XZ, since there is a XZ header) and 5 the Zstandard compression. There have been other compression algorithms used before (2: zlib, 3: bzip2) which have been removed. The zimlib uses [http://tukaani.org/xz/ xz-utils] as a C++ implementation of lzma2, for Java see [http://tukaani.org/xz/java.html XZ-Java].
The first fourth low bits identifies if the cluster compression type:
* No compression is indicated by a value of 1
* Compressed clusters are indicated by a value of 4 ([[LZMA2 compression]] (or more precisely XZ, since there is a XZ header)) or 5 (Zstandard compression).
* There have been other compression algorithms used before which have been removed: 2 for zlib and 3 for bzip2.
* 0 is an obselete code for no compression (inhereted from the Zeno)


The firth bit identifies if the cluster is extended or not :
The fifth bit identifies the cluster is extended or not :
* By default (5th bit == 0) the cluster is not extended. It means that the offsets are stored in a 4 bytes length integer. Thus contents stored in the cluster cannot exceed 4Go.
* By default (5th bit == 0) the cluster is not extended. It means that the offsets are stored in a 4 bytes length integer. Thus contents stored in the cluster cannot exceed 4Go.
* If the cluster is extended (5th bit == 1), the offsets are stored in 8 bytes length integer. Thus contents stored in the cluster can exceed 4Go.
* If the cluster is extended (5th bit == 1), the offsets are stored in 8 bytes length integer. Thus contents stored in the cluster can exceed 4Go.
A cluster can be extended only if the zim major version is 6. Else (major version == 5) cluster will always be not extended.
A cluster can be extended only if the zim major version is 6. Else (major version == 5) cluster will always be not extended.
The zimlib uses [http://tukaani.org/xz/ xz-utils] as a C++ implementation of lzma2, for Java see [http://tukaani.org/xz/java.html XZ-Java].


To find the data of a specific directory entry within a cluster the uncompressed cluster has a list of pointers to blobs within the uncompressed cluster after the first byte.
To find the data of a specific directory entry within a cluster the uncompressed cluster has a list of pointers to blobs within the uncompressed cluster after the first byte.
Line 211: Line 206:
! Field Name !! Type !!Offset!!Length!! Description                 
! Field Name !! Type !!Offset!!Length!! Description                 
|-
|-
| cluster information || integer || 0 || 1 || Fourth low bits : 0: default (no compression), 1: none (inherited from Zeno), 4: LZMA2 compressed
| cluster information || integer || 0 || 1 || Fourth low bits : 1: no compression, 4: LZMA2 compressed, 5: zstd compressed
Firth bits : 0: normal (OFFSET_SIZE=4) 1: extended (OFFSET_SIZE=8)               
Firth bits : 0: normal (OFFSET_SIZE=4) 1: extended (OFFSET_SIZE=8)               
|-
|-
Line 236: Line 231:


== Namespaces ==
== Namespaces ==
Namespaces seperate different types of directory entries - which might have the same title - stored in the ZIM File Format.
Namespaces separate different types of directory entries - which might have the same title or url - stored in the ZIM archive Format.
 
The new namespace usage put a strong semantics on the namespaces. The libzim uses this semantics and provide different kind of API to access the different kind of entries.
 
The libzim hide the namespace to the user, so a entry `foo.html` in namespace `C` will be accessible as `foo.html`. libzim provides specific API to access metadata.


They can be distinguished by prepending the article namespace before the article name in the URL path, eg. ''http://localhost/A/Articlename''.
Other implementation are free to explicit the namespace or not (but need to provide some fallback for all namespace usage)


{| class="sortable" style="border-width:1px; border-style:solid; border-color:#888888; background-color:#eeeeee; border-collapse:collapse; empty-cells:show" cellspacing="0" cellpadding="4" {{Prettytable}}
{| class="sortable" style="border-width:1px; border-style:solid; border-color:#888888; background-color:#eeeeee; border-collapse:collapse; empty-cells:show" cellspacing="0" cellpadding="4" {{Prettytable}}
! Namespace !! Description   
! Namespace !! Description   
|-
|-
| - || layout, eg. the LayoutPage, CSS, favicon.png (48x48), JavaScript and images not related to the articles       
| C || User content entries - see [[Article Format]]         
|-
| 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]]         
| M || ZIM metadata - see [[Metadata]]         
|-
|-
| U || categories, text - see [[Category Handling]]         
| W || Well know entries (MainPage, Favicon) - see [[Well known entries]]         
|-
|-
| V || categories, article list - see [[Category Handling]]        
| X || search indexes - see [[Search indexes]]
|-
| W || categories per article, category list - see [[Category Handling]]       
|-
| X || search indexes
|}
|}


== URLs ==
== URLs ==


ZIM contents are addressed using URLs fitting the following pattern: <namespace>/<article_url>. The references in articles HTML code (''<a href=""></a>'', ''<img src="">'', etc.) are URL-encoded following the [http://www.ietf.org/rfc/rfc1738.txt RFC 1738] rules.
=== URL Encoding ===
 
The URLs in the UrlPointerlist are utf-8 and are not url encoded (https://www.ietf.org/rfc/rfc1738.txt)
Absolute URLs, ie. with a leading slash (''/'') are forbidden, because this avoid including the ZIM contents in any HTTP sub-hierachy. ZIM contents URLs must consequently be relative.


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.
Some readers process the requests that already do the url 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 libzim.


=== Local Anchors ===
=== Local Anchors ===
Line 276: Line 262:


<pre>
<pre>
<a href="../A/foo#headline1">jump to article foo, headline 1</a>
<a href="foo#headline1">jump to article foo, headline 1</a>
</pre>
</pre>


The browser handles these local anchors by itself. It will determine if another article has to be loaded (local anchor inside another article than the currently shown) and will send a request only with the article URL without the local anchor - in our example "../A/foo". After the article has been loaded the browser will then search for the local anchor tag and jump to the right location.
The browser handles these local anchors by itself. It will determine if another article has to be loaded (local anchor inside another article than the currently shown) and will send a request only with the article URL without the local anchor - in our example "foo". After the article has been loaded the browser will then search for the local anchor tag and jump to the right location.


If you use a common rendering engine or HTML widget you don't have to care for this cases, you can just use the requests as they are submitted by the engine / widget.
If you use a common rendering engine or HTML widget you don't have to care for this cases, you can just use the requests as they are submitted by the engine / widget.
Line 287: Line 273:
== Encodings ==
== Encodings ==
=== Character Encoding ===
=== Character Encoding ===
The standard encoding for ZIM file content is UTF-8. So both article data and URLs should be handled accordingly.
The standard encoding for ZIM archive content is UTF-8. So both article data and URLs should be handled accordingly.
 
Old Zeno files used a mixture of Latin1 and UTF-8 so there is still some "auto detection" code left in the ''zimlib'', a workaround for this bug. This will be removed in future versions. Zeno files are not supported anymore.


=== Integer Encoding ===
=== Integer Encoding ===
For integer encoding the same algorithm as UTF-8 encoding is used. This encoding is also known as "integer compression". It safes some bytes by using variable lengths of integer fields, depending on the actual value of the number.
All types are little-endian.


See also http://en.wikipedia.org/wiki/UTF-8#Design.
All integers are unsigned integers (uint_16, uint_32, uint_64).


Old Zeno files used the QUnicode library instead. By switching to UTF-8 the new format is more standard-adherent and easier to understand.
All lengths are bytes.


== Split ZIM files ==
== Split ZIM files ==
ZIM files can be split in multiple chunks. This is necessary to be able to store big (over 4GB for example) ZIM files to limited file systems (like FAT32). That said, the chunks can be of any size, but the naming is really important. The ZIM file chunks should be named like following (the file name extensions matter): ''foobar.zimaa, foobar.zimab, foobar.zimac''...
ZIM archives can be split in multiple files. This is necessary to be able to store big (over 4GB for example) ZIM archives to limited file systems (like FAT32). That said, the files can be of any size, but the naming is really important. The ZIM files should be named like following (the file name extensions matter): ''foobar.zimaa, foobar.zimab, foobar.zimac''...


== See also ==
== See also ==

Navigation menu