Skip to content

Commit 0db0295

Browse files
authored
Merge pull request #997 from openzim/illustration_api_enhancement
Illustration API enhancement
2 parents abf8d4a + c84e2f4 commit 0db0295

File tree

11 files changed

+347
-30
lines changed

11 files changed

+347
-30
lines changed

include/zim/archive.h

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#include "zim.h"
2626
#include "entry.h"
27+
#include "illustration.h"
2728
#include "uuid.h"
2829

2930
#include <string>
@@ -101,6 +102,7 @@ namespace zim
101102
public:
102103
template<EntryOrder order> class EntryRange;
103104
template<EntryOrder order> class iterator;
105+
typedef std::vector<IllustrationInfo> IllustrationInfos;
104106

105107
/** Archive constructor.
106108
*
@@ -338,6 +340,18 @@ namespace zim
338340
*/
339341
std::vector<std::string> getMetadataKeys() const;
340342

343+
/** Get the illustration item of the archive with specified parameters.
344+
*
345+
* Illustration is an icon for the archive that can be used in catalog
346+
* and so to illustrate the archive.
347+
*
348+
* @param ii parameters of the requested illustration.
349+
* @return The illustration item.
350+
* @exception EntryNotFound If no illustration item with specified
351+
* parameters can be found.
352+
*/
353+
Item getIllustrationItem(const IllustrationInfo& ii) const;
354+
341355
/** Get the illustration item of the archive.
342356
*
343357
* Illustration is a icon for the archive that can be used in catalog and so to illustrate the archive.
@@ -348,15 +362,34 @@ namespace zim
348362
*/
349363
Item getIllustrationItem(unsigned int size=48) const;
350364

351-
/** Return a list of available sizes (width) for the illustations in the archive.
365+
/** Return a list of available sizes (width) for the illustrations in the archive.
352366
*
353367
* Illustration is an icon for the archive that can be used in catalog and elsewehere to illustrate the archive.
354368
* An Archive may contains several illustrations with different size.
355369
* This method allows to know which illustration are in the archive (by size: width)
356370
*
357371
* @return A set of size.
358372
*/
359-
std::set<unsigned int> getIllustrationSizes() const;
373+
DEPRECATED std::set<unsigned int> getIllustrationSizes() const;
374+
375+
376+
/** Return the list of available illustrations in the archive.
377+
*
378+
* @return A vector of IllustrationInfo data.
379+
*/
380+
IllustrationInfos getIllustrationInfos() const;
381+
382+
/** Return the list of illustrations with the specified dimensions.
383+
*
384+
* @param w width in CSS pixels
385+
* @param h height in CSS pixels
386+
* @param minScale lower limit on the devicePixelRatio value of the
387+
* targeted display media. The result will include
388+
* any entries with the scale value equal to or greater
389+
* than this filter value.
390+
* @return A vector of IllustrationInfo data.
391+
*/
392+
IllustrationInfos getIllustrationInfos(uint32_t w, uint32_t h, float minScale) const;
360393

361394

362395
/** Get an entry using its "path" index.

include/zim/illustration.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright (C) 2025 Veloman Yunkan
3+
*
4+
* This program is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU General Public License as
6+
* published by the Free Software Foundation; either version 2 of the
7+
* License, or (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful, but
10+
* is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
11+
* warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
12+
* NON-INFRINGEMENT. See the GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
*
18+
*/
19+
20+
#ifndef ZIM_ILLUSTRATION_H
21+
#define ZIM_ILLUSTRATION_H
22+
23+
#include "zim/zim.h"
24+
25+
#include <map>
26+
27+
namespace zim
28+
{
29+
30+
class LIBZIM_API Attributes : public std::map<std::string, std::string>
31+
{
32+
typedef std::map<std::string, std::string> ImplType;
33+
34+
public:
35+
Attributes() {}
36+
Attributes(const ImplType& x) : ImplType(x) {}
37+
38+
Attributes(const std::initializer_list<ImplType::value_type>& x)
39+
: ImplType(x)
40+
{}
41+
42+
static Attributes parse(const std::string& s);
43+
};
44+
45+
/**
46+
* Information about illustrations stored as metadata in the ZIM archive
47+
*/
48+
struct LIBZIM_API IllustrationInfo
49+
{
50+
uint32_t width; // in CSS pixels
51+
uint32_t height; // in CSS pixels
52+
float scale; // devicePixelRatio value of the targeted display media
53+
Attributes extraAttributes; // additional attributes of the illustration
54+
55+
std::string asMetadataItemName() const;
56+
static IllustrationInfo fromMetadataItemName(const std::string& s);
57+
};
58+
59+
inline bool operator==(const IllustrationInfo& a, const IllustrationInfo& b) {
60+
return a.width == b.width
61+
&& a.height == b.height
62+
&& a.scale == b.scale
63+
&& a.extraAttributes == b.extraAttributes;
64+
}
65+
66+
} // namespace zim
67+
68+
#endif // ZIM_ILLUSTRATION_H

