From 510fff156db65795cbf211e7910a67fb316cb404 Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Sun, 27 Jul 2008 19:13:28 +0200 Subject: Initial commit --- LICENSE | 339 +++++++++++++ Makefile | 40 ++ README | 80 +++ cgg-dirgen | 51 ++ cgg.c | 156 ++++++ config.h | 26 + gallery-utils.c | 122 +++++ gallery-utils.h | 41 ++ generators.c | 882 +++++++++++++++++++++++++++++++++ generators.h | 64 +++ items.c | 203 ++++++++ items.h | 75 +++ jpeg-utils.c | 280 +++++++++++ jpeg-utils.h | 65 +++ sample/gen.sh | 4 + sample/src/CIAF_1/img_6802.jpg | Bin 0 -> 797526 bytes sample/src/CIAF_1/img_6802b.jpg | 1 + sample/src/CIAF_1/img_6802c.jpg | 1 + sample/src/CIAF_1/img_6850.jpg | Bin 0 -> 852418 bytes sample/src/CIAF_1/img_6850b.jpg | 1 + sample/src/CIAF_1/img_7270.jpg | Bin 0 -> 857597 bytes sample/src/CIAF_1/index.xml | 48 ++ sample/src/CIAF_1/preview/img_6802.jpg | Bin 0 -> 170320 bytes sample/src/index.xml | 30 ++ sample/src/setup.xml | 35 ++ sample/src/subdir/CIAF_2/img_6803.jpg | Bin 0 -> 170496 bytes sample/src/subdir/CIAF_2/img_6813.jpg | Bin 0 -> 125719 bytes sample/src/subdir/CIAF_2/img_6815.jpg | Bin 0 -> 112015 bytes sample/src/subdir/CIAF_2/img_6830.jpg | Bin 0 -> 111134 bytes sample/src/subdir/CIAF_2/img_6855.jpg | Bin 0 -> 72126 bytes sample/src/subdir/CIAF_2/img_6857.jpg | Bin 0 -> 94683 bytes sample/src/subdir/CIAF_2/index.xml | 50 ++ sample/src/subdir/Tatry/img_7738.jpg | Bin 0 -> 371722 bytes sample/src/subdir/Tatry/img_7755.jpg | Bin 0 -> 321602 bytes sample/src/subdir/Tatry/img_7792.jpg | Bin 0 -> 257911 bytes sample/src/subdir/Tatry/index.xml | 27 + sample/src/subdir/index.xml | 23 + setup.c | 225 +++++++++ setup.h | 77 +++ templates/scripts-general.js | 22 + templates/styles.css | 357 +++++++++++++ templates/template-album.tmpl | 70 +++ templates/template-index.tmpl | 73 +++ templates/template-view_photo.tmpl | 71 +++ xml-parser.c | 211 ++++++++ xml-parser.h | 60 +++ 46 files changed, 3810 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100755 cgg-dirgen create mode 100644 cgg.c create mode 100644 config.h create mode 100644 gallery-utils.c create mode 100644 gallery-utils.h create mode 100644 generators.c create mode 100644 generators.h create mode 100644 items.c create mode 100644 items.h create mode 100644 jpeg-utils.c create mode 100644 jpeg-utils.h create mode 100755 sample/gen.sh create mode 100644 sample/src/CIAF_1/img_6802.jpg create mode 120000 sample/src/CIAF_1/img_6802b.jpg create mode 120000 sample/src/CIAF_1/img_6802c.jpg create mode 100644 sample/src/CIAF_1/img_6850.jpg create mode 120000 sample/src/CIAF_1/img_6850b.jpg create mode 100644 sample/src/CIAF_1/img_7270.jpg create mode 100644 sample/src/CIAF_1/index.xml create mode 100644 sample/src/CIAF_1/preview/img_6802.jpg create mode 100644 sample/src/index.xml create mode 100644 sample/src/setup.xml create mode 100644 sample/src/subdir/CIAF_2/img_6803.jpg create mode 100644 sample/src/subdir/CIAF_2/img_6813.jpg create mode 100644 sample/src/subdir/CIAF_2/img_6815.jpg create mode 100644 sample/src/subdir/CIAF_2/img_6830.jpg create mode 100644 sample/src/subdir/CIAF_2/img_6855.jpg create mode 100644 sample/src/subdir/CIAF_2/img_6857.jpg create mode 100644 sample/src/subdir/CIAF_2/index.xml create mode 100644 sample/src/subdir/Tatry/img_7738.jpg create mode 100644 sample/src/subdir/Tatry/img_7755.jpg create mode 100644 sample/src/subdir/Tatry/img_7792.jpg create mode 100644 sample/src/subdir/Tatry/index.xml create mode 100644 sample/src/subdir/index.xml create mode 100644 setup.c create mode 100644 setup.h create mode 100644 templates/scripts-general.js create mode 100644 templates/styles.css create mode 100644 templates/template-album.tmpl create mode 100644 templates/template-index.tmpl create mode 100644 templates/template-view_photo.tmpl create mode 100644 xml-parser.c create mode 100644 xml-parser.h diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..86707ce --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +# path definitions +INSTALLPREFIX = /usr +INSTALL=/usr/bin/install -c +INSTALL_DATA = ${INSTALL} -m 644 + +# compiler options +CC = gcc +CPP = g++ +CFLAGS = -I. -I/usr/include -Wall -fPIC -g \ + -D__DEBUG__ -D__DEBUG_ALL__x + +OBJECTS=gallery-utils.o jpeg-utils.o xml-parser.o setup.o items.o generators.o cgg.o + +.SUFFIXES: .c .cpp +.c.o: + $(CC) $(CFLAGS) `pkg-config libxml-2.0 glib-2.0 libexif --cflags` `Wand-config --cflags` -c $< +.cpp.o: + $(CC) $(CFLAGS) `pkg-config libxml-2.0 glib-2.0 libexif --cflags` `Wand-config --cppflags` -c $< + +all cgg: check $(OBJECTS) + $(CC) -o cgg $(OBJECTS) -lm $(CFLAGS) `pkg-config libxml-2.0 glib-2.0 libexif --libs` `Wand-config --ldflags --libs` + +cgg.o: cgg.c +jpeg-utils.o: jpeg-utils.c jpeg-utils.h +gallery-utils.o: gallery-utils.c gallery-utils.h +setup.o: setup.c setup.h +xml-parser.o: xml-parser.c xml-parser.h +items.o: items.c items.h +generators.o: generators.c generators.h + +check:: + @pkg-config --cflags libxml-2.0 glib-2.0 libexif > /dev/null || exit 1 + @Wand-config --cflags > /dev/null || exit 1 + +install:: + $(INSTALL) ./cgg $(INSTALLPREFIX)/bin + +clean: + rm -f *.o *.d cgg + diff --git a/README b/README new file mode 100644 index 0000000..c97022f --- /dev/null +++ b/README @@ -0,0 +1,80 @@ +CATARACT +Static web photo gallery generator +http://cgg.bzatek.net/ + +version 0.99.0 [2008-07-27] +Copyright (C) 2008 Tomas Bzatek + + +DESCRIPTION +----------- + +This is the CGG, Cataract Gallery Generator - a simple static web photo +gallery, designed to be clean and easily usable. Due to the static design, +there's no way to comment pictures on the web. + +Feature highlights: + * static HTML gallery, no extra requirements on server + * fast, easy to cache/mirror + * XML-based description files + * modern design, valid XHTML 1.0 and CSS 2 + * EXIF support + * console application, allowing easy scripting + (e.g. auto-refresh after new images are uploaded via FTP) + +Planned features: + * prefetch of next image (JavaScript) + * strip unnecessary blocks from images - EXIF, thumbnails + * port to exiv2 library to get rid of that weird shutter times, IPTC support + * support adding comments and copyright notes to images + * switchable flat album view + * groups in the album - e.g. separate years, decades... + * jBrout db support? + + +DOCUMENTATION +------------- + +CGG creates a set of HTML files and images from the source structure. +Input files are structured in subdirectories, each directory may contain +one index XML file and the generated directory tree copies the source structure. +All links to sub-galleries, items, descriptions and individual settings +are specified in XML files. No more files other than global setup XML file +are needed. + +For convenience, sources contain the cgg-dirgen.sh script, which creates +index XML template from images in current directory. That can be used +as a skeleton of new album, writing down title, description and comments. + +For detailed syntax of input XML files please check the sample gallery included +in the source tree. It demonstrates basic features and contains detailed tag +description inside the XML files. + + +BUILDING +-------- + +First make sure you have meet the following requirements: + libxml-2.0 (tested with 2.6.32) + glib-2.0 (2.12.0 recommented, tested with 2.16.3) + libexif (tested with 0.6.16) + ImageMagick (tested with 6.4.2.0) + +To compile cgg, type 'make' + + +DEVELOPMENT +----------- + +gitweb: http://git.bzatek.net/?p=tuxcmd/.git;a=summary +repository snapshot command: + git clone git://git.bzatek.net/tuxcmd + +For write access to the repository please contact developers. + + +LICENSE +------- + +Please see 'LICENSE' + diff --git a/cgg-dirgen b/cgg-dirgen new file mode 100755 index 0000000..6946ca6 --- /dev/null +++ b/cgg-dirgen @@ -0,0 +1,51 @@ +#!/bin/bash +## Cataract Gallery Generator - a simple static web photo gallery +## cgg-dirgen - Directory index.xml generator +## Copyright (C) 2008 Tomas Bzatek +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +## + +## Optional arguments (must be in order, for now): +## -d Use preview pictures from the "preview" folder +## -o Do not include original image (removes link to original size) + + +cat << XML_HEADER_STOP + + + + Album ID + Album Title + + ]]> + + + +XML_HEADER_STOP + +for i in `find -L . -maxdepth 1 -type f -iname '*.jpg' -or -iname '*.jpeg' -or -iname '*.gif' -or -iname '*.png' | sort`; do + INCL=""; + INCL2=""; + if [ "$1" = "-d" ]; then INCL=" preview=\"preview/`echo $i | cut -b 3-`\""; fi + if [ "$2" = "-o" ]; then INCL2=" \n"; fi + echo -e " \n${INCL2} \n \n \n"; +done + +cat << XML_FOOTER_STOP + + +XML_FOOTER_STOP + diff --git a/cgg.c b/cgg.c new file mode 100644 index 0000000..690c919 --- /dev/null +++ b/cgg.c @@ -0,0 +1,156 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2008 Tomas Bzatek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include +#include +#include +#include + +#include + +#include + +#include "config.h" +#include "setup.h" +#include "generators.h" + + + + +#define APP_VERSION "0.99.0" +#define APP_BUILD_DATE "2008-07-27" + + + +/* + * parse_cmd: parse commandline and fill global variable parameters + */ +gboolean +parse_cmd (int argc, char* argv[], char **source_dir, char **dst_dir, gboolean *verbose) +{ + static gboolean _verbose = FALSE; + static gchar *_source_dir = NULL; + static gchar *_dst_dir = NULL; + + static GOptionEntry entries[] = + { + { "verbose", 'v', 0, G_OPTION_ARG_NONE, &_verbose, "Be verbose", NULL }, + { "source", 's', 0, G_OPTION_ARG_STRING, &_source_dir, "Specifies path to source structure", NULL }, + { "output", 'o', 0, G_OPTION_ARG_STRING, &_dst_dir, "Generate files to the specified directory instead of current", NULL }, + { NULL } + }; + + GError *error = NULL; + GOptionContext *context; + char *s1; + + g_set_prgname ("cgg"); + + context = g_option_context_new ("- web gallery generator"); + s1 = g_strdup_printf ("cgg v%s [%s] Copyright (c) 2008 Tomas Bzatek", APP_VERSION, APP_BUILD_DATE); + g_option_context_set_summary (context, s1); + g_free (s1); + g_option_context_add_main_entries (context, entries, NULL); + + if (argc == 1) { + s1 = g_option_context_get_help (context, TRUE, NULL); + g_print (s1); + g_free (s1); + g_option_context_free (context); + return FALSE; + } + + if (! g_option_context_parse (context, &argc, &argv, &error)) { + g_print ("option parsing failed: %s\n", error->message); + s1 = g_option_context_get_help (context, TRUE, NULL); + g_print (s1); + g_free (s1); + g_option_context_free (context); + return FALSE; + } + g_option_context_free (context); + + *source_dir = _source_dir; + *dst_dir = _dst_dir; + *verbose = _verbose; + + return TRUE; +} + + + +int +main(int argc, char* argv[]) +{ + char *source_dir; + char *dst_dir; + gboolean verbose; + TGallerySetup *setup; + + /* + * this initialize the library and check potential ABI mismatches + * between the version it was compiled for and the actual shared + * library used. + */ + LIBXML_TEST_VERSION; + + source_dir = NULL; + dst_dir = NULL; + setup = malloc(sizeof(TGallerySetup)); + + + /* Parse commandline */ + if (! parse_cmd (argc, argv, &source_dir, &dst_dir, &verbose)) + return -1; + + if ((! source_dir) || (access (source_dir, R_OK))) { + fprintf (stderr, "error: source directory must be specified and pointing to valid structure\n"); + return -4; + } + if (! dst_dir) { + fprintf (stderr, "error: target directory must be specified\n"); + return -5; + } + + /* Read gallery settings */ + if (! find_setup_xml (setup)) { + fprintf (stderr, "error: could not parse gallery settings file\n"); + return -2; + } + + setup->real_templates_dir = find_templates_directory (setup); + if (setup->real_templates_dir == NULL) { + fprintf (stderr, "error: could not determine templates directory\n"); + return -3; + } + + + /* Start building the gallery tree */ + setup->verbose = verbose; + build_tree (setup, source_dir, dst_dir, NULL); + + + /* Cleanup function for the XML library. */ + xmlCleanupParser(); + + free (source_dir); + free (dst_dir); + free_setup_data (setup); + + return (0); +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..21ac1cd --- /dev/null +++ b/config.h @@ -0,0 +1,26 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2008 Tomas Bzatek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + + +/* Directory names of smaller images */ +#define THUMBNAIL_DIR "_thumb" +#define IMG_BIG_DIR "_big" +#define IMG_ORIG_DIR "_orig" + +#define SETUP_XML "setup.xml" diff --git a/gallery-utils.c b/gallery-utils.c new file mode 100644 index 0000000..6de0224 --- /dev/null +++ b/gallery-utils.c @@ -0,0 +1,122 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2008 Tomas Bzatek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include +#include +#include + +#include "gallery-utils.h" + + +/* + * str_replace: replace substring 'search' with a 'replace' string + * - only one occurence of the string is replaced + * - reallocates the original string + */ +void +str_replace (char **dst, const char *search, const char *replace) +{ + char *res; + char *tms; + int new_str_len; + + /* is 'search' present in 'dst'? */ + res = strstr (*dst, search); + if (res == NULL) + return; + + new_str_len = strlen (*dst) - strlen (search) + strlen (replace) + 1; + + tms = malloc (new_str_len); + memset (tms, 0, new_str_len); + + memcpy (tms, *dst, res - *dst); + memcpy ((void*) ((long int) tms + (long int) res - (long int) *dst), + replace, + strlen (replace)); + memcpy ((void*) ((long int) tms + (long int) res - (long int) *dst + strlen (replace)), + (void*) ((long int) res + strlen (search)), + strlen (res) - strlen (search)); + + /* return fixed string */ + free (*dst); + *dst = tms; +} + + +/* + * copy_file: copy file from src to dst + */ +gboolean +copy_file (const char *src, const char *dst) +{ + #define BUFFER_SIZE 65536 + + FILE *fin; + FILE *fout; + void *buffer; + int size_r; + + fin = fopen (src, "r"); + if (fin == NULL) { + fprintf (stderr, "copy_file: error reading file \"%s\": %s\n", src, strerror (errno)); + return FALSE; + } + + fout = fopen (dst, "w"); + if (fout == NULL) { + fprintf (stderr, "copy_file: error writing to file \"%s\": %s\n", dst, strerror (errno)); + fclose (fin); + return FALSE; + } + + buffer = malloc (BUFFER_SIZE); + memset (buffer, 0, BUFFER_SIZE); + size_r = BUFFER_SIZE; + + while ((! feof (fin)) && (size_r == BUFFER_SIZE)) { + size_r = fread (buffer, 1, BUFFER_SIZE, fin); + fwrite (buffer, 1, size_r, fout); + } + + fclose (fout); + fclose (fin); + free (buffer); + + return TRUE; +} + + +/* + * make_string: make string of 'substr' substrings + * - returns newly allocated string + */ +char * +make_string (const char* substr, const int count) +{ + int i; + char *s; + + s = malloc (strlen (substr) * count + 1); + for (i = 0; i < count; i++) + memcpy (s + (strlen (substr) * i), substr, strlen (substr)); + s[strlen (substr) * count] = 0; + return s; +} + + diff --git a/gallery-utils.h b/gallery-utils.h new file mode 100644 index 0000000..1547c90 --- /dev/null +++ b/gallery-utils.h @@ -0,0 +1,41 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2008 Tomas Bzatek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include + +#define IS_DIR_SEP(ch) ((ch) == '/' || (ch) == '~') +#define IS_EQUAL_SIGN(ch) ((ch) == '=') + + +/* + * str_replace: replace substring 'search' with a 'replace' string + * - only one occurence of the string is replaced + * - reallocates the original string + */ +void str_replace (char **dst, const char *search, const char *replace); + +/* + * copy_file: copy file from src to dst + */ +gboolean copy_file (const char *src, const char *dst); + +/* + * make_string: make string of 'substr' substrings + * - returns newly allocated string + */ +char *make_string (const char* substr, const int count); + diff --git a/generators.c b/generators.c new file mode 100644 index 0000000..a3abe24 --- /dev/null +++ b/generators.c @@ -0,0 +1,882 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2008 Tomas Bzatek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include +#include +#include +#include +#include + +#include + +#include "config.h" +#include "setup.h" +#include "items.h" +#include "jpeg-utils.h" +#include "gallery-utils.h" + + +/* + * generate_image: auxiliary function for write_html_album + */ +void +generate_image (TGallerySetup *setup, + TAlbum *items, + TIndexItem *item, + const char *dst, + unsigned long *img_w, unsigned long *img_h, + const char **img_src) +{ + unsigned long new_w, new_h; + char *thumb_dst; + char *big_dst; + char *big_src; + char *orig_dst; + char *img_src_full; + char *s1, *s2; + int bigq; + + + *img_src = item->thumbnail; + if (items->type == GALLERY_TYPE_INDEX) + *img_src = item->thumbnail; + else + if (items->type == GALLERY_TYPE_ALBUM) + *img_src = item->path; + + img_src_full = g_strconcat (items->base_dir, "/", *img_src, NULL); + get_image_sizes (img_src_full, img_w, img_h); + + if ((img_w > 0) && (img_h > 0)) { + new_w = *img_w; + new_h = *img_h; + + + /* Generate thumbnail */ + s1 = g_path_get_dirname (dst); + s2 = g_path_get_basename (*img_src); + thumb_dst = g_strconcat (s1, "/", THUMBNAIL_DIR, "/", s2, NULL); + g_free (s1); + g_free (s2); + if (setup->verbose) printf (" Generating thumbnail of '%s' ...", *img_src); + + if ((*img_w / *img_h) >= 1) + calculate_sizes (setup->thumbnail_landscape_width, setup->thumbnail_landscape_height, &new_w, &new_h); + else + calculate_sizes (setup->thumbnail_portrait_width, setup->thumbnail_portrait_height, &new_w, &new_h); + if (! resize_image (img_src_full, thumb_dst, new_w, new_h, setup->thumbnail_quality)) + fprintf (stderr, "write_html_index: error resizing thumbnail %s\n", img_src_full); + else + if (setup->verbose) printf (" done.\n"); + g_free (thumb_dst); + + + /* Generate/copy preview and original image */ + if (items->type == GALLERY_TYPE_ALBUM) + { + s1 = g_path_get_dirname (dst); + big_dst = g_strconcat (s1, "/", IMG_BIG_DIR, "/", *img_src, NULL); + g_free (s1); + + if (item->preview == NULL) { + /* No preview image supplied, generate it from original */ + bigq = setup->preview_quality; + if ((items->quality > 0) && (items->quality <= 100)) + bigq = items->quality; + if ((item->quality > 0) && (item->quality <= 100)) + bigq = item->quality; + new_w = *img_w; + new_h = *img_h; + + if (setup->verbose) printf (" Generating preview of '%s' ...", *img_src); + if ((item->width > 0) && (item->height > 0)) { + calculate_sizes (item->width, item->height, &new_w, &new_h); + } else { + if ((*img_w / *img_h) >= 1) + { + if ((items->landscape_width > 0) && (items->landscape_height > 0)) + calculate_sizes (items->landscape_width, items->landscape_height, &new_w, &new_h); + else + calculate_sizes (setup->preview_landscape_width, setup->preview_landscape_height, &new_w, &new_h); + } + else + { + if ((items->portrait_width > 0) && (items->portrait_height > 0)) + calculate_sizes (items->portrait_width, items->portrait_height, &new_w, &new_h); + else + calculate_sizes (setup->preview_portrait_width, setup->preview_portrait_height, &new_w, &new_h); + } + } + + if (! resize_image (img_src_full, big_dst, new_w, new_h, bigq)) + fprintf (stderr, "write_html_index: error resizing big image %s\n", img_src_full); + else + if (setup->verbose) printf (" done.\n"); + } + else + { + /* Copy the preview (big) image provided */ + big_src = g_strconcat (items->base_dir, "/", item->preview, NULL); + if (setup->verbose) printf (" Copying preview image '%s' ...", *img_src); + if (! copy_file (big_src, big_dst)) + fprintf (stderr, "write_html_index: error copying preview image %s\n", big_src); + else + if (setup->verbose) printf (" done.\n"); + g_free (big_src); + } + g_free (big_dst); + + if (! item->nofullsize) + { + s1 = g_path_get_dirname(dst); + orig_dst = g_strconcat (s1, "/", IMG_ORIG_DIR, "/", *img_src, NULL); + g_free (s1); + if (setup->verbose) printf (" Copying original image '%s' ...", *img_src); + if (! copy_file(img_src_full, orig_dst)) + fprintf (stderr, "write_html_index: error copying original image %s\n", img_src_full); + else + if (setup->verbose) printf(" done.\n"); + g_free (orig_dst); + } + } + } + g_free (img_src_full); +} + + + +/* + * write_html_album: process album and index template files + * + * template_src = template file of the album/index + * dst = save generated file as + * items = array of items in the album/index + * + */ +gboolean +write_html_album (TGallerySetup *setup, + const char *template_src, + const char *dst, + TAlbum *items) +{ + #define BUFFER_SIZE 65536 + FILE *fin; + FILE *fout; + char *buffer; + char *buf_img_list_landscape; + char *buf_img_list_portrait; + char *buf_go_up_string; + gboolean in_img_list; + gboolean in_img_list_landscape; + gboolean in_img_list_portrait; + gboolean in_go_up_string; + char *b; + char *s1, *s2, *s3; + TAlbum *parent; + TIndexItem *item; + int level; + gboolean res; + int i; + unsigned long img_w, img_h; + const char *img_src; + + + fin = fopen (template_src, "r"); + if (fin == NULL) { + fprintf (stderr, "write_html_index: error reading file \"%s\": %s\n", template_src, strerror (errno)); + return FALSE; + } + fout = fopen (dst, "w"); + if (fout == NULL) { + fprintf (stderr, "write_html_index: error writing to file \"%s\": %s\n", dst, strerror (errno)); + fclose (fin); + return FALSE; + } + + buffer = malloc (BUFFER_SIZE); + buf_img_list_landscape = malloc (BUFFER_SIZE); + buf_img_list_portrait = malloc (BUFFER_SIZE); + buf_go_up_string = malloc (BUFFER_SIZE); + in_img_list = FALSE; + in_img_list_landscape = FALSE; + in_img_list_portrait = FALSE; + in_go_up_string = FALSE; + res = TRUE; + + /* Read through the template and replace placeholders with real data */ + while (! feof (fin)) + { + memset (buffer, 0, BUFFER_SIZE); + if (! fgets (buffer, BUFFER_SIZE, fin)) + break; + if (buffer == NULL) + continue; + b = strdup (buffer); + + + /* Block placeholders */ + if (in_img_list && (strstr (buffer, ""))) { + in_img_list_landscape = TRUE; + free (b); + continue; + } + if (in_img_list && (strstr (buffer, ""))) { + in_img_list_landscape = FALSE; + free (b); + continue; + } + if (in_img_list && (strstr (buffer, ""))) { + in_img_list_portrait = TRUE; + free (b); + continue; + } + if (in_img_list && (strstr (buffer, ""))) { + in_img_list_portrait = FALSE; + free (b); + continue; + } + if (in_img_list && in_img_list_landscape) { + buf_img_list_landscape = strncat (buf_img_list_landscape, b, BUFFER_SIZE - strlen (buf_img_list_landscape) - 2); + free (b); + continue; + } + if (in_img_list && in_img_list_portrait) { + buf_img_list_portrait = strncat (buf_img_list_portrait, b, BUFFER_SIZE - strlen (buf_img_list_portrait) - 2); + free (b); + continue; + } + + if (strstr (buffer, "")) { + memset (buf_go_up_string, 0, BUFFER_SIZE); + in_go_up_string = TRUE; + free (b); + continue; + } + if (in_go_up_string && (strstr (buffer, ""))) { + in_go_up_string = FALSE; + free (b); + /* print the "Go Up" string if not toplevel */ + if (items->parent_index) + b = strdup (buf_go_up_string); + else continue; + } + if (in_go_up_string) { + buf_go_up_string = strncat (buf_go_up_string, b, BUFFER_SIZE - strlen (buf_go_up_string) - 2); + free (b); + continue; + } + + + /* Simple placeholders */ + if (strstr (b, "") && items->ID) + str_replace (&b, "", items->ID); + if (strstr (b, "") && items->title) + str_replace (&b, "", items->title); + if (strstr (b, "") && items->desc) + str_replace (&b, "", items->desc); + if (strstr (b, "") && setup->footer) + str_replace (&b, "", setup->footer); + if (strstr (b, "")) { + s1 = g_strdup_printf ("%d", items->items->len); + str_replace (&b, "", s1); + g_free (s1); + } + if (strstr (b, "")) { + s1 = g_strdup (items->ID); + parent = items->parent_index; + level = 1; + while (parent) { + s3 = make_string ("../", level); + s2 = g_strconcat ("", parent->ID, " > ", s1, NULL); + free (s3); + g_free (s1); + s1 = s2; + parent = parent->parent_index; + level++; + } + str_replace (&b, "", s1); + g_free (s1); + } + + /* Image list, nested placeholders */ + if (strstr (buffer, "")) { + memset (buf_img_list_landscape, 0, BUFFER_SIZE); + memset (buf_img_list_portrait, 0, BUFFER_SIZE); + in_img_list = TRUE; + in_img_list_landscape = FALSE; + in_img_list_portrait = FALSE; + free (b); + continue; + } + + if (in_img_list && (strstr (buffer, ""))) { + in_img_list = FALSE; + in_img_list_landscape = FALSE; + in_img_list_portrait = FALSE; + + /* Now we have all block placeholders read, generate the items: */ + for (i = 0; i < items->items->len; i++) + { + item = g_ptr_array_index (items->items, i); + if (item == NULL) { + fprintf (stderr, "write_html_index: error getting item %d\n", i); + free (b); + continue; + } + + /* Generate the images (preview, original, thumbnail) */ + img_w = 0; + img_h = 0; + img_src = NULL; + generate_image (setup, items, item, dst, &img_w, &img_h, &img_src); + + /* Process HTML box code */ + if ((img_w / img_h) >= 1) + s1 = strdup (buf_img_list_landscape); + else + s1 = strdup (buf_img_list_portrait); + + while (strstr (s1, "")) { + s2 = g_strconcat (item->path, "/index.html", NULL); + str_replace (&s1, "", s2); + g_free (s2); + } + while (strstr (s1, "")) { + s2 = g_strconcat (img_src, ".html", NULL); + str_replace (&s1, "", s2); + g_free (s2); + } + while (strstr (s1, "") && item->title) + str_replace (&s1, "", item->title); + while (strstr (s1, "") && item->title_description) + str_replace (&s1, "", item->title_description); + while (strstr(s1, "")) { + s3 = g_strconcat (items->base_dir, "/", item->path, "/index.xml", NULL); + s2 = g_strdup_printf ("%d", get_album_objects_count(s3)); + str_replace (&s1, "", s2); + g_free (s2); + g_free (s3); + } + while (strstr (s1, "")) { + s3 = g_path_get_basename (img_src); + s2 = g_strconcat (THUMBNAIL_DIR, "/", s3, NULL); + str_replace (&s1, "", s2); + g_free (s2); + g_free (s3); + } + while (strstr (s1, "")) + str_replace (&s1, "", img_src); + + + #ifdef __DEBUG_ALL__ + printf("***** %s ******\n", s1); + #endif + if (! fputs (s1, fout)) { + fprintf (stderr, "write_html_index: error writing to file \"%s\": %s\n", dst, strerror (errno)); + res = FALSE; + free (s1); + break; + } + free (s1); + } + free (b); + continue; /* no need to write anything */ + } + + /* Write the generated string to target file */ + if (! fputs (b, fout)) { + fprintf (stderr, "write_html_index: error writing to file \"%s\": %s\n", dst, strerror (errno)); + res = FALSE; + free (b); + break; + } + free (b); + } + + fclose (fout); + fclose (fin); + + free (buffer); + free (buf_img_list_landscape); + free (buf_img_list_portrait); + free (buf_go_up_string); + return res; +} + + +/* + * write_html_image: process single image template file + * + * template_src = template file of the album/index + * original_img = source image file (original full-size) to get EXIF data from + * dst = save generated file as + * item = data for the current item + * parent_items = array of items in the album, to determine our position and make links to previous/next image + * + */ +gboolean +write_html_image (TGallerySetup *setup, + const char *template_src, + const char *original_img, + const char *dst, + TIndexItem *item, + TAlbum *parent_items) +{ + #define BUFFER_SIZE 65536 + FILE *fin; + FILE *fout; + char *buffer; + char *big_dst; + char *orig_dst; + char *buf_img_fullsize_link; + gboolean in_img_fullsize_link; + TExifData *exif; + unsigned long img_big_w, img_big_h, img_orig_w, img_orig_h; + unsigned int item_index; + TIndexItem *previous_item; + TIndexItem *next_item; + TAlbum *parent; + int i; + char *s1, *s2, *s3; + char *b; + gboolean res; + int level; + + + fin = fopen (template_src, "r"); + if (fin == NULL) { + fprintf (stderr, "write_html_image: error reading file \"%s\": %s\n", template_src, strerror (errno)); + return FALSE; + } + fout = fopen (dst, "w"); + if (fout == NULL) { + fprintf (stderr, "write_html_image: error writing to file \"%s\": %s\n", dst, strerror (errno)); + fclose (fin); + return FALSE; + } + + buffer = malloc (BUFFER_SIZE); + s1 = g_path_get_dirname (dst); + big_dst = g_strconcat (s1, "/", IMG_BIG_DIR, "/", item->path, NULL); + orig_dst = g_strconcat (s1, "/", IMG_ORIG_DIR, "/", item->path, NULL); + g_free (s1); + buf_img_fullsize_link = malloc (BUFFER_SIZE); + memset (buf_img_fullsize_link, 0, BUFFER_SIZE); + in_img_fullsize_link = FALSE; + res = TRUE; + + + /* Get EXIF data from the original image */ + exif = malloc (sizeof (TExifData)); + if (get_exif (original_img, exif)) + fprintf (stderr, "write_html_image: error getting exif data from file \"%s\"\n", orig_dst); + + /* Retrieve image sizes of preview and original image */ + get_image_sizes (big_dst, &img_big_w, &img_big_h); + if (! item->nofullsize) + get_image_sizes (orig_dst, &img_orig_w, &img_orig_h); + + /* Get our index in the album */ + item_index = 0; + for (i = 0; i < parent_items->items->len; i++) + if (g_ptr_array_index (parent_items->items, i) == item) { + item_index = i + 1; + break; + } + + /* Get previous and next items */ + previous_item = NULL; + next_item = NULL; + if (item_index > 1) + previous_item = g_ptr_array_index (parent_items->items, item_index - 2); + if (item_index < parent_items->items->len) + next_item = g_ptr_array_index (parent_items->items, item_index); + + + /* Read through the template and replace placeholders with real data */ + while (! feof (fin)) { + memset (buffer, 0, BUFFER_SIZE); + if (! fgets (buffer, BUFFER_SIZE, fin)) + break; + b = strdup (buffer); + + + /* Block placeholders */ + if (strstr (buffer, "")) { + in_img_fullsize_link = TRUE; + free (b); + continue; + } + if (strstr (buffer, "")) { + in_img_fullsize_link = FALSE; + free (b); + if (! item->nofullsize) + b = strdup (buf_img_fullsize_link); + else continue; + } + if (in_img_fullsize_link) { + buf_img_fullsize_link = strncat (buf_img_fullsize_link, b, BUFFER_SIZE - strlen (buf_img_fullsize_link) - 2); + free (b); + continue; + } + + + /* Simple placeholders */ + if (strstr (b, "")) + str_replace (&b, "", item->path); + if (strstr (b, "") && item->title) + str_replace (&b, "", item->title); + if (strstr (b, "") && item->title_description) + str_replace (&b, "", item->title_description); + if (strstr (b, "")) { + s1 = g_strdup_printf ("%d", parent_items->items->len); + str_replace (&b, "", s1); + g_free (s1); + } + if (strstr (b, "")) { + s1 = g_strdup_printf ("%d", item_index); + str_replace(&b, "", s1); + g_free (s1); + } + if (strstr (b, "")) { + s1 = g_strconcat (item->title, " (", item->path, ")", NULL); + parent = parent_items; + level = 0; + while (parent) { + s3 = make_string ("../", level); + s2 = g_strconcat ("", parent->ID, " > ", s1, NULL); + free (s3); + g_free (s1); + s1 = s2; + parent = parent->parent_index; + level++; + } + str_replace (&b, "", s1); + g_free (s1); + } + if (strstr (b, "")) { + s1 = g_strconcat (IMG_BIG_DIR, "/", item->path, NULL); + str_replace (&b, "", s1); + g_free (s1); + } + if (strstr(b, "")) { + s1 = g_strconcat (IMG_ORIG_DIR, "/", item->path, NULL); + str_replace (&b, "", s1); + g_free (s1); + } + if (strstr(b, "")) { + s1 = g_strdup_printf ("%lu", img_big_w); + str_replace (&b, "", s1); + g_free (s1); + } + if (strstr(b, "")) { + s1 = g_strdup_printf ("%lu", img_big_h); + str_replace (&b, "", s1); + g_free (s1); + } + if (strstr(b, "")) { + s1 = g_strdup_printf ("%lu", img_orig_w); + str_replace (&b, "", s1); + g_free (s1); + } + if (strstr(b, "")) { + s1 = g_strdup_printf ("%lu", img_orig_h); + str_replace (&b, "", s1); + g_free (s1); + } + + if (strstr (b, "")) { + if (exif->iso) + str_replace (&b, "", exif->iso); + else + str_replace (&b, "", "??"); + } + if (strstr (b, "")) { + if (exif->exposure) + str_replace (&b, "", exif->exposure); + else + str_replace (&b, "", "??"); + } + if (strstr (b, "")) { + if (exif->aperture) + str_replace (&b, "", exif->aperture); + else + str_replace (&b, "", "??"); + } + if (strstr (b, "")) { + if (exif->focal_length) + str_replace (&b, "", exif->focal_length); + else + str_replace (&b, "", "??"); + } + if (strstr (b, "")) { + if (exif->flash) + str_replace (&b, "", exif->flash); + else + str_replace (&b, "", "??"); + } + if (strstr (b, "")) { + if (exif->datetime) + str_replace (&b, "", exif->datetime); + else + str_replace (&b, "", "??"); + } + if (strstr (b, "")) { + if (exif->camera_model) + str_replace (&b, "", exif->camera_model); + else + str_replace (&b, "", "??"); + } + if (strstr (b, "")) { + if (exif->focal_length_35mm) { + s1 = g_strconcat ("(", exif->focal_length_35mm, ")", NULL); + str_replace (&b, "", s1); + g_free (s1); + } + else + str_replace (&b, "", ""); + } + + if (strstr (b, "")) { + if (next_item) { + s1 = g_strconcat (next_item->path, ".html", NULL); + str_replace (&b, "", s1); + g_free (s1); + } + else + str_replace (&b, "", "index.html"); + } + if (strstr(b, "")) { + if (previous_item) { + s1 = g_strconcat (previous_item->path, ".html", NULL); + str_replace (&b, "", s1); + g_free (s1); + } + else + str_replace (&b, "", "index.html"); + } + + if (strstr (b, "")) + str_replace (&b, "", setup->footer); + + if (! fputs (b, fout)) { + fprintf (stderr, "write_html_image: error writing to file \"%s\": %s\n", dst, strerror (errno)); + res = FALSE; + free (b); + break; + } + free (b); + } + + fclose (fout); + fclose (fin); + free (buffer); + free (big_dst); + free (orig_dst); + free (buf_img_fullsize_link); + free_exif_data (exif); + return res; +} + + +/* + * build_tree: generate complete gallery tree based on source xml files + * + * src_tree = source directory of the items + * dst_dir = destination of the generated items + * parent_index = parent album to determine our descent in the tree + * + */ +gboolean +build_tree (TGallerySetup *setup, + const char *src_tree, + const char *dst_dir, + TAlbum *parent_index) +{ + char *idx_file; + TAlbum *items; + TIndexItem *item; + char *s1, *s2, *s3; + char *thumb_dir; + char *img_big_dir; + char *img_orig_dir; + char *template; + gboolean res; + int i; + + printf ("*** Entering directory '%s'\n", src_tree); + #ifdef __DEBUG_ALL__ + printf ("setup->real_templates_dir = %s\n", setup->real_templates_dir); + #endif + + /* Check access permissions */ + if (access (src_tree, R_OK | X_OK)) { + fprintf (stderr, "error accessing source directory: %s\n", strerror (errno)); + return FALSE; + } + if (access (dst_dir, R_OK | W_OK | X_OK)) { + if (mkdir (dst_dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) { + fprintf (stderr, "error creating destination directory: %s\n", strerror (errno)); + return FALSE; + } + } + + /* Check the index file */ + idx_file = g_strconcat (src_tree, "/index.xml", NULL); + if (access (src_tree, R_OK | X_OK)) { + fprintf (stderr, "error accessing index file '%s': %s\n", idx_file, strerror (errno)); + g_free (idx_file); + return FALSE; + } + + /* Read the index file and fill items array */ + items = malloc (sizeof (TAlbum)); + if (! parse_album_xml (idx_file, items)) { + fprintf (stderr, "error reading index file '%s'\n", idx_file); + g_free (idx_file); + free_album_data (items); + return FALSE; + } + g_free (idx_file); + items->parent_index = parent_index; + + + /* Copy support files */ + if (setup->verbose) printf ("Writing '%s' ...", setup->styles); + s1 = g_strconcat (setup->real_templates_dir, "/", setup->styles, NULL); + s2 = g_strconcat (dst_dir, "/", setup->styles, NULL); + copy_file (s1, s2); + g_free (s1); + g_free (s2); + if (setup->verbose) printf (" done.\n"); + + if (setup->verbose) printf ("Writing '%s' ...", setup->scripts); + s1 = g_strconcat (setup->real_templates_dir, "/", setup->scripts, NULL); + s2 = g_strconcat (dst_dir, "/", setup->scripts, NULL); + copy_file (s1, s2); + g_free (s1); + g_free (s2); + if (setup->verbose) printf (" done.\n"); + + + /* Prepare target thumbnail directory */ + thumb_dir = g_strconcat (dst_dir, "/", THUMBNAIL_DIR, NULL); + if (access (thumb_dir, R_OK | W_OK | X_OK)) + if (mkdir (thumb_dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) { + fprintf (stderr, "error making target thumbnail directory: %s\n", strerror (errno)); + g_free (thumb_dir); + free_album_data (items); + return FALSE; + } + g_free (thumb_dir); + + /* Prepare target preview and orig directories */ + if (items->type == GALLERY_TYPE_ALBUM) + { + res = TRUE; + img_big_dir = g_strconcat (dst_dir, "/", IMG_BIG_DIR, NULL); + img_orig_dir = g_strconcat (dst_dir, "/", IMG_ORIG_DIR, NULL); + if (access (img_big_dir, R_OK | W_OK | X_OK)) + if (mkdir (img_big_dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) { + fprintf (stderr, "error making target preview directory: %s\n", strerror (errno)); + res = FALSE; + } + if (access (img_orig_dir, R_OK | W_OK | X_OK)) + if (mkdir (img_orig_dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) { + fprintf (stderr, "error making target full size directory: %s\n", strerror (errno)); + res = FALSE; + } + g_free (img_big_dir); + g_free (img_orig_dir); + if (! res) { + free_album_data (items); + return FALSE; + } + } + + + /* Start generating items */ + if (items->type == GALLERY_TYPE_INDEX) + template = g_strconcat ("/", setup->template_index, NULL); + else + if (items->type == GALLERY_TYPE_ALBUM) + template = g_strconcat ("/", setup->template_album, NULL); + + if (setup->verbose) printf ("Writing 'index.html' ...\n"); + s1 = g_strconcat (setup->real_templates_dir, template, NULL); + s2 = g_strconcat (dst_dir, "/index.html", NULL); + res = write_html_album (setup, s1, s2, items); + g_free (s1); + g_free (s2); + g_free (template); + if (! res) { + fprintf (stderr, "error generating target index file\n"); + free_album_data (items); + return FALSE; + } + if (setup->verbose) printf (" done.\n"); + + + /* Recurse to sub-albums (in case of album index) */ + if (items->type == GALLERY_TYPE_INDEX) + { + if (items->items->len > 0) { + for (i = 0; i < items->items->len; i++) { + item = g_ptr_array_index (items->items, i); + if (item == NULL) { + fprintf (stderr, "build_tree: error getting item %d\n", i); + continue; + } + s1 = g_strconcat (src_tree, "/", item->path, "/", NULL); + s2 = g_strconcat (dst_dir, "/", item->path, "/", NULL); + build_tree (setup, s1, s2, items); + g_free (s1); + g_free (s2); + } + } + } + + /* Generate separate image pages (in case of album) */ + if (items->type == GALLERY_TYPE_ALBUM) + { + if (items->items->len > 0) { + for (i = 0; i < items->items->len; i++) { + item = g_ptr_array_index (items->items, i); + if (item == NULL) { + fprintf (stderr, "build_tree: error getting item %d\n", i); + continue; + } + if (setup->verbose) printf ("Writing '%s.html' ...", item->path); + s1 = g_strconcat (setup->real_templates_dir, "/", setup->template_photo, NULL); + s2 = g_strconcat (items->base_dir, "/", item->path, NULL); + s3 = g_strconcat (dst_dir, "/", item->path, ".html", NULL); + res = write_html_image (setup, s1, s2, s3, item, items); + g_free (s1); + g_free (s2); + g_free (s3); + if (! res ) continue; + if (setup->verbose) printf (" done.\n"); + } + } + } + + printf ("*** Leaving directory '%s'\n", src_tree); + free_album_data (items); + + return TRUE; +} diff --git a/generators.h b/generators.h new file mode 100644 index 0000000..9dc9446 --- /dev/null +++ b/generators.h @@ -0,0 +1,64 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2008 Tomas Bzatek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "setup.h" +#include "items.h" + + +/* + * write_html_album: process album and index template files + * + * template_src = template file of the album/index + * dst = save generated file as + * items = array of items in the album/index + * + */ +gboolean write_html_album (TGallerySetup *setup, + const char *template_src, + const char *dst, + TAlbum *items); + +/* + * write_html_image: process single image template file + * + * template_src = template file of the album/index + * original_img = source image file (original full-size) to get EXIF data from + * dst = save generated file as + * item = data for the current item + * parent_items = array of items in the album, to determine our position and make links to previous/next image + * + */ +gboolean write_html_image (TGallerySetup *setup, + const char *template_src, + const char *original_img, + const char *dst, + TIndexItem *item, + TAlbum *parent_items); + +/* + * build_tree: generate complete gallery tree based on source xml files + * + * src_tree = source directory of the items + * dst_dir = destination of the generated items + * parent_index = parent album to determine our descent in the tree + * + */ +gboolean build_tree (TGallerySetup *setup, + const char *src_tree, + const char *dst_dir, + TAlbum *parent_index); + diff --git a/items.c b/items.c new file mode 100644 index 0000000..c1e1564 --- /dev/null +++ b/items.c @@ -0,0 +1,203 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2008 Tomas Bzatek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include +#include + +#include +#include +#include +#include + +#include "items.h" +#include "xml-parser.h" + + + +/* + * parse_album_xml: XML parser for gallery index.xml files + */ +gboolean +parse_album_xml (const char *filename, TAlbum *index) +{ + TXMLFile *xml; + char *gallery_type; + int count; + int i; + char *s; + TIndexItem *item; + + xml = xml_parser_load (filename); + if (xml == NULL) + return FALSE; + + /* Initialize data struct */ + if (index == NULL) + index = malloc (sizeof (TAlbum)); + memset (index, 0, sizeof (TAlbum)); + + index->base_dir = g_path_get_dirname (filename); + + /* Retrieve gallery type */ + gallery_type = xml_file_get_node_attribute (xml, "/gallery", "type"); + #ifdef __DEBUG_ALL__ + printf("gallery_type = %s\n", gallery_type); + #endif + if (strcmp (gallery_type, "index") == 0) + index->type = GALLERY_TYPE_INDEX; + else + if (strcmp (gallery_type, "album") == 0) + index->type = GALLERY_TYPE_ALBUM; + else { + printf ("Invalid gallery type (%s)\n", gallery_type); + free (index); + return FALSE; + } + + /* Section General */ + index->ID = xml_file_get_node_value (xml, "/gallery/general/ID/text()"); + index->title = xml_file_get_node_value (xml, "/gallery/general/title/text()"); + index->desc = xml_file_get_node_value (xml, "/gallery/general/description/text()"); + + index->quality = xml_file_get_node_attribute_long (xml, "/gallery/general/images", "quality", -1); + index->landscape_width = xml_file_get_node_attribute_long (xml, "/gallery/general/images", "landscape_w", 0); + index->landscape_height = xml_file_get_node_attribute_long (xml, "/gallery/general/images", "landscape_h", 0); + index->portrait_width = xml_file_get_node_attribute_long (xml, "/gallery/general/images", "portrait_w", 0); + index->portrait_height = xml_file_get_node_attribute_long (xml, "/gallery/general/images", "portrait_h", 0); + + /* Section Items */ + count = xml_file_node_get_children_count (xml, "/gallery/items/item"); + index->items = g_ptr_array_new(); + + for (i = 0; i < count; i++) + { + item = malloc (sizeof (TIndexItem)); + memset (item, 0, sizeof (TIndexItem)); + + s = g_strdup_printf ("/gallery/items/item[%d]", i + 1); + if (index->type == GALLERY_TYPE_INDEX) + item->path = xml_file_get_node_attribute (xml, s, "path"); + else + item->path = xml_file_get_node_attribute (xml, s, "src"); + item->preview = xml_file_get_node_attribute (xml, s, "preview"); + item->quality = xml_file_get_node_attribute_long (xml, s, "quality", -1); + item->width = xml_file_get_node_attribute_long (xml, s, "width", 0); + item->height = xml_file_get_node_attribute_long (xml, s, "height", 0); + g_free (s); + + s = g_strdup_printf ("/gallery/items/item[%d]/title/text()", i + 1); + item->title = xml_file_get_node_value (xml, s); + g_free (s); + + s = g_strdup_printf ("/gallery/items/item[%d]/title_description/text()", i + 1); + item->title_description = xml_file_get_node_value (xml, s); + g_free (s); + + s = g_strdup_printf ("/gallery/items/item[%d]/thumbnail", i + 1); + item->thumbnail = xml_file_get_node_attribute (xml, s, "src"); + g_free (s); + + s = g_strdup_printf ("/gallery/items/item[%d]/nofullsize", i + 1); + item->nofullsize = xml_file_get_node_present (xml, s); + g_free (s); + + g_ptr_array_add (index->items, item); + } + + xml_parser_close (xml); + + /* Print the items */ + #ifdef __DEBUG_ALL__ + printf("ID = '%s'\ntitle = '%s'\ndescription = '%s'\n", index->ID, index->title, index->desc); + for (i = 0; i < index->items->len; i++) { + TIndexItem *item = g_ptr_array_index(index->items, i); + printf("item %d: path = '%s', title = '%s', title_description = '%s', thumbnail = '%s'\n", + i, item->path, item->title, item->title_description, item->thumbnail); + } + #endif + return TRUE; +} + + +/* + * free_album_data: free allocated album data + */ +void +free_album_data (TAlbum *album) +{ + if (album) { + if (album->ID) + free (album->ID); + if (album->title) + free (album->title); + if (album->desc) + free (album->desc); + if (album->base_dir) + free (album->base_dir); + + if (album->items) { + if (album->items->len > 0) { + TIndexItem *item; + int i; + + for (i = 0; i < album->items->len; i++) { + item = g_ptr_array_index (album->items, i); + if (item != NULL) { + if (item->path) + free (item->path); + if (item->title) + free (item->title); + if (item->title_description) + free (item->title_description); + if (item->thumbnail) + free (item->thumbnail); + if (item->preview) + free (item->preview); + free (item); + } + } + } + g_ptr_array_free (album->items, TRUE); + } + free (album); + album = NULL; + } +} + + +/* + * get_gallery_objects_count: retrieve number of items in specified album + */ +int +get_album_objects_count (const char *filename) +{ + TXMLFile *xml; + int count; + + xml = xml_parser_load (filename); + if (xml == NULL) + return 0; + + count = xml_file_node_get_children_count (xml, "/gallery/items/item"); + xml_parser_close (xml); + + #ifdef __DEBUG_ALL__ + printf ("get_objects_count(%s) = %d\n", filename, count); + #endif + return count; +} diff --git a/items.h b/items.h new file mode 100644 index 0000000..b89728c --- /dev/null +++ b/items.h @@ -0,0 +1,75 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2008 Tomas Bzatek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef __ITEMS_H__ +#define __ITEMS_H__ + + +#include + + +typedef enum { + GALLERY_TYPE_INDEX = 1 << 0, + GALLERY_TYPE_ALBUM = 1 << 1 +} TGalleryType; + +typedef struct { + TGalleryType type; + char *ID; + char *title; + char *desc; + GPtrArray *items; + char *base_dir; + void *parent_index; // pointer to the parent TAlbum structure + int quality; + unsigned long landscape_width; + unsigned long landscape_height; + unsigned long portrait_width; + unsigned long portrait_height; +} TAlbum; + +typedef struct { + char *path; + char *title; + char *title_description; + char *thumbnail; + char *preview; + int quality; + unsigned long width; + unsigned long height; + gboolean nofullsize; +} TIndexItem; + + + +/* + * parse_album_xml: XML parser for gallery index.xml files + */ +gboolean parse_album_xml (const char *filename, TAlbum *index); + +/* + * free_album_data: free allocated album data + */ +void free_album_data (TAlbum *index); + +/* + * get_album_objects_count: retrieve number of items in specified album + */ +int get_album_objects_count (const char *filename); + + +#endif /* __ITEMS_H__ */ diff --git a/jpeg-utils.c b/jpeg-utils.c new file mode 100644 index 0000000..dc1c880 --- /dev/null +++ b/jpeg-utils.c @@ -0,0 +1,280 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2008 Tomas Bzatek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include +#include + +#include +#include +#include + +#include + +#include "jpeg-utils.h" + + + + +/* + * get_exif: retrieve EXIF informations from a JPEG image + */ +int +get_exif (const char *filename, TExifData *data) +{ + ExifData *edata; + ExifEntry *entry; + ExifContent *content; + int i, j; + + if (data == NULL) + data = malloc (sizeof (TExifData)); + memset (data, 0, sizeof (TExifData)); + edata = exif_data_new_from_file (filename); + if (edata == NULL) + return -1; + + for (i = 0; i < EXIF_IFD_COUNT; i++) { + content = edata->ifd[i]; + if ((content == NULL) || (content->count == 0)) + continue; + + for (j = 0; j < content->count; j++) { + entry = content->entries[j]; + + if (! content->entries[j]) + continue; + + #define VALUE_LEN 1024 + char value[VALUE_LEN + 1]; + + switch (entry->tag) + { + case EXIF_TAG_DATE_TIME: + data->datetime = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); + break; + + case EXIF_TAG_DATE_TIME_ORIGINAL: + case EXIF_TAG_DATE_TIME_DIGITIZED: + if (data->datetime == NULL) + data->datetime = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); + break; + + case EXIF_TAG_MODEL: + if (data->camera_model == NULL) + data->camera_model = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); + break; + + case EXIF_TAG_MAKE: + if (data->camera_model == NULL) + data->camera_model = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); + break; + + case EXIF_TAG_ISO_SPEED_RATINGS: + data->iso = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); + break; + + case EXIF_TAG_FOCAL_LENGTH: + data->focal_length = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); + break; + + case EXIF_TAG_FOCAL_LENGTH_IN_35MM_FILM: + data->focal_length_35mm = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); + break; + + case EXIF_TAG_FNUMBER: + data->aperture = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); + break; + + case EXIF_TAG_APERTURE_VALUE: + if (data->aperture == NULL) + data->aperture = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); + break; + + case EXIF_TAG_EXPOSURE_TIME: + data->exposure = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); + break; + + case EXIF_TAG_FLASH: + data->flash = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); + break; + + case EXIF_TAG_PIXEL_X_DIMENSION: + data->width = atol (exif_entry_get_value (entry, value, VALUE_LEN)); + break; + + case EXIF_TAG_PIXEL_Y_DIMENSION: + data->height = atol (exif_entry_get_value (entry, value, VALUE_LEN)); + break; + + default: + break; + } + } + } + exif_data_free (edata); + + #ifdef __DEBUG_ALL__ + printf("EXIF_TAG_DATE_TIME = %s\n", data->datetime); + printf("EXIF_TAG_MODEL = %s\n", data->camera_model); + printf("EXIF_TAG_ISO_SPEED_RATINGS = %s\n", data->iso); + printf("EXIF_TAG_FOCAL_LENGTH = %s\n", data->focal_length); + printf("EXIF_TAG_FOCAL_LENGTH_IN_35MM_FILM = %s\n", data->focal_length_35mm); + printf("EXIF_TAG_FNUMBER = %s\n", data->aperture); + printf("EXIF_TAG_EXPOSURE_TIME = %s\n", data->exposure); + printf("EXIF_TAG_FLASH = %s\n", data->flash); + printf("EXIF_TAG_PIXEL_X_DIMENSION = %lu\n", data->width); + printf("EXIF_TAG_PIXEL_Y_DIMENSION = %lu\n", data->height); + #endif + + return 0; +} + +/* + * free_exif_struct: free allocated structure + */ +void +free_exif_data (TExifData *data) +{ + if (data) { + if (data->aperture) + free (data->aperture); + if (data->camera_model) + free (data->camera_model); + if (data->datetime) + free (data->datetime); + if (data->exposure) + free (data->exposure); + if (data->flash) + free (data->flash); + if (data->focal_length) + free (data->focal_length); + if (data->focal_length_35mm) + free (data->focal_length_35mm); + if (data->iso) + free (data->iso); + free (data); + data = NULL; + } +} + + +/* + * resize_image: resize image pointed by src and save result to dst + */ +gboolean +resize_image (const char *src, const char *dst, + int size_x, int size_y, + int quality) +{ + #define ThrowWandException(wand) \ + { \ + char *description; \ + ExceptionType severity; \ + \ + description = MagickGetException (wand, &severity); \ + (void) fprintf (stderr, "Error converting image: %s %s %ld %s\n", GetMagickModule(), description); \ + description = (char*) MagickRelinquishMemory (description); \ + return FALSE; \ + } + + MagickBooleanType status; + MagickWand *magick_wand; + + /* Read an image. */ + MagickWandGenesis(); + magick_wand = NewMagickWand(); + status = MagickReadImage (magick_wand, src); + if (status == MagickFalse) + ThrowWandException (magick_wand); + MagickResizeImage (magick_wand, size_x, size_y, LanczosFilter, 1.0); + MagickSetImageCompressionQuality (magick_wand, quality); + + /* Write the image and destroy it. */ + status = MagickWriteImage (magick_wand, dst); + if (status == MagickFalse) + ThrowWandException (magick_wand); + magick_wand = DestroyMagickWand (magick_wand); + MagickWandTerminus(); + + return TRUE; +} + + +/* + * get_image_sizes: retrieve image dimensions + */ +void +get_image_sizes (const char *img, + unsigned long *width, unsigned long *height) +{ + #define xThrowWandException(wand) \ + { \ + char *description; \ + ExceptionType severity; \ + \ + description = MagickGetException (wand, &severity); \ + (void) fprintf (stderr, "Error reading image info: %s %s %ld %s\n", GetMagickModule(), description); \ + description = (char*) MagickRelinquishMemory(description); \ + return; \ + } + + MagickBooleanType status; + MagickWand *magick_wand; + + *width = -1; + *height = -1; + + /* Read an image. */ + MagickWandGenesis(); + magick_wand = NewMagickWand(); + status = MagickPingImage (magick_wand, img); + if (status == MagickFalse) + xThrowWandException (magick_wand); + *width = MagickGetImageWidth (magick_wand); + *height = MagickGetImageHeight (magick_wand); + + magick_wand = DestroyMagickWand (magick_wand); + MagickWandTerminus(); +} + + +/* + * calculate_sizes: calculate maximal image sizes within specified limits keeping aspect ratio + */ +void +calculate_sizes (const unsigned long max_width, const unsigned long max_height, + unsigned long *width, unsigned long *height) +{ + if ((max_width > *width) && (max_height > *height)) + return; + + double max_ratio = (double) max_width / (double) max_height; + double real_ratio = (double) *width / (double) *height; + + if ((*width > *height) && (max_ratio <= real_ratio)) + { + *height = max_width / real_ratio; + *width = max_width; + } + else + { + *width = max_height * real_ratio; + *height = max_height; + } +} + diff --git a/jpeg-utils.h b/jpeg-utils.h new file mode 100644 index 0000000..56bb51d --- /dev/null +++ b/jpeg-utils.h @@ -0,0 +1,65 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2008 Tomas Bzatek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include + + +typedef struct { + char *datetime; + char *camera_model; + char *iso; + char *focal_length; + char *focal_length_35mm; + char *aperture; + char *exposure; + char *flash; + unsigned long width; + unsigned long height; +} TExifData; + + +/* + * get_exif: retrieve EXIF info from a JPEG image + */ +int get_exif (const char *filename, TExifData *data); + +/* + * free_exif_struct: free allocated structure + */ +void free_exif_data (TExifData *data); + + +/* + * resize_image: resize image pointed by src and save result to dst + */ +gboolean resize_image (const char *src, const char *dst, + int size_x, int size_y, + int quality); + +/* + * get_image_sizes: retrieve image dimensions + */ +void get_image_sizes (const char *img, + unsigned long *width, unsigned long *height); + + +/* + * calculate_sizes: calculate maximal image sizes within specified limits keeping aspect ratio + */ +void calculate_sizes (const unsigned long max_width, const unsigned long max_height, + unsigned long *width, unsigned long *height); + diff --git a/sample/gen.sh b/sample/gen.sh new file mode 100755 index 0000000..084d756 --- /dev/null +++ b/sample/gen.sh @@ -0,0 +1,4 @@ +#!/bin/sh +cd src +../../cgg -s . -o ../dst -v + diff --git a/sample/src/CIAF_1/img_6802.jpg b/sample/src/CIAF_1/img_6802.jpg new file mode 100644 index 0000000..2d939b8 Binary files /dev/null and b/sample/src/CIAF_1/img_6802.jpg differ diff --git a/sample/src/CIAF_1/img_6802b.jpg b/sample/src/CIAF_1/img_6802b.jpg new file mode 120000 index 0000000..d6b7d1d --- /dev/null +++ b/sample/src/CIAF_1/img_6802b.jpg @@ -0,0 +1 @@ +img_6802.jpg \ No newline at end of file diff --git a/sample/src/CIAF_1/img_6802c.jpg b/sample/src/CIAF_1/img_6802c.jpg new file mode 120000 index 0000000..d6b7d1d --- /dev/null +++ b/sample/src/CIAF_1/img_6802c.jpg @@ -0,0 +1 @@ +img_6802.jpg \ No newline at end of file diff --git a/sample/src/CIAF_1/img_6850.jpg b/sample/src/CIAF_1/img_6850.jpg new file mode 100644 index 0000000..dfbc151 Binary files /dev/null and b/sample/src/CIAF_1/img_6850.jpg differ diff --git a/sample/src/CIAF_1/img_6850b.jpg b/sample/src/CIAF_1/img_6850b.jpg new file mode 120000 index 0000000..09b1b1b --- /dev/null +++ b/sample/src/CIAF_1/img_6850b.jpg @@ -0,0 +1 @@ +img_6850.jpg \ No newline at end of file diff --git a/sample/src/CIAF_1/img_7270.jpg b/sample/src/CIAF_1/img_7270.jpg new file mode 100644 index 0000000..7844c82 Binary files /dev/null and b/sample/src/CIAF_1/img_7270.jpg differ diff --git a/sample/src/CIAF_1/index.xml b/sample/src/CIAF_1/index.xml new file mode 100644 index 0000000..9d619f5 --- /dev/null +++ b/sample/src/CIAF_1/index.xml @@ -0,0 +1,48 @@ + + + + CIAF 2007 + CIAF - Czech International Air Fest 2007 + + mezinárodní letiště Brno - Tuřany
+ http://www.airshow.cz/18-CZ-CIAF-07-Home.html
+ ]]>
+ + + +
+ + + + Photo title + The preview image (and thumbnail of course) are automatically generated from original image. + + + + Photo title + This photo uses supplied preview image. Some people (me) can see the difference in sharpness. + + + + + Photo title + This photo doesn't contain link to original image, though it's still needed for EXIF data extraction. + + + + -- + Different quality settings. + + + + -- + Different preview sizes. Sizes are maximal and aspect ratio is always kept. + + + + <![CDATA[Photo containing <em>CDATA</em> <u>structures</u>]]> + links etc...]]> + + + +
diff --git a/sample/src/CIAF_1/preview/img_6802.jpg b/sample/src/CIAF_1/preview/img_6802.jpg new file mode 100644 index 0000000..60b29b5 Binary files /dev/null and b/sample/src/CIAF_1/preview/img_6802.jpg differ diff --git a/sample/src/index.xml b/sample/src/index.xml new file mode 100644 index 0000000..fb91c93 --- /dev/null +++ b/sample/src/index.xml @@ -0,0 +1,30 @@ + + + + CGG Sample gallery root + Welcome to sample gallery for Cataract Gallery Generator! + + Please check the source XML documents for detailed syntax and description.
+
+ From XML nature you can use CDATA containers as string values, allowing you to put whetever HTML tags you want. + Just be aware to be XHTML compliant (at least use lowercase tags). + ]]> +
+
+ + + + CIAF 2007 + 8. - 9. září 2007 + + + + + Going deeper... + nesting + + + + +
diff --git a/sample/src/setup.xml b/sample/src/setup.xml new file mode 100644 index 0000000..ae92918 --- /dev/null +++ b/sample/src/setup.xml @@ -0,0 +1,35 @@ + + + + + ../../templates + + + template-index.tmpl + + template-album.tmpl + + template-view_photo.tmpl + + + styles.css + scripts-general.js + + + + + + + + +
So this is footer, suitable place for copyright, W3C validation links and advertisement! | + Generated with Cataract Gallery Generator. + + ]]>
+ +
diff --git a/sample/src/subdir/CIAF_2/img_6803.jpg b/sample/src/subdir/CIAF_2/img_6803.jpg new file mode 100644 index 0000000..12bbcfd Binary files /dev/null and b/sample/src/subdir/CIAF_2/img_6803.jpg differ diff --git a/sample/src/subdir/CIAF_2/img_6813.jpg b/sample/src/subdir/CIAF_2/img_6813.jpg new file mode 100644 index 0000000..a998ff7 Binary files /dev/null and b/sample/src/subdir/CIAF_2/img_6813.jpg differ diff --git a/sample/src/subdir/CIAF_2/img_6815.jpg b/sample/src/subdir/CIAF_2/img_6815.jpg new file mode 100644 index 0000000..4ce0a8f Binary files /dev/null and b/sample/src/subdir/CIAF_2/img_6815.jpg differ diff --git a/sample/src/subdir/CIAF_2/img_6830.jpg b/sample/src/subdir/CIAF_2/img_6830.jpg new file mode 100644 index 0000000..a3241ed Binary files /dev/null and b/sample/src/subdir/CIAF_2/img_6830.jpg differ diff --git a/sample/src/subdir/CIAF_2/img_6855.jpg b/sample/src/subdir/CIAF_2/img_6855.jpg new file mode 100644 index 0000000..f0c34c5 Binary files /dev/null and b/sample/src/subdir/CIAF_2/img_6855.jpg differ diff --git a/sample/src/subdir/CIAF_2/img_6857.jpg b/sample/src/subdir/CIAF_2/img_6857.jpg new file mode 100644 index 0000000..f13206f Binary files /dev/null and b/sample/src/subdir/CIAF_2/img_6857.jpg differ diff --git a/sample/src/subdir/CIAF_2/index.xml b/sample/src/subdir/CIAF_2/index.xml new file mode 100644 index 0000000..c5e25ca --- /dev/null +++ b/sample/src/subdir/CIAF_2/index.xml @@ -0,0 +1,50 @@ + + + + CIAF 2007 + CIAF - Czech International Air Fest 2007 + + mezinárodní letiště Brno - Tuřany
+ http://www.airshow.cz/18-CZ-CIAF-07-Home.html
+ ]]>
+
+ + + + + L-159 ALCA + http://www.airshow.cz/115-CZ-Letecka-technika-L159.html]]> + + + + + L-159 ALCA + http://www.airshow.cz/115-CZ-Letecka-technika-L159.html]]> + + + + + L-159 ALCA + http://www.airshow.cz/115-CZ-Letecka-technika-L159.html]]> + + + + + Bell-412 + + + + + + Péter Besenyei - EXTRA 300S + http://www.airshow.cz/157-CZ-CIAF-07-Besenyei-Peter.html]]> + + + + + Péter Besenyei - EXTRA 300S + http://www.airshow.cz/157-CZ-CIAF-07-Besenyei-Peter.html]]> + + + +
diff --git a/sample/src/subdir/Tatry/img_7738.jpg b/sample/src/subdir/Tatry/img_7738.jpg new file mode 100644 index 0000000..0263f12 Binary files /dev/null and b/sample/src/subdir/Tatry/img_7738.jpg differ diff --git a/sample/src/subdir/Tatry/img_7755.jpg b/sample/src/subdir/Tatry/img_7755.jpg new file mode 100644 index 0000000..9a73cf9 Binary files /dev/null and b/sample/src/subdir/Tatry/img_7755.jpg differ diff --git a/sample/src/subdir/Tatry/img_7792.jpg b/sample/src/subdir/Tatry/img_7792.jpg new file mode 100644 index 0000000..dc518f9 Binary files /dev/null and b/sample/src/subdir/Tatry/img_7792.jpg differ diff --git a/sample/src/subdir/Tatry/index.xml b/sample/src/subdir/Tatry/index.xml new file mode 100644 index 0000000..0e31ae3 --- /dev/null +++ b/sample/src/subdir/Tatry/index.xml @@ -0,0 +1,27 @@ + + + + Bystrá + Račkova dolina, Bystrá + + ]]> + + + + + + + + + + + + + + + + + + + + diff --git a/sample/src/subdir/index.xml b/sample/src/subdir/index.xml new file mode 100644 index 0000000..2eb87b1 --- /dev/null +++ b/sample/src/subdir/index.xml @@ -0,0 +1,23 @@ + + + + Subfolder + Subfolder + Some description... + + + + + CIAF 2007 (part II.) + 8. - 9. září 2007 + + + + + Tatry 2007 + 27. - 30. září 2007 + + + + + diff --git a/setup.c b/setup.c new file mode 100644 index 0000000..5f31a41 --- /dev/null +++ b/setup.c @@ -0,0 +1,225 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2008 Tomas Bzatek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "config.h" +#include "gallery-utils.h" +#include "setup.h" +#include "xml-parser.h" + + + +/* + * find_setup_xml: try to find setup.xml in standard paths + */ +gboolean +find_setup_xml (TGallerySetup *setup) +{ + #define BUFFER_SIZE 65536 + + char *pth; + char *cwd; + gboolean b; + + cwd = malloc (BUFFER_SIZE); + cwd = getcwd (cwd, BUFFER_SIZE); + pth = g_strconcat (cwd, "/", SETUP_XML, NULL); + free (cwd); + + b = parse_setup_xml (pth, setup); + g_free (pth); + if (b) return TRUE; + + pth = g_strconcat (getenv ("HOME"), "/.cgg/", SETUP_XML, NULL); + b = parse_setup_xml (pth, setup); + g_free (pth); + if (b) return TRUE; + + pth = g_strconcat("/usr/share/cgg/", SETUP_XML, NULL); + b = parse_setup_xml (pth, setup); + g_free (pth); + if (b) return TRUE; + + pth = g_strconcat("/usr/local/share/cgg/", SETUP_XML, NULL); + b = parse_setup_xml (pth, setup); + g_free (pth); + return b; +} + + +/* + * parse_setup_xml: XML parser for setup.xml file + */ +gboolean +parse_setup_xml (const char *filename, TGallerySetup *setup) +{ + TXMLFile *xml; + + xml = xml_parser_load (filename); + if (xml == NULL) + return FALSE; + + /* initialize data struct */ + if (setup == NULL) + return FALSE; + memset (setup, 0, sizeof (TGallerySetup)); + setup->setup_xml_path = strdup (filename); + + setup->templates_path = xml_file_get_node_value (xml, "/gallery_setup/templates/path/text()"); + setup->template_index = xml_file_get_node_value (xml, "/gallery_setup/templates/index/text()"); + setup->template_album = xml_file_get_node_value (xml, "/gallery_setup/templates/album/text()"); + setup->template_photo = xml_file_get_node_value (xml, "/gallery_setup/templates/photo/text()"); + setup->styles = xml_file_get_node_value (xml, "/gallery_setup/templates/styles/text()"); + setup->scripts = xml_file_get_node_value (xml, "/gallery_setup/templates/scripts/text()"); + setup->thumbnail_landscape_width = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/thumbnail", "landscape_w", 0); + setup->thumbnail_landscape_height = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/thumbnail", "landscape_h", 0); + setup->thumbnail_portrait_width = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/thumbnail", "portrait_w", 0); + setup->thumbnail_portrait_height = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/thumbnail", "portrait_h", 0); + setup->thumbnail_quality = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/thumbnail", "quality", -1); + setup->preview_landscape_width = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/preview", "landscape_w", 0); + setup->preview_landscape_height = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/preview", "landscape_h", 0); + setup->preview_portrait_width = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/preview", "portrait_w", 0); + setup->preview_portrait_height = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/preview", "portrait_h", 0); + setup->preview_quality = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/preview", "quality", -1); + setup->footer = xml_file_get_node_value (xml, "/gallery_setup/footer/text()"); + + xml_parser_close (xml); + + #ifdef __DEBUG_ALL__ + printf("setup: templates_path = '%s'\n", setup->templates_path); + printf("setup: template_index = '%s'\n", setup->template_index); + printf("setup: template_album = '%s'\n", setup->template_album); + printf("setup: template_photo = '%s'\n", setup->template_photo); + printf("setup: styles = '%s'\n", setup->styles); + printf("setup: scripts = '%s'\n", setup->scripts); + printf("setup: thumbnail_quality = %d\n", setup->thumbnail_quality); + printf("setup: thumbnail_landscape_width = %ld\n", setup->thumbnail_landscape_width); + printf("setup: thumbnail_landscape_height = %ld\n", setup->thumbnail_landscape_height); + printf("setup: thumbnail_portrait_width = %ld\n", setup->thumbnail_portrait_width); + printf("setup: thumbnail_portrait_height = %ld\n", setup->thumbnail_portrait_height); + printf("setup: preview_quality = %d\n", setup->preview_quality); + printf("setup: preview_landscape_width = %ld\n", setup->preview_landscape_width); + printf("setup: preview_landscape_height = %ld\n", setup->preview_landscape_height); + printf("setup: preview_portrait_width = %ld\n", setup->preview_portrait_width); + printf("setup: preview_portrait_height = %ld\n", setup->preview_portrait_height); + printf("setup: footer = '%s'\n", setup->footer); + #endif + + return TRUE; +} + + + +int +test_tmpl_access (const char *dir, const char *path) +{ + char *s; + int b; + + s = g_strconcat (dir, "/", path, NULL); + b = access (s, R_OK); + g_free (s); + return b; +} + +int +test_tmpl_files (const char *dir, TGallerySetup *setup) +{ + return test_tmpl_access (dir, setup->template_album) | test_tmpl_access (dir, setup->template_photo) | + test_tmpl_access (dir, setup->template_index) | test_tmpl_access (dir, setup->styles) | + test_tmpl_access (dir, setup->scripts); +} + +/* + * find_templates_directory: absolute/relative path checks, trying to find templates directory + * - returned string should be freed + */ +char * +find_templates_directory (TGallerySetup *setup) +{ + char *base_dir; + char *pth; + + if (IS_DIR_SEP (*setup->templates_path)) + { + #ifdef __DEBUG_ALL__ + printf("Warning: using absolute paths to templates\n"); + #endif + + if (! test_tmpl_files (setup->templates_path, setup)) + return strdup (setup->templates_path); + } + else + { + base_dir = g_path_get_dirname (setup->setup_xml_path); + pth = g_strconcat (base_dir, "/", setup->templates_path, NULL); + g_free (base_dir); + + #ifdef __DEBUG_ALL__ + printf("Warning: using relative paths to templates\n"); + #endif + + if (! test_tmpl_files (pth, setup)) + return pth; + } + + fprintf (stderr, "Couldn't find proper templates directory (tried '%s')\n", setup->templates_path); + return NULL; +} + + +/* + * free_setup_data: free allocated setup data + */ +void +free_setup_data (TGallerySetup *setup) +{ + if (setup) { + if (setup->real_templates_dir) + free (setup->real_templates_dir); + if (setup->setup_xml_path) + free (setup->setup_xml_path); + if (setup->templates_path) + free (setup->templates_path); + if (setup->template_index) + free (setup->template_index); + if (setup->template_album) + free (setup->template_album); + if (setup->template_photo) + free (setup->template_photo); + if (setup->styles) + free (setup->styles); + if (setup->scripts) + free (setup->scripts); + if (setup->footer) + free (setup->footer); + free (setup); + setup = NULL; + } +} diff --git a/setup.h b/setup.h new file mode 100644 index 0000000..0cde34c --- /dev/null +++ b/setup.h @@ -0,0 +1,77 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2008 Tomas Bzatek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef __SETUP_H__ +#define __SETUP_H__ + + +#include + + +typedef struct { + gboolean verbose; + char *real_templates_dir; + + char *setup_xml_path; + + char *templates_path; + char *template_index; + char *template_album; + char *template_photo; + char *styles; + char *scripts; + + char *footer; + + int thumbnail_quality; + unsigned long thumbnail_landscape_width; + unsigned long thumbnail_landscape_height; + unsigned long thumbnail_portrait_width; + unsigned long thumbnail_portrait_height; + + int preview_quality; + unsigned long preview_landscape_width; + unsigned long preview_landscape_height; + unsigned long preview_portrait_width; + unsigned long preview_portrait_height; +} TGallerySetup; + + + +/* + * find_setup_xml: try to find setup.xml in standard paths + */ +gboolean find_setup_xml (TGallerySetup *setup); + +/* + * parse_setup_xml: XML parser for setup.xml file + */ +gboolean parse_setup_xml (const char *filename, TGallerySetup *setup); + +/* + * free_setup_data: free allocated setup data + */ +void free_setup_data (TGallerySetup *setup); + +/* + * find_templates_directory: absolute/relative path checks, trying to find templates directory + * - returned string should be freed + */ +char *find_templates_directory (TGallerySetup *setup); + + +#endif /* __SETUP_H__ */ diff --git a/templates/scripts-general.js b/templates/scripts-general.js new file mode 100644 index 0000000..cf558c0 --- /dev/null +++ b/templates/scripts-general.js @@ -0,0 +1,22 @@ +function readCookie() { + var theme = document.cookie; + var theme = unescape(theme); + + return theme; +} + +function writeCookie(theme) { + //FIXME - set expires + var original_cookie = "theme=" + escape(theme); + document.cookie = original_cookie; +} + + +function toggle_div(classname) { + var div = document.getElementById(classname); + if(div.style.display == 'none') { + div.style.display = 'block'; + } else { + div.style.display = 'none'; + } +} diff --git a/templates/styles.css b/templates/styles.css new file mode 100644 index 0000000..e0ae25e --- /dev/null +++ b/templates/styles.css @@ -0,0 +1,357 @@ +body { + /* + margin: 0px 50px 0px 50px; + width: auto; + */ + margin: 20px 0px 20px 0px; + font-family: "DejaVu Sans", "Bitstream Vera Sans", "Verdana", sans-serif; + font-size: 12px; + color: #EEEEEC; + background: #343D42; +} + +img { + border: 0px; +} + +a:link { + color: #C9CED1; +} + +a:visited { + color: #C9CED1; +} + +a:hover { + color: #A3BED4; +} + +div.navigation { + position: fixed; + z-index: 1; + top: 0px; + background-color: #606466; + width: 100%; + padding-top: 5px; + padding-bottom: 5px; + font-size: 13px; + border-top: 10px solid #343D42; + border-bottom: 1px dotted #80878A; +} + +.navigation tr td:first-child { + padding-left: 15px; +} + +.navigation tr td:last-child { + padding-right: 20px; +} + +div.desc { + margin-left: 15px; + margin-top: 60px; + margin-bottom: 40px; + font-size: 21px; + font-weight: bold; + color: #EEEEEC; +} + +div.desc_date { + margin-top: 8px; + font-size: 12px; + font-weight: normal; + color: #D0D0D0; +} + +div.footer { + text-align: center; + margin-top: 10px; + background-color: #4F5659; + width: 100%; + padding: 3px; + font-size: 11px; + font-family: "Trebuchet MS", "DejaVu Sans", "Bitstream Vera Sans", sans-serif; + color: #9A9C97; + border-top: 1px dotted #7C8285; + position: fixed; + bottom: 0px; +} + +a.footermail { + color: #B1B3AD; + text-decoration: none; +} + +a.footermail:hover { + color: #C5C7C0; + text-decoration: underline; +} + +img#preview { +/* border: 6px solid #384347; */ + background-color: #6b6c69; + padding: 1px; +/* -moz-border-radius: 5px; */ +} + +img#preview:hover { +/* border: 6px solid #A3BED4; */ +} + +div#img_preview { + text-align: center; +} + +div.exif { + margin-left: 15px; + margin-top: 30px; + margin-bottom: 40px; + font-size: 12px; + color: #EEEEEC; +} + +.exif tr td:first-child { + color: #babdb6; +} + +.exif td + td { + text-align: left; + padding-left: 2px; +} + +.exif table { + padding-left: 30px; + padding-top: 10px; + font-size: 9px; + +} + +a.exif_line_a { +/* color: #B1B3AD; */ + text-decoration: none; + font-weight: bold; + border-bottom: 1px solid; +} + +a.exif_line_a:hover { + color: #C5C7C0; + border-bottom: 1px dotted; +} + + + + +div.bigthumbnails { + text-align: left; + padding-left: 20px; + padding-right: 20px; + margin-bottom: 50px; + color: #babdb6; + font-size: 11px; +} + + +div.bigthumbnails table { + float: left; + text-align: center; +/* text-decoration: none; */ + font-size: 11px; + border: 0px; +} + +div.one_thumbnail { + float: left; + text-align: center; + width: 215px; + height: 232px; +} + +div.bigthumbnails a { + position: relative; +/* display: block; */ + float: left; + width: 205px; + height: 220px; +/* text-align: center; */ + text-decoration: none; + background-color: #232729; + margin: 1px; /* Needs to be changed below in div.bigthumbnails a:hover */ + border: 1px solid #6b6c69; + -moz-border-radius: 6px; +} + +div.bigthumbnails a:hover { + background-color: #32332F; + margin: 0px; /* div.bigthumbnails a->margin - 1px */ + border: 2px solid #A3BED4; +} + +div.bigthumbnails img { + /* landscape */ + padding-top: 25px; + padding-bottom: 30px; +} + +div.bigthumbnails img.portrait { + /* portrait */ + padding-top: 10px; + padding-bottom: 4px; +} + +.thumb_name { + position: absolute; + bottom: 10px; + left: 65px; +} + + + + + + + + + + + + + + + + + +div.thumbnail_text { + padding-top: 3px; +} + +.thumbnail_text { + padding-top: 3px; + font-size: 12px; +} + +a#thumbnail_text_a { +/* color: #B1B3AD; */ + text-decoration: none; + font-weight: bold; + border-bottom: 1px solid; +} + +a#thumbnail_text_a:hover { + color: #C5C7C0; + border-bottom: 1px dotted; +} + + + + + + + + + + + + + + + + + + + + + + + +div.albumlist { + text-align: left; + padding-left: 20px; + padding-right: 20px; + margin-bottom: 50px; +} + +div.one_album { + float: left; + width: 600px; + height: 230px; +} + +div.albumlist a { + position: relative; + display: block; + float: left; + width: 580px; + height: 210px; + text-decoration: none; + background-color: #232729; + margin: 1px; /* Needs to be changed below in div.bigthumbnails a:hover */ + border: 1px solid #6b6c69; + color: #babdb6; + -moz-border-radius: 4px; +} + +div.albumlist img { + /* landscape */ + position: absolute; + left: 15px; + top: 15px; +} + +div.albumlist a:hover { + background-color: #32332F; + margin: 0px; /* div.bigthumbnails a->margin - 1px */ + border: 2px solid #A3BED4; + color: #A3BED4; +} + +.album_text { + font-size: 20px; + font-weight: bold; + position: relative; + left: 215px; + top: 15px; +} + +.album_subtext { + font-size: 16px; + font-weight: normal; + position: relative; + left: 215px; + top: 15px; +} + +.album_note { + font-size: 11px; + font-weight: normal; + position: absolute; + right: 25px; + bottom: 20px; +} + +.album_text_portrait { + font-size: 20px; + font-weight: bold; + position: relative; + left: 155px; + top: 15px; +} + +.album_subtext_portrait { + font-size: 16px; + font-weight: normal; + position: relative; + left: 155px; + top: 15px; +} + + + +div.contentx { + height: 200px; + width: 300px; + overflow: auto; + border: 1px solid #666; + background-color: #ccc; + padding: 8px; + +} diff --git a/templates/template-album.tmpl b/templates/template-album.tmpl new file mode 100644 index 0000000..e8eb115 --- /dev/null +++ b/templates/template-album.tmpl @@ -0,0 +1,70 @@ + + + + + Browsing album "<!-- $(ID) -->" + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+ + + +
+ + + + + + + + +
+ + + +
+ + + + + + + diff --git a/templates/template-index.tmpl b/templates/template-index.tmpl new file mode 100644 index 0000000..2f941a2 --- /dev/null +++ b/templates/template-index.tmpl @@ -0,0 +1,73 @@ + + + + + <!-- $(ID) --> + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+ + + + + + +
+ + + + + + + diff --git a/templates/template-view_photo.tmpl b/templates/template-view_photo.tmpl new file mode 100644 index 0000000..2cd9b5c --- /dev/null +++ b/templates/template-view_photo.tmpl @@ -0,0 +1,71 @@ + + + + + Viewing photo "<!-- $(FILE_NAME) -->" [<!-- $(FILE_NO) -->/<!-- $(TOTAL_ITEMS) -->] + + + + + + + + + + + + + + + + + +
+ +
+
+
+ + + + + + + + +
+:: Show EXIF +
+
+ + + + + + + diff --git a/xml-parser.c b/xml-parser.c new file mode 100644 index 0000000..88373dd --- /dev/null +++ b/xml-parser.c @@ -0,0 +1,211 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2008 Tomas Bzatek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "xml-parser.h" + + + +/* + * xml_parser_load: initialize and load the XML document + */ +TXMLFile * +xml_parser_load (const char *filename) +{ + TXMLFile *file; + + file = malloc (sizeof (TXMLFile)); + memset (file, 0, sizeof (TXMLFile)); + + /* Load XML document */ + file->doc = xmlParseFile (filename); + if (file->doc == NULL) { + fprintf (stderr, "Error: unable to parse file \"%s\"\n", filename); + free (file); + return NULL; + } + + /* Create xpath evaluation context */ + file->xpathCtx = xmlXPathNewContext (file->doc); + if (file->xpathCtx == NULL) { + fprintf (stderr, "Error: unable to create new XPath context\n"); + xmlFreeDoc (file->doc); + free (file); + return FALSE; + } + + return file; +} + + +/* + * xml_parser_close: close the XML document parser + */ +void +xml_parser_close (TXMLFile *file) +{ + if (file) + { + xmlXPathFreeContext (file->xpathCtx); + xmlFreeDoc (file->doc); + free (file); + file = NULL; + } +} + + +/* + * xml_file_get_node_value: retrieve value from XPath node + */ +char * +xml_file_get_node_value (TXMLFile *file, const char *x_path) +{ + xmlXPathObjectPtr xpathObj; + xmlNodePtr cur; + char *val; + + if ((! file) || (! x_path)) + return NULL; + + /* Evaluate xpath expression */ + xpathObj = xmlXPathEvalExpression ((const xmlChar *) x_path, file->xpathCtx); + if (xpathObj == NULL) { + fprintf (stderr, "Error: unable to evaluate xpath expression \"%s\"\n", x_path); + return NULL; + } + + val = NULL; + if ((xpathObj->nodesetval) && (xpathObj->nodesetval->nodeNr > 0)) { + cur = xpathObj->nodesetval->nodeTab[0]; + + #ifdef __DEBUG_ALL__ + printf("Result (%d nodes):\n", xpathObj->nodesetval->nodeNr); + printf(" XPATH matched: element node \"%s\", value = '%s'\n", cur->name, cur->content); + #endif + + if (cur->content) + val = strdup ((char *) cur->content); + } + + xmlXPathFreeObject (xpathObj); + return val; +} + + +/* + * xml_file_get_node_attribute: retrieve attribute value from XPath node + */ +char * +xml_file_get_node_attribute (TXMLFile *file, const char *x_path, const char *attr) +{ + xmlXPathObjectPtr xpathObj; + xmlNodePtr cur; + xmlChar *attrvx; + char *attrv; + + if ((! file) || (! x_path)) + return NULL; + + /* Evaluate xpath expression */ + xpathObj = xmlXPathEvalExpression ((const xmlChar *) x_path, file->xpathCtx); + if (xpathObj == NULL) { + fprintf (stderr, "Error: unable to evaluate xpath expression \"%s\"\n", x_path); + return NULL; + } + + attrv = NULL; + if ((xpathObj->nodesetval) && (xpathObj->nodesetval->nodeNr > 0)) { + cur = xpathObj->nodesetval->nodeTab[0]; + attrvx = xmlGetProp (cur, (const xmlChar *) attr); + if (attrvx) { + attrv = strdup ((char*) attrvx); + xmlFree (attrvx); + } + + #ifdef __DEBUG_ALL__ + printf("Result (%d nodes):\n", xpathObj->nodesetval->nodeNr); + printf(" XPATH matched: element node \"%s\", value = '%s', property value = '%s'\n", cur->name, cur->content, attrv); + #endif + } + + xmlXPathFreeObject (xpathObj); + return attrv; +} + +long +xml_file_get_node_attribute_long (TXMLFile *file, const char *x_path, const char *attr, const int _default) +{ + char *s; + long int i; + + s = xml_file_get_node_attribute (file, x_path, attr); + if (s == NULL) + return _default; + + i = atol (s); + free (s); + return i; +} + +/* + * xml_file_get_node_present: existency test of the XPath node + */ +gboolean +xml_file_get_node_present (TXMLFile *file, const char *x_path) +{ + return xml_file_node_get_children_count (file, x_path) > 0; +} + + +/* + * xml_file_node_get_children_count: retrieve number of children items of the specified XPath node + */ +int +xml_file_node_get_children_count (TXMLFile *file, const char *x_path) +{ + xmlXPathObjectPtr xpathObj; + int count; + + if ((! file) || (! x_path)) + return 0; + + /* Evaluate xpath expression */ + xpathObj = xmlXPathEvalExpression ((const xmlChar *) x_path, file->xpathCtx); + if (xpathObj == NULL) { + fprintf (stderr, "Error: unable to evaluate xpath expression \"%s\"\n", x_path); + return 0; + } + + count = 0; + if (xpathObj->nodesetval) + count = xpathObj->nodesetval->nodeNr; + + xmlXPathFreeObject (xpathObj); + return count; +} diff --git a/xml-parser.h b/xml-parser.h new file mode 100644 index 0000000..53d7b5a --- /dev/null +++ b/xml-parser.h @@ -0,0 +1,60 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2008 Tomas Bzatek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include + +#include +#include + + +typedef struct { + xmlDocPtr doc; + xmlXPathContextPtr xpathCtx; +} TXMLFile; + + + +/* + * xml_parser_load: initialize and load the XML document + */ +TXMLFile * xml_parser_load (const char *filename); + +/* + * xml_parser_close: close the XML document parser + */ +void xml_parser_close (TXMLFile *file); + +/* + * xml_file_get_node_value: retrieve value from XPath node + */ +char * xml_file_get_node_value (TXMLFile *file, const char *x_path); + +/* + * xml_file_get_node_attribute: retrieve attribute value from XPath node + */ +char * xml_file_get_node_attribute (TXMLFile *file, const char *x_path, const char *attr); +long xml_file_get_node_attribute_long (TXMLFile *file, const char *x_path, const char *attr, const int _default); + +/* + * xml_file_get_node_present: existency test of the XPath node + */ +gboolean xml_file_get_node_present (TXMLFile *file, const char *x_path); + +/* + * xml_file_node_get_children_count: retrieve number of children items of the specified XPath node + */ +int xml_file_node_get_children_count (TXMLFile *file, const char *x_path); -- cgit v1.2.3