Difference between revisions of "ZIM file format"

Jump to navigation Jump to search
m
Minor typos
(→‎Clusters: Clarification around the cluster compression types)
m (Minor typos)
(21 intermediate revisions by 3 users not shown)
Line 12: Line 12:
| magicNumber || integer || 0 || 4 || Magic number to recognise the file format, must be 72173914 (0x44D495A)
| magicNumber || integer || 0 || 4 || Magic number to recognise the file format, must be 72173914 (0x44D495A)
|-
|-
|majorVersion
| [[#Major_.26_Minor_versions|majorVersion]]
|integer
| integer
|4
| 4
|2
| 2
|Major version of the ZIM archive format (6)
| Major version of the ZIM archive format. Major version is updated when an incompatible change is integrated in the format (a lib made for a version N will probably not be able to read a version N+1)
|-
|-
| minorVersion || integer || 6 || 2 || Minor version of the ZIM archive format (1 for new namespace usage, 0 for old namespace usage)                      
| [[#Major_.26_Minor_versions|minorVersion]] || integer || 6 || 2 || Minor version of the ZIM archive format. 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)                      
|-
|-
| uuid || integer || 8 || 16 || unique id of this zim archive                           
| uuid || integer || 8 || 16 || unique id of this zim archive                           
Line 26: Line 26:
| clusterCount || integer || 28 || 4 || total number of clusters                   
| clusterCount || integer || 28 || 4 || total number of clusters                   
|-
|-
| urlPtrPos || integer || 32 || 8 || position of the directory pointerlist ordered by URL                      
| pathPtrPos || integer || 32 || 8 || position of the directory pointerlist ordered by Path                      
|-
|-
| titlePtrPos || integer || 40 || 8 || position of the directory pointerlist ordered by Title
| titlePtrPos || integer || 40 || 8 || position of the directory pointerlist ordered by Title
Line 42: Line 42:
|}
|}


Major version is updated when an incompatible change is integrated in the format (a lib made for a version N will probably not be able to read a version N+1)
A ZIM archive may be embedded in another file at a specific offset. In the context of the ZIM format, the start of the ZIM header is the offset 0. Readers allowing to read an embedded archive must adapt offset accordingly.


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)
=== Major & Minor 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.
Versioning of the file format specification has not been done [https://semver.org/ rigorously] until version 5.


The minor version can be :
Before version 5, there was only one version number and no Major vs Minor distinction.
* 0 : We use the old namespace usage (see [[ZIM file format old namespace]])
 
* 1 : We use the new namespace usage (describe here).
{| class="wikitable"
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.
|+ ZIM format versions
|-
! Major !! Minor !! Backward compatible !! Description
!libzim version
|-
| colspan="2" | 0 || no || ''This version features have not been tracked properly''
|Unknown
|-
| colspan="2" | 1 || no || ''This version features have not been tracked properly''
|Unknown
|-
| colspan="2" | 2 || no || ''This version features have not been tracked properly''
|Unknown
|-
| colspan="2" | 3 || no || ''This version features have not been tracked properly''
|Unknown
|-
| colspan="2" | 4 || no ||''This version features have not been tracked properly''
|Unknown
|-
| colspan="2" | 5 || yes || Introduces:
- Path index (was only title indexed before)
 
- MimeList Pos
|From date 2009-11-29
Until 6.3.1 (included)
|-
|5
|0
|yes
|Introduces:
- Notion of Major and Minor version
|From 3.2.0
|-
| rowspan="3" |6|| 0 || no  || Introduces extended clusters
Still uses [[ZIM file format old namespace|"old" namespaces]]
|From 3.2.0
|-
|                    1 || yes  || Introduces [[#Namespaces|"new" namespaces]] scheme
|From 7.0.0
|-
|                    2 || yes || Explicitly allows alias entries (several entries pointing to the same cluster/blob)
|From 9.1.0
|}


== MIME Type List (mimeListPos) ==
== MIME Type List (mimeListPos) ==
Line 70: Line 113:
|}
|}


== URL Pointer List (urlPtrPos) ==
== Path Pointer List (pathPtrPos) ==
The URL pointer list is a list of 8 byte offsets to the directory entries.
The Path pointer list is a list of 8 byte offsets to the directory entries.


The directory entries are always ordered by "full" URL (<code><namespace><path></code>). Ordering is simply done by comparing the URL strings.
The directory entries are always ordered by "full" path (<code><namespace><path></code>). Ordering is simply done by comparing the path strings (utf8 encoded)


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 80: Line 123:
! Field Name !! Type !!Offset!!Length!! Description                 
! Field Name !! Type !!Offset!!Length!! Description                 
|-
|-
| <1st URL> || integer || 0 || 8 || pointer to the directory entry of <1st URL>                       
| <1st path> || integer || 0 || 8 || pointer to the directory entry of <1st path>                       
|-
|-
| <2nd URL> || integer || 8 || 8 || pointer to the directory entry of <2nd URL>                       
| <2nd path> || integer || 8 || 8 || pointer to the directory entry of <2nd path>                       
|-
|-
| <nth URL> || integer ||(n-1)*8|| 8 || pointer to the directory entry of <nth URL>                 
| <nth path> || integer ||(n-1)*8|| 8 || pointer to the directory entry of <nth path>                 
|-
|-
| ... || integer || ... || 8 || ...                           
| ... || integer || ... || 8 || ...                           
|}
|}


