├── .gitignore ├── .gitlab-ci.yml ├── AUTHORS ├── COPYING ├── ChangeLog ├── MAINTAINERS ├── NEWS ├── README ├── TODO ├── configure ├── libgepub.doap ├── libgepub ├── gepub-archive.c ├── gepub-archive.h ├── gepub-doc.c ├── gepub-doc.h ├── gepub-text-chunk.c ├── gepub-text-chunk.h ├── gepub-utils.c ├── gepub-utils.h ├── gepub-widget.c ├── gepub-widget.h ├── gepub.h ├── gepub.map └── meson.build ├── meson.build ├── meson_options.txt └── tests ├── meson.build └── test-gepub.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.gir 2 | *.typelib 3 | Makefile 4 | Makefile.in 5 | aclocal.m4 6 | autom4te.cache 7 | config.* 8 | configure 9 | depcomp 10 | libtool 11 | *.pc 12 | *.stamp 13 | *.la 14 | *.o 15 | *.lo 16 | .deps 17 | .libs 18 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - build 3 | 4 | variables: 5 | FEDORA_DEPENDENCIES: 6 | gcc 7 | webkit2gtk3-devel 8 | libsoup-devel 9 | glib2-devel 10 | libxml2-devel 11 | libarchive-devel 12 | gobject-introspection-devel 13 | meson 14 | git 15 | FEDORA_DEPENDENCIES_ABI_CHECK: 16 | libabigail 17 | LAST_ABI_BREAK: "3dd049036d6d4c70307d1167dc56b283c297f066" 18 | 19 | build-fedora: 20 | image: fedora:latest 21 | stage: build 22 | except: 23 | - tags 24 | before_script: 25 | - dnf update -y --nogpgcheck 26 | - dnf -y install --nogpgcheck $FEDORA_DEPENDENCIES 27 | script: 28 | - meson _build 29 | - ninja -C _build dist 30 | - curl https://gitlab.freedesktop.org/hadess/check-abi/-/raw/main/contrib/check-abi-fedora.sh | bash 31 | - dnf install -y $FEDORA_DEPENDENCIES_ABI_CHECK 32 | - check-abi ${LAST_ABI_BREAK} $(git rev-parse HEAD) 33 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Daniel Garcia Moreno 2 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU LIBRARY GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the library GPL. It is 10 | numbered 2 because it goes with version 2 of the ordinary GPL.] 11 | 12 | Preamble 13 | 14 | The licenses for most software are designed to take away your 15 | freedom to share and change it. By contrast, the GNU General Public 16 | Licenses are intended to guarantee your freedom to share and change 17 | free software--to make sure the software is free for all its users. 18 | 19 | This license, the Library General Public License, applies to some 20 | specially designated Free Software Foundation software, and to any 21 | other libraries whose authors decide to use it. You can use it for 22 | your libraries, too. 23 | 24 | When we speak of free software, we are referring to freedom, not 25 | price. Our General Public Licenses are designed to make sure that you 26 | have the freedom to distribute copies of free software (and charge for 27 | this service if you wish), that you receive source code or can get it 28 | if you want it, that you can change the software or use pieces of it 29 | in new free programs; and that you know you can do these things. 30 | 31 | To protect your rights, we need to make restrictions that forbid 32 | anyone to deny you these rights or to ask you to surrender the rights. 33 | These restrictions translate to certain responsibilities for you if 34 | you distribute copies of the library, or if you modify it. 35 | 36 | For example, if you distribute copies of the library, whether gratis 37 | or for a fee, you must give the recipients all the rights that we gave 38 | you. You must make sure that they, too, receive or can get the source 39 | code. If you link a program with the library, you must provide 40 | complete object files to the recipients so that they can relink them 41 | with the library, after making changes to the library and recompiling 42 | it. And you must show them these terms so they know their rights. 43 | 44 | Our method of protecting your rights has two steps: (1) copyright 45 | the library, and (2) offer you this license which gives you legal 46 | permission to copy, distribute and/or modify the library. 47 | 48 | Also, for each distributor's protection, we want to make certain 49 | that everyone understands that there is no warranty for this free 50 | library. If the library is modified by someone else and passed on, we 51 | want its recipients to know that what they have is not the original 52 | version, so that any problems introduced by others will not reflect on 53 | the original authors' reputations. 54 | 55 | Finally, any free program is threatened constantly by software 56 | patents. We wish to avoid the danger that companies distributing free 57 | software will individually obtain patent licenses, thus in effect 58 | transforming the program into proprietary software. To prevent this, 59 | we have made it clear that any patent must be licensed for everyone's 60 | free use or not licensed at all. 61 | 62 | Most GNU software, including some libraries, is covered by the ordinary 63 | GNU General Public License, which was designed for utility programs. This 64 | license, the GNU Library General Public License, applies to certain 65 | designated libraries. This license is quite different from the ordinary 66 | one; be sure to read it in full, and don't assume that anything in it is 67 | the same as in the ordinary license. 68 | 69 | The reason we have a separate public license for some libraries is that 70 | they blur the distinction we usually make between modifying or adding to a 71 | program and simply using it. Linking a program with a library, without 72 | changing the library, is in some sense simply using the library, and is 73 | analogous to running a utility program or application program. However, in 74 | a textual and legal sense, the linked executable is a combined work, a 75 | derivative of the original library, and the ordinary General Public License 76 | treats it as such. 77 | 78 | Because of this blurred distinction, using the ordinary General 79 | Public License for libraries did not effectively promote software 80 | sharing, because most developers did not use the libraries. We 81 | concluded that weaker conditions might promote sharing better. 82 | 83 | However, unrestricted linking of non-free programs would deprive the 84 | users of those programs of all benefit from the free status of the 85 | libraries themselves. This Library General Public License is intended to 86 | permit developers of non-free programs to use free libraries, while 87 | preserving your freedom as a user of such programs to change the free 88 | libraries that are incorporated in them. (We have not seen how to achieve 89 | this as regards changes in header files, but we have achieved it as regards 90 | changes in the actual functions of the Library.) The hope is that this 91 | will lead to faster development of free libraries. 92 | 93 | The precise terms and conditions for copying, distribution and 94 | modification follow. Pay close attention to the difference between a 95 | "work based on the library" and a "work that uses the library". The 96 | former contains code derived from the library, while the latter only 97 | works together with the library. 98 | 99 | Note that it is possible for a library to be covered by the ordinary 100 | General Public License rather than by this special one. 101 | 102 | GNU LIBRARY GENERAL PUBLIC LICENSE 103 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 104 | 105 | 0. This License Agreement applies to any software library which 106 | contains a notice placed by the copyright holder or other authorized 107 | party saying it may be distributed under the terms of this Library 108 | General Public License (also called "this License"). Each licensee is 109 | addressed as "you". 110 | 111 | A "library" means a collection of software functions and/or data 112 | prepared so as to be conveniently linked with application programs 113 | (which use some of those functions and data) to form executables. 114 | 115 | The "Library", below, refers to any such software library or work 116 | which has been distributed under these terms. A "work based on the 117 | Library" means either the Library or any derivative work under 118 | copyright law: that is to say, a work containing the Library or a 119 | portion of it, either verbatim or with modifications and/or translated 120 | straightforwardly into another language. (Hereinafter, translation is 121 | included without limitation in the term "modification".) 122 | 123 | "Source code" for a work means the preferred form of the work for 124 | making modifications to it. For a library, complete source code means 125 | all the source code for all modules it contains, plus any associated 126 | interface definition files, plus the scripts used to control compilation 127 | and installation of the library. 128 | 129 | Activities other than copying, distribution and modification are not 130 | covered by this License; they are outside its scope. The act of 131 | running a program using the Library is not restricted, and output from 132 | such a program is covered only if its contents constitute a work based 133 | on the Library (independent of the use of the Library in a tool for 134 | writing it). Whether that is true depends on what the Library does 135 | and what the program that uses the Library does. 136 | 137 | 1. You may copy and distribute verbatim copies of the Library's 138 | complete source code as you receive it, in any medium, provided that 139 | you conspicuously and appropriately publish on each copy an 140 | appropriate copyright notice and disclaimer of warranty; keep intact 141 | all the notices that refer to this License and to the absence of any 142 | warranty; and distribute a copy of this License along with the 143 | Library. 144 | 145 | You may charge a fee for the physical act of transferring a copy, 146 | and you may at your option offer warranty protection in exchange for a 147 | fee. 148 | 149 | 2. You may modify your copy or copies of the Library or any portion 150 | of it, thus forming a work based on the Library, and copy and 151 | distribute such modifications or work under the terms of Section 1 152 | above, provided that you also meet all of these conditions: 153 | 154 | a) The modified work must itself be a software library. 155 | 156 | b) You must cause the files modified to carry prominent notices 157 | stating that you changed the files and the date of any change. 158 | 159 | c) You must cause the whole of the work to be licensed at no 160 | charge to all third parties under the terms of this License. 161 | 162 | d) If a facility in the modified Library refers to a function or a 163 | table of data to be supplied by an application program that uses 164 | the facility, other than as an argument passed when the facility 165 | is invoked, then you must make a good faith effort to ensure that, 166 | in the event an application does not supply such function or 167 | table, the facility still operates, and performs whatever part of 168 | its purpose remains meaningful. 169 | 170 | (For example, a function in a library to compute square roots has 171 | a purpose that is entirely well-defined independent of the 172 | application. Therefore, Subsection 2d requires that any 173 | application-supplied function or table used by this function must 174 | be optional: if the application does not supply it, the square 175 | root function must still compute square roots.) 176 | 177 | These requirements apply to the modified work as a whole. If 178 | identifiable sections of that work are not derived from the Library, 179 | and can be reasonably considered independent and separate works in 180 | themselves, then this License, and its terms, do not apply to those 181 | sections when you distribute them as separate works. But when you 182 | distribute the same sections as part of a whole which is a work based 183 | on the Library, the distribution of the whole must be on the terms of 184 | this License, whose permissions for other licensees extend to the 185 | entire whole, and thus to each and every part regardless of who wrote 186 | it. 187 | 188 | Thus, it is not the intent of this section to claim rights or contest 189 | your rights to work written entirely by you; rather, the intent is to 190 | exercise the right to control the distribution of derivative or 191 | collective works based on the Library. 192 | 193 | In addition, mere aggregation of another work not based on the Library 194 | with the Library (or with a work based on the Library) on a volume of 195 | a storage or distribution medium does not bring the other work under 196 | the scope of this License. 197 | 198 | 3. You may opt to apply the terms of the ordinary GNU General Public 199 | License instead of this License to a given copy of the Library. To do 200 | this, you must alter all the notices that refer to this License, so 201 | that they refer to the ordinary GNU General Public License, version 2, 202 | instead of to this License. (If a newer version than version 2 of the 203 | ordinary GNU General Public License has appeared, then you can specify 204 | that version instead if you wish.) Do not make any other change in 205 | these notices. 206 | 207 | Once this change is made in a given copy, it is irreversible for 208 | that copy, so the ordinary GNU General Public License applies to all 209 | subsequent copies and derivative works made from that copy. 210 | 211 | This option is useful when you wish to copy part of the code of 212 | the Library into a program that is not a library. 213 | 214 | 4. You may copy and distribute the Library (or a portion or 215 | derivative of it, under Section 2) in object code or executable form 216 | under the terms of Sections 1 and 2 above provided that you accompany 217 | it with the complete corresponding machine-readable source code, which 218 | must be distributed under the terms of Sections 1 and 2 above on a 219 | medium customarily used for software interchange. 220 | 221 | If distribution of object code is made by offering access to copy 222 | from a designated place, then offering equivalent access to copy the 223 | source code from the same place satisfies the requirement to 224 | distribute the source code, even though third parties are not 225 | compelled to copy the source along with the object code. 226 | 227 | 5. A program that contains no derivative of any portion of the 228 | Library, but is designed to work with the Library by being compiled or 229 | linked with it, is called a "work that uses the Library". Such a 230 | work, in isolation, is not a derivative work of the Library, and 231 | therefore falls outside the scope of this License. 232 | 233 | However, linking a "work that uses the Library" with the Library 234 | creates an executable that is a derivative of the Library (because it 235 | contains portions of the Library), rather than a "work that uses the 236 | library". The executable is therefore covered by this License. 237 | Section 6 states terms for distribution of such executables. 238 | 239 | When a "work that uses the Library" uses material from a header file 240 | that is part of the Library, the object code for the work may be a 241 | derivative work of the Library even though the source code is not. 242 | Whether this is true is especially significant if the work can be 243 | linked without the Library, or if the work is itself a library. The 244 | threshold for this to be true is not precisely defined by law. 245 | 246 | If such an object file uses only numerical parameters, data 247 | structure layouts and accessors, and small macros and small inline 248 | functions (ten lines or less in length), then the use of the object 249 | file is unrestricted, regardless of whether it is legally a derivative 250 | work. (Executables containing this object code plus portions of the 251 | Library will still fall under Section 6.) 252 | 253 | Otherwise, if the work is a derivative of the Library, you may 254 | distribute the object code for the work under the terms of Section 6. 255 | Any executables containing that work also fall under Section 6, 256 | whether or not they are linked directly with the Library itself. 257 | 258 | 6. As an exception to the Sections above, you may also compile or 259 | link a "work that uses the Library" with the Library to produce a 260 | work containing portions of the Library, and distribute that work 261 | under terms of your choice, provided that the terms permit 262 | modification of the work for the customer's own use and reverse 263 | engineering for debugging such modifications. 264 | 265 | You must give prominent notice with each copy of the work that the 266 | Library is used in it and that the Library and its use are covered by 267 | this License. You must supply a copy of this License. If the work 268 | during execution displays copyright notices, you must include the 269 | copyright notice for the Library among them, as well as a reference 270 | directing the user to the copy of this License. Also, you must do one 271 | of these things: 272 | 273 | a) Accompany the work with the complete corresponding 274 | machine-readable source code for the Library including whatever 275 | changes were used in the work (which must be distributed under 276 | Sections 1 and 2 above); and, if the work is an executable linked 277 | with the Library, with the complete machine-readable "work that 278 | uses the Library", as object code and/or source code, so that the 279 | user can modify the Library and then relink to produce a modified 280 | executable containing the modified Library. (It is understood 281 | that the user who changes the contents of definitions files in the 282 | Library will not necessarily be able to recompile the application 283 | to use the modified definitions.) 284 | 285 | b) Accompany the work with a written offer, valid for at 286 | least three years, to give the same user the materials 287 | specified in Subsection 6a, above, for a charge no more 288 | than the cost of performing this distribution. 289 | 290 | c) If distribution of the work is made by offering access to copy 291 | from a designated place, offer equivalent access to copy the above 292 | specified materials from the same place. 293 | 294 | d) Verify that the user has already received a copy of these 295 | materials or that you have already sent this user a copy. 296 | 297 | For an executable, the required form of the "work that uses the 298 | Library" must include any data and utility programs needed for 299 | reproducing the executable from it. However, as a special exception, 300 | the source code distributed need not include anything that is normally 301 | distributed (in either source or binary form) with the major 302 | components (compiler, kernel, and so on) of the operating system on 303 | which the executable runs, unless that component itself accompanies 304 | the executable. 305 | 306 | It may happen that this requirement contradicts the license 307 | restrictions of other proprietary libraries that do not normally 308 | accompany the operating system. Such a contradiction means you cannot 309 | use both them and the Library together in an executable that you 310 | distribute. 311 | 312 | 7. You may place library facilities that are a work based on the 313 | Library side-by-side in a single library together with other library 314 | facilities not covered by this License, and distribute such a combined 315 | library, provided that the separate distribution of the work based on 316 | the Library and of the other library facilities is otherwise 317 | permitted, and provided that you do these two things: 318 | 319 | a) Accompany the combined library with a copy of the same work 320 | based on the Library, uncombined with any other library 321 | facilities. This must be distributed under the terms of the 322 | Sections above. 323 | 324 | b) Give prominent notice with the combined library of the fact 325 | that part of it is a work based on the Library, and explaining 326 | where to find the accompanying uncombined form of the same work. 327 | 328 | 8. You may not copy, modify, sublicense, link with, or distribute 329 | the Library except as expressly provided under this License. Any 330 | attempt otherwise to copy, modify, sublicense, link with, or 331 | distribute the Library is void, and will automatically terminate your 332 | rights under this License. However, parties who have received copies, 333 | or rights, from you under this License will not have their licenses 334 | terminated so long as such parties remain in full compliance. 335 | 336 | 9. You are not required to accept this License, since you have not 337 | signed it. However, nothing else grants you permission to modify or 338 | distribute the Library or its derivative works. These actions are 339 | prohibited by law if you do not accept this License. Therefore, by 340 | modifying or distributing the Library (or any work based on the 341 | Library), you indicate your acceptance of this License to do so, and 342 | all its terms and conditions for copying, distributing or modifying 343 | the Library or works based on it. 344 | 345 | 10. Each time you redistribute the Library (or any work based on the 346 | Library), the recipient automatically receives a license from the 347 | original licensor to copy, distribute, link with or modify the Library 348 | subject to these terms and conditions. You may not impose any further 349 | restrictions on the recipients' exercise of the rights granted herein. 350 | You are not responsible for enforcing compliance by third parties to 351 | this License. 352 | 353 | 11. If, as a consequence of a court judgment or allegation of patent 354 | infringement or for any other reason (not limited to patent issues), 355 | conditions are imposed on you (whether by court order, agreement or 356 | otherwise) that contradict the conditions of this License, they do not 357 | excuse you from the conditions of this License. If you cannot 358 | distribute so as to satisfy simultaneously your obligations under this 359 | License and any other pertinent obligations, then as a consequence you 360 | may not distribute the Library at all. For example, if a patent 361 | license would not permit royalty-free redistribution of the Library by 362 | all those who receive copies directly or indirectly through you, then 363 | the only way you could satisfy both it and this License would be to 364 | refrain entirely from distribution of the Library. 365 | 366 | If any portion of this section is held invalid or unenforceable under any 367 | particular circumstance, the balance of the section is intended to apply, 368 | and the section as a whole is intended to apply in other circumstances. 369 | 370 | It is not the purpose of this section to induce you to infringe any 371 | patents or other property right claims or to contest validity of any 372 | such claims; this section has the sole purpose of protecting the 373 | integrity of the free software distribution system which is 374 | implemented by public license practices. Many people have made 375 | generous contributions to the wide range of software distributed 376 | through that system in reliance on consistent application of that 377 | system; it is up to the author/donor to decide if he or she is willing 378 | to distribute software through any other system and a licensee cannot 379 | impose that choice. 380 | 381 | This section is intended to make thoroughly clear what is believed to 382 | be a consequence of the rest of this License. 383 | 384 | 12. If the distribution and/or use of the Library is restricted in 385 | certain countries either by patents or by copyrighted interfaces, the 386 | original copyright holder who places the Library under this License may add 387 | an explicit geographical distribution limitation excluding those countries, 388 | so that distribution is permitted only in or among countries not thus 389 | excluded. In such case, this License incorporates the limitation as if 390 | written in the body of this License. 391 | 392 | 13. The Free Software Foundation may publish revised and/or new 393 | versions of the Library General Public License from time to time. 394 | Such new versions will be similar in spirit to the present version, 395 | but may differ in detail to address new problems or concerns. 396 | 397 | Each version is given a distinguishing version number. If the Library 398 | specifies a version number of this License which applies to it and 399 | "any later version", you have the option of following the terms and 400 | conditions either of that version or of any later version published by 401 | the Free Software Foundation. If the Library does not specify a 402 | license version number, you may choose any version ever published by 403 | the Free Software Foundation. 404 | 405 | 14. If you wish to incorporate parts of the Library into other free 406 | programs whose distribution conditions are incompatible with these, 407 | write to the author to ask for permission. For software which is 408 | copyrighted by the Free Software Foundation, write to the Free 409 | Software Foundation; we sometimes make exceptions for this. Our 410 | decision will be guided by the two goals of preserving the free status 411 | of all derivatives of our free software and of promoting the sharing 412 | and reuse of software generally. 413 | 414 | NO WARRANTY 415 | 416 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 417 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 418 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 419 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 420 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 421 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 422 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 423 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 424 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 425 | 426 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 427 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 428 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 429 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 430 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 431 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 432 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 433 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 434 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 435 | DAMAGES. 436 | 437 | END OF TERMS AND CONDITIONS 438 | 439 | How to Apply These Terms to Your New Libraries 440 | 441 | If you develop a new library, and you want it to be of the greatest 442 | possible use to the public, we recommend making it free software that 443 | everyone can redistribute and change. You can do so by permitting 444 | redistribution under these terms (or, alternatively, under the terms of the 445 | ordinary General Public License). 446 | 447 | To apply these terms, attach the following notices to the library. It is 448 | safest to attach them to the start of each source file to most effectively 449 | convey the exclusion of warranty; and each file should have at least the 450 | "copyright" line and a pointer to where the full notice is found. 451 | 452 | 453 | Copyright (C) 454 | 455 | This library is free software; you can redistribute it and/or 456 | modify it under the terms of the GNU Library General Public 457 | License as published by the Free Software Foundation; either 458 | version 2 of the License, or (at your option) any later version. 459 | 460 | This library is distributed in the hope that it will be useful, 461 | but WITHOUT ANY WARRANTY; without even the implied warranty of 462 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 463 | Library General Public License for more details. 464 | 465 | You should have received a copy of the GNU Library General Public 466 | License along with this library; if not, write to the 467 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, 468 | Boston, MA 02111-1307 USA. 469 | 470 | Also add information on how to contact you by electronic and paper mail. 471 | 472 | You should also get your employer (if you work as a programmer) or your 473 | school, if any, to sign a "copyright disclaimer" for the library, if 474 | necessary. Here is a sample; alter the names: 475 | 476 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 477 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 478 | 479 | , 1 April 1990 480 | Ty Coon, President of Vice 481 | 482 | That's all there is to it! 483 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danigm/libgepub/323d502507beba4e9f401a74d4303a648fe1788c/ChangeLog -------------------------------------------------------------------------------- /MAINTAINERS: -------------------------------------------------------------------------------- 1 | Daniel Garcia Moreno 2 | E-mail: danigm@wadobo.com 3 | Userid: danigm 4 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | Version 0.6.0 2 | ============= 3 | 4 | - Support setting font-family in GepubWidget 5 | - Fix the gir generation 6 | - widget: Fix warning at run-time 7 | - lib: Fix gepub-widget chapter change signal 8 | 9 | Version 0.5.3 10 | ============= 11 | 12 | - Fixed SVG image resource replacement 13 | - Rename introspection build option 14 | - Removed autotools 15 | - build: Conform to build-api expectations 16 | - build: Remove default warning level 17 | - build: Remove unused defines 18 | - build: Improved linker script handling 19 | - build: Set prefix-relative install_dir for libgepub 20 | - build: Fix typo in symbol path creation 21 | - tests: Fix crash on exit 22 | - widget: Replace "100" with a constant 23 | - widget: Don't change LC_NUMERIC at runtime 24 | 25 | Version 0.5.2 26 | ============= 27 | 28 | - Fixed gir version 29 | 30 | Version 0.5.1 31 | ============= 32 | 33 | - Added GError to gepub_doc_new new method 34 | - Fixed meson build scripts 35 | 36 | Version 0.5 37 | =========== 38 | 39 | - Fixed page next and page prev signals 40 | - Added file property to support GFile initialization 41 | - Fixed some memory leaks 42 | - Removed all compilation warnings 43 | - Added pagination to the GepubWidget 44 | - Added Simple text style modification in the widget 45 | - Ported to meson build system 46 | - Fixed epub:// SoupURI creation and concatenation 47 | 48 | Version 0.4 49 | =========== 50 | 51 | - Improve GepubDoc API 52 | - Resource management inside the lib 53 | - Better GTK+ doc and glib use 54 | - New GepubWidget based on WebKitWebView 55 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | What is libgepub 2 | ================ 3 | 4 | libgepub is a GObject based library for handling and rendering epub 5 | documents. 6 | 7 | libgepub is free software; you can redistribute it and/or modify it 8 | under the terms of the GNU Lesser General Public License as published 9 | by the Free Software Foundation; either version 2.1 of the License, or 10 | (at your option) any later version. 11 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - support for multi-page display: get_current_next() or get_current_nth() 2 | to the the page nth 3 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # configure script adapter for Meson 3 | # Based on build-api: https://github.com/cgwalters/build-api 4 | # Copyright 2010, 2011, 2013 Colin Walters 5 | # Copyright 2016, 2017 Emmanuele Bassi 6 | # Copyright 2017 Iñigo Martínez 7 | # Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php) 8 | 9 | # Build API variables: 10 | 11 | # Little helper function for reading args from the commandline. 12 | # it automatically handles -a b and -a=b variants, and returns 1 if 13 | # we need to shift $3. 14 | read_arg() { 15 | # $1 = arg name 16 | # $2 = arg value 17 | # $3 = arg parameter 18 | local rematch='^[^=]*=(.*)$' 19 | if [[ $2 =~ $rematch ]]; then 20 | read "$1" <<< "${BASH_REMATCH[1]}" 21 | else 22 | read "$1" <<< "$3" 23 | # There is no way to shift our callers args, so 24 | # return 1 to indicate they should do it instead. 25 | return 1 26 | fi 27 | } 28 | 29 | sanitycheck() { 30 | # $1 = arg name 31 | # $1 = arg command 32 | # $2 = arg alternates 33 | local cmd=$( which $2 2>/dev/null ) 34 | 35 | if [ -x "$cmd" ]; then 36 | read "$1" <<< "$cmd" 37 | return 0 38 | fi 39 | 40 | test -z $3 || { 41 | for alt in $3; do 42 | cmd=$( which $alt 2>/dev/null ) 43 | 44 | if [ -x "$cmd" ]; then 45 | read "$1" <<< "$cmd" 46 | return 0 47 | fi 48 | done 49 | } 50 | 51 | echo -e "\e[1;31mERROR\e[0m: Command '$2' not found" 52 | exit 1 53 | } 54 | 55 | checkoption() { 56 | # $1 = arg 57 | option="${1#*--}" 58 | action="${option%%-*}" 59 | name="${option#*-}" 60 | if [ ${default_options[$name]+_} ]; then 61 | case "$action" in 62 | enable) meson_options[$name]=true;; 63 | disable) meson_options[$name]=false;; 64 | *) echo -e "\e[1;33mINFO\e[0m: Ignoring unknown action '$action'";; 65 | esac 66 | else 67 | echo -e "\e[1;33mINFO\e[0m: Ignoring unknown option '$option'" 68 | fi 69 | } 70 | 71 | echooption() { 72 | # $1 = option 73 | if [ ${meson_options[$1]+_} ]; then 74 | echo ${meson_options[$1]} 75 | elif [ ${default_options[$1]+_} ]; then 76 | echo ${default_options[$1]} 77 | fi 78 | } 79 | 80 | sanitycheck MESON 'meson' 81 | sanitycheck MESONTEST 'mesontest' 82 | sanitycheck NINJA 'ninja' 'ninja-build' 83 | 84 | declare -A default_options=( 85 | ['introspection']=true 86 | ) 87 | 88 | declare -A meson_options 89 | 90 | while (($# > 0)); do 91 | case "${1%%=*}" in 92 | --prefix) read_arg prefix "$@" || shift;; 93 | --bindir) read_arg bindir "$@" || shift;; 94 | --sbindir) read_arg sbindir "$@" || shift;; 95 | --libexecdir) read_arg libexecdir "$@" || shift;; 96 | --datarootdir) read_arg datarootdir "$@" || shift;; 97 | --datadir) read_arg datadir "$@" || shift;; 98 | --sysconfdir) read_arg sysconfdir "$@" || shift;; 99 | --libdir) read_arg libdir "$@" || shift;; 100 | --mandir) read_arg mandir "$@" || shift;; 101 | --includedir) read_arg includedir "$@" || shift;; 102 | *) checkoption $1;; 103 | esac 104 | shift 105 | done 106 | 107 | # Defaults 108 | test -z ${prefix} && prefix="/usr/local" 109 | test -z ${bindir} && bindir=${prefix}/bin 110 | test -z ${sbindir} && sbindir=${prefix}/sbin 111 | test -z ${libexecdir} && libexecdir=${prefix}/bin 112 | test -z ${datarootdir} && datarootdir=${prefix}/share 113 | test -z ${datadir} && datadir=${datarootdir} 114 | test -z ${sysconfdir} && sysconfdir=${prefix}/etc 115 | test -z ${libdir} && libdir=${prefix}/lib 116 | test -z ${mandir} && mandir=${prefix}/share/man 117 | test -z ${includedir} && includedir=${prefix}/include 118 | 119 | # The source directory is the location of this file 120 | srcdir=$(dirname $0) 121 | 122 | # The build directory is the current location 123 | builddir=`pwd` 124 | 125 | # If we're calling this file from the source directory then 126 | # we automatically create a build directory and ensure that 127 | # both Meson and Ninja invocations are relative to that 128 | # location 129 | if [[ -f "${builddir}/meson.build" ]]; then 130 | mkdir -p _build 131 | builddir="${builddir}/_build" 132 | NINJA_OPT="-C ${builddir}" 133 | fi 134 | 135 | # Wrapper Makefile for Ninja 136 | cat > Makefile < 6 | 7 | libgepub 8 | Simple library to read epub files using glib 9 | libgepub is a GObject based library for handling and rendering epub 10 | documents. 11 | C 12 | 13 | 14 | 15 | Daniel García Moreno 16 | danigm 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /libgepub/gepub-archive.c: -------------------------------------------------------------------------------- 1 | /* GepubArchive 2 | * 3 | * Copyright (C) 2011 Daniel Garcia 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "gepub-archive.h" 27 | #include "gepub-utils.h" 28 | 29 | #define BUFZISE 1024 30 | 31 | struct _GepubArchive { 32 | GObject parent; 33 | 34 | struct archive *archive; 35 | gchar *path; 36 | }; 37 | 38 | struct _GepubArchiveClass { 39 | GObjectClass parent_class; 40 | }; 41 | 42 | G_DEFINE_TYPE (GepubArchive, gepub_archive, G_TYPE_OBJECT) 43 | 44 | static gboolean 45 | gepub_archive_open (GepubArchive *archive) 46 | { 47 | int r; 48 | 49 | archive->archive = archive_read_new (); 50 | archive_read_support_format_zip (archive->archive); 51 | 52 | r = archive_read_open_filename (archive->archive, archive->path, 10240); 53 | 54 | if (r != ARCHIVE_OK) { 55 | return FALSE; 56 | } 57 | 58 | return TRUE; 59 | } 60 | 61 | static void 62 | gepub_archive_close (GepubArchive *archive) 63 | { 64 | if (!archive->archive) 65 | return; 66 | 67 | archive_read_free (archive->archive); 68 | archive->archive = NULL; 69 | } 70 | 71 | static void 72 | gepub_archive_finalize (GObject *object) 73 | { 74 | GepubArchive *archive = GEPUB_ARCHIVE (object); 75 | 76 | g_clear_pointer (&archive->path, g_free); 77 | 78 | gepub_archive_close (archive); 79 | 80 | G_OBJECT_CLASS (gepub_archive_parent_class)->finalize (object); 81 | } 82 | 83 | static void 84 | gepub_archive_init (GepubArchive *archive) 85 | { 86 | } 87 | 88 | static void 89 | gepub_archive_class_init (GepubArchiveClass *klass) 90 | { 91 | GObjectClass *object_class = G_OBJECT_CLASS (klass); 92 | 93 | object_class->finalize = gepub_archive_finalize; 94 | } 95 | 96 | GepubArchive * 97 | gepub_archive_new (const gchar *path) 98 | { 99 | GepubArchive *archive; 100 | 101 | archive = GEPUB_ARCHIVE (g_object_new (GEPUB_TYPE_ARCHIVE, NULL)); 102 | archive->path = g_strdup (path); 103 | archive->archive = NULL; 104 | 105 | return archive; 106 | } 107 | 108 | /** 109 | * gepub_archive_list_files: 110 | * @archive: a #GepubArchive 111 | * 112 | * Returns: (element-type utf8) (transfer full): list of files in the archive 113 | */ 114 | GList * 115 | gepub_archive_list_files (GepubArchive *archive) 116 | { 117 | struct archive_entry *entry; 118 | GList *file_list = NULL; 119 | 120 | if (!gepub_archive_open (archive)) 121 | return NULL; 122 | while (archive_read_next_header (archive->archive, &entry) == ARCHIVE_OK) { 123 | file_list = g_list_prepend (file_list, g_strdup (archive_entry_pathname (entry))); 124 | archive_read_data_skip (archive->archive); 125 | } 126 | gepub_archive_close (archive); 127 | 128 | return file_list; 129 | } 130 | 131 | GBytes * 132 | gepub_archive_read_entry (GepubArchive *archive, 133 | const gchar *path) 134 | { 135 | struct archive_entry *entry; 136 | guchar *buffer; 137 | gint size; 138 | const gchar *_path; 139 | 140 | if (path[0] == '/') { 141 | _path = path + 1; 142 | } 143 | else { 144 | _path = path; 145 | } 146 | 147 | if (!gepub_archive_open (archive)) 148 | return NULL; 149 | 150 | while (archive_read_next_header (archive->archive, &entry) == ARCHIVE_OK) { 151 | if (g_ascii_strcasecmp (_path, archive_entry_pathname (entry)) == 0) 152 | break; 153 | archive_read_data_skip (archive->archive); 154 | } 155 | 156 | size = archive_entry_size (entry); 157 | buffer = g_malloc0 (size); 158 | archive_read_data (archive->archive, buffer, size); 159 | 160 | gepub_archive_close (archive); 161 | return g_bytes_new_take (buffer, size); 162 | } 163 | 164 | gchar * 165 | gepub_archive_get_root_file (GepubArchive *archive) 166 | { 167 | xmlDoc *doc = NULL; 168 | xmlNode *root_element = NULL; 169 | xmlNode *root_node = NULL; 170 | GBytes *bytes; 171 | const gchar *buffer; 172 | gsize bufsize; 173 | gchar *root_file = NULL; 174 | 175 | // root file is in META-INF/container.xml 176 | bytes = gepub_archive_read_entry (archive, "META-INF/container.xml"); 177 | if (!bytes) 178 | return NULL; 179 | 180 | buffer = g_bytes_get_data (bytes, &bufsize); 181 | doc = xmlRecoverMemory (buffer, bufsize); 182 | root_element = xmlDocGetRootElement (doc); 183 | root_node = gepub_utils_get_element_by_tag (root_element, "rootfile"); 184 | root_file = gepub_utils_get_prop (root_node, "full-path"); 185 | 186 | xmlFreeDoc (doc); 187 | g_bytes_unref (bytes); 188 | 189 | return root_file; 190 | } 191 | -------------------------------------------------------------------------------- /libgepub/gepub-archive.h: -------------------------------------------------------------------------------- 1 | /* GepubArchive 2 | * 3 | * Copyright (C) 2011 Daniel Garcia 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #ifndef __GEPUB_ARCHIVE_H__ 21 | #define __GEPUB_ARCHIVE_H__ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | G_BEGIN_DECLS 28 | 29 | #define GEPUB_TYPE_ARCHIVE (gepub_archive_get_type ()) 30 | #define GEPUB_ARCHIVE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GEPUB_TYPE_ARCHIVE, GepubArchive)) 31 | #define GEPUB_ARCHIVE_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GEPUB_TYPE_ARCHIVE, GepubArchiveClass)) 32 | #define GEPUB_IS_ARCHIVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GEPUB_TYPE_ARCHIVE)) 33 | #define GEPUB_IS_ARCHIVE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GEPUB_TYPE_ARCHIVE)) 34 | #define GEPUB_ARCHIVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEPUB_TYPE_ARCHIVE, GepubArchiveClass)) 35 | 36 | typedef struct _GepubArchive GepubArchive; 37 | typedef struct _GepubArchiveClass GepubArchiveClass; 38 | 39 | GType gepub_archive_get_type (void) G_GNUC_CONST; 40 | 41 | GepubArchive *gepub_archive_new (const gchar *path); 42 | GList *gepub_archive_list_files (GepubArchive *archive); 43 | GBytes *gepub_archive_read_entry (GepubArchive *archive, 44 | const gchar *path); 45 | gchar *gepub_archive_get_root_file (GepubArchive *archive); 46 | 47 | G_END_DECLS 48 | 49 | #endif /* __GEPUB_ARCHIVE_H__ */ 50 | -------------------------------------------------------------------------------- /libgepub/gepub-doc.c: -------------------------------------------------------------------------------- 1 | /* GepubDoc 2 | * 3 | * Copyright (C) 2011 Daniel Garcia 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "gepub-utils.h" 26 | #include "gepub-doc.h" 27 | #include "gepub-archive.h" 28 | #include "gepub-text-chunk.h" 29 | 30 | 31 | static GQuark 32 | gepub_error_quark (void) 33 | { 34 | static GQuark q = 0; 35 | if (q == 0) 36 | q = g_quark_from_string ("gepub-quark"); 37 | return q; 38 | } 39 | 40 | /** 41 | * GepubDocError: 42 | * @GEPUB_ERROR_INVALID: Invalid file 43 | * 44 | * Common errors that may be reported by GepubDoc. 45 | */ 46 | typedef enum { 47 | GEPUB_ERROR_INVALID = 0, /*< nick=Invalid >*/ 48 | } GepubDocError; 49 | 50 | 51 | 52 | static void gepub_doc_fill_resources (GepubDoc *doc); 53 | static void gepub_doc_fill_spine (GepubDoc *doc); 54 | static void gepub_doc_fill_toc (GepubDoc *doc, gchar *toc_id); 55 | static void gepub_doc_initable_iface_init (GInitableIface *iface); 56 | static gint navpoint_compare (GepubNavPoint *a, GepubNavPoint *b); 57 | 58 | struct _GepubDoc { 59 | GObject parent; 60 | 61 | GepubArchive *archive; 62 | GBytes *content; 63 | gchar *content_base; 64 | gchar *path; 65 | GHashTable *resources; 66 | 67 | GList *spine; 68 | GList *chapter; 69 | GList *toc; 70 | }; 71 | 72 | struct _GepubDocClass { 73 | GObjectClass parent_class; 74 | }; 75 | 76 | enum { 77 | PROP_0, 78 | PROP_PATH, 79 | PROP_CHAPTER, 80 | NUM_PROPS 81 | }; 82 | 83 | static GParamSpec *properties[NUM_PROPS] = { NULL, }; 84 | 85 | G_DEFINE_TYPE_WITH_CODE (GepubDoc, gepub_doc, G_TYPE_OBJECT, 86 | G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gepub_doc_initable_iface_init)) 87 | 88 | static void 89 | gepub_resource_free (GepubResource *res) 90 | { 91 | g_free (res->mime); 92 | g_free (res->uri); 93 | g_free (res); 94 | } 95 | 96 | static void 97 | gepub_doc_finalize (GObject *object) 98 | { 99 | GepubDoc *doc = GEPUB_DOC (object); 100 | 101 | g_clear_object (&doc->archive); 102 | g_clear_pointer (&doc->content, g_bytes_unref); 103 | g_clear_pointer (&doc->path, g_free); 104 | g_clear_pointer (&doc->resources, g_hash_table_destroy); 105 | 106 | if (doc->spine) { 107 | g_list_foreach (doc->spine, (GFunc)g_free, NULL); 108 | g_clear_pointer (&doc->spine, g_list_free); 109 | g_list_foreach (doc->toc, (GFunc)g_free, NULL); 110 | g_clear_pointer (&doc->toc, g_list_free); 111 | } 112 | 113 | G_OBJECT_CLASS (gepub_doc_parent_class)->finalize (object); 114 | } 115 | 116 | static void 117 | gepub_doc_set_property (GObject *object, 118 | guint prop_id, 119 | const GValue *value, 120 | GParamSpec *pspec) 121 | { 122 | GepubDoc *doc = GEPUB_DOC (object); 123 | 124 | switch (prop_id) { 125 | case PROP_PATH: 126 | doc->path = g_value_dup_string (value); 127 | break; 128 | case PROP_CHAPTER: 129 | gepub_doc_set_chapter (doc, g_value_get_int (value)); 130 | break; 131 | default: 132 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 133 | break; 134 | } 135 | } 136 | 137 | static void 138 | gepub_doc_get_property (GObject *object, 139 | guint prop_id, 140 | GValue *value, 141 | GParamSpec *pspec) 142 | { 143 | GepubDoc *doc = GEPUB_DOC (object); 144 | 145 | switch (prop_id) { 146 | case PROP_PATH: 147 | g_value_set_string (value, doc->path); 148 | break; 149 | case PROP_CHAPTER: 150 | g_value_set_int (value, gepub_doc_get_chapter (doc)); 151 | break; 152 | default: 153 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 154 | break; 155 | } 156 | } 157 | 158 | static void 159 | gepub_doc_init (GepubDoc *doc) 160 | { 161 | /* doc resources hashtable: 162 | * id : (mime, path) 163 | */ 164 | doc->resources = g_hash_table_new_full (g_str_hash, 165 | g_str_equal, 166 | (GDestroyNotify)g_free, 167 | (GDestroyNotify)gepub_resource_free); 168 | } 169 | 170 | static void 171 | gepub_doc_class_init (GepubDocClass *klass) 172 | { 173 | GObjectClass *object_class = G_OBJECT_CLASS (klass); 174 | 175 | object_class->finalize = gepub_doc_finalize; 176 | object_class->set_property = gepub_doc_set_property; 177 | object_class->get_property = gepub_doc_get_property; 178 | 179 | properties[PROP_PATH] = 180 | g_param_spec_string ("path", 181 | "Path", 182 | "Path to the EPUB document", 183 | NULL, 184 | G_PARAM_READWRITE | 185 | G_PARAM_CONSTRUCT_ONLY | 186 | G_PARAM_STATIC_STRINGS); 187 | properties[PROP_CHAPTER] = 188 | g_param_spec_int ("chapter", 189 | "Current chapter", 190 | "The current chapter index", 191 | -1, G_MAXINT, 0, 192 | G_PARAM_READWRITE | 193 | G_PARAM_STATIC_STRINGS); 194 | 195 | g_object_class_install_properties (object_class, NUM_PROPS, properties); 196 | } 197 | 198 | static gboolean 199 | gepub_doc_initable_init (GInitable *initable, 200 | GCancellable *cancellable, 201 | GError **error) 202 | { 203 | GepubDoc *doc = GEPUB_DOC (initable); 204 | gchar *file; 205 | gint i = 0, len; 206 | g_autofree gchar *unescaped = NULL; 207 | 208 | g_assert (doc->path != NULL); 209 | 210 | doc->archive = gepub_archive_new (doc->path); 211 | file = gepub_archive_get_root_file (doc->archive); 212 | if (!file) { 213 | if (error != NULL) { 214 | g_set_error (error, gepub_error_quark (), GEPUB_ERROR_INVALID, 215 | "Invalid epub file: %s", doc->path); 216 | } 217 | return FALSE; 218 | } 219 | unescaped = g_uri_unescape_string (file, NULL); 220 | doc->content = gepub_archive_read_entry (doc->archive, unescaped); 221 | if (!doc->content) { 222 | if (error != NULL) { 223 | g_set_error (error, gepub_error_quark (), GEPUB_ERROR_INVALID, 224 | "Invalid epub file: %s", doc->path); 225 | } 226 | return FALSE; 227 | } 228 | 229 | len = strlen (file); 230 | doc->content_base = g_strdup (""); 231 | for (i=0; icontent_base); 234 | doc->content_base = g_strndup (file, i+1); 235 | break; 236 | } 237 | } 238 | 239 | gepub_doc_fill_resources (doc); 240 | gepub_doc_fill_spine (doc); 241 | 242 | g_free (file); 243 | 244 | return TRUE; 245 | } 246 | 247 | static void 248 | gepub_doc_initable_iface_init (GInitableIface *iface) 249 | { 250 | iface->init = gepub_doc_initable_init; 251 | } 252 | 253 | /** 254 | * gepub_doc_new: 255 | * @path: the epub doc path 256 | * @error: (nullable): Error 257 | * 258 | * Returns: (transfer full): the new GepubDoc created 259 | */ 260 | GepubDoc * 261 | gepub_doc_new (const gchar *path, GError **error) 262 | { 263 | return g_initable_new (GEPUB_TYPE_DOC, 264 | NULL, error, 265 | "path", path, 266 | NULL); 267 | } 268 | 269 | static void 270 | gepub_doc_fill_resources (GepubDoc *doc) 271 | { 272 | xmlDoc *xdoc = NULL; 273 | xmlNode *root_element = NULL; 274 | xmlNode *mnode = NULL; 275 | xmlNode *item = NULL; 276 | gchar *id, *tmpuri, *uri; 277 | GepubResource *res; 278 | const char *data; 279 | gsize size; 280 | 281 | data = g_bytes_get_data (doc->content, &size); 282 | xdoc = xmlRecoverMemory (data, size); 283 | root_element = xmlDocGetRootElement (xdoc); 284 | mnode = gepub_utils_get_element_by_tag (root_element, "manifest"); 285 | 286 | item = mnode->children; 287 | while (item) { 288 | if (item->type != XML_ELEMENT_NODE ) { 289 | item = item->next; 290 | continue; 291 | } 292 | 293 | id = gepub_utils_get_prop (item, "id"); 294 | tmpuri = gepub_utils_get_prop (item, "href"); 295 | uri = g_strdup_printf ("%s%s", doc->content_base, tmpuri); 296 | g_free (tmpuri); 297 | 298 | res = g_malloc (sizeof (GepubResource)); 299 | res->mime = gepub_utils_get_prop (item, "media-type"); 300 | res->uri = uri; 301 | g_hash_table_insert (doc->resources, id, res); 302 | item = item->next; 303 | } 304 | 305 | xmlFreeDoc (xdoc); 306 | } 307 | 308 | static void 309 | gepub_doc_fill_spine (GepubDoc *doc) 310 | { 311 | xmlDoc *xdoc = NULL; 312 | xmlNode *root_element = NULL; 313 | xmlNode *snode = NULL; 314 | xmlNode *item = NULL; 315 | gchar *id; 316 | const char *data; 317 | gsize size; 318 | GList *spine = NULL; 319 | gchar *toc = NULL; 320 | 321 | data = g_bytes_get_data (doc->content, &size); 322 | xdoc = xmlRecoverMemory (data, size); 323 | root_element = xmlDocGetRootElement (xdoc); 324 | snode = gepub_utils_get_element_by_tag (root_element, "spine"); 325 | 326 | toc = gepub_utils_get_prop (snode, "toc"); 327 | if (toc) { 328 | gepub_doc_fill_toc (doc, toc); 329 | g_free (toc); 330 | } 331 | 332 | item = snode->children; 333 | while (item) { 334 | if (item->type != XML_ELEMENT_NODE ) { 335 | item = item->next; 336 | continue; 337 | } 338 | 339 | id = gepub_utils_get_prop (item, "idref"); 340 | 341 | spine = g_list_prepend (spine, id); 342 | item = item->next; 343 | } 344 | 345 | doc->spine = g_list_reverse (spine); 346 | doc->chapter = doc->spine; 347 | 348 | xmlFreeDoc (xdoc); 349 | } 350 | 351 | static gint 352 | navpoint_compare (GepubNavPoint *a, GepubNavPoint *b) 353 | { 354 | return a->playorder - b->playorder; 355 | } 356 | 357 | static void 358 | gepub_doc_fill_toc (GepubDoc *doc, gchar *toc_id) 359 | { 360 | xmlDoc *xdoc = NULL; 361 | xmlNode *root_element = NULL; 362 | xmlNode *mapnode = NULL; 363 | xmlNode *item = NULL; 364 | const char *data; 365 | gsize size; 366 | GList *toc = NULL; 367 | GBytes *toc_data = NULL; 368 | 369 | doc->toc = toc; 370 | 371 | toc_data = gepub_doc_get_resource_by_id (doc, toc_id); 372 | if (!toc_data) { 373 | return; 374 | } 375 | 376 | data = g_bytes_get_data (toc_data, &size); 377 | xdoc = xmlRecoverMemory (data, size); 378 | root_element = xmlDocGetRootElement (xdoc); 379 | mapnode = gepub_utils_get_element_by_tag (root_element, "navMap"); 380 | 381 | // TODO: get docTitle 382 | // TODO: parse metadata (dtb:totalPageCount, dtb:depth, dtb:maxPageNumber) 383 | 384 | item = mapnode->children; 385 | while (item) { 386 | GepubNavPoint *navpoint = NULL; 387 | gchar *order; 388 | xmlNode *navchilds = NULL; 389 | 390 | if (item->type != XML_ELEMENT_NODE || 391 | g_strcmp0 ((const gchar *)item->name, "navPoint")) { 392 | item = item->next; 393 | continue; 394 | } 395 | 396 | navpoint = g_malloc0 (sizeof (GepubNavPoint)); 397 | 398 | order = gepub_utils_get_prop (item, "playOrder"); 399 | if (order) { 400 | g_ascii_string_to_unsigned (order, 10, 0, INT_MAX, 401 | &navpoint->playorder, NULL); 402 | g_free (order); 403 | } 404 | 405 | // parsing navPoint->navLabel->text and navPoint->content 406 | navchilds = item->children; 407 | while (navchilds) { 408 | if (item->type != XML_ELEMENT_NODE) { 409 | navchilds = navchilds->next; 410 | continue; 411 | } 412 | 413 | if (!g_strcmp0 ((const gchar *)navchilds->name, "content")) { 414 | gchar **split; 415 | gchar *tmpuri; 416 | tmpuri = gepub_utils_get_prop (navchilds, "src"); 417 | // removing # params. Maybe we should store the # params in the 418 | // navpoint to use in the future if the doc references to a position 419 | // inside the chapter 420 | split = g_strsplit (tmpuri, "#", -1); 421 | 422 | // adding the base path 423 | navpoint->content = g_strdup_printf ("%s%s", doc->content_base, split[0]); 424 | 425 | g_strfreev (split); 426 | g_free (tmpuri); 427 | } 428 | 429 | if (!g_strcmp0 ((const gchar *)navchilds->name, "navLabel")) { 430 | xmlNode *text = gepub_utils_get_element_by_tag (navchilds, "text"); 431 | if (text->children && text->children->type == XML_TEXT_NODE) { 432 | navpoint->label = g_strdup ((gchar *)text->children->content); 433 | } 434 | } 435 | 436 | navchilds = navchilds->next; 437 | } 438 | 439 | toc = g_list_prepend (toc, navpoint); 440 | item = item->next; 441 | } 442 | 443 | doc->toc = g_list_sort (toc, (GCompareFunc) navpoint_compare); 444 | 445 | xmlFreeDoc (xdoc); 446 | g_bytes_unref (toc_data); 447 | } 448 | 449 | /** 450 | * gepub_doc_get_content: 451 | * @doc: a #GepubDoc 452 | * 453 | * Returns: (transfer none): the document content 454 | */ 455 | GBytes * 456 | gepub_doc_get_content (GepubDoc *doc) 457 | { 458 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 459 | 460 | return doc->content; 461 | } 462 | 463 | /** 464 | * gepub_doc_get_metadata: 465 | * @doc: a #GepubDoc 466 | * @mdata: a metadata name string, GEPUB_META_TITLE for example 467 | * 468 | * Returns: (transfer full): metadata string 469 | */ 470 | gchar * 471 | gepub_doc_get_metadata (GepubDoc *doc, const gchar *mdata) 472 | { 473 | xmlDoc *xdoc = NULL; 474 | xmlNode *root_element = NULL; 475 | xmlNode *mnode = NULL; 476 | xmlNode *mdata_node = NULL; 477 | gchar *ret; 478 | xmlChar *text; 479 | const char *data; 480 | gsize size; 481 | 482 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 483 | g_return_val_if_fail (mdata != NULL, NULL); 484 | 485 | data = g_bytes_get_data (doc->content, &size); 486 | xdoc = xmlRecoverMemory (data, size); 487 | root_element = xmlDocGetRootElement (xdoc); 488 | mnode = gepub_utils_get_element_by_tag (root_element, "metadata"); 489 | mdata_node = gepub_utils_get_element_by_tag (mnode, mdata); 490 | 491 | text = xmlNodeGetContent (mdata_node); 492 | ret = g_strdup ((const char *) text); 493 | xmlFree (text); 494 | 495 | xmlFreeDoc (xdoc); 496 | 497 | return ret; 498 | } 499 | 500 | /** 501 | * gepub_doc_get_resources: 502 | * @doc: a #GepubDoc 503 | * 504 | * Returns: (element-type utf8 Gepub.Resource) (transfer none): doc resource table 505 | */ 506 | GHashTable * 507 | gepub_doc_get_resources (GepubDoc *doc) 508 | { 509 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 510 | 511 | return doc->resources; 512 | } 513 | 514 | /** 515 | * gepub_doc_get_resource_by_id: 516 | * @doc: a #GepubDoc 517 | * @id: the resource id 518 | * 519 | * Returns: (transfer full): the resource content 520 | */ 521 | GBytes * 522 | gepub_doc_get_resource_by_id (GepubDoc *doc, const gchar *id) 523 | { 524 | GepubResource *gres; 525 | g_autofree gchar *unescaped = NULL; 526 | 527 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 528 | g_return_val_if_fail (id != NULL, NULL); 529 | 530 | gres = g_hash_table_lookup (doc->resources, id); 531 | if (!gres) { 532 | // not found 533 | return NULL; 534 | } 535 | 536 | unescaped = g_uri_unescape_string (gres->uri, NULL); 537 | return gepub_archive_read_entry (doc->archive, unescaped); 538 | } 539 | 540 | /** 541 | * gepub_doc_get_resource: 542 | * @doc: a #GepubDoc 543 | * @path: the resource path 544 | * 545 | * Returns: (transfer full): the resource content 546 | */ 547 | GBytes * 548 | gepub_doc_get_resource (GepubDoc *doc, const gchar *path) 549 | { 550 | g_autofree gchar *unescaped = NULL; 551 | 552 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 553 | g_return_val_if_fail (path != NULL, NULL); 554 | 555 | // we need to decode the path because we can get URL encoded paths 556 | // like "some%20text.jpg" 557 | unescaped = g_uri_unescape_string (path, NULL); 558 | 559 | return gepub_archive_read_entry (doc->archive, unescaped); 560 | } 561 | 562 | /** 563 | * gepub_doc_get_resource_mime_by_id: 564 | * @doc: a #GepubDoc 565 | * @id: the resource id 566 | * 567 | * Returns: (transfer full): the resource content 568 | */ 569 | gchar * 570 | gepub_doc_get_resource_mime_by_id (GepubDoc *doc, const gchar *id) 571 | { 572 | GepubResource *gres; 573 | 574 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 575 | g_return_val_if_fail (id != NULL, NULL); 576 | 577 | gres = g_hash_table_lookup (doc->resources, id); 578 | if (!gres) { 579 | // not found 580 | return NULL; 581 | } 582 | 583 | return g_strdup (gres->mime); 584 | } 585 | 586 | /** 587 | * gepub_doc_get_resource_mime: 588 | * @doc: a #GepubDoc 589 | * @path: the resource path 590 | * 591 | * Returns: (transfer full): the resource mime 592 | */ 593 | gchar * 594 | gepub_doc_get_resource_mime (GepubDoc *doc, const gchar *path) 595 | { 596 | GepubResource *gres; 597 | GList *keys; 598 | const gchar *_path; 599 | 600 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 601 | g_return_val_if_fail (path != NULL, NULL); 602 | 603 | if (path[0] == '/') { 604 | _path = path + 1; 605 | } 606 | else { 607 | _path = path; 608 | } 609 | 610 | keys = g_hash_table_get_keys (doc->resources); 611 | 612 | while (keys) { 613 | gres = ((GepubResource*)g_hash_table_lookup (doc->resources, keys->data)); 614 | if (!strcmp (gres->uri, _path)) 615 | break; 616 | keys = keys->next; 617 | } 618 | 619 | if (keys) 620 | return g_strdup (gres->mime); 621 | else 622 | return NULL; 623 | } 624 | 625 | /** 626 | * gepub_doc_get_current_mime: 627 | * @doc: a #GepubDoc 628 | * 629 | * Returns: (transfer full): the current resource mime 630 | */ 631 | gchar * 632 | gepub_doc_get_current_mime (GepubDoc *doc) 633 | { 634 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 635 | g_return_val_if_fail (doc->chapter != NULL, NULL); 636 | 637 | return gepub_doc_get_resource_mime_by_id (doc, doc->chapter->data); 638 | } 639 | 640 | /** 641 | * gepub_doc_get_current: 642 | * @doc: a #GepubDoc 643 | * 644 | * Returns: (transfer full): the current chapter data 645 | */ 646 | GBytes * 647 | gepub_doc_get_current (GepubDoc *doc) 648 | { 649 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 650 | g_return_val_if_fail (doc->chapter != NULL, NULL); 651 | 652 | return gepub_doc_get_resource_by_id (doc, doc->chapter->data); 653 | } 654 | 655 | /** 656 | * gepub_doc_get_current_with_epub_uris: 657 | * @doc: a #GepubDoc 658 | * 659 | * Returns: (transfer full): the current chapter 660 | * data, with resource uris renamed so they have the epub:/// prefix and all 661 | * are relative to the root file 662 | */ 663 | GBytes * 664 | gepub_doc_get_current_with_epub_uris (GepubDoc *doc) 665 | { 666 | GBytes *content, *replaced; 667 | gchar *path, *base; 668 | 669 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 670 | 671 | content = gepub_doc_get_current (doc); 672 | path = gepub_doc_get_current_path (doc); 673 | // getting the basepath of the current xhtml loaded 674 | base = g_path_get_dirname (path); 675 | 676 | replaced = gepub_utils_replace_resources (content, base); 677 | 678 | g_free (base); 679 | g_free (path); 680 | g_bytes_unref (content); 681 | 682 | return replaced; 683 | } 684 | 685 | /** 686 | * gepub_doc_get_text: 687 | * @doc: a #GepubDoc 688 | * 689 | * Returns: (element-type Gepub.TextChunk) (transfer full): the list of text in the current chapter. 690 | */ 691 | GList * 692 | gepub_doc_get_text (GepubDoc *doc) 693 | { 694 | xmlDoc *xdoc = NULL; 695 | xmlNode *root_element = NULL; 696 | GBytes *current; 697 | const gchar *data; 698 | gsize size; 699 | 700 | GList *texts = NULL; 701 | 702 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 703 | 704 | current = gepub_doc_get_current (doc); 705 | if (!current) { 706 | return NULL; 707 | } 708 | data = g_bytes_get_data (current, &size); 709 | xdoc = htmlReadMemory (data, size, "", NULL, HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR); 710 | root_element = xmlDocGetRootElement (xdoc); 711 | texts = gepub_utils_get_text_elements (root_element); 712 | 713 | g_bytes_unref (current); 714 | xmlFreeDoc (xdoc); 715 | 716 | return texts; 717 | } 718 | 719 | /** 720 | * gepub_doc_get_text_by_id: 721 | * @doc: a #GepubDoc 722 | * @id: the resource id 723 | * 724 | * Returns: (element-type Gepub.TextChunk) (transfer full): the list of text in the current chapter. 725 | */ 726 | GList * 727 | gepub_doc_get_text_by_id (GepubDoc *doc, const gchar *id) 728 | { 729 | xmlDoc *xdoc = NULL; 730 | xmlNode *root_element = NULL; 731 | gsize size; 732 | const gchar *res; 733 | GBytes *contents; 734 | 735 | GList *texts = NULL; 736 | 737 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 738 | g_return_val_if_fail (id != NULL, NULL); 739 | 740 | contents = gepub_doc_get_resource_by_id (doc, id); 741 | if (!contents) { 742 | return NULL; 743 | } 744 | 745 | res = g_bytes_get_data (contents, &size); 746 | xdoc = htmlReadMemory (res, size, "", NULL, HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR); 747 | root_element = xmlDocGetRootElement (xdoc); 748 | texts = gepub_utils_get_text_elements (root_element); 749 | 750 | g_bytes_unref (contents); 751 | xmlFreeDoc (xdoc); 752 | 753 | return texts; 754 | } 755 | 756 | static gboolean 757 | gepub_doc_set_chapter_internal (GepubDoc *doc, 758 | GList *chapter) 759 | { 760 | if (!chapter || doc->chapter == chapter) 761 | return FALSE; 762 | 763 | doc->chapter = chapter; 764 | g_object_notify_by_pspec (G_OBJECT (doc), properties[PROP_CHAPTER]); 765 | 766 | return TRUE; 767 | } 768 | 769 | /** 770 | * gepub_doc_go_next: 771 | * @doc: a #GepubDoc 772 | * 773 | * Returns: TRUE on success, FALSE if there's no next chapter 774 | */ 775 | gboolean 776 | gepub_doc_go_next (GepubDoc *doc) 777 | { 778 | g_return_val_if_fail (GEPUB_IS_DOC (doc), FALSE); 779 | g_return_val_if_fail (doc->chapter != NULL, FALSE); 780 | 781 | return gepub_doc_set_chapter_internal (doc, doc->chapter->next); 782 | } 783 | 784 | /** 785 | * gepub_doc_go_prev: 786 | * @doc: a #GepubDoc 787 | * 788 | * Returns: TRUE on success, FALSE if there's no previous chapter 789 | */ 790 | gboolean 791 | gepub_doc_go_prev (GepubDoc *doc) 792 | { 793 | g_return_val_if_fail (GEPUB_IS_DOC (doc), FALSE); 794 | g_return_val_if_fail (doc->chapter != NULL, FALSE); 795 | 796 | return gepub_doc_set_chapter_internal (doc, doc->chapter->prev); 797 | } 798 | 799 | /** 800 | * gepub_doc_get_n_chapters: 801 | * @doc: a #GepubDoc 802 | * 803 | * Returns: the number of chapters in the document 804 | */ 805 | int 806 | gepub_doc_get_n_chapters (GepubDoc *doc) 807 | { 808 | g_return_val_if_fail (GEPUB_IS_DOC (doc), 0); 809 | 810 | return g_list_length (doc->spine); 811 | } 812 | 813 | /** 814 | * gepub_doc_get_chapter: 815 | * @doc: a #GepubDoc 816 | * 817 | * Returns: the current chapter index, starting from 0 818 | */ 819 | int 820 | gepub_doc_get_chapter (GepubDoc *doc) 821 | { 822 | g_return_val_if_fail (GEPUB_IS_DOC (doc), 0); 823 | g_return_val_if_fail (doc->spine != NULL, 0); 824 | g_return_val_if_fail (doc->chapter != NULL, 0); 825 | 826 | return g_list_position (doc->spine, doc->chapter); 827 | } 828 | 829 | /** 830 | * gepub_doc_set_chapter: 831 | * @doc: a #GepubDoc 832 | * @index: the index of the new chapter 833 | * 834 | * Sets the document current chapter to @index. 835 | */ 836 | void 837 | gepub_doc_set_chapter (GepubDoc *doc, 838 | gint index) 839 | { 840 | GList *chapter; 841 | 842 | g_return_if_fail (GEPUB_IS_DOC (doc)); 843 | 844 | g_return_if_fail (index >= 0 && index <= gepub_doc_get_n_chapters (doc)); 845 | 846 | chapter = g_list_nth (doc->spine, index); 847 | gepub_doc_set_chapter_internal (doc, chapter); 848 | } 849 | 850 | /** 851 | * gepub_doc_get_cover: 852 | * @doc: a #GepubDoc 853 | * 854 | * Returns: (transfer full): cover file path to retrieve with 855 | * gepub_doc_get_resource 856 | */ 857 | gchar * 858 | gepub_doc_get_cover (GepubDoc *doc) 859 | { 860 | xmlDoc *xdoc = NULL; 861 | xmlNode *root_element = NULL; 862 | xmlNode *mnode = NULL; 863 | gchar *ret; 864 | const char *data; 865 | gsize size; 866 | 867 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 868 | g_return_val_if_fail (doc->content != NULL, NULL); 869 | 870 | data = g_bytes_get_data (doc->content, &size); 871 | xdoc = xmlRecoverMemory (data, size); 872 | root_element = xmlDocGetRootElement (xdoc); 873 | mnode = gepub_utils_get_element_by_attr (root_element, "name", "cover"); 874 | ret = gepub_utils_get_prop (mnode, "content"); 875 | 876 | xmlFreeDoc (xdoc); 877 | 878 | return ret; 879 | } 880 | 881 | /** 882 | * gepub_doc_get_resource_path: 883 | * @doc: a #GepubDoc 884 | * @id: the resource id 885 | * 886 | * Returns: (transfer full): the resource path 887 | */ 888 | gchar * 889 | gepub_doc_get_resource_path (GepubDoc *doc, const gchar *id) 890 | { 891 | GepubResource *gres; 892 | 893 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 894 | g_return_val_if_fail (id != NULL, NULL); 895 | 896 | gres = g_hash_table_lookup (doc->resources, id); 897 | if (!gres) { 898 | // not found 899 | return NULL; 900 | } 901 | 902 | return g_strdup (gres->uri); 903 | } 904 | 905 | /** 906 | * gepub_doc_get_current_path: 907 | * @doc: a #GepubDoc 908 | * 909 | * Returns: (transfer full): the current resource path 910 | */ 911 | gchar * 912 | gepub_doc_get_current_path (GepubDoc *doc) 913 | { 914 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 915 | g_return_val_if_fail (doc->chapter != NULL, NULL); 916 | 917 | return gepub_doc_get_resource_path (doc, doc->chapter->data); 918 | } 919 | 920 | /** 921 | * gepub_doc_get_current_id: 922 | * @doc: a #GepubDoc 923 | * 924 | 925 | * Returns: (transfer none): the current resource id 926 | */ 927 | const gchar * 928 | gepub_doc_get_current_id (GepubDoc *doc) 929 | { 930 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 931 | g_return_val_if_fail (doc->chapter != NULL, NULL); 932 | 933 | return doc->chapter->data; 934 | } 935 | 936 | /** 937 | * gepub_doc_get_toc: 938 | * @doc: a #GepubDoc 939 | * 940 | 941 | * Returns: (element-type Gepub.NavPoint) (transfer none): the navigation list in order 942 | */ 943 | GList * 944 | gepub_doc_get_toc (GepubDoc *doc) 945 | { 946 | g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL); 947 | return doc->toc; 948 | } 949 | 950 | /** 951 | * gepub_doc_resource_uri_to_chapter: 952 | * @doc: a #GepubDoc 953 | * @uri: The resource path 954 | * 955 | * This method tries to find the resource by path in the doc spine and 956 | * will return the index in that list. If the resourse isn't there this method 957 | * will return -1. 958 | 959 | * Returns: the chapter index to use with gepub_doc_set_chapter or -1 if the 960 | * resource isn't found 961 | */ 962 | gint 963 | gepub_doc_resource_uri_to_chapter (GepubDoc *doc, 964 | const gchar *uri) 965 | { 966 | GHashTableIter iter; 967 | gchar *key; 968 | GepubResource *res; 969 | gchar *id = NULL; 970 | const gchar *_uri; 971 | 972 | if (uri[0] == '/') { 973 | _uri = uri + 1; 974 | } 975 | else { 976 | _uri = uri; 977 | } 978 | 979 | g_return_val_if_fail (GEPUB_IS_DOC (doc), -1); 980 | g_return_val_if_fail (doc->spine != NULL, -1); 981 | 982 | g_hash_table_iter_init (&iter, doc->resources); 983 | while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&res)) { 984 | if (!g_strcmp0 (res->uri, _uri)) { 985 | id = key; 986 | break; 987 | } 988 | } 989 | 990 | if (!id) { 991 | return -1; 992 | } 993 | 994 | return gepub_doc_resource_id_to_chapter (doc, id); 995 | } 996 | 997 | /** 998 | * gepub_doc_resource_id_to_chapter: 999 | * @doc: a #GepubDoc 1000 | * @id: The resource id 1001 | * 1002 | * This method tries to find the resource by id in the doc spine and 1003 | * will return the index in that list. If the resourse isn't there this method 1004 | * will return -1. 1005 | 1006 | * Returns: the chapter index to use with gepub_doc_set_chapter or -1 if the 1007 | * resource isn't found 1008 | */ 1009 | gint 1010 | gepub_doc_resource_id_to_chapter (GepubDoc *doc, 1011 | const gchar *id) 1012 | { 1013 | GList *spine; 1014 | gint chapter = 0; 1015 | 1016 | g_return_val_if_fail (GEPUB_IS_DOC (doc), -1); 1017 | g_return_val_if_fail (doc->spine != NULL, -1); 1018 | 1019 | spine = g_list_first (doc->spine); 1020 | while (spine && spine->data) { 1021 | if (!g_strcmp0 (spine->data, id)) { 1022 | return chapter; 1023 | } 1024 | chapter++; 1025 | spine = spine->next; 1026 | } 1027 | 1028 | return -1; 1029 | } 1030 | 1031 | -------------------------------------------------------------------------------- /libgepub/gepub-doc.h: -------------------------------------------------------------------------------- 1 | /* GepubDoc 2 | * 3 | * Copyright (C) 2011 Daniel Garcia 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #ifndef __GEPUB_DOC_H__ 21 | #define __GEPUB_DOC_H__ 22 | 23 | #include 24 | #include 25 | 26 | G_BEGIN_DECLS 27 | 28 | #define GEPUB_TYPE_DOC (gepub_doc_get_type ()) 29 | #define GEPUB_DOC(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GEPUB_TYPE_DOC, GepubDoc)) 30 | #define GEPUB_DOC_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GEPUB_TYPE_DOC, GepubDocClass)) 31 | #define GEPUB_IS_DOC(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GEPUB_TYPE_DOC)) 32 | #define GEPUB_IS_DOC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GEPUB_TYPE_DOC)) 33 | #define GEPUB_DOC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEPUB_TYPE_DOC, GepubDocClass)) 34 | 35 | typedef struct _GepubDoc GepubDoc; 36 | typedef struct _GepubDocClass GepubDocClass; 37 | 38 | struct _GepubResource { 39 | gchar *mime; 40 | gchar *uri; 41 | }; 42 | 43 | struct _GepubNavPoint { 44 | gchar *label; 45 | gchar *content; 46 | guint64 playorder; 47 | }; 48 | 49 | typedef struct _GepubResource GepubResource; 50 | typedef struct _GepubNavPoint GepubNavPoint; 51 | 52 | GType gepub_doc_get_type (void) G_GNUC_CONST; 53 | 54 | GepubDoc *gepub_doc_new (const gchar *path, GError **error); 55 | GBytes *gepub_doc_get_content (GepubDoc *doc); 56 | gchar *gepub_doc_get_metadata (GepubDoc *doc, const gchar *mdata); 57 | GBytes *gepub_doc_get_resource (GepubDoc *doc, const gchar *path); 58 | GBytes *gepub_doc_get_resource_by_id (GepubDoc *doc, const gchar *id); 59 | GHashTable *gepub_doc_get_resources (GepubDoc *doc); 60 | gchar *gepub_doc_get_resource_mime (GepubDoc *doc, const gchar *path); 61 | gchar *gepub_doc_get_resource_mime_by_id (GepubDoc *doc, const gchar *id); 62 | gchar *gepub_doc_get_current_mime (GepubDoc *doc); 63 | GList *gepub_doc_get_text (GepubDoc *doc); 64 | GList *gepub_doc_get_text_by_id (GepubDoc *doc, const gchar *id); 65 | GBytes *gepub_doc_get_current (GepubDoc *doc); 66 | GBytes *gepub_doc_get_current_with_epub_uris (GepubDoc *doc); 67 | gchar *gepub_doc_get_cover (GepubDoc *doc); 68 | gchar *gepub_doc_get_resource_path (GepubDoc *doc, const gchar *id); 69 | gchar *gepub_doc_get_current_path (GepubDoc *doc); 70 | const gchar *gepub_doc_get_current_id (GepubDoc *doc); 71 | 72 | gboolean gepub_doc_go_next (GepubDoc *doc); 73 | gboolean gepub_doc_go_prev (GepubDoc *doc); 74 | gint gepub_doc_get_n_chapters (GepubDoc *doc); 75 | gint gepub_doc_get_chapter (GepubDoc *doc); 76 | void gepub_doc_set_chapter (GepubDoc *doc, 77 | gint index); 78 | 79 | GList *gepub_doc_get_toc (GepubDoc *doc); 80 | gint gepub_doc_resource_uri_to_chapter (GepubDoc *doc, 81 | const gchar *uri); 82 | gint gepub_doc_resource_id_to_chapter (GepubDoc *doc, 83 | const gchar *id); 84 | 85 | G_END_DECLS 86 | 87 | /** 88 | * GEPUB_META_TITLE: 89 | * The book title. 90 | */ 91 | #define GEPUB_META_TITLE "title" 92 | 93 | /** 94 | * GEPUB_META_LANG: 95 | * The book lang. 96 | */ 97 | #define GEPUB_META_LANG "language" 98 | 99 | /** 100 | * GEPUB_META_ID: 101 | * The book id. 102 | */ 103 | #define GEPUB_META_ID "identifier" 104 | 105 | /** 106 | * GEPUB_META_AUTHOR: 107 | * The book author. 108 | */ 109 | #define GEPUB_META_AUTHOR "creator" 110 | 111 | /** 112 | * GEPUB_META_DESC: 113 | * The book description. 114 | */ 115 | #define GEPUB_META_DESC "description" 116 | 117 | #endif /* __GEPUB_DOC_H__ */ 118 | 119 | -------------------------------------------------------------------------------- /libgepub/gepub-text-chunk.c: -------------------------------------------------------------------------------- 1 | /* GepubTextChunk 2 | * 3 | * Copyright (C) 2011 Daniel Garcia 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #include 21 | #include "gepub-text-chunk.h" 22 | 23 | 24 | G_DEFINE_TYPE (GepubTextChunk, gepub_text_chunk, G_TYPE_OBJECT) 25 | 26 | 27 | static void 28 | gepub_text_chunk_finalize (GObject *object) 29 | { 30 | GepubTextChunk *t = GEPUB_TEXT_CHUNK (object); 31 | 32 | g_clear_pointer (&t->text, g_free); 33 | 34 | G_OBJECT_CLASS (gepub_text_chunk_parent_class)->finalize (object); 35 | } 36 | 37 | static void 38 | gepub_text_chunk_init (GepubTextChunk *t) 39 | { 40 | } 41 | 42 | static void 43 | gepub_text_chunk_class_init (GepubTextChunkClass *klass) 44 | { 45 | GObjectClass *object_class = G_OBJECT_CLASS (klass); 46 | object_class->finalize = gepub_text_chunk_finalize; 47 | } 48 | 49 | /** 50 | * gepub_text_chunk_new: 51 | * @type: a #GepubTextChunkType, the chunk type 52 | * @text: the chunk text 53 | * 54 | * Returns: (transfer none): the chunk text 55 | */ 56 | GepubTextChunk * 57 | gepub_text_chunk_new (GepubTextChunkType type, const gchar *text) 58 | { 59 | GepubTextChunk *t; 60 | 61 | t = GEPUB_TEXT_CHUNK (g_object_new (GEPUB_TYPE_TEXT_CHUNK, NULL)); 62 | t->type = type; 63 | t->text = g_strdup (text); 64 | 65 | return t; 66 | } 67 | 68 | /** 69 | * gepub_text_chunk_type_str: 70 | * @chunk: a #GepubTextChunk 71 | * 72 | * Returns: the chunk text 73 | */ 74 | const char * 75 | gepub_text_chunk_type_str (GepubTextChunk *chunk) 76 | { 77 | switch (chunk->type) { 78 | case GEPUBTextBold: 79 | return "bold"; 80 | case GEPUBTextItalic: 81 | return "italic"; 82 | case GEPUBTextHeader: 83 | return "header"; 84 | case GEPUBTextNormal: 85 | default: 86 | return "normal"; 87 | } 88 | } 89 | 90 | /** 91 | * gepub_text_chunk_text: 92 | * @chunk: a #GepubTextChunk 93 | * 94 | * Returns: the chunk text 95 | */ 96 | const char * 97 | gepub_text_chunk_text (GepubTextChunk *chunk) 98 | { 99 | return chunk->text; 100 | } 101 | 102 | /** 103 | * gepub_text_chunk_type: 104 | * @chunk: a #GepubTextChunk 105 | * 106 | * Returns: (transfer none): the chunk type 107 | */ 108 | GepubTextChunkType 109 | gepub_text_chunk_type (GepubTextChunk *chunk) 110 | { 111 | return chunk->type; 112 | } 113 | -------------------------------------------------------------------------------- /libgepub/gepub-text-chunk.h: -------------------------------------------------------------------------------- 1 | /* GepubTextChunk 2 | * 3 | * Copyright (C) 2011 Daniel Garcia 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #ifndef __GEPUB_TEXT_CHUNK_H__ 21 | #define __GEPUB_TEXT_CHUNK_H__ 22 | 23 | #include 24 | #include 25 | 26 | G_BEGIN_DECLS 27 | 28 | #define GEPUB_TYPE_TEXT_CHUNK (gepub_text_chunk_get_type ()) 29 | #define GEPUB_TEXT_CHUNK(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GEPUB_TYPE_TEXT_CHUNK, GepubTextChunk)) 30 | #define GEPUB_TEXT_CHUNK_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GEPUB_TYPE_TEXT_CHUNK, GepubTextChunkClass)) 31 | #define GEPUB_IS_TEXT_CHUNK(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GEPUB_TYPE_TEXT_CHUNK)) 32 | #define GEPUB_IS_TEXT_CHUNK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GEPUB_TYPE_TEXT_CHUNK)) 33 | #define GEPUB_TEXT_CHUNK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEPUB_TYPE_TEXT_CHUNK, GepubTextChunkClass)) 34 | 35 | typedef enum { 36 | GEPUBTextHeader, 37 | GEPUBTextBold, 38 | GEPUBTextItalic, 39 | GEPUBTextNormal 40 | } GepubTextChunkType; 41 | 42 | struct _GepubTextChunk { 43 | GObject parent; 44 | 45 | GepubTextChunkType type; 46 | gchar *text; 47 | }; 48 | 49 | struct _GepubTextChunkClass { 50 | GObjectClass parent_class; 51 | }; 52 | 53 | typedef struct _GepubTextChunk GepubTextChunk; 54 | typedef struct _GepubTextChunkClass GepubTextChunkClass; 55 | 56 | GType gepub_text_chunk_get_type (void) G_GNUC_CONST; 57 | GepubTextChunk *gepub_text_chunk_new (GepubTextChunkType type, const gchar *text); 58 | const char *gepub_text_chunk_type_str (GepubTextChunk *chunk); 59 | const char *gepub_text_chunk_text (GepubTextChunk *chunk); 60 | GepubTextChunkType gepub_text_chunk_type (GepubTextChunk *chunk); 61 | 62 | G_END_DECLS 63 | 64 | #endif /* __GEPUB_TEXT_CHUNK_H__ */ 65 | -------------------------------------------------------------------------------- /libgepub/gepub-utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Daniel Garcia 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * This library 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 GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "gepub-utils.h" 27 | #include "gepub-text-chunk.h" 28 | 29 | 30 | /* Replaces the attr value with epub:/// prefix for the tagname. This 31 | * function also makes the resource absolute based on the epub root 32 | */ 33 | static void 34 | set_epub_uri (xmlNode *node, 35 | const gchar *path, 36 | const gchar *tagname, 37 | const gchar *attr, 38 | const gchar *ns) 39 | { 40 | xmlNode *cur_node = NULL; 41 | xmlChar *text = NULL; 42 | 43 | gchar *attrname = NULL; 44 | 45 | SoupURI *baseURI; 46 | gchar *basepath = g_strdup_printf ("epub:///%s/", path); 47 | 48 | baseURI = soup_uri_new (basepath); 49 | g_free (basepath); 50 | 51 | if (ns) { 52 | attrname = g_strdup_printf ("%s:%s", ns, attr); 53 | } else { 54 | attrname = g_strdup (attr); 55 | } 56 | 57 | for (cur_node = node; cur_node; cur_node = cur_node->next) { 58 | if (cur_node->type == XML_ELEMENT_NODE ) { 59 | text = xmlGetProp (cur_node, BAD_CAST (attr)); 60 | 61 | if (!strcmp ((const char *) cur_node->name, tagname) && text && text[0] != '#') { 62 | SoupURI *uri = soup_uri_new_with_base (baseURI, (const char *) text); 63 | gchar *value = soup_uri_to_string (uri, FALSE); 64 | 65 | if (!g_str_equal (soup_uri_get_scheme (uri), "epub")) 66 | g_clear_pointer (&value, g_free); 67 | 68 | xmlSetProp (cur_node, BAD_CAST (attrname), BAD_CAST (value)); 69 | 70 | soup_uri_free (uri); 71 | g_free (value); 72 | } 73 | if (text) { 74 | xmlFree (text); 75 | text = NULL; 76 | } 77 | } 78 | 79 | if (cur_node->children) 80 | set_epub_uri (cur_node->children, path, tagname, attr, ns); 81 | } 82 | 83 | g_free (attrname); 84 | 85 | soup_uri_free (baseURI); 86 | } 87 | 88 | static gboolean 89 | gepub_utils_has_parent_tag (xmlNode *node, const char *name, ...) 90 | { 91 | va_list ap; 92 | 93 | xmlNode *cur_node = NULL; 94 | GList *tags = NULL; 95 | GList *l = NULL; 96 | const char *name2 = NULL; 97 | gboolean ret = FALSE; 98 | 99 | va_start (ap, name); 100 | 101 | for (name2 = name; name2 != NULL; name2 = va_arg(ap, gchar*)) { 102 | tags = g_list_append (tags, g_ascii_strdown (name2, -1)); 103 | } 104 | 105 | for (cur_node = node; cur_node; cur_node = cur_node->parent) { 106 | if (cur_node->type == XML_ELEMENT_NODE) { 107 | for (l = tags; l; l = l->next) { 108 | gchar *nodetag = g_ascii_strdown ((char *) cur_node->name, -1); 109 | name2 = l->data; 110 | 111 | if (!strcmp (nodetag, name2)) 112 | ret = TRUE; 113 | 114 | g_free (nodetag); 115 | 116 | if (ret == TRUE) 117 | goto out; 118 | } 119 | } 120 | } 121 | 122 | va_end (ap); 123 | 124 | out: 125 | g_list_free_full (tags, g_free); 126 | 127 | return ret; 128 | } 129 | 130 | /** 131 | * gepub_utils_get_element_by_tag: (skip): 132 | * @node: an #xmlNode 133 | * @name: the tag name 134 | * 135 | * Returns: the tag matching @name. 136 | */ 137 | xmlNode * 138 | gepub_utils_get_element_by_tag (xmlNode *node, const gchar *name) 139 | { 140 | xmlNode *cur_node = NULL; 141 | xmlNode *ret = NULL; 142 | 143 | for (cur_node = node; cur_node; cur_node = cur_node->next) { 144 | if (cur_node->type == XML_ELEMENT_NODE ) { 145 | if (!strcmp ((const char *) cur_node->name, name)) 146 | return cur_node; 147 | } 148 | 149 | ret = gepub_utils_get_element_by_tag (cur_node->children, name); 150 | if (ret) 151 | return ret; 152 | } 153 | return ret; 154 | } 155 | 156 | /** 157 | * gepub_utils_get_element_by_attr: (skip): 158 | * @node: an #xmlNode 159 | * @attr: the attribute 160 | * @value: the value 161 | * 162 | * Returns: the element matching @attr and @value. 163 | */ 164 | xmlNode * 165 | gepub_utils_get_element_by_attr (xmlNode *node, const gchar *attr, const gchar *value) 166 | { 167 | xmlNode *cur_node = NULL; 168 | xmlNode *ret = NULL; 169 | xmlChar *text = NULL; 170 | 171 | for (cur_node = node; cur_node; cur_node = cur_node->next) { 172 | if (cur_node->type == XML_ELEMENT_NODE ) { 173 | text = xmlGetProp (cur_node, BAD_CAST (attr)); 174 | if (text && !strcmp ((const char *) text, value)) { 175 | xmlFree (text); 176 | return cur_node; 177 | } 178 | if (text) { 179 | xmlFree (text); 180 | text = NULL; 181 | } 182 | } 183 | 184 | if (cur_node->children) 185 | ret = gepub_utils_get_element_by_attr (cur_node->children, attr, value); 186 | 187 | if (ret) 188 | return ret; 189 | } 190 | return ret; 191 | } 192 | 193 | /** 194 | * gepub_utils_get_text_elements: 195 | * @node: an #xmlNode 196 | * 197 | * Returns: (element-type Gepub.TextChunk) (transfer full): the text elements 198 | * of @node. 199 | */ 200 | GList * 201 | gepub_utils_get_text_elements (xmlNode *node) 202 | { 203 | GList *text_list = NULL; 204 | GList *sub_texts = NULL; 205 | 206 | xmlNode *cur_node = NULL; 207 | 208 | for (cur_node = node; cur_node; cur_node = cur_node->next) { 209 | if (cur_node->type == XML_TEXT_NODE) { 210 | GepubTextChunk *text_chunk = NULL; 211 | 212 | if (gepub_utils_has_parent_tag (cur_node, "b", "strong", NULL)) { 213 | text_chunk = gepub_text_chunk_new (GEPUBTextBold, (char *) cur_node->content); 214 | } else if (gepub_utils_has_parent_tag (cur_node, "i", "em", NULL)) { 215 | text_chunk = gepub_text_chunk_new (GEPUBTextItalic, (char *) cur_node->content); 216 | } else if (gepub_utils_has_parent_tag (cur_node, "h1", "h2", "h3", "h4", "h5", NULL)) { 217 | text_chunk = gepub_text_chunk_new (GEPUBTextHeader, (char *) cur_node->content); 218 | } else if (gepub_utils_has_parent_tag (cur_node, "p", NULL)) { 219 | text_chunk = gepub_text_chunk_new (GEPUBTextNormal, (char *) cur_node->content); 220 | } 221 | 222 | if (text_chunk) 223 | text_list = g_list_append (text_list, text_chunk); 224 | } 225 | 226 | if (cur_node->type == XML_ELEMENT_NODE) { 227 | GepubTextChunk *text_chunk = NULL; 228 | gchar *nodetag = g_ascii_strup ((const char *) cur_node->name, -1); 229 | if (text_list && (!strcmp (nodetag, "P") || !strcmp (nodetag, "BR"))) { 230 | gchar *old_text; 231 | text_chunk = (GepubTextChunk*)(g_list_last (text_list)->data); 232 | old_text = text_chunk->text; 233 | text_chunk->text = g_strdup_printf ("%s\n", old_text); 234 | g_free (old_text); 235 | } 236 | g_free (nodetag); 237 | } 238 | 239 | // TODO add images to this list of objects 240 | 241 | sub_texts = gepub_utils_get_text_elements (cur_node->children); 242 | if (sub_texts) 243 | text_list = g_list_concat (text_list, sub_texts); 244 | } 245 | 246 | return text_list; 247 | } 248 | 249 | /** 250 | * gepub_utils_replace_resources: 251 | * @content: a #GBytes containing the XML data 252 | * @path: The path to replace 253 | * 254 | * Replacing epub media paths, for css, image and svg files, to be 255 | * able to provide these files to webkit from the epub file. 256 | * 257 | * Returns: a new #GBytes containing the updated XML data 258 | */ 259 | GBytes * 260 | gepub_utils_replace_resources (GBytes *content, const gchar *path) 261 | { 262 | xmlDoc *doc = NULL; 263 | xmlNode *root_element = NULL; 264 | guchar *buffer; 265 | const gchar *data; 266 | gsize bufsize; 267 | 268 | data = g_bytes_get_data (content, &bufsize); 269 | doc = xmlReadMemory (data, bufsize, "", NULL, XML_PARSE_NOWARNING | XML_PARSE_NOERROR); 270 | root_element = xmlDocGetRootElement (doc); 271 | 272 | // replacing css resources 273 | set_epub_uri (root_element, path, "link", "href", NULL); 274 | // replacing images resources 275 | set_epub_uri (root_element, path, "img", "src", NULL); 276 | // replacing svg images resources 277 | set_epub_uri (root_element, path, "image", "href", "xlink"); 278 | // replacing crosslinks 279 | set_epub_uri (root_element, path, "a", "href", NULL); 280 | 281 | xmlDocDumpFormatMemory (doc, (xmlChar**)&buffer, (int*)&bufsize, 1); 282 | xmlFreeDoc (doc); 283 | 284 | return g_bytes_new_take (buffer, bufsize); 285 | } 286 | 287 | 288 | /** 289 | * gepub_utils_get_prop: 290 | * @node: an #xmlNode 291 | * @prop: a property 292 | * 293 | * Returns: a string with the property contained in @prop 294 | */ 295 | gchar * 296 | gepub_utils_get_prop (xmlNode *node, const gchar *prop) 297 | { 298 | xmlChar *p = NULL; 299 | gchar *ret = NULL; 300 | 301 | p = xmlGetProp (node, (const xmlChar *) prop); 302 | if (p) { 303 | ret = g_strdup ((char *) p); 304 | xmlFree (p); 305 | } 306 | 307 | return ret; 308 | } 309 | -------------------------------------------------------------------------------- /libgepub/gepub-utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Daniel Garcia 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * This library 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 GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #ifndef __GEPUB_UTILS_H__ 20 | #define __GEPUB_UTILS_H__ 21 | 22 | #include 23 | #include 24 | 25 | xmlNode * gepub_utils_get_element_by_tag (xmlNode *node, const gchar *name); 26 | xmlNode * gepub_utils_get_element_by_attr (xmlNode *node, const gchar *attr, const gchar *value); 27 | GList * gepub_utils_get_text_elements (xmlNode *node); 28 | GBytes * gepub_utils_replace_resources (GBytes *content, const gchar *path); 29 | gchar * gepub_utils_get_prop (xmlNode *node, const gchar *prop); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /libgepub/gepub-widget.c: -------------------------------------------------------------------------------- 1 | /* GepubWidget 2 | * 3 | * Copyright (C) 2016 Daniel Garcia 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "gepub-widget.h" 26 | 27 | struct _GepubWidget { 28 | WebKitWebView parent; 29 | 30 | GepubDoc *doc; 31 | gboolean paginate; 32 | gint chapter_length; // real chapter length 33 | gint chapter_pos; // position in the chapter, a percentage based on chapter_length 34 | gint length; 35 | gint init_chapter_pos; 36 | gint margin; // lateral margin in px 37 | gint font_size; // font size in pt 38 | gchar *font_family; 39 | gfloat line_height; 40 | }; 41 | 42 | struct _GepubWidgetClass { 43 | WebKitWebViewClass parent_class; 44 | }; 45 | 46 | enum { 47 | PROP_0, 48 | PROP_DOC, 49 | PROP_PAGINATE, 50 | PROP_CHAPTER, 51 | PROP_N_CHAPTERS, 52 | PROP_CHAPTER_POS, 53 | NUM_PROPS 54 | }; 55 | 56 | static GParamSpec *properties[NUM_PROPS] = { NULL, }; 57 | 58 | G_DEFINE_TYPE (GepubWidget, gepub_widget, WEBKIT_TYPE_WEB_VIEW) 59 | 60 | #define HUNDRED_PERCENT 100.0 61 | 62 | static void 63 | scroll_to_chapter_pos (GepubWidget *widget) { 64 | gchar *script = g_strdup_printf("document.querySelector('body').scrollTo(%d, 0)", widget->chapter_pos); 65 | webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (widget), script, NULL, NULL, NULL); 66 | g_free(script); 67 | } 68 | 69 | static void 70 | adjust_chapter_pos (GepubWidget *widget) 71 | { 72 | // integer division to make a page start 73 | gint page = widget->chapter_pos / widget->length; 74 | gint next = page + 1; 75 | gint d1 = widget->chapter_pos - (widget->length * page); 76 | gint d2 = (widget->length * next) - widget->chapter_pos; 77 | 78 | if (d1 < d2) { 79 | widget->chapter_pos = widget->length * page; 80 | } else { 81 | widget->chapter_pos = widget->length * next; 82 | } 83 | scroll_to_chapter_pos (widget); 84 | } 85 | 86 | static void 87 | pagination_initialize_finished (GObject *object, 88 | GAsyncResult *result, 89 | gpointer user_data) 90 | { 91 | WebKitJavascriptResult *js_result; 92 | JSCValue *value; 93 | GError *error = NULL; 94 | GepubWidget *widget = GEPUB_WIDGET (user_data); 95 | 96 | js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error); 97 | if (!js_result) { 98 | g_warning ("Error running javascript: %s", error->message); 99 | g_error_free (error); 100 | return; 101 | } 102 | 103 | value = webkit_javascript_result_get_js_value (js_result); 104 | if (jsc_value_is_number (value)) { 105 | double n; 106 | 107 | n = jsc_value_to_double (value); 108 | widget->chapter_length = (int)n; 109 | 110 | if (widget->init_chapter_pos) { 111 | widget->chapter_pos = widget->init_chapter_pos * widget->chapter_length / HUNDRED_PERCENT; 112 | if (widget->chapter_pos > (widget->chapter_length - widget->length)) { 113 | widget->chapter_pos = (widget->chapter_length - widget->length); 114 | } 115 | widget->init_chapter_pos = 0; 116 | } 117 | 118 | if (widget->chapter_pos) { 119 | adjust_chapter_pos (widget); 120 | } 121 | } else { 122 | g_warning ("Error running javascript: unexpected return value"); 123 | } 124 | webkit_javascript_result_unref (js_result); 125 | } 126 | 127 | static void 128 | get_length_finished (GObject *object, 129 | GAsyncResult *result, 130 | gpointer user_data) 131 | { 132 | WebKitJavascriptResult *js_result; 133 | JSCValue *value; 134 | GError *error = NULL; 135 | GepubWidget *widget = GEPUB_WIDGET (user_data); 136 | 137 | js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error); 138 | if (!js_result) { 139 | g_warning ("Error running javascript: %s", error->message); 140 | g_error_free (error); 141 | return; 142 | } 143 | 144 | value = webkit_javascript_result_get_js_value (js_result); 145 | if (jsc_value_is_number (value)) { 146 | double n; 147 | 148 | n = jsc_value_to_double (value); 149 | widget->length = (int)n; 150 | } else { 151 | g_warning ("Error running javascript: unexpected return value"); 152 | } 153 | webkit_javascript_result_unref (js_result); 154 | } 155 | 156 | static void 157 | reload_length_cb (GtkWidget *widget, 158 | GdkRectangle *allocation, 159 | gpointer user_data) 160 | { 161 | GepubWidget *gwidget = GEPUB_WIDGET (widget); 162 | WebKitWebView *web_view = WEBKIT_WEB_VIEW (widget); 163 | int margin, font_size; 164 | float line_height; 165 | gchar *script, *font_family; 166 | 167 | webkit_web_view_run_javascript (web_view, 168 | "window.innerWidth", 169 | NULL, get_length_finished, (gpointer)widget); 170 | 171 | margin = gwidget->margin; 172 | font_size = gwidget->font_size; 173 | font_family = gwidget->font_family; 174 | line_height = gwidget->line_height; 175 | 176 | script = g_strdup_printf ( 177 | "if (!document.querySelector('#gepubwrap'))" 178 | "document.body.innerHTML = '
' + document.body.innerHTML + '
';" 179 | 180 | "document.querySelector('#gepubwrap').style.marginLeft = '%dpx';" 181 | "document.querySelector('#gepubwrap').style.marginRight = '%dpx';" 182 | , margin, margin); 183 | webkit_web_view_run_javascript (web_view, script, NULL, NULL, NULL); 184 | g_free (script); 185 | 186 | if (font_size) { 187 | script = g_strdup_printf ( 188 | "document.querySelector('#gepubwrap').style.fontSize = '%dpt';" 189 | , font_size); 190 | webkit_web_view_run_javascript (web_view, script, NULL, NULL, NULL); 191 | g_free (script); 192 | } 193 | 194 | if (font_family) { 195 | script = g_strdup_printf ( 196 | "document.querySelector('#gepubwrap').style.fontFamily = '%s';" 197 | , font_family); 198 | webkit_web_view_run_javascript (web_view, script, NULL, NULL, NULL); 199 | g_free (script); 200 | } 201 | 202 | if (line_height) { 203 | gchar line_height_buffer[G_ASCII_DTOSTR_BUF_SIZE]; 204 | 205 | g_ascii_formatd (line_height_buffer, 206 | G_ASCII_DTOSTR_BUF_SIZE, 207 | "%f", 208 | line_height); 209 | script = g_strdup_printf ( 210 | "document.querySelector('#gepubwrap').style.lineHeight = %s;" 211 | , line_height_buffer); 212 | webkit_web_view_run_javascript (web_view, script, NULL, NULL, NULL); 213 | g_free (script); 214 | } 215 | 216 | if (gwidget->paginate) { 217 | webkit_web_view_run_javascript (web_view, 218 | "document.body.style.overflow = 'hidden';" 219 | "document.body.style.margin = '20px 0px 20px 0px';" 220 | "document.body.style.padding = '0px';" 221 | "document.body.style.columnWidth = window.innerWidth+'px';" 222 | "document.body.style.height = (window.innerHeight - 40) +'px';" 223 | "document.body.style.columnGap = '0px';" 224 | "document.body.scrollWidth", 225 | NULL, pagination_initialize_finished, (gpointer)widget); 226 | } 227 | } 228 | 229 | static void 230 | set_current_chapter_by_uri (WebKitWebView *web_view) 231 | { 232 | GepubWidget *widget = GEPUB_WIDGET (web_view); 233 | const gchar *uri_string; 234 | SoupURI *uri; 235 | const gchar *path; 236 | gint chapter; 237 | 238 | uri_string = webkit_web_view_get_uri (web_view); 239 | 240 | if (g_strcmp0 ("about:blank", uri_string)) { 241 | uri = soup_uri_new (uri_string); 242 | path = soup_uri_get_path (uri); 243 | chapter = gepub_doc_resource_uri_to_chapter (widget->doc, path); 244 | gepub_doc_set_chapter (widget->doc, chapter); 245 | soup_uri_free (uri); 246 | } 247 | // Else we're on the cover or table of contents (and can't tell which) 248 | // but we can only get there through setting the chapter number 249 | // so we don't need to do anything here 250 | } 251 | 252 | static void 253 | reload_current_chapter (GepubWidget *widget); 254 | 255 | static void 256 | docready_cb (WebKitWebView *web_view, 257 | WebKitLoadEvent load_event, 258 | gpointer user_data) 259 | { 260 | GepubWidget *widget = GEPUB_WIDGET (web_view); 261 | 262 | if (load_event == WEBKIT_LOAD_FINISHED) { 263 | reload_length_cb (GTK_WIDGET (widget), NULL, NULL); 264 | g_signal_handlers_disconnect_by_func (widget->doc, 265 | reload_current_chapter, widget); 266 | set_current_chapter_by_uri (web_view); 267 | g_signal_connect_swapped (widget->doc, "notify::chapter", 268 | G_CALLBACK (reload_current_chapter), widget); 269 | } 270 | } 271 | 272 | static void 273 | resource_callback (WebKitURISchemeRequest *request, gpointer user_data) 274 | { 275 | GInputStream *stream; 276 | gchar *path; 277 | gchar *mime; 278 | GepubWidget *widget = user_data; 279 | GBytes *contents; 280 | 281 | if (!widget->doc) 282 | return; 283 | 284 | path = g_strdup (webkit_uri_scheme_request_get_path (request)); 285 | contents = gepub_doc_get_resource (widget->doc, path); 286 | mime = gepub_doc_get_resource_mime (widget->doc, path); 287 | 288 | // if the resource requested doesn't exist, we should serve an 289 | // empty document instead of nothing at all (otherwise some 290 | // poorly-structured ebooks will fail to render). 291 | if (!contents) { 292 | contents = g_byte_array_free_to_bytes(g_byte_array_sized_new(0)); 293 | mime = g_strdup("application/octet-stream"); 294 | } 295 | 296 | if (!mime) { 297 | mime = g_strdup("application/octet-stream"); 298 | } 299 | 300 | stream = g_memory_input_stream_new_from_bytes (contents); 301 | webkit_uri_scheme_request_finish (request, stream, g_bytes_get_size (contents), mime); 302 | 303 | g_object_unref (stream); 304 | g_bytes_unref (contents); 305 | g_free (mime); 306 | g_free (path); 307 | } 308 | 309 | static void 310 | gepub_widget_set_property (GObject *object, 311 | guint prop_id, 312 | const GValue *value, 313 | GParamSpec *pspec) 314 | { 315 | GepubWidget *widget = GEPUB_WIDGET (object); 316 | 317 | switch (prop_id) { 318 | case PROP_DOC: 319 | gepub_widget_set_doc (widget, g_value_get_object (value)); 320 | break; 321 | case PROP_PAGINATE: 322 | gepub_widget_set_paginate (widget, g_value_get_boolean (value)); 323 | break; 324 | case PROP_CHAPTER: 325 | gepub_doc_set_chapter (widget->doc, g_value_get_int (value)); 326 | break; 327 | case PROP_CHAPTER_POS: 328 | gepub_widget_set_pos (widget, g_value_get_float (value)); 329 | break; 330 | default: 331 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 332 | break; 333 | } 334 | } 335 | 336 | static void 337 | gepub_widget_get_property (GObject *object, 338 | guint prop_id, 339 | GValue *value, 340 | GParamSpec *pspec) 341 | { 342 | GepubWidget *widget = GEPUB_WIDGET (object); 343 | 344 | switch (prop_id) { 345 | case PROP_DOC: 346 | g_value_set_object (value, gepub_widget_get_doc (widget)); 347 | break; 348 | case PROP_PAGINATE: 349 | g_value_set_boolean (value, widget->paginate); 350 | break; 351 | case PROP_CHAPTER: 352 | g_value_set_int (value, gepub_doc_get_chapter (widget->doc)); 353 | break; 354 | case PROP_N_CHAPTERS: 355 | g_value_set_int (value, gepub_doc_get_n_chapters (widget->doc)); 356 | break; 357 | case PROP_CHAPTER_POS: 358 | g_value_set_float (value, gepub_widget_get_pos (widget)); 359 | break; 360 | default: 361 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 362 | break; 363 | } 364 | } 365 | 366 | static void 367 | gepub_widget_finalize (GObject *object) 368 | { 369 | GepubWidget *widget = GEPUB_WIDGET (object); 370 | 371 | g_clear_pointer (&widget->font_family, g_free); 372 | g_clear_object (&widget->doc); 373 | 374 | G_OBJECT_CLASS (gepub_widget_parent_class)->finalize (object); 375 | } 376 | 377 | static void 378 | gepub_widget_init (GepubWidget *widget) 379 | { 380 | widget->chapter_length = 0; 381 | widget->paginate = FALSE; 382 | widget->chapter_pos = 0; 383 | widget->length = 0; 384 | widget->init_chapter_pos = 0; 385 | widget->margin = 20; 386 | widget->font_size = 0; 387 | widget->font_family = NULL; 388 | widget->line_height = 0; 389 | } 390 | 391 | static GObject * 392 | gepub_widget_constructor (GType gtype, 393 | guint n_properties, 394 | GObjectConstructParam *properties) 395 | { 396 | GObject *object; 397 | GObjectClass *parent_class; 398 | 399 | parent_class = G_OBJECT_CLASS (gepub_widget_parent_class); 400 | object = parent_class->constructor (gtype, n_properties, properties); 401 | 402 | g_object_set (object, 403 | "web-context", g_object_new (WEBKIT_TYPE_WEB_CONTEXT, NULL), 404 | NULL); 405 | 406 | return object; 407 | } 408 | 409 | 410 | static void 411 | gepub_widget_constructed (GObject *object) 412 | { 413 | WebKitWebContext *ctx; 414 | WebKitSettings *settings; 415 | GepubWidget *widget = GEPUB_WIDGET (object); 416 | 417 | G_OBJECT_CLASS (gepub_widget_parent_class)->constructed (object); 418 | 419 | ctx = webkit_web_view_get_context (WEBKIT_WEB_VIEW (widget)); 420 | webkit_web_context_register_uri_scheme (ctx, "epub", resource_callback, widget, NULL); 421 | 422 | settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (widget)); 423 | g_object_set (G_OBJECT (settings), 424 | "enable-javascript-markup", FALSE, 425 | "enable-java", FALSE, 426 | "javascript-can-access-clipboard", FALSE, 427 | "javascript-can-open-windows-automatically", FALSE, 428 | NULL); 429 | 430 | g_signal_connect (widget, "load-changed", G_CALLBACK (docready_cb), NULL); 431 | g_signal_connect (widget, "size-allocate", G_CALLBACK (reload_length_cb), NULL); 432 | } 433 | 434 | static void 435 | gepub_widget_class_init (GepubWidgetClass *klass) 436 | { 437 | GObjectClass *object_class = G_OBJECT_CLASS (klass); 438 | 439 | object_class->constructor = gepub_widget_constructor; 440 | object_class->constructed = gepub_widget_constructed; 441 | object_class->finalize = gepub_widget_finalize; 442 | object_class->set_property = gepub_widget_set_property; 443 | object_class->get_property = gepub_widget_get_property; 444 | 445 | properties[PROP_DOC] = 446 | g_param_spec_object ("doc", 447 | "The GepubDoc", 448 | "The GepubDoc for this widget", 449 | GEPUB_TYPE_DOC, 450 | G_PARAM_READWRITE | 451 | G_PARAM_STATIC_STRINGS); 452 | 453 | properties[PROP_PAGINATE] = 454 | g_param_spec_boolean ("paginate", 455 | "paginate", 456 | "If the widget should paginate", 457 | FALSE, 458 | G_PARAM_READWRITE); 459 | 460 | properties[PROP_CHAPTER] = 461 | g_param_spec_int ("chapter", 462 | "Current chapter", 463 | "Current chapter in the doc", 464 | -1, G_MAXINT, 0, 465 | G_PARAM_READWRITE); 466 | 467 | properties[PROP_N_CHAPTERS] = 468 | g_param_spec_int ("nchapters", 469 | "Number of chapters in the doc", 470 | "Number of chapters in the doc", 471 | -1, G_MAXINT, 0, 472 | G_PARAM_READABLE); 473 | 474 | properties[PROP_CHAPTER_POS] = 475 | g_param_spec_float ("chapter_pos", 476 | "Current position in chapter", 477 | "Current position in chapter as a percentage", 478 | 0.0, HUNDRED_PERCENT, 0.0, 479 | G_PARAM_READWRITE); 480 | 481 | g_object_class_install_properties (object_class, NUM_PROPS, properties); 482 | } 483 | 484 | /** 485 | * gepub_widget_new: 486 | * 487 | * Returns: (transfer full): the new GepubWidget created 488 | */ 489 | GtkWidget * 490 | gepub_widget_new (void) 491 | { 492 | return g_object_new (GEPUB_TYPE_WIDGET, 493 | NULL); 494 | } 495 | 496 | /** 497 | * gepub_widget_get_doc: 498 | * @widget: a #GepubWidget 499 | * 500 | * Returns: (transfer none): the #GepubDoc 501 | */ 502 | GepubDoc * 503 | gepub_widget_get_doc (GepubWidget *widget) 504 | { 505 | g_return_val_if_fail (GEPUB_IS_WIDGET (widget), NULL); 506 | 507 | return widget->doc; 508 | } 509 | 510 | static void 511 | reload_current_chapter (GepubWidget *widget) 512 | { 513 | GBytes *current; 514 | 515 | widget->chapter_length = 0; 516 | widget->chapter_pos = 0; 517 | widget->length = 0; 518 | 519 | if (widget->doc == NULL) 520 | return; 521 | 522 | current = gepub_doc_get_current_with_epub_uris (widget->doc); 523 | webkit_web_view_load_bytes (WEBKIT_WEB_VIEW (widget), 524 | current, 525 | gepub_doc_get_current_mime (widget->doc), 526 | "UTF-8", NULL); 527 | g_bytes_unref (current); 528 | } 529 | 530 | /** 531 | * gepub_widget_set_doc: 532 | * @widget: a #GepubWidget 533 | * @doc: (nullable): a #GepubDoc 534 | * 535 | * Sets @doc as the document displayed by the widget. 536 | */ 537 | void 538 | gepub_widget_set_doc (GepubWidget *widget, 539 | GepubDoc *doc) 540 | { 541 | g_return_if_fail (GEPUB_IS_WIDGET (widget)); 542 | 543 | if (widget->doc == doc) 544 | return; 545 | 546 | if (widget->doc != NULL) { 547 | g_signal_handlers_disconnect_by_func (widget->doc, 548 | reload_current_chapter, widget); 549 | g_object_unref (widget->doc); 550 | } 551 | 552 | widget->doc = doc; 553 | 554 | if (widget->doc != NULL) { 555 | g_object_ref (widget->doc); 556 | reload_current_chapter (widget); 557 | g_signal_connect_swapped (widget->doc, "notify::chapter", 558 | G_CALLBACK (reload_current_chapter), widget); 559 | } 560 | 561 | g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_DOC]); 562 | } 563 | 564 | /** 565 | * gepub_widget_get_paginate: 566 | * @widget: a #GepubWidget 567 | * 568 | * Returns whether pagination is enabled or disabled 569 | */ 570 | gboolean 571 | gepub_widget_get_paginate (GepubWidget *widget) 572 | { 573 | g_return_val_if_fail (GEPUB_IS_WIDGET (widget), FALSE); 574 | 575 | return widget->paginate; 576 | } 577 | 578 | /** 579 | * gepub_widget_set_paginate: 580 | * @widget: a #GepubWidget 581 | * @p: true if the widget should paginate 582 | * 583 | * Enable or disable pagination 584 | */ 585 | void 586 | gepub_widget_set_paginate (GepubWidget *widget, 587 | gboolean p) 588 | { 589 | g_return_if_fail (GEPUB_IS_WIDGET (widget)); 590 | 591 | widget->paginate = p; 592 | reload_current_chapter (widget); 593 | } 594 | 595 | /** 596 | * gepub_widget_get_n_chapters: 597 | * @widget: a #GepubWidget 598 | * 599 | * Returns: the number of chapters in the document 600 | */ 601 | gint 602 | gepub_widget_get_n_chapters (GepubWidget *widget) 603 | { 604 | g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0); 605 | return gepub_doc_get_n_chapters (widget->doc); 606 | } 607 | 608 | /** 609 | * gepub_widget_get_chapter: 610 | * @widget: a #GepubWidget 611 | * 612 | * Returns: the current chapter in the document 613 | */ 614 | gint 615 | gepub_widget_get_chapter (GepubWidget *widget) 616 | { 617 | g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0); 618 | return gepub_doc_get_chapter (widget->doc); 619 | } 620 | 621 | /** 622 | * gepub_widget_get_chapter_length: 623 | * @widget: a #GepubWidget 624 | * 625 | * Returns: the current chapter length 626 | */ 627 | gint 628 | gepub_widget_get_chapter_length (GepubWidget *widget) 629 | { 630 | g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0); 631 | return widget->chapter_length; 632 | } 633 | 634 | /** 635 | * gepub_widget_set_chapter: 636 | * @widget: a #GepubWidget 637 | * @index: the new chapter 638 | * 639 | * Sets the current chapter in the doc 640 | */ 641 | void 642 | gepub_widget_set_chapter (GepubWidget *widget, 643 | gint index) 644 | { 645 | g_return_if_fail (GEPUB_IS_DOC (widget->doc)); 646 | return gepub_doc_set_chapter (widget->doc, index); 647 | } 648 | 649 | /** 650 | * gepub_widget_chapter_next: 651 | * @widget: a #GepubWidget 652 | * 653 | * Returns: TRUE on success, FALSE if there's no next chapter 654 | */ 655 | gboolean 656 | gepub_widget_chapter_next (GepubWidget *widget) 657 | { 658 | g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE); 659 | return gepub_doc_go_next (widget->doc); 660 | } 661 | 662 | /** 663 | * gepub_widget_chapter_prev: 664 | * @widget: a #GepubWidget 665 | * 666 | * Returns: TRUE on success, FALSE if there's no prev chapter 667 | */ 668 | gboolean 669 | gepub_widget_chapter_prev (GepubWidget *widget) 670 | { 671 | g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE); 672 | return gepub_doc_go_prev (widget->doc); 673 | } 674 | 675 | /** 676 | * gepub_widget_page_next: 677 | * @widget: a #GepubWidget 678 | * 679 | * Returns: TRUE on success, FALSE if there's no next page 680 | */ 681 | gboolean 682 | gepub_widget_page_next (GepubWidget *widget) 683 | { 684 | g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE); 685 | widget->chapter_pos = widget->chapter_pos + widget->length; 686 | 687 | if (widget->chapter_pos > (widget->chapter_length - widget->length)) { 688 | widget->chapter_pos = (widget->chapter_length - widget->length); 689 | return gepub_doc_go_next (widget->doc); 690 | } 691 | 692 | scroll_to_chapter_pos (widget); 693 | 694 | g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_CHAPTER_POS]); 695 | return TRUE; 696 | } 697 | 698 | /** 699 | * gepub_widget_page_prev: 700 | * @widget: a #GepubWidget 701 | * 702 | * Returns: TRUE on success, FALSE if there's no next page 703 | */ 704 | gboolean 705 | gepub_widget_page_prev (GepubWidget *widget) 706 | { 707 | g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE); 708 | widget->chapter_pos = widget->chapter_pos - widget->length; 709 | 710 | if (widget->chapter_pos < 0) { 711 | widget->init_chapter_pos = HUNDRED_PERCENT; 712 | return gepub_doc_go_prev (widget->doc); 713 | } 714 | 715 | scroll_to_chapter_pos (widget); 716 | 717 | g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_CHAPTER_POS]); 718 | return TRUE; 719 | } 720 | 721 | /** 722 | * gepub_widget_get_pos: 723 | * @widget: a #GepubWidget 724 | * 725 | * Returns: the current position in the chapter 726 | */ 727 | gfloat 728 | gepub_widget_get_pos (GepubWidget *widget) 729 | { 730 | g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0); 731 | 732 | if (!widget->chapter_length) { 733 | return 0; 734 | } 735 | 736 | return widget->chapter_pos * HUNDRED_PERCENT / (float)(widget->chapter_length); 737 | } 738 | 739 | /** 740 | * gepub_widget_set_pos: 741 | * @widget: a #GepubWidget 742 | * @index: the new pos 743 | * 744 | * Sets the current position in the chapter 745 | */ 746 | void 747 | gepub_widget_set_pos (GepubWidget *widget, 748 | gfloat index) 749 | { 750 | g_return_if_fail (GEPUB_IS_DOC (widget->doc)); 751 | widget->chapter_pos = index * widget->chapter_length / HUNDRED_PERCENT; 752 | adjust_chapter_pos (widget); 753 | 754 | g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_CHAPTER_POS]); 755 | } 756 | 757 | 758 | /** 759 | * gepub_widget_set_margin: 760 | * @widget: a #GepubWidget 761 | * @margin: the margin in pixels 762 | * 763 | * Sets the widget left and right margin 764 | */ 765 | void 766 | gepub_widget_set_margin (GepubWidget *widget, 767 | gint margin) 768 | { 769 | widget->margin = margin; 770 | reload_length_cb (GTK_WIDGET (widget), NULL, NULL); 771 | } 772 | 773 | /** 774 | * gepub_widget_get_margin: 775 | * @widget: a #GepubWidget 776 | * 777 | * Gets the widget left and right margin 778 | */ 779 | gint 780 | gepub_widget_get_margin (GepubWidget *widget) 781 | { 782 | return widget->margin; 783 | } 784 | 785 | /** 786 | * gepub_widget_get_fontsize: 787 | * @widget: a #GepubWidget 788 | * 789 | * Gets the widget custom font size in pt, if 0, it's not set 790 | */ 791 | gint 792 | gepub_widget_get_fontsize (GepubWidget *widget) 793 | { 794 | return widget->font_size; 795 | } 796 | 797 | /** 798 | * gepub_widget_set_fontsize: 799 | * @widget: a #GepubWidget 800 | * @size: the custom font size in pt 801 | * 802 | * Sets the widget custom font size, use 0 to show book's styles 803 | */ 804 | void 805 | gepub_widget_set_fontsize (GepubWidget *widget, 806 | gint size) 807 | { 808 | widget->font_size = size; 809 | reload_length_cb (GTK_WIDGET (widget), NULL, NULL); 810 | } 811 | 812 | /** 813 | * gepub_widget_get_fontfamily: 814 | * @widget: a #GepubWidget 815 | * 816 | * Gets the widget custom font family 817 | */ 818 | gchar * 819 | gepub_widget_get_fontfamily (GepubWidget *widget) 820 | { 821 | return widget->font_family; 822 | } 823 | 824 | /** 825 | * gepub_widget_set_fontfamily: 826 | * @widget: a #GepubWidget 827 | * @family: the custom font family name 828 | * 829 | * Sets the widget custom font family 830 | */ 831 | void 832 | gepub_widget_set_fontfamily (GepubWidget *widget, 833 | gchar *family) 834 | { 835 | g_clear_pointer (&widget->font_family, g_free); 836 | 837 | widget->font_family = g_strdup (family); 838 | reload_length_cb (GTK_WIDGET (widget), NULL, NULL); 839 | } 840 | 841 | /** 842 | * gepub_widget_get_lineheight: 843 | * @widget: a #GepubWidget 844 | * 845 | * Gets the widget custom line height, if 0, it's not set 846 | */ 847 | gfloat 848 | gepub_widget_get_lineheight (GepubWidget *widget) 849 | { 850 | return widget->line_height; 851 | } 852 | 853 | /** 854 | * gepub_widget_set_lineheight: 855 | * @widget: a #GepubWidget 856 | * @size: the custom line height 857 | * 858 | * Sets the widget custom line height, the real size will be this 859 | * number multiplied by the font size. 860 | * Use 0 to show book's styles 861 | */ 862 | void 863 | gepub_widget_set_lineheight (GepubWidget *widget, 864 | gfloat size) 865 | { 866 | widget->line_height = size; 867 | reload_length_cb (GTK_WIDGET (widget), NULL, NULL); 868 | } 869 | -------------------------------------------------------------------------------- /libgepub/gepub-widget.h: -------------------------------------------------------------------------------- 1 | /* GepubWidget 2 | * 3 | * Copyright (C) 2016 Daniel Garcia 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #ifndef __GEPUB_WIDGET_H__ 21 | #define __GEPUB_WIDGET_H__ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "gepub-doc.h" 28 | 29 | G_BEGIN_DECLS 30 | 31 | #define GEPUB_TYPE_WIDGET (gepub_widget_get_type ()) 32 | #define GEPUB_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GEPUB_TYPE_WIDGET, GepubWidget)) 33 | #define GEPUB_WIDGET_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GEPUB_TYPE_WIDGET, GepubWidgetClass)) 34 | #define GEPUB_IS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GEPUB_TYPE_WIDGET)) 35 | #define GEPUB_IS_WIDGET_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GEPUB_TYPE_WIDGET)) 36 | #define GEPUB_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEPUB_TYPE_WIDGET, GepubWidgetClass)) 37 | 38 | typedef struct _GepubWidget GepubWidget; 39 | typedef struct _GepubWidgetClass GepubWidgetClass; 40 | 41 | GType gepub_widget_get_type (void) G_GNUC_CONST; 42 | 43 | GtkWidget *gepub_widget_new (void); 44 | 45 | GepubDoc *gepub_widget_get_doc (GepubWidget *widget); 46 | void gepub_widget_set_doc (GepubWidget *widget, 47 | GepubDoc *doc); 48 | 49 | gboolean gepub_widget_get_paginate (GepubWidget *widget); 50 | void gepub_widget_set_paginate (GepubWidget *widget, gboolean p); 51 | 52 | gint gepub_widget_get_n_chapters (GepubWidget *widget); 53 | gint gepub_widget_get_chapter (GepubWidget *widget); 54 | gint gepub_widget_get_chapter_length (GepubWidget *widget); 55 | void gepub_widget_set_chapter (GepubWidget *widget, 56 | gint index); 57 | gboolean gepub_widget_chapter_next (GepubWidget *widget); 58 | gboolean gepub_widget_chapter_prev (GepubWidget *widget); 59 | 60 | gfloat gepub_widget_get_pos (GepubWidget *widget); 61 | void gepub_widget_set_pos (GepubWidget *widget, 62 | gfloat index); 63 | gboolean gepub_widget_page_next (GepubWidget *widget); 64 | gboolean gepub_widget_page_prev (GepubWidget *widget); 65 | 66 | gint gepub_widget_get_margin (GepubWidget *widget); 67 | void gepub_widget_set_margin (GepubWidget *widget, 68 | gint margin); 69 | 70 | gint gepub_widget_get_fontsize (GepubWidget *widget); 71 | void gepub_widget_set_fontsize (GepubWidget *widget, 72 | gint size); 73 | 74 | gchar *gepub_widget_get_fontfamily (GepubWidget *widget); 75 | void gepub_widget_set_fontfamily (GepubWidget *widget, 76 | gchar *family); 77 | 78 | gfloat gepub_widget_get_lineheight (GepubWidget *widget); 79 | void gepub_widget_set_lineheight (GepubWidget *widget, 80 | gfloat size); 81 | 82 | G_END_DECLS 83 | 84 | #endif /* __GEPUB_WIDGET_H__ */ 85 | 86 | -------------------------------------------------------------------------------- /libgepub/gepub.h: -------------------------------------------------------------------------------- 1 | #ifndef _GEPUB__H_ 2 | #define _GEPUB__H_ 3 | 4 | #include "gepub-archive.h" 5 | #include "gepub-text-chunk.h" 6 | #include "gepub-doc.h" 7 | #include "gepub-widget.h" 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /libgepub/gepub.map: -------------------------------------------------------------------------------- 1 | { 2 | global: 3 | gepub_*; 4 | local: 5 | *; 6 | }; 7 | -------------------------------------------------------------------------------- /libgepub/meson.build: -------------------------------------------------------------------------------- 1 | headers = files( 2 | 'gepub-archive.h', 3 | 'gepub-doc.h', 4 | 'gepub-text-chunk.h', 5 | 'gepub-widget.h', 6 | 'gepub.h' 7 | ) 8 | 9 | install_headers( 10 | headers, 11 | subdir: gepub_lib_name 12 | ) 13 | 14 | private_headers = files('gepub-utils.h') 15 | 16 | sources = files( 17 | 'gepub-archive.c', 18 | 'gepub-doc.c', 19 | 'gepub-text-chunk.c', 20 | 'gepub-utils.c', 21 | 'gepub-widget.c' 22 | ) 23 | 24 | symbol_map = join_paths(meson.current_source_dir(), 'gepub.map') 25 | 26 | test_ldflag = '-Wl,--version-script,' + symbol_map 27 | ldflags = [] 28 | 29 | if cc.has_link_argument(test_ldflag) 30 | ldflags += test_ldflag 31 | endif 32 | 33 | libgepub = library( 34 | 'gepub-'+gepub_api_version, 35 | sources: sources, 36 | version: libversion, 37 | soversion: soversion, 38 | include_directories: top_inc, 39 | dependencies: gepub_deps, 40 | link_args: ldflags, 41 | link_depends: symbol_map, 42 | install: true, 43 | install_dir: gepub_libdir 44 | ) 45 | 46 | libgepub_dep = declare_dependency( 47 | link_with: libgepub, 48 | include_directories: include_directories('.'), 49 | dependencies: gepub_deps 50 | ) 51 | 52 | pkg.generate( 53 | libraries: libgepub, 54 | version: gepub_version, 55 | name: gepub_lib_name, 56 | description: 'epub Documents library', 57 | filebase: gepub_lib_name, 58 | subdirs: gepub_lib_name, 59 | requires: 'gio-2.0', 60 | requires_private: [ 61 | 'libxml-2.0', 62 | 'libarchive' 63 | ], 64 | variables: 'exec_prefix=' + gepub_libexecdir, 65 | install_dir: join_paths(get_option('libdir'), 'pkgconfig') 66 | ) 67 | 68 | if get_option('introspection') and get_option('default_library') == 'shared' 69 | gir_incs = [ 70 | 'GObject-2.0', 71 | 'libxml2-2.0', 72 | 'WebKit2-4.0' 73 | ] 74 | 75 | gir_extra_args = '--warn-all' 76 | 77 | gir_dir = join_paths(gepub_datadir, '@0@-@1@'.format('gir', gepub_gir_version)) 78 | typelib_dir = join_paths(gepub_libdir, '@0@-@1@'.format('girepository', gepub_gir_version)) 79 | 80 | libgepub_gir = gnome.generate_gir( 81 | libgepub, 82 | header: 'gepub.h', 83 | sources: sources + headers + private_headers, 84 | namespace: gepub_gir_ns, 85 | nsversion: gepub_api_version, 86 | includes: gir_incs, 87 | extra_args: gir_extra_args, 88 | install: true, 89 | install_dir_gir: gir_dir, 90 | install_dir_typelib: typelib_dir 91 | ) 92 | endif 93 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'libgepub', 'c', 3 | version: '0.6.0', 4 | license: 'LGPL2+', 5 | default_options: 'buildtype=debugoptimized', 6 | meson_version: '>= 0.46.0' 7 | ) 8 | 9 | gepub_version = meson.project_version() 10 | version_array = gepub_version.split('.') 11 | gepub_major_version = version_array[0].to_int() 12 | gepub_minor_version = version_array[1].to_int() 13 | gepub_version_micro = version_array[2].to_int() 14 | gepub_api_version = '@0@.@1@'.format(gepub_major_version, gepub_minor_version) 15 | gepub_lib_name = '@0@-@1@'.format(meson.project_name(), gepub_api_version) 16 | 17 | gepub_gir_ns = 'Gepub' 18 | gepub_gir_version = '1.0' 19 | 20 | gepub_prefix = get_option('prefix') 21 | gepub_datadir = join_paths(gepub_prefix, get_option('datadir')) 22 | gepub_libdir = join_paths(gepub_prefix, get_option('libdir')) 23 | gepub_libexecdir = join_paths(gepub_prefix, get_option('libexecdir')) 24 | gepub_includedir = join_paths(gepub_prefix, get_option('includedir')) 25 | 26 | soversion = 0 27 | current = 0 28 | revision = 0 29 | libversion = '@0@.@1@.@2@'.format(soversion, current, revision) 30 | 31 | cc = meson.get_compiler('c') 32 | 33 | gepub_deps = [ 34 | dependency('webkit2gtk-4.0'), 35 | dependency('libsoup-2.4'), 36 | dependency('glib-2.0'), 37 | dependency('gobject-2.0'), 38 | dependency('gio-2.0'), 39 | dependency('libxml-2.0'), 40 | dependency('libarchive') 41 | ] 42 | 43 | gnome = import('gnome') 44 | pkg = import('pkgconfig') 45 | 46 | top_inc = include_directories('.') 47 | 48 | subdir('libgepub') 49 | subdir('tests') 50 | 51 | configure_file( 52 | output: 'config.h', 53 | configuration: configuration_data() 54 | ) 55 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('introspection', type: 'boolean', value: true, description: 'Enable GObject Introspection (depends on GObject)') 2 | -------------------------------------------------------------------------------- /tests/meson.build: -------------------------------------------------------------------------------- 1 | test_gepub = 'test-gepub' 2 | 3 | executable( 4 | test_gepub, 5 | test_gepub + '.c', 6 | include_directories: top_inc, 7 | dependencies: [ 8 | libgepub_dep, 9 | dependency('gtk+-3.0') 10 | ] 11 | ) 12 | -------------------------------------------------------------------------------- /tests/test-gepub.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | gchar *buf = NULL; 7 | gchar *buf2 = NULL; 8 | gchar *tmpbuf; 9 | 10 | GtkTextBuffer *page_buffer; 11 | GtkWidget *PAGE_LABEL; 12 | 13 | #define PTEST1(...) { printf (__VA_ARGS__); } 14 | #define PTEST2(...) { buf = g_strdup_printf (__VA_ARGS__);\ 15 | tmpbuf = buf2;\ 16 | buf2 = g_strdup_printf ("%s%s", buf2, buf);\ 17 | g_free (buf);\ 18 | g_free (tmpbuf);\ 19 | } 20 | #define PTEST PTEST2 21 | 22 | #define TEST(f,arg...) PTEST ("\n### TESTING " #f " ###\n\n"); f (arg); PTEST ("\n\n"); 23 | 24 | static void 25 | reload_current_chapter (GepubWidget *widget) 26 | { 27 | gchar *txt = g_strdup_printf ("%02.2f", gepub_widget_get_pos (widget)); 28 | gtk_label_set_text (GTK_LABEL (PAGE_LABEL), txt); 29 | g_free (txt); 30 | } 31 | 32 | static void 33 | update_text (GepubDoc *doc) 34 | { 35 | GList *l, *chunks; 36 | GtkTextIter start, end; 37 | 38 | gtk_text_buffer_get_start_iter (page_buffer, &start); 39 | gtk_text_buffer_get_end_iter (page_buffer, &end); 40 | gtk_text_buffer_delete (page_buffer, &start, &end); 41 | 42 | chunks = gepub_doc_get_text (doc); 43 | 44 | for (l=chunks; l; l = l->next) { 45 | GepubTextChunk *chunk = GEPUB_TEXT_CHUNK (l->data); 46 | if (chunk->type == GEPUBTextHeader) { 47 | gtk_text_buffer_insert_at_cursor (page_buffer, "\n", -1); 48 | gtk_text_buffer_get_end_iter (page_buffer, &end); 49 | gtk_text_buffer_insert_with_tags_by_name (page_buffer, &end, chunk->text, -1, "head", NULL); 50 | gtk_text_buffer_insert_at_cursor (page_buffer, "\n", -1); 51 | } else if (chunk->type == GEPUBTextNormal) { 52 | gtk_text_buffer_insert_at_cursor (page_buffer, "\n", -1); 53 | gtk_text_buffer_insert_at_cursor (page_buffer, chunk->text, -1); 54 | gtk_text_buffer_insert_at_cursor (page_buffer, "\n", -1); 55 | } else if (chunk->type == GEPUBTextItalic) { 56 | gtk_text_buffer_get_end_iter (page_buffer, &end); 57 | gtk_text_buffer_insert_with_tags_by_name (page_buffer, &end, chunk->text, -1, "italic", NULL); 58 | } else if (chunk->type == GEPUBTextBold) { 59 | gtk_text_buffer_get_end_iter (page_buffer, &end); 60 | gtk_text_buffer_insert_with_tags_by_name (page_buffer, &end, chunk->text, -1, "bold", NULL); 61 | } 62 | } 63 | } 64 | 65 | #if 0 66 | static void 67 | print_replaced_text (GepubDoc *doc) 68 | { 69 | GBytes *content; 70 | gsize s; 71 | const guchar *data; 72 | content = gepub_doc_get_current_with_epub_uris (doc); 73 | 74 | data = g_bytes_get_data (content, &s); 75 | printf ("\n\nREPLACED:\n%s\n", data); 76 | g_bytes_unref (content); 77 | } 78 | #endif 79 | 80 | static void 81 | button_pressed (GtkButton *button, GepubWidget *widget) 82 | { 83 | GepubDoc *doc = gepub_widget_get_doc (widget); 84 | printf("CLICKED %s\n", gtk_button_get_label (button)); 85 | 86 | if (!strcmp (gtk_button_get_label (button), "< chapter")) { 87 | gepub_doc_go_prev (doc); 88 | } else if (!strcmp (gtk_button_get_label (button), "chapter >")) { 89 | gepub_doc_go_next (doc); 90 | } else if (!strcmp (gtk_button_get_label (button), "paginated")) { 91 | gboolean b = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); 92 | gepub_widget_set_paginate (widget, b); 93 | } else if (!strcmp (gtk_button_get_label (button), "< page")) { 94 | gepub_widget_page_prev (widget); 95 | } else if (!strcmp (gtk_button_get_label (button), "page >")) { 96 | gepub_widget_page_next (widget); 97 | } else if (!strcmp (gtk_button_get_label (button), "margin +")) { 98 | gepub_widget_set_margin (widget, gepub_widget_get_margin (widget) + 20); 99 | } else if (!strcmp (gtk_button_get_label (button), "margin -")) { 100 | gepub_widget_set_margin (widget, gepub_widget_get_margin (widget) - 20); 101 | } else if (!strcmp (gtk_button_get_label (button), "font +")) { 102 | gint f = gepub_widget_get_fontsize (widget); 103 | f = f ? f : 12; 104 | printf ("font %d\n", f); 105 | gepub_widget_set_fontsize (widget, f + 2); 106 | } else if (!strcmp (gtk_button_get_label (button), "font -")) { 107 | gint f = gepub_widget_get_fontsize (widget); 108 | f = f ? f : 12; 109 | printf ("font %d\n", f); 110 | gepub_widget_set_fontsize (widget, f - 2); 111 | } else if (!strcmp (gtk_button_get_label (button), "line height +")) { 112 | gfloat l = gepub_widget_get_lineheight (widget); 113 | l = l ? l : 1.5; 114 | gepub_widget_set_lineheight (widget, l + 0.1); 115 | } else if (!strcmp (gtk_button_get_label (button), "line height -")) { 116 | gfloat l = gepub_widget_get_lineheight (widget); 117 | l = l ? l : 1.5; 118 | gepub_widget_set_lineheight (widget, l - 0.1); 119 | } 120 | update_text (doc); 121 | //print_replaced_text (doc); 122 | } 123 | 124 | static void 125 | test_open (const char *path) 126 | { 127 | GepubArchive *a; 128 | GList *list_files; 129 | gint i; 130 | gint size; 131 | 132 | a = gepub_archive_new (path); 133 | list_files = gepub_archive_list_files (a); 134 | if (!list_files) { 135 | PTEST ("ERROR: BAD epub file"); 136 | g_object_unref (a); 137 | return; 138 | } 139 | 140 | size = g_list_length (list_files); 141 | PTEST ("%d\n", size); 142 | for (i = 0; i < size; i++) { 143 | PTEST ("file: %s\n", (char *)g_list_nth_data (list_files, i)); 144 | g_free (g_list_nth_data (list_files, i)); 145 | } 146 | 147 | g_list_free (list_files); 148 | 149 | g_object_unref (a); 150 | } 151 | 152 | static void 153 | find_xhtml (gchar *key, GepubResource *value, gpointer data) 154 | { 155 | gchar **d = (gchar **)data; 156 | if (g_strcmp0 (value->mime, "application/xhtml+xml") == 0) { 157 | *d = value->uri; 158 | } 159 | } 160 | 161 | static void 162 | test_read (const char *path) 163 | { 164 | GepubArchive *a; 165 | GList *list_files = NULL; 166 | const guchar *buffer; 167 | gchar *file = NULL; 168 | gsize bufsize; 169 | GBytes *bytes; 170 | GepubDoc *doc; 171 | GHashTable *ht; 172 | 173 | a = gepub_archive_new (path); 174 | 175 | doc = gepub_doc_new (path, NULL); 176 | ht = (GHashTable*)gepub_doc_get_resources (doc); 177 | g_hash_table_foreach (ht, (GHFunc)find_xhtml, &file); 178 | 179 | bytes = gepub_archive_read_entry (a, file); 180 | if (bytes) { 181 | buffer = g_bytes_get_data (bytes, &bufsize); 182 | PTEST ("doc:%s\n----\n%s\n-----\n", file, buffer); 183 | g_bytes_unref (bytes); 184 | } 185 | 186 | g_list_foreach (list_files, (GFunc)g_free, NULL); 187 | g_list_free (list_files); 188 | 189 | g_object_unref (a); 190 | } 191 | 192 | static void 193 | test_root_file (const char *path) 194 | { 195 | GepubArchive *a; 196 | gchar *root_file = NULL; 197 | 198 | a = gepub_archive_new (path); 199 | 200 | root_file = gepub_archive_get_root_file (a); 201 | PTEST ("root file: %s\n", root_file); 202 | if (root_file) 203 | g_free (root_file); 204 | 205 | g_object_unref (a); 206 | } 207 | 208 | static void 209 | test_doc_name (const char *path) 210 | { 211 | GepubDoc *doc = gepub_doc_new (path, NULL); 212 | gchar *title = gepub_doc_get_metadata (doc, GEPUB_META_TITLE); 213 | gchar *lang = gepub_doc_get_metadata (doc, GEPUB_META_LANG); 214 | gchar *id = gepub_doc_get_metadata (doc, GEPUB_META_ID); 215 | gchar *author = gepub_doc_get_metadata (doc, GEPUB_META_AUTHOR); 216 | gchar *description = gepub_doc_get_metadata (doc, GEPUB_META_DESC); 217 | gchar *cover = gepub_doc_get_cover (doc); 218 | gchar *cover_mime = NULL; 219 | 220 | if (cover) 221 | cover_mime = gepub_doc_get_resource_mime_by_id (doc, cover); 222 | 223 | PTEST ("title: %s\n", title); 224 | PTEST ("author: %s\n", author); 225 | PTEST ("id: %s\n", id); 226 | PTEST ("lang: %s\n", lang); 227 | PTEST ("desc: %s\n", description); 228 | if (cover) 229 | PTEST ("cover: %s\n", cover); 230 | if (cover_mime) 231 | PTEST ("cover mime: %s\n", cover_mime); 232 | 233 | g_free (title); 234 | g_free (lang); 235 | g_free (id); 236 | g_free (author); 237 | g_free (description); 238 | g_free (cover); 239 | g_object_unref (G_OBJECT (doc)); 240 | } 241 | 242 | static void 243 | pk (gchar *key, GepubResource *value, gpointer data) 244 | { 245 | PTEST ("%s: %s, %s\n", key, value->mime, value->uri); 246 | } 247 | 248 | static void 249 | test_doc_resources (const char *path) 250 | { 251 | GepubDoc *doc; 252 | GHashTable *ht; 253 | GBytes *ncx; 254 | const guchar *data; 255 | gsize size; 256 | 257 | doc = gepub_doc_new (path, NULL); 258 | ht = (GHashTable*)gepub_doc_get_resources (doc); 259 | g_hash_table_foreach (ht, (GHFunc)pk, NULL); 260 | 261 | ncx = gepub_doc_get_resource_by_id (doc, "ncx"); 262 | data = g_bytes_get_data (ncx, &size); 263 | PTEST ("ncx:\n%.*s\n", size, data); 264 | g_bytes_unref (ncx); 265 | 266 | g_object_unref (G_OBJECT (doc)); 267 | } 268 | 269 | static void 270 | test_doc_spine (const char *path) 271 | { 272 | GepubDoc *doc = gepub_doc_new (path, NULL); 273 | int id = 0; 274 | 275 | do { 276 | PTEST ("%d: %s\n", id++, gepub_doc_get_current_id (doc)); 277 | } while (gepub_doc_go_next (doc)); 278 | 279 | g_object_unref (G_OBJECT (doc)); 280 | } 281 | 282 | static void 283 | test_doc_toc (const char *path) 284 | { 285 | GepubDoc *doc = gepub_doc_new (path, NULL); 286 | 287 | GList *nav = gepub_doc_get_toc (doc); 288 | while (nav && nav->data) { 289 | GepubNavPoint *point = (GepubNavPoint*)nav->data; 290 | PTEST ("%02d: %s -> %s\n", (gint)point->playorder, point->label, point->content); 291 | PTEST (" -> Chapter: %d\n", gepub_doc_resource_uri_to_chapter (doc, point->content)); 292 | nav = nav->next; 293 | } 294 | 295 | g_object_unref (G_OBJECT (doc)); 296 | } 297 | 298 | static void 299 | destroy_cb (GtkWidget *window, 300 | GtkWidget *view) 301 | { 302 | g_signal_handlers_disconnect_by_func (G_OBJECT (view), 303 | reload_current_chapter, 304 | view); 305 | gtk_main_quit (); 306 | } 307 | 308 | int 309 | main (int argc, char **argv) 310 | { 311 | GtkWidget *window; 312 | GtkWidget *vpaned; 313 | GtkWidget *textview; 314 | GtkWidget *scrolled; 315 | 316 | GtkWidget *vbox; 317 | GtkWidget *hbox; 318 | GtkWidget *hbox2; 319 | GtkWidget *b_next; 320 | GtkWidget *b_prev; 321 | 322 | GtkWidget *p_next; 323 | GtkWidget *p_prev; 324 | 325 | GtkWidget *margin1; 326 | GtkWidget *margin2; 327 | 328 | GtkWidget *font1; 329 | GtkWidget *font2; 330 | 331 | GtkWidget *lh1; 332 | GtkWidget *lh2; 333 | 334 | GtkWidget *paginate; 335 | 336 | GtkTextBuffer *buffer; 337 | 338 | GepubDoc *doc; 339 | GtkWidget *textview2; 340 | 341 | GtkWidget *widget; 342 | 343 | gtk_init (&argc, &argv); 344 | 345 | widget = gepub_widget_new (); 346 | 347 | webkit_settings_set_enable_developer_extras ( 348 | webkit_web_view_get_settings (WEBKIT_WEB_VIEW (widget)), TRUE); 349 | 350 | if (argc < 2) { 351 | printf ("you should provide an .epub file\n"); 352 | return 1; 353 | } 354 | 355 | window = gtk_window_new (GTK_WINDOW_TOPLEVEL); 356 | g_signal_connect (window, "destroy", G_CALLBACK(destroy_cb), widget); 357 | gtk_widget_set_size_request (GTK_WIDGET (window), 1200, 800); 358 | vpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL); 359 | gtk_container_add (GTK_CONTAINER (window), vpaned); 360 | 361 | // gepub widget 362 | doc = gepub_doc_new (argv[1], NULL); 363 | if (!doc) { 364 | perror ("BAD epub FILE"); 365 | return -1; 366 | } 367 | 368 | gepub_widget_set_doc (GEPUB_WIDGET (widget), doc); 369 | 370 | scrolled = gtk_scrolled_window_new (NULL, NULL); 371 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); 372 | textview2 = gtk_text_view_new (); 373 | gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (textview2), GTK_WRAP_WORD_CHAR); 374 | page_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview2)); 375 | gtk_text_buffer_create_tag (page_buffer, "bold", "weight", PANGO_WEIGHT_BOLD, "foreground", "#ff0000", NULL); 376 | gtk_text_buffer_create_tag (page_buffer, "italic", "style", PANGO_STYLE_ITALIC, "foreground", "#005500", NULL); 377 | gtk_text_buffer_create_tag (page_buffer, "head", "size-points", 20.0, NULL); 378 | update_text (doc); 379 | gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (textview2)); 380 | gtk_widget_set_size_request (GTK_WIDGET (textview2), 500, 300); 381 | 382 | vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); 383 | hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); 384 | hbox2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); 385 | b_prev = gtk_button_new_with_label ("< chapter"); 386 | g_signal_connect (b_prev, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget)); 387 | b_next = gtk_button_new_with_label ("chapter >"); 388 | g_signal_connect (b_next, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget)); 389 | 390 | p_prev = gtk_button_new_with_label ("< page"); 391 | g_signal_connect (p_prev, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget)); 392 | p_next = gtk_button_new_with_label ("page >"); 393 | g_signal_connect (p_next, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget)); 394 | 395 | margin1 = gtk_button_new_with_label ("margin +"); 396 | g_signal_connect (margin1, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget)); 397 | margin2 = gtk_button_new_with_label ("margin -"); 398 | g_signal_connect (margin2, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget)); 399 | 400 | font1 = gtk_button_new_with_label ("font +"); 401 | g_signal_connect (font1, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget)); 402 | font2 = gtk_button_new_with_label ("font -"); 403 | g_signal_connect (font2, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget)); 404 | 405 | lh1 = gtk_button_new_with_label ("line height +"); 406 | g_signal_connect (lh1, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget)); 407 | lh2 = gtk_button_new_with_label ("line height -"); 408 | g_signal_connect (lh2, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget)); 409 | 410 | PAGE_LABEL = gtk_label_new ("0"); 411 | 412 | g_signal_connect_swapped (widget, "notify", 413 | G_CALLBACK (reload_current_chapter), widget); 414 | 415 | paginate = gtk_check_button_new_with_label ("paginated"); 416 | g_signal_connect (paginate, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget)); 417 | 418 | gtk_container_add (GTK_CONTAINER (hbox), b_prev); 419 | gtk_container_add (GTK_CONTAINER (hbox), b_next); 420 | 421 | gtk_container_add (GTK_CONTAINER (hbox), p_prev); 422 | gtk_container_add (GTK_CONTAINER (hbox), p_next); 423 | 424 | gtk_container_add (GTK_CONTAINER (hbox), PAGE_LABEL); 425 | gtk_container_add (GTK_CONTAINER (hbox), paginate); 426 | 427 | gtk_container_add (GTK_CONTAINER (hbox2), margin1); 428 | gtk_container_add (GTK_CONTAINER (hbox2), margin2); 429 | 430 | gtk_container_add (GTK_CONTAINER (hbox2), font1); 431 | gtk_container_add (GTK_CONTAINER (hbox2), font2); 432 | 433 | gtk_container_add (GTK_CONTAINER (hbox2), lh1); 434 | gtk_container_add (GTK_CONTAINER (hbox2), lh2); 435 | 436 | gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 5); 437 | gtk_box_pack_start (GTK_BOX (vbox), hbox2, FALSE, FALSE, 5); 438 | gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 5); 439 | 440 | textview = gtk_text_view_new (); 441 | scrolled = gtk_scrolled_window_new (NULL, NULL); 442 | gtk_container_add (GTK_CONTAINER (scrolled), textview); 443 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); 444 | gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 5); 445 | 446 | gtk_widget_set_size_request (GTK_WIDGET (vbox), 600, 500); 447 | gtk_paned_add1 (GTK_PANED (vpaned), vbox); 448 | gtk_paned_add2 (GTK_PANED (vpaned), widget); 449 | 450 | gtk_widget_show_all (window); 451 | 452 | 453 | // Testing all 454 | TEST(test_open, argv[1]) 455 | TEST(test_read, argv[1]) 456 | TEST(test_root_file, argv[1]) 457 | TEST(test_doc_name, argv[1]) 458 | TEST(test_doc_resources, argv[1]) 459 | TEST(test_doc_spine, argv[1]) 460 | TEST(test_doc_toc, argv[1]) 461 | 462 | // Freeing the mallocs :P 463 | if (buf2) { 464 | buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview)); 465 | gtk_text_buffer_set_text (buffer, buf2, strlen (buf2)); 466 | g_free (buf2); 467 | } 468 | 469 | gtk_main (); 470 | 471 | g_object_unref (doc); 472 | 473 | return 0; 474 | } 475 | --------------------------------------------------------------------------------