include/zim/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ install_headers(
55
'archive.h',
66
'blob.h',
77
'error.h',
8+
'illustration.h',
89
'item.h',
910
'entry.h',
1011
'uuid.h',

include/zim/writer/creator.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#include <memory>
2626
#include <zim/zim.h>
27+
#include <zim/illustration.h>
2728
#include <zim/writer/item.h>
2829

2930
namespace zim
@@ -161,6 +162,24 @@ namespace zim
161162
*/
162163
void addMetadata(const std::string& name, std::unique_ptr<ContentProvider> provider, const std::string& mimetype = "text/plain;charset=utf-8");
163164

165+
/**
166+
* Add illustration to the archive.
167+
*
168+
* @param ii parameters of the illustration.
169+
* @param content the content of the illustration (must be a png
170+
* content)
171+
*/
172+
void addIllustration(const IllustrationInfo& ii, const std::string& content);
173+
174+
/**
175+
* Add illustration to the archive.
176+
*
177+
* @param ii parameters of the illustration.
178+
* @param provider the provider of the content of the illustration
179+
* (must be a png content)
180+
*/
181+
void addIllustration(const IllustrationInfo& ii, std::unique_ptr<ContentProvider> provider);
182+
164183
/**
165184
* Add illustration to the archive.
166185
*

include/zim/zim.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
#elif defined(_MSC_VER)
3131
#define DEPRECATED __declspec(deprecated)
3232
#else
33-
#praga message("WARNING: You need to implement DEPRECATED for this compiler")
33+
#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
3434
#define DEPRECATED
3535
#endif
3636

src/archive.cpp

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -181,24 +181,19 @@ namespace zim
181181
}
182182

183183
Item Archive::getIllustrationItem(unsigned int size) const {
184-
auto r = m_impl->findx('M', Formatter() << "Illustration_" << size << "x"
185-
<< size << "@" << 1);
184+
// XXX: A square illustration of the requested size may exist with
185+
// XXX: non-empty attributes or at a scale different from 1. Shouldn't
186+
// XXX: we test for it too, if the {size}x{size}@1 one is not available?
187+
return getIllustrationItem(IllustrationInfo{size, size, 1.0, {}});
188+
}
189+
190+
Item Archive::getIllustrationItem(const IllustrationInfo& ii) const {
191+
auto r = m_impl->findx('M', ii.asMetadataItemName());
186192
if (r.first) {
187193
return getEntryByPath(entry_index_type(r.second)).getItem();
188194
}
189-
// We haven't found the exact entry. Let's "search" for a illustration and
190-
// use the first one we found.
191-
#if 0
192-
// We have decided to not implement fallback in case of wrong resolution for now.
193-
// We keep this code for reference.
194-
r = m_impl->findx('M', "Illustration");
195-
auto entry = getEntryByPath(entry_index_type(r.second));
196-
if (entry.getPath().find("Illustration") == 0) {
197-
return entry.getItem();
198-
}
199-
#endif
200195
// For 48x48 illustration, return favicon for older zims.
201-
if (size == 48) {
196+
if ( ii == IllustrationInfo{48, 48, 1.0, {}} ) {
202197
auto r = findFavicon(*m_impl);
203198
return getEntryByPath(entry_index_type(r.second)).getItem(true);
204199
}
@@ -233,6 +228,45 @@ namespace zim
233228
return ret;
234229
}
235230

231+
Archive::IllustrationInfos Archive::getIllustrationInfos() const {
232+
IllustrationInfos r;
233+
for(auto e = m_impl->findx('M', "Illustration_").second; ; ++e ) {
234+
try {
235+
const auto path = getEntryByPath(entry_index_type(e)).getPath();
236+
if (path.find("Illustration_") != 0) {
237+
break;
238+
}
239+
try {
240+
r.push_back(zim::IllustrationInfo::fromMetadataItemName(path));
241+
} catch (...) {}
242+
} catch (const std::out_of_range& e) {
243+
break;
244+
}
245+
}
246+
const IllustrationInfo faviconLikeIllustration{48, 48, 1.0, {}};
247+
if (std::find(r.begin(), r.end(), faviconLikeIllustration) == r.end()) {
248+
try {
249+
// raise a exception if we cannot find the (old format) favicon.
250+
findFavicon(*m_impl);
251+
r.push_back(faviconLikeIllustration);
252+
} catch(EntryNotFound&) {}
253+
}
254+
return r;
255+
}
256+
257+
Archive::IllustrationInfos Archive::getIllustrationInfos(uint32_t w,
258+
uint32_t h,
259+
float minScale) const
260+
{
261+
IllustrationInfos r;
262+
for ( const auto& ii : getIllustrationInfos() ) {
263+
if ( ii.width == w && ii.height == h && ii.scale >= minScale ) {
264+
r.push_back(ii);
265+
}
266+
}
267+
return r;
268+
}
269+
236270
bool Archive::hasIllustration(unsigned int size) const {
237271
try {
238272
getIllustrationItem(size);

src/tools.cpp

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include "tools.h"
2323
#include "zim/tools.h"
24+
#include "zim/illustration.h"
2425
#include "fs.h"
2526
#include "writer/_dirent.h"
2627

@@ -110,13 +111,64 @@ unsigned int zim::parseIllustrationPathToSize(const std::string& s)
110111
{
111112
int nw(0), nh(0), nEnd(0);
112113
long int w(-1), h(-1);
113-
if ( sscanf(s.c_str(), "Illustration_%n%ldx%n%ld@1%n)", &nw, &w, &nh, &h, &nEnd) == 2
114+
if ( sscanf(s.c_str(), "Illustration_%n%ldx%n%ld@1%n", &nw, &w, &nh, &h, &nEnd) == 2
114115
&& (size_t)nEnd == s.size() && !isspace(s[nw]) && !isspace(s[nh]) && w == h && w >= 0) {
115116
return (unsigned int)w;
116117
}
117118
throw std::runtime_error("");
118119
}
119120

121+
std::string zim::IllustrationInfo::asMetadataItemName() const
122+
{
123+
std::ostringstream oss;
124+
oss << "Illustration_" << width << "x" << height << "@" << scale;
125+
for ( const auto& kv : extraAttributes ) {
126+
oss << ";" << kv.first << "=" << kv.second;
127+
}
128+
return oss.str();
129+
}
130+
131+
zim::Attributes zim::Attributes::parse(const std::string& s)
132+
{
133+
zim::Attributes a;
134+
for (const std::string& nameAndValue : split(s, ";") ) {
135+
const auto i = nameAndValue.find('=');
136+
const std::string name = nameAndValue.substr(0, i);
137+
const std::string value = i == std::string::npos
138+
? ""
139+
: nameAndValue.substr(i+1);
140+
a[name] = value;
141+
}
142+
return a;
143+
}
144+
145+
zim::IllustrationInfo zim::IllustrationInfo::fromMetadataItemName(const std::string& s)
146+
{
147+
int nw(0), nh(0), ns(0), nEnd(0);
148+
long int w(-1), h(-1);
149+
float scale(0);
150+
151+
const char fmt[] = "Illustration_%n%ldx%n%ld@%n%f%n;";
152+
if ( sscanf(s.c_str(), fmt, &nw, &w, &nh, &h, &ns, &scale, &nEnd) != 3
153+
|| isspace(s[nw])
154+
|| isspace(s[nh])
155+
|| isspace(s[ns])
156+
|| w < 0
157+
|| h < 0
158+
|| scale < 0
159+
|| (size_t(nEnd) != s.size() && s[nEnd] != ';') ) {
160+
throw std::runtime_error("Invalid name of illustration metadata item");
161+
}
162+
163+
const auto attrStart = std::min(s.size(), size_t(nEnd) + 1);
164+
const auto attributes = Attributes::parse(s.substr(attrStart));
165+
const auto ii = IllustrationInfo{uint32_t(w), uint32_t(h), scale, attributes};
166+
if ( ii.asMetadataItemName() != s ) {
167+
throw std::runtime_error("Invalid name of illustration metadata item");
168+
}
169+
return ii;
170+
}
171+
120172
uint32_t zim::randomNumber(uint32_t max)
121173
{
122174
static std::default_random_engine random(

src/writer/creator.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,18 +182,27 @@ namespace zim
182182
data->handle(dirent);
183183
}
184184

185-
void Creator::addIllustration(unsigned int size, const std::string& content)
185+
void Creator::addIllustration(const IllustrationInfo& ii, const std::string& content)
186186
{
187187
checkError();
188188
auto provider = std::unique_ptr<ContentProvider>(new StringProvider(content));
189-
addIllustration(size, std::move(provider));
189+
addIllustration(ii, std::move(provider));
190190
}
191191

192-
void Creator::addIllustration(unsigned int size, std::unique_ptr<ContentProvider> provider)
192+
void Creator::addIllustration(const IllustrationInfo& ii, std::unique_ptr<ContentProvider> provider)
193193
{
194194
checkError();
195-
addMetadata(Formatter() << "Illustration_" << size << "x" << size << "@1",
196-
std::move(provider), "image/png");
195+
addMetadata(ii.asMetadataItemName(), std::move(provider), "image/png");
196+
}
197+
198+
void Creator::addIllustration(unsigned int size, const std::string& content)
199+
{
200+
addIllustration(IllustrationInfo{size, size, 1, {}}, content);
201+
}
202+
203+
void Creator::addIllustration(unsigned int size, std::unique_ptr<ContentProvider> provider)
204+
{
205+
addIllustration(IllustrationInfo{size, size, 1, {}}, std::move(provider));
197206
}
198207

199208
void Creator::addRedirection(const std::string& path, const std::string& title, const std::string& targetPath, const Hints& hints)

0 commit comments

Comments
 (0)