Zimlib caches directory entries and references the cached entries via the URL pointers.
Libzim caches directory entries and references the cached entries via the path pointers.


== Title Pointer List (titlePtrPos) ==
== Title Pointer List (titlePtrPos) ==
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.
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 path pointer list.


Note that the title pointers are only 4 bytes. They are not offsets in the file but entry numbers.
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.
To get the offset of an entry from the title pointer list, you have to look it up in the path 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}}
! Field Name !! Type !!Offset!!Length!! Description                 
! Field Name !! Type !!Offset!!Length!! Description                 
|-
|-
| <1st Title> || integer || 0 || 4 || pointer to the URL pointer of <1st Title>                     
| <1st Title> || integer || 0 || 4 || pointer to the path pointer of <1st Title>                     
|-
|-
| <2nd Title> || integer || 4 || 4 || pointer to the URL pointer of <2nd Title>                     
| <2nd Title> || integer || 4 || 4 || pointer to the path pointer of <2nd Title>                     
|-
|-
| <nth Title> || integer ||(n-1)*4|| 4 || pointer to the URL pointer of <nth Title>               
| <nth Title> || integer ||(n-1)*4|| 4 || pointer to the path pointer of <nth Title>               
|-
|-
| ... || integer || ... || 4 || ...                           
| ... || integer || ... || 4 || ...                           
|}
|}


The indirection from titles via URLs to directory entries has two reasons:
The indirection from titles via Paths to directory entries has two reasons:
* the pointer list is only half in size as 4 bytes are enough for each entry
* 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.
* accessing directory entries by title also makes use of cached directory entries which are referenced by the path pointers, as implemented in libzim.


== Directory Entries ==
== Directory Entries ==
Line 135: Line 178:
| blob number || integer || 12 || 4 || blob number inside the compressed cluster where the contents are stored                   
| blob number || integer || 12 || 4 || blob number inside the compressed cluster where the contents are stored                   
|-
|-
| url || string || 16 ||zero terminated|| string with the URL as refered in the URL pointer list                         
| path || string || 16 ||zero terminated|| string with the path as referred in the path 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                       
| title || string || n/a ||zero terminated|| string with an title as referred in the Title pointer list or empty; in case it is empty, the path is used as title                       
|-
|-
| parameter || data || ||see parameter len|| (not used) extra parameters                         
| parameter || data || ||see parameter len|| (not used) extra parameters                         
Line 156: Line 199:
| 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                 
|-
|-
| url || string || 12 ||zero terminated|| string with the URL as refered in the URL pointer list                           
| path || string || 12 ||zero terminated|| string with the path as referred in the path 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                       
| title || string || n/a ||zero terminated|| string with an title as referred in the Title pointer list or empty; in case it is empty, the path is used as title                       
|-
|-
| parameter || data || ||see parameter len|| (not used) extra parameters                         
| parameter || data || ||see parameter len|| (not used) extra parameters                         
|}
|}
None of the strings should have control characters from U+0000 through U+001F.


