libmobi 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # libmobi.rb
2
+
3
+ This is a ruby binding for library for handling Mobipocket/Kindle (MOBI) ebook format documents: https://github.com/bfabiszewski/libmobi.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'libmobi'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install libmobi
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Development
26
+
27
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run
28
+ the tests. You can also run `bin/console` for an interactive prompt that will allow you to
29
+ experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new
32
+ version, update the version number in `version.rb`, and then run `bundle exec rake release`, which
33
+ will create a git tag for the version, push git commits and tags, and push the `.gem` file to
34
+ [rubygems.org](https://rubygems.org).
35
+
36
+ ## Contributing
37
+
38
+ Bug reports and pull requests are welcome on GitHub at https://github.com/avsej/libmobi.rb.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ # libmobi - library for handling Kindle (MOBI) formats of ebook documents
2
+ # Copyright (C) 2018 Sergey Avseyev <sergey.avseyev@gmail.com>
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # 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, see <https://www.gnu.org/licenses/>.
16
+
17
+ require 'bundler/gem_tasks'
18
+ require 'rake/testtask'
19
+
20
+ module Bundler
21
+ class GemHelper
22
+
23
+ def tag_version
24
+ sh "git tag -s -m \"Version #{version}\" #{version_tag}"
25
+ Bundler.ui.confirm "Tagged #{version_tag}."
26
+ yield if block_given?
27
+ rescue
28
+ Bundler.ui.error "Untagging #{version_tag} due to error."
29
+ sh_with_code "git tag -d #{version_tag}"
30
+ raise
31
+ end
32
+ end
33
+ end
34
+
35
+ Rake::TestTask.new(:test) do |t|
36
+ t.libs << 'test'
37
+ t.libs << 'lib'
38
+ t.test_files = FileList['test/**/*_test.rb']
39
+ end
40
+
41
+ require "rake/extensiontask"
42
+
43
+ def gemspec
44
+ @clean_gemspec ||= eval(File.read(File.expand_path('libmobi.gemspec', File.dirname(__FILE__))))
45
+ end
46
+
47
+ Rake::ExtensionTask.new("mobi_ext", gemspec) do |ext|
48
+ ext.ext_dir = "ext/mobi_ext"
49
+ CLEAN.include "#{ext.lib_dir}/*.#{RbConfig::CONFIG['DLEXT']}"
50
+ end
51
+ Rake::Task['test'].prerequisites.unshift('compile')
52
+
53
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # libmobi - library for handling Kindle (MOBI) formats of ebook documents
4
+ # Copyright (C) 2018 Sergey Avseyev <sergey.avseyev@gmail.com>
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
18
+
19
+ require 'bundler/setup'
20
+ require 'libmobi'
21
+
22
+ require 'irb'
23
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # libmobi - library for handling Kindle (MOBI) formats of ebook documents
4
+ # Copyright (C) 2018 Sergey Avseyev <sergey.avseyev@gmail.com>
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
18
+
19
+ set -euo pipefail
20
+ IFS=$'\n\t'
21
+ set -vx
22
+
23
+ bundle install
@@ -0,0 +1,33 @@
1
+ # libmobi - library for handling Kindle (MOBI) formats of ebook documents
2
+ # Copyright (C) 2018 Sergey Avseyev <sergey.avseyev@gmail.com>
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # 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, see <https://www.gnu.org/licenses/>.
16
+
17
+ require 'rbconfig'
18
+ require 'mkmf'
19
+
20
+ def define(macro, value = nil)
21
+ $defs.push("-D #{[macro.upcase, Shellwords.shellescape(value)].compact.join('=')}")
22
+ end
23
+
24
+ pkg_config('libmobi') || abort('libmobi headers not found. (dnf install libmobi-devel on Fedora)')
25
+
26
+ $CFLAGS << ' -pedantic -Wall -Wextra -Werror '
27
+ if ENV['DEBUG_BUILD']
28
+ $CFLAGS.gsub!(/\W-Wp,-D_FORTIFY_SOURCE=\d+\W/, ' ')
29
+ $CFLAGS << ' -ggdb3 -O0 '
30
+ end
31
+
32
+ create_header('mobi_config.h')
33
+ create_makefile('mobi_ext')
@@ -0,0 +1,909 @@
1
+ /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+
3
+ #include <mobi.h>
4
+
5
+ #include <ruby.h>
6
+
7
+ VALUE mb_mMOBI;
8
+ VALUE mb_eError;
9
+ VALUE mb_cBook;
10
+
11
+ static const char *mb_libmobi_strerror(MOBI_RET code)
12
+ {
13
+ switch (code) {
14
+ case MOBI_ERROR:
15
+ return "generic error return value";
16
+ case MOBI_PARAM_ERR:
17
+ return "wrong function parameter";
18
+ case MOBI_DATA_CORRUPT:
19
+ return "corrupted data";
20
+ case MOBI_FILE_NOT_FOUND:
21
+ return "file not found";
22
+ case MOBI_FILE_ENCRYPTED:
23
+ return "unsupported encrypted data";
24
+ case MOBI_FILE_UNSUPPORTED:
25
+ return "unsupported document type";
26
+ case MOBI_MALLOC_FAILED:
27
+ return "memory allocation error";
28
+ case MOBI_INIT_FAILED:
29
+ return "initialization error";
30
+ case MOBI_BUFFER_END:
31
+ return "out of buffer error";
32
+ case MOBI_XML_ERR:
33
+ return "XMLwriter error";
34
+ case MOBI_DRM_PIDINV:
35
+ return "invalid DRM PID";
36
+ case MOBI_DRM_KEYNOTFOUND:
37
+ return "key not found";
38
+ case MOBI_DRM_UNSUPPORTED:
39
+ return "DRM support not included";
40
+ case MOBI_WRITE_FAILED:
41
+ return "writing to file failed";
42
+ default:
43
+ return "FIXME: unkwnown code";
44
+ }
45
+ }
46
+
47
+ static void mb_raise_at(MOBI_RET code, const char *message, const char *file, int line)
48
+ {
49
+ VALUE exc, str;
50
+
51
+ str = rb_str_new_cstr(message);
52
+ if (code > 0) {
53
+ rb_str_catf(str, ": (0x%02x) \"%s\"", (int)code, mb_libmobi_strerror(code));
54
+ }
55
+ rb_str_buf_cat_ascii(str, " at ");
56
+ rb_str_buf_cat_ascii(str, file);
57
+ rb_str_catf(str, ":%d", line);
58
+ exc = rb_exc_new3(mb_eError, str);
59
+ rb_ivar_set(exc, rb_intern("@code"), INT2FIX(code));
60
+ rb_exc_raise(exc);
61
+ }
62
+
63
+ #define mb_raise(code, message) mb_raise_at(code, message, __FILE__, __LINE__)
64
+ #define mb_raise_msg(message) mb_raise_at(0, message, __FILE__, __LINE__)
65
+
66
+ typedef struct mb_BOOK {
67
+ MOBIData *data;
68
+ } mb_BOOK;
69
+
70
+ static void mb_book_mark(void *ptr)
71
+ {
72
+ mb_BOOK *book = ptr;
73
+ (void)book;
74
+ }
75
+
76
+ static void mb_book_free(void *ptr)
77
+ {
78
+ mb_BOOK *book = ptr;
79
+ if (book) {
80
+ if (book->data) {
81
+ mobi_free(book->data);
82
+ }
83
+ book->data = NULL;
84
+ }
85
+ }
86
+
87
+ static VALUE mb_book_alloc(VALUE klass)
88
+ {
89
+ VALUE obj;
90
+ mb_BOOK *book;
91
+
92
+ obj = Data_Make_Struct(klass, mb_BOOK, mb_book_mark, mb_book_free, book);
93
+ return obj;
94
+ }
95
+
96
+ static VALUE mb_book_init(VALUE self, VALUE path)
97
+ {
98
+ mb_BOOK *book = DATA_PTR(self);
99
+ MOBI_RET rc;
100
+
101
+ Check_Type(path, T_STRING);
102
+
103
+ book->data = mobi_init();
104
+ if (book->data == NULL) {
105
+ mb_raise(MOBI_MALLOC_FAILED, "unable to allocate MOBIData struct");
106
+ }
107
+
108
+ rc = mobi_load_filename(book->data, RSTRING_PTR(path));
109
+ if (rc != MOBI_SUCCESS) {
110
+ mb_raise(rc, "unable to load book from path");
111
+ }
112
+ return self;
113
+ }
114
+
115
+ static VALUE mb_book_next(VALUE self)
116
+ {
117
+ mb_BOOK *book = DATA_PTR(self);
118
+
119
+ if (book == NULL || book->data == NULL) {
120
+ mb_raise(MOBI_INIT_FAILED, "the MOBI data is not loaded");
121
+ }
122
+ if (mobi_is_hybrid(book->data) && book->data->next != NULL) {
123
+ VALUE obj;
124
+ mb_BOOK *next;
125
+
126
+ obj = Data_Make_Struct(mb_cBook, mb_BOOK, mb_book_mark, mb_book_free, next);
127
+ next->data = book->data->next;
128
+ return obj;
129
+ }
130
+
131
+ return Qnil;
132
+ }
133
+
134
+ #define COPY_HEADER_INT(NAME) \
135
+ if (hdr->NAME) { \
136
+ rb_hash_aset(res, ID2SYM(rb_intern(#NAME)), INT2FIX(*hdr->NAME)); \
137
+ }
138
+ static VALUE mb_book_mobi_header(VALUE self)
139
+ {
140
+ mb_BOOK *book = DATA_PTR(self);
141
+ VALUE res;
142
+ MOBIMobiHeader *hdr;
143
+
144
+ if (book == NULL || book->data == NULL) {
145
+ mb_raise(MOBI_INIT_FAILED, "the MOBI data is not loaded");
146
+ }
147
+ hdr = book->data->mh;
148
+ if (hdr == NULL) {
149
+ return Qnil;
150
+ }
151
+
152
+ res = rb_hash_new();
153
+ rb_hash_aset(res, ID2SYM(rb_intern("magic")), rb_str_new_cstr(hdr->mobi_magic));
154
+ COPY_HEADER_INT(header_length);
155
+ COPY_HEADER_INT(mobi_type);
156
+ if (hdr->text_encoding) {
157
+ rb_hash_aset(res, ID2SYM(rb_intern("text_encoding")), INT2FIX(*hdr->text_encoding));
158
+ switch (*hdr->text_encoding) {
159
+ case MOBI_CP1252:
160
+ rb_hash_aset(res, ID2SYM(rb_intern("text_encoding_sym")), ID2SYM(rb_intern("cp1252")));
161
+ break;
162
+ case MOBI_UTF8:
163
+ rb_hash_aset(res, ID2SYM(rb_intern("text_encoding_sym")), ID2SYM(rb_intern("utf8")));
164
+ break;
165
+ case MOBI_UTF16:
166
+ rb_hash_aset(res, ID2SYM(rb_intern("text_encoding_sym")), ID2SYM(rb_intern("utf16")));
167
+ break;
168
+ default:
169
+ rb_hash_aset(res, ID2SYM(rb_intern("text_encoding_sym")), ID2SYM(rb_intern("unknown")));
170
+ break;
171
+ }
172
+ }
173
+ if (hdr->locale) {
174
+ const char *locale_string = mobi_get_locale_string(*hdr->locale);
175
+ if (locale_string) {
176
+ rb_hash_aset(res, ID2SYM(rb_intern("locale_str")), rb_str_new_cstr(locale_string));
177
+ }
178
+ rb_hash_aset(res, ID2SYM(rb_intern("locale")), INT2FIX(*hdr->locale));
179
+ }
180
+ if (hdr->dict_input_lang) {
181
+ const char *locale_string = mobi_get_locale_string(*hdr->dict_input_lang);
182
+ if (locale_string) {
183
+ rb_hash_aset(res, ID2SYM(rb_intern("dict_input_lang_str")), rb_str_new_cstr(locale_string));
184
+ }
185
+ rb_hash_aset(res, ID2SYM(rb_intern("dict_input_lang")), INT2FIX(*hdr->dict_input_lang));
186
+ }
187
+ if (hdr->dict_output_lang) {
188
+ const char *locale_string = mobi_get_locale_string(*hdr->dict_output_lang);
189
+ if (locale_string) {
190
+ rb_hash_aset(res, ID2SYM(rb_intern("dict_output_lang_str")), rb_str_new_cstr(locale_string));
191
+ }
192
+ rb_hash_aset(res, ID2SYM(rb_intern("dict_output_lang")), INT2FIX(*hdr->dict_output_lang));
193
+ }
194
+ COPY_HEADER_INT(uid);
195
+ COPY_HEADER_INT(version);
196
+ COPY_HEADER_INT(orth_index);
197
+ COPY_HEADER_INT(infl_index);
198
+ COPY_HEADER_INT(names_index);
199
+ COPY_HEADER_INT(keys_index);
200
+ COPY_HEADER_INT(keys_index);
201
+ COPY_HEADER_INT(extra0_index);
202
+ COPY_HEADER_INT(extra1_index);
203
+ COPY_HEADER_INT(extra2_index);
204
+ COPY_HEADER_INT(extra3_index);
205
+ COPY_HEADER_INT(extra4_index);
206
+ COPY_HEADER_INT(extra5_index);
207
+ COPY_HEADER_INT(non_text_index);
208
+ COPY_HEADER_INT(full_name_offset);
209
+ COPY_HEADER_INT(full_name_length);
210
+ COPY_HEADER_INT(min_version);
211
+ COPY_HEADER_INT(image_index);
212
+ COPY_HEADER_INT(huff_rec_index);
213
+ COPY_HEADER_INT(huff_rec_count);
214
+ COPY_HEADER_INT(datp_rec_index);
215
+ COPY_HEADER_INT(datp_rec_count);
216
+ COPY_HEADER_INT(exth_flags);
217
+ COPY_HEADER_INT(unknown6);
218
+ COPY_HEADER_INT(drm_offset);
219
+ COPY_HEADER_INT(drm_count);
220
+ COPY_HEADER_INT(drm_size);
221
+ COPY_HEADER_INT(drm_flags);
222
+ COPY_HEADER_INT(first_text_index);
223
+ COPY_HEADER_INT(last_text_index);
224
+ COPY_HEADER_INT(fdst_index);
225
+ COPY_HEADER_INT(fdst_section_count);
226
+ COPY_HEADER_INT(fcis_index);
227
+ COPY_HEADER_INT(fcis_count);
228
+ COPY_HEADER_INT(flis_index);
229
+ COPY_HEADER_INT(flis_count);
230
+ COPY_HEADER_INT(unknown10);
231
+ COPY_HEADER_INT(unknown11);
232
+ COPY_HEADER_INT(srcs_index);
233
+ COPY_HEADER_INT(srcs_count);
234
+ COPY_HEADER_INT(unknown12);
235
+ COPY_HEADER_INT(unknown13);
236
+ COPY_HEADER_INT(extra_flags);
237
+ COPY_HEADER_INT(ncx_index);
238
+ COPY_HEADER_INT(unknown14);
239
+ COPY_HEADER_INT(unknown15);
240
+ COPY_HEADER_INT(fragment_index);
241
+ COPY_HEADER_INT(skeleton_index);
242
+ COPY_HEADER_INT(datp_index);
243
+ COPY_HEADER_INT(unknown16);
244
+ COPY_HEADER_INT(guide_index);
245
+ COPY_HEADER_INT(unknown17);
246
+ COPY_HEADER_INT(unknown18);
247
+ COPY_HEADER_INT(unknown19);
248
+ COPY_HEADER_INT(unknown20);
249
+
250
+ return res;
251
+ }
252
+ #undef COPY_HEADER_INT
253
+
254
+ static VALUE mb_book_pdb_header(VALUE self)
255
+ {
256
+ mb_BOOK *book = DATA_PTR(self);
257
+ VALUE res;
258
+ MOBIPdbHeader *hdr;
259
+
260
+ if (book == NULL || book->data == NULL) {
261
+ mb_raise(MOBI_INIT_FAILED, "the MOBI data is not loaded");
262
+ }
263
+ hdr = book->data->ph;
264
+ if (hdr == NULL) {
265
+ return Qnil;
266
+ }
267
+ res = rb_hash_new();
268
+ rb_hash_aset(res, ID2SYM(rb_intern("name")), rb_str_new_cstr(hdr->name));
269
+ rb_hash_aset(res, ID2SYM(rb_intern("attributes")), INT2FIX(hdr->attributes));
270
+ rb_hash_aset(res, ID2SYM(rb_intern("version")), INT2FIX(hdr->version));
271
+ rb_hash_aset(res, ID2SYM(rb_intern("ctime")), INT2FIX(hdr->ctime));
272
+ if (hdr->ctime) {
273
+ rb_hash_aset(res, ID2SYM(rb_intern("ctime_time")), rb_time_new(mktime(mobi_pdbtime_to_time(hdr->ctime)), 0));
274
+ }
275
+ rb_hash_aset(res, ID2SYM(rb_intern("mtime")), INT2FIX(hdr->mtime));
276
+ if (hdr->mtime) {
277
+ rb_hash_aset(res, ID2SYM(rb_intern("mtime_time")), rb_time_new(mktime(mobi_pdbtime_to_time(hdr->mtime)), 0));
278
+ }
279
+ rb_hash_aset(res, ID2SYM(rb_intern("btime")), INT2FIX(hdr->btime));
280
+ if (hdr->btime) {
281
+ rb_hash_aset(res, ID2SYM(rb_intern("btime_time")), rb_time_new(mktime(mobi_pdbtime_to_time(hdr->btime)), 0));
282
+ }
283
+ rb_hash_aset(res, ID2SYM(rb_intern("mod_num")), INT2FIX(hdr->mod_num));
284
+ rb_hash_aset(res, ID2SYM(rb_intern("appinfo_offset")), INT2FIX(hdr->appinfo_offset));
285
+ rb_hash_aset(res, ID2SYM(rb_intern("sortinfo_offset")), INT2FIX(hdr->sortinfo_offset));
286
+ rb_hash_aset(res, ID2SYM(rb_intern("type")), rb_str_new_cstr(hdr->type));
287
+ rb_hash_aset(res, ID2SYM(rb_intern("creator")), rb_str_new_cstr(hdr->creator));
288
+ rb_hash_aset(res, ID2SYM(rb_intern("uid")), INT2FIX(hdr->uid));
289
+ rb_hash_aset(res, ID2SYM(rb_intern("next_rec")), INT2FIX(hdr->next_rec));
290
+ rb_hash_aset(res, ID2SYM(rb_intern("rec_count")), INT2FIX(hdr->rec_count));
291
+ return res;
292
+ }
293
+
294
+ static VALUE mb_book_record0_header(VALUE self)
295
+ {
296
+ mb_BOOK *book = DATA_PTR(self);
297
+ VALUE res;
298
+ MOBIRecord0Header *hdr;
299
+
300
+ if (book == NULL || book->data == NULL) {
301
+ mb_raise(MOBI_INIT_FAILED, "the MOBI data is not loaded");
302
+ }
303
+ hdr = book->data->rh;
304
+ if (hdr == NULL) {
305
+ return Qnil;
306
+ }
307
+
308
+ res = rb_hash_new();
309
+ rb_hash_aset(res, ID2SYM(rb_intern("compression_type")), INT2FIX(hdr->compression_type));
310
+ switch (hdr->compression_type) {
311
+ case 1:
312
+ rb_hash_aset(res, ID2SYM(rb_intern("compression_type_sym")), ID2SYM(rb_intern("none")));
313
+ break;
314
+ case 2:
315
+ rb_hash_aset(res, ID2SYM(rb_intern("compression_type_sym")), ID2SYM(rb_intern("palm_doc")));
316
+ break;
317
+ case 17480:
318
+ rb_hash_aset(res, ID2SYM(rb_intern("compression_type_sym")), ID2SYM(rb_intern("huff_cdic")));
319
+ break;
320
+ }
321
+ rb_hash_aset(res, ID2SYM(rb_intern("text_length")), INT2FIX(hdr->text_length));
322
+ rb_hash_aset(res, ID2SYM(rb_intern("text_record_count")), INT2FIX(hdr->text_record_count));
323
+ rb_hash_aset(res, ID2SYM(rb_intern("text_record_size")), INT2FIX(hdr->text_record_size));
324
+ rb_hash_aset(res, ID2SYM(rb_intern("encryption_type")), INT2FIX(hdr->encryption_type));
325
+ switch (hdr->encryption_type) {
326
+ case 0:
327
+ rb_hash_aset(res, ID2SYM(rb_intern("encryption_type_sym")), ID2SYM(rb_intern("none")));
328
+ break;
329
+ case 1:
330
+ rb_hash_aset(res, ID2SYM(rb_intern("encryption_type_sym")), ID2SYM(rb_intern("old")));
331
+ break;
332
+ case 2:
333
+ rb_hash_aset(res, ID2SYM(rb_intern("encryption_type_sym")), ID2SYM(rb_intern("mobi")));
334
+ break;
335
+ }
336
+ rb_hash_aset(res, ID2SYM(rb_intern("unknown1")), INT2FIX(hdr->unknown1));
337
+ return res;
338
+ }
339
+
340
+ static VALUE mb_book_exth_header(VALUE self)
341
+ {
342
+ mb_BOOK *book = DATA_PTR(self);
343
+ VALUE res;
344
+ MOBIExthHeader *hdr;
345
+
346
+ if (book == NULL || book->data == NULL) {
347
+ mb_raise(MOBI_INIT_FAILED, "the MOBI data is not loaded");
348
+ }
349
+ hdr = book->data->eh;
350
+ if (hdr == NULL) {
351
+ return Qnil;
352
+ }
353
+
354
+ res = rb_ary_new();
355
+ while (hdr != NULL) {
356
+ MOBIExthMeta tag = mobi_get_exthtagmeta_by_tag(hdr->tag);
357
+ uint32_t val32;
358
+ VALUE item = rb_hash_new();
359
+
360
+ rb_hash_aset(item, ID2SYM(rb_intern("code")), INT2FIX(tag.tag));
361
+ switch (hdr->tag) {
362
+ case EXTH_SAMPLE:
363
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("sample")));
364
+ break;
365
+ case EXTH_STARTREADING:
366
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("start_reading")));
367
+ break;
368
+ case EXTH_KF8BOUNDARY:
369
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("kf8_boundary")));
370
+ break;
371
+ case EXTH_COUNTRESOURCES:
372
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("count_resources")));
373
+ break;
374
+ case EXTH_RESCOFFSET:
375
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("resc_offset")));
376
+ break;
377
+ case EXTH_COVEROFFSET:
378
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("cover_offset")));
379
+ break;
380
+ case EXTH_THUMBOFFSET:
381
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("thumb_offset")));
382
+ break;
383
+ case EXTH_HASFAKECOVER:
384
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("has_fake_cover")));
385
+ break;
386
+ case EXTH_CREATORSOFT:
387
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("creator_soft")));
388
+ break;
389
+ case EXTH_CREATORMAJOR:
390
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("creator_major")));
391
+ break;
392
+ case EXTH_CREATORMINOR:
393
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("creator_minor")));
394
+ break;
395
+ case EXTH_CREATORBUILD:
396
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("creator_build")));
397
+ break;
398
+ case EXTH_CLIPPINGLIMIT:
399
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("clipping_limit")));
400
+ break;
401
+ case EXTH_PUBLISHERLIMIT:
402
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("publisher_limit")));
403
+ break;
404
+ case EXTH_TTSDISABLE:
405
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("tts_disabled")));
406
+ break;
407
+ case EXTH_RENTAL:
408
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("rental")));
409
+ break;
410
+ case EXTH_DRMSERVER:
411
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("drm_server")));
412
+ break;
413
+ case EXTH_DRMCOMMERCE:
414
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("drm_commerce")));
415
+ break;
416
+ case EXTH_DRMEBOOKBASE:
417
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("drm_ebookbase")));
418
+ break;
419
+ case EXTH_TITLE:
420
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("title")));
421
+ break;
422
+ case EXTH_AUTHOR:
423
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("creator")));
424
+ break;
425
+ case EXTH_PUBLISHER:
426
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("publisher")));
427
+ break;
428
+ case EXTH_IMPRINT:
429
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("imprint")));
430
+ break;
431
+ case EXTH_DESCRIPTION:
432
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("description")));
433
+ break;
434
+ case EXTH_ISBN:
435
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("isbn")));
436
+ break;
437
+ case EXTH_SUBJECT:
438
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("subject")));
439
+ break;
440
+ case EXTH_PUBLISHINGDATE:
441
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("published")));
442
+ break;
443
+ case EXTH_REVIEW:
444
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("review")));
445
+ break;
446
+ case EXTH_CONTRIBUTOR:
447
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("contributor")));
448
+ break;
449
+ case EXTH_RIGHTS:
450
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("rights")));
451
+ break;
452
+ case EXTH_SUBJECTCODE:
453
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("subject_code")));
454
+ break;
455
+ case EXTH_TYPE:
456
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("type")));
457
+ break;
458
+ case EXTH_SOURCE:
459
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("source")));
460
+ break;
461
+ case EXTH_ASIN:
462
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("asin")));
463
+ break;
464
+ case EXTH_VERSION:
465
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("version")));
466
+ break;
467
+ case EXTH_ADULT:
468
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("adult")));
469
+ break;
470
+ case EXTH_PRICE:
471
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("price")));
472
+ break;
473
+ case EXTH_CURRENCY:
474
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("currency")));
475
+ break;
476
+ case EXTH_FIXEDLAYOUT:
477
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("fixed_layout")));
478
+ break;
479
+ case EXTH_BOOKTYPE:
480
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("book_type")));
481
+ break;
482
+ case EXTH_ORIENTATIONLOCK:
483
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("orientation_lock")));
484
+ break;
485
+ case EXTH_ORIGRESOLUTION:
486
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("orig_resolution")));
487
+ break;
488
+ case EXTH_ZEROGUTTER:
489
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("zero_gutter")));
490
+ break;
491
+ case EXTH_ZEROMARGIN:
492
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("zero_margin")));
493
+ break;
494
+ case EXTH_KF8COVERURI:
495
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("kf8_cover_uri")));
496
+ break;
497
+ case EXTH_REGIONMAGNI:
498
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("region_magnification")));
499
+ break;
500
+ case EXTH_DICTNAME:
501
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("dict_name")));
502
+ break;
503
+ case EXTH_WATERMARK:
504
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("watermark")));
505
+ break;
506
+ case EXTH_DOCTYPE:
507
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("doc_type")));
508
+ break;
509
+ case EXTH_LASTUPDATE:
510
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("last_update")));
511
+ break;
512
+ case EXTH_UPDATEDTITLE:
513
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("updated_title")));
514
+ break;
515
+ case EXTH_ASIN504:
516
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("asin_504")));
517
+ break;
518
+ case EXTH_TITLEFILEAS:
519
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("title_file_as")));
520
+ break;
521
+ case EXTH_CREATORFILEAS:
522
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("creator_file_as")));
523
+ break;
524
+ case EXTH_PUBLISHERFILEAS:
525
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("publisher_file_as")));
526
+ break;
527
+ case EXTH_LANGUAGE:
528
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("language")));
529
+ break;
530
+ case EXTH_ALIGNMENT:
531
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("alignment")));
532
+ break;
533
+ case EXTH_PAGEDIR:
534
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("page_dir")));
535
+ break;
536
+ case EXTH_OVERRIDEFONTS:
537
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("override_fonts")));
538
+ break;
539
+ case EXTH_SORCEDESC:
540
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("source_desc")));
541
+ break;
542
+ case EXTH_DICTLANGIN:
543
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("dict_lang_in")));
544
+ break;
545
+ case EXTH_DICTLANGOUT:
546
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("dict_lang_out")));
547
+ break;
548
+ case EXTH_INPUTSOURCE:
549
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("input_source")));
550
+ break;
551
+ case EXTH_CREATORBUILDREV:
552
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("creator_build_rev")));
553
+ break;
554
+ case EXTH_CREATORSTRING:
555
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("creator_string")));
556
+ break;
557
+ case EXTH_TAMPERKEYS:
558
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("tamper_keys")));
559
+ break;
560
+ case EXTH_FONTSIGNATURE:
561
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("font_signature")));
562
+ break;
563
+ case EXTH_UNK403:
564
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("unknown_403")));
565
+ break;
566
+ case EXTH_UNK405:
567
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("unknown_405")));
568
+ break;
569
+ case EXTH_UNK407:
570
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("unknown_407")));
571
+ break;
572
+ case EXTH_UNK450:
573
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("unknown_450")));
574
+ break;
575
+ case EXTH_UNK451:
576
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("unknown_451")));
577
+ break;
578
+ case EXTH_UNK452:
579
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("unknown_452")));
580
+ break;
581
+ case EXTH_UNK453:
582
+ rb_hash_aset(item, ID2SYM(rb_intern("id")), ID2SYM(rb_intern("unknown_453")));
583
+ break;
584
+ }
585
+ if (tag.tag == 0) {
586
+ rb_hash_aset(item, ID2SYM(rb_intern("val_bin")), rb_str_new((const char *)hdr->data, hdr->size));
587
+ val32 = mobi_decode_exthvalue(hdr->data, hdr->size);
588
+ rb_hash_aset(item, ID2SYM(rb_intern("val_num")), INT2FIX(val32));
589
+ } else {
590
+ char *str;
591
+ rb_hash_aset(item, ID2SYM(rb_intern("name")), rb_str_new_cstr(tag.name));
592
+ switch (tag.type) {
593
+ case EXTH_NUMERIC:
594
+ val32 = mobi_decode_exthvalue(hdr->data, hdr->size);
595
+ rb_hash_aset(item, ID2SYM(rb_intern("val_num")), INT2FIX(val32));
596
+ break;
597
+ case EXTH_STRING:
598
+ str = mobi_decode_exthstring(book->data, hdr->data, hdr->size);
599
+ if (str) {
600
+ rb_hash_aset(item, ID2SYM(rb_intern("val_str")), rb_str_new_cstr(str));
601
+ free(str);
602
+ }
603
+ break;
604
+ case EXTH_BINARY:
605
+ rb_hash_aset(item, ID2SYM(rb_intern("val_bin")), rb_str_new((const char *)hdr->data, hdr->size));
606
+ break;
607
+ default:
608
+ break;
609
+ }
610
+ }
611
+ rb_ary_push(res, item);
612
+ hdr = hdr->next;
613
+ }
614
+ return res;
615
+ }
616
+
617
+ #define FULL_NAME_MAX 1024
618
+ static VALUE mb_book_full_name(VALUE self)
619
+ {
620
+ mb_BOOK *book = DATA_PTR(self);
621
+ MOBI_RET rc;
622
+ char full_name[FULL_NAME_MAX + 1] = {0};
623
+
624
+ if (book == NULL || book->data == NULL) {
625
+ mb_raise(MOBI_INIT_FAILED, "the MOBI data is not loaded");
626
+ }
627
+ rc = mobi_get_fullname(book->data, full_name, FULL_NAME_MAX);
628
+ if (rc != MOBI_SUCCESS) {
629
+ mb_raise(rc, "unable to fetch book full name");
630
+ }
631
+ return rb_str_new_cstr(full_name);
632
+ }
633
+ #undef FULL_NAME_MAX
634
+
635
+ #define DEFINE_META_GETTER_STR(ATTR) \
636
+ static VALUE mb_book_##ATTR(VALUE self) \
637
+ { \
638
+ mb_BOOK *book = DATA_PTR(self); \
639
+ const char *str; \
640
+ \
641
+ if (book == NULL || book->data == NULL) { \
642
+ mb_raise(MOBI_INIT_FAILED, "the MOBI data is not loaded"); \
643
+ } \
644
+ str = mobi_meta_get_##ATTR(book->data); \
645
+ if (str) { \
646
+ return rb_str_new_cstr(str); \
647
+ } \
648
+ return Qnil; \
649
+ }
650
+
651
+ DEFINE_META_GETTER_STR(title)
652
+ DEFINE_META_GETTER_STR(author)
653
+ DEFINE_META_GETTER_STR(publisher)
654
+ DEFINE_META_GETTER_STR(imprint)
655
+ DEFINE_META_GETTER_STR(description)
656
+ DEFINE_META_GETTER_STR(isbn)
657
+ DEFINE_META_GETTER_STR(subject)
658
+ DEFINE_META_GETTER_STR(publishdate)
659
+ DEFINE_META_GETTER_STR(review)
660
+ DEFINE_META_GETTER_STR(contributor)
661
+ DEFINE_META_GETTER_STR(copyright)
662
+ DEFINE_META_GETTER_STR(asin)
663
+ DEFINE_META_GETTER_STR(language)
664
+
665
+ #define DEFINE_PREDICATE(METHOD, FUNCTION) \
666
+ static VALUE mb_book_p_##METHOD(VALUE self) \
667
+ { \
668
+ mb_BOOK *book = DATA_PTR(self); \
669
+ \
670
+ if (book == NULL || book->data == NULL) { \
671
+ mb_raise(MOBI_INIT_FAILED, "the MOBI data is not loaded"); \
672
+ } \
673
+ if (FUNCTION(book->data)) { \
674
+ return Qtrue; \
675
+ } \
676
+ return Qfalse; \
677
+ }
678
+
679
+ DEFINE_PREDICATE(has_mobi_header, mobi_exists_mobiheader)
680
+ DEFINE_PREDICATE(has_fdst, mobi_exists_fdst)
681
+ DEFINE_PREDICATE(has_skeleton_index, mobi_exists_skel_indx)
682
+ DEFINE_PREDICATE(has_fragments_index, mobi_exists_frag_indx)
683
+ DEFINE_PREDICATE(has_guide_index, mobi_exists_guide_indx)
684
+ DEFINE_PREDICATE(has_ncx_index, mobi_exists_ncx)
685
+ DEFINE_PREDICATE(has_orth_index, mobi_exists_orth)
686
+ DEFINE_PREDICATE(has_infl_index, mobi_exists_infl)
687
+ DEFINE_PREDICATE(is_hybrid, mobi_is_hybrid)
688
+ DEFINE_PREDICATE(is_encrypted, mobi_is_encrypted)
689
+ DEFINE_PREDICATE(is_mobipocket, mobi_is_mobipocket)
690
+ DEFINE_PREDICATE(is_dictionary, mobi_is_dictionary)
691
+ DEFINE_PREDICATE(is_kf8, mobi_is_kf8)
692
+
693
+ static VALUE mb_book_records(VALUE self)
694
+ {
695
+ mb_BOOK *book = DATA_PTR(self);
696
+ VALUE res;
697
+ const MOBIPdbRecord *rec;
698
+
699
+ if (book == NULL || book->data == NULL) {
700
+ mb_raise(MOBI_INIT_FAILED, "the MOBI data is not loaded");
701
+ }
702
+ rec = book->data->rec;
703
+ if (rec == NULL) {
704
+ return Qnil;
705
+ }
706
+
707
+ res = rb_ary_new();
708
+ while (rec != NULL) {
709
+ VALUE item = rb_hash_new();
710
+ rb_hash_aset(item, ID2SYM(rb_intern("offset")), INT2FIX(rec->offset));
711
+ rb_hash_aset(item, ID2SYM(rb_intern("size")), INT2FIX(rec->size));
712
+ rb_hash_aset(item, ID2SYM(rb_intern("attributes")), INT2FIX(rec->attributes));
713
+ rb_hash_aset(item, ID2SYM(rb_intern("uid")), INT2FIX(rec->uid));
714
+ rb_hash_aset(item, ID2SYM(rb_intern("data")), rb_str_new((const char *)rec->data, rec->size));
715
+ rb_ary_push(res, item);
716
+ rec = rec->next;
717
+ }
718
+ return res;
719
+ }
720
+
721
+ static VALUE mb_book_rawml(VALUE self)
722
+ {
723
+ mb_BOOK *book = DATA_PTR(self);
724
+ MOBI_RET rc;
725
+ char *text = NULL;
726
+ size_t size;
727
+ VALUE res;
728
+
729
+ if (book == NULL || book->data == NULL) {
730
+ mb_raise(MOBI_INIT_FAILED, "the MOBI data is not loaded");
731
+ }
732
+ size = mobi_get_text_maxsize(book->data);
733
+ if (size == MOBI_NOTSET) {
734
+ mb_raise(MOBI_DATA_CORRUPT, "unable to determine size for rawml");
735
+ }
736
+ text = calloc(size + 1, sizeof(char));
737
+ if (text == NULL) {
738
+ mb_raise(MOBI_MALLOC_FAILED, "unable to allocate memory for rawml buffer");
739
+ }
740
+ rc = mobi_get_rawml(book->data, text, &size);
741
+ if (rc != MOBI_SUCCESS) {
742
+ free(text);
743
+ mb_raise(rc, "unable to generate rawml");
744
+ return Qnil;
745
+ }
746
+ res = rb_str_new(text, size);
747
+ free(text);
748
+ return res;
749
+ }
750
+
751
+ static VALUE mb_extract_mobiparts(const MOBIPart *part)
752
+ {
753
+ VALUE items = rb_ary_new();
754
+ while (part != NULL) {
755
+ VALUE item = rb_hash_new();
756
+ rb_hash_aset(item, ID2SYM(rb_intern("type")), INT2FIX(part->type));
757
+ switch (part->type) {
758
+ case T_UNKNOWN:
759
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("unknown")));
760
+ break;
761
+ case T_HTML:
762
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("html")));
763
+ break;
764
+ case T_CSS:
765
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("css")));
766
+ break;
767
+ case T_SVG:
768
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("svg")));
769
+ break;
770
+ case T_OPF:
771
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("opf")));
772
+ break;
773
+ case T_NCX:
774
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("ncx")));
775
+ break;
776
+ case T_JPG:
777
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("jpg")));
778
+ break;
779
+ case T_GIF:
780
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("gif")));
781
+ break;
782
+ case T_PNG:
783
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("png")));
784
+ break;
785
+ case T_BMP:
786
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("bmp")));
787
+ break;
788
+ case T_OTF:
789
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("otf")));
790
+ break;
791
+ case T_TTF:
792
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("ttf")));
793
+ break;
794
+ case T_MP3:
795
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("mp3")));
796
+ break;
797
+ case T_MPG:
798
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("mpg")));
799
+ break;
800
+ case T_PDF:
801
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("pdf")));
802
+ break;
803
+ case T_FONT:
804
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("font")));
805
+ break;
806
+ case T_AUDIO:
807
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("audio")));
808
+ break;
809
+ case T_VIDEO:
810
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("video")));
811
+ break;
812
+ case T_BREAK:
813
+ rb_hash_aset(item, ID2SYM(rb_intern("type_sym")), ID2SYM(rb_intern("break")));
814
+ break;
815
+ }
816
+ rb_hash_aset(item, ID2SYM(rb_intern("uid")), INT2FIX(part->uid));
817
+ rb_hash_aset(item, ID2SYM(rb_intern("size")), INT2FIX(part->size));
818
+ rb_hash_aset(item, ID2SYM(rb_intern("data")), rb_str_new((const char *)part->data, part->size));
819
+ part = part->next;
820
+ rb_ary_push(items, item);
821
+ }
822
+ return items;
823
+ }
824
+
825
+ static VALUE mb_book_rawml_parts(VALUE self)
826
+ {
827
+ mb_BOOK *book = DATA_PTR(self);
828
+ MOBI_RET rc;
829
+ MOBIRawml *rawml;
830
+ VALUE res;
831
+
832
+ if (book == NULL || book->data == NULL) {
833
+ mb_raise(MOBI_INIT_FAILED, "the MOBI data is not loaded");
834
+ }
835
+ rawml = mobi_init_rawml(book->data);
836
+ if (rawml == NULL) {
837
+ mb_raise(MOBI_MALLOC_FAILED, "unable to allocate memory for rawml structure");
838
+ }
839
+ rc = mobi_parse_rawml(rawml, book->data);
840
+ if (rc != MOBI_SUCCESS) {
841
+ mobi_free_rawml(rawml);
842
+ mb_raise(rc, "unable to generate rawml");
843
+ return Qnil;
844
+ }
845
+ res = rb_hash_new();
846
+ rb_hash_aset(res, ID2SYM(rb_intern("version")), INT2FIX(rawml->version));
847
+ if (rawml->markup != NULL) {
848
+ rb_hash_aset(res, ID2SYM(rb_intern("markup")), mb_extract_mobiparts(rawml->markup));
849
+ }
850
+ if (rawml->flow != NULL) {
851
+ rb_hash_aset(res, ID2SYM(rb_intern("flow")), mb_extract_mobiparts(rawml->flow));
852
+ }
853
+ if (rawml->resources != NULL) {
854
+ rb_hash_aset(res, ID2SYM(rb_intern("resources")), mb_extract_mobiparts(rawml->resources));
855
+ }
856
+ mobi_free_rawml(rawml);
857
+ return res;
858
+ }
859
+
860
+ static void init_mobi_book()
861
+ {
862
+ mb_cBook = rb_define_class_under(mb_mMOBI, "Book", rb_cObject);
863
+ rb_define_alloc_func(mb_cBook, mb_book_alloc);
864
+ rb_define_method(mb_cBook, "initialize", mb_book_init, 1);
865
+ rb_define_method(mb_cBook, "next", mb_book_next, 0);
866
+ rb_define_method(mb_cBook, "mobi_header", mb_book_mobi_header, 0);
867
+ rb_define_method(mb_cBook, "pdb_header", mb_book_pdb_header, 0);
868
+ rb_define_method(mb_cBook, "record0_header", mb_book_record0_header, 0);
869
+ rb_define_method(mb_cBook, "exth_header", mb_book_exth_header, 0);
870
+ rb_define_method(mb_cBook, "full_name", mb_book_full_name, 0);
871
+ rb_define_method(mb_cBook, "title", mb_book_title, 0);
872
+ rb_define_method(mb_cBook, "author", mb_book_author, 0);
873
+ rb_define_method(mb_cBook, "publisher", mb_book_publisher, 0);
874
+ rb_define_method(mb_cBook, "imprint", mb_book_imprint, 0);
875
+ rb_define_method(mb_cBook, "description", mb_book_description, 0);
876
+ rb_define_method(mb_cBook, "isbn", mb_book_isbn, 0);
877
+ rb_define_method(mb_cBook, "subject", mb_book_subject, 0);
878
+ rb_define_method(mb_cBook, "publishdate", mb_book_publishdate, 0);
879
+ rb_define_method(mb_cBook, "review", mb_book_review, 0);
880
+ rb_define_method(mb_cBook, "contributor", mb_book_contributor, 0);
881
+ rb_define_method(mb_cBook, "copyright", mb_book_copyright, 0);
882
+ rb_define_method(mb_cBook, "asin", mb_book_asin, 0);
883
+ rb_define_method(mb_cBook, "language", mb_book_language, 0);
884
+ rb_define_method(mb_cBook, "has_mobi_header?", mb_book_p_has_mobi_header, 0);
885
+ rb_define_method(mb_cBook, "has_fdst?", mb_book_p_has_fdst, 0);
886
+ rb_define_method(mb_cBook, "has_skeleton_index?", mb_book_p_has_skeleton_index, 0);
887
+ rb_define_method(mb_cBook, "has_fragments_index?", mb_book_p_has_fragments_index, 0);
888
+ rb_define_method(mb_cBook, "has_guide_index?", mb_book_p_has_guide_index, 0);
889
+ rb_define_method(mb_cBook, "has_ncx_index?", mb_book_p_has_ncx_index, 0);
890
+ rb_define_method(mb_cBook, "has_orth_index?", mb_book_p_has_orth_index, 0);
891
+ rb_define_method(mb_cBook, "has_infl_index?", mb_book_p_has_infl_index, 0);
892
+ rb_define_method(mb_cBook, "is_hybrid?", mb_book_p_is_hybrid, 0);
893
+ rb_define_method(mb_cBook, "is_encrypted?", mb_book_p_is_encrypted, 0);
894
+ rb_define_method(mb_cBook, "is_mobipocket?", mb_book_p_is_mobipocket, 0);
895
+ rb_define_method(mb_cBook, "is_dictionary?", mb_book_p_is_dictionary, 0);
896
+ rb_define_method(mb_cBook, "is_kf8?", mb_book_p_is_kf8, 0);
897
+ rb_define_method(mb_cBook, "records", mb_book_records, 0);
898
+ rb_define_method(mb_cBook, "rawml", mb_book_rawml, 0);
899
+ rb_define_method(mb_cBook, "rawml_parts", mb_book_rawml_parts, 0);
900
+ }
901
+
902
+ void Init_mobi_ext()
903
+ {
904
+ mb_mMOBI = rb_define_module("MOBI");
905
+ rb_define_const(mb_mMOBI, "LIB_VERSION", rb_str_freeze(rb_external_str_new_cstr(mobi_version())));
906
+ mb_eError = rb_const_get(mb_mMOBI, rb_intern("Error"));
907
+
908
+ init_mobi_book();
909
+ }