=== Linktarget or deleted Entry (DEPRECATED) ===
=== Linktarget or deleted Entry (DEPRECATED) ===
Line 199: Line 244:
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].
The libzim 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 231: Line 276:


== Namespaces ==
== Namespaces ==
Namespaces separate different types of directory entries - which might have the same title or url - stored in the ZIM archive Format.
Namespaces separate different types of directory entries - which might have the same title or path - 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 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.
Line 251: Line 296:
|}
|}


== URLs ==
== Paths ==
 
=== Path Encoding in the ZIM ===
The Path in the PathPointerlist are encoded in utf-8 and are '''not''' url encoded.
 
For instance, if you store in the ZIM an HTML document with a href pointing to `characters%20%C3%A9ncoding.html`, you have to store the corresponding ZIM entry at `characters éncoding.html` Path.


=== URL Encoding ===
Or if you want to store a ZIM entry at `index.html?param=value`, the HTML document pointing to it will have to use the `index.html%3Fparam%3Dvalue` href.
The URLs in the UrlPointerlist are utf-8 and are not url encoded (https://www.ietf.org/rfc/rfc1738.txt)


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.
The reason behind it is that libzim is agnostic of which kind of content and which kind of readers will be used. Everything around URL encoding is purely linked to HTTP / HTML / Web standards.
 
When serving web content (which is usually the case), some readers process the requests and already do the url decoding internally, whereas most readers will handle the paths directly.
 
The same applies to querystring which might be absorbed by some webservers and not passed to the libzim.
 
In any case, the reader will have to do the HTTP URL decoding before passing the parameter to libzim.


=== Local Anchors ===
=== Local Anchors ===
Many articles - especially when a table of contents is used - use local anchors to jump within an article.   
Many HTML href - especially when a table of contents is used - use local anchors to jump within a document.   


<pre>
<pre>
Line 265: Line 320:
</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 "foo". After the article has been loaded the browser will then search for the local anchor tag and jump to the right location.
When a web browser is used a reader, it handles these local anchors locally client-side. This is never sent to the webserver, and even less to libzim. The browser will determine by itself if another ZIM entry has to be loaded (local anchor inside another document than the currently shown) and will send a request only with the document URL without the local anchor - in our example "foo". After the document 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.


Should you render the article contents by yourself you have to consider this and take care of it before you hand requests to zimlib.
Should you render the article contents by yourself you have to consider this and take care of it before you hand-out requests to libzim.


== Encodings ==
== Encodings ==
=== Character Encoding ===
=== Character Encoding ===
The standard encoding for ZIM archive 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 Path should be handled accordingly.


=== Integer Encoding ===
=== Integer Encoding ===
Line 284: Line 339:
== Split ZIM files ==
== Split ZIM files ==
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''...
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''...
== Other ==
<code>pathPtrPos</code> was initially called <code>urlPtrPos</code> (and <code>path</code> in dirent structure was called <code>url</code>). In April 2024, we changed the wording from <code>url</code> to <code>path</code> as it better conveys the fact that we are storing a path for the entry and not a full url (with scheme, host...). Note that this is just a wording change and the semantic has not changed at all. Implementations do not need to change anything (except a potential renaming of variables to better follow the spec).


== See also ==
== See also ==
4

edits

Navigation menu