summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomas Bzatek <tbzatek@users.sourceforge.net>2008-07-27 19:13:28 +0200
committerTomas Bzatek <tbzatek@users.sourceforge.net>2008-07-27 19:13:28 +0200
commit510fff156db65795cbf211e7910a67fb316cb404 (patch)
treedfbec486ffeab2d6886f9f51863f723a2d5cd574
downloadcataract-0.99.0.tar.xz
Initial commitv0.99.0
-rw-r--r--LICENSE339
-rw-r--r--Makefile40
-rw-r--r--README80
-rwxr-xr-xcgg-dirgen51
-rw-r--r--cgg.c156
-rw-r--r--config.h26
-rw-r--r--gallery-utils.c122
-rw-r--r--gallery-utils.h41
-rw-r--r--generators.c882
-rw-r--r--generators.h64
-rw-r--r--items.c203
-rw-r--r--items.h75
-rw-r--r--jpeg-utils.c280
-rw-r--r--jpeg-utils.h65
-rwxr-xr-xsample/gen.sh4
-rw-r--r--sample/src/CIAF_1/img_6802.jpgbin0 -> 797526 bytes
l---------sample/src/CIAF_1/img_6802b.jpg1
l---------sample/src/CIAF_1/img_6802c.jpg1
-rw-r--r--sample/src/CIAF_1/img_6850.jpgbin0 -> 852418 bytes
l---------sample/src/CIAF_1/img_6850b.jpg1
-rw-r--r--sample/src/CIAF_1/img_7270.jpgbin0 -> 857597 bytes
-rw-r--r--sample/src/CIAF_1/index.xml48
-rw-r--r--sample/src/CIAF_1/preview/img_6802.jpgbin0 -> 170320 bytes
-rw-r--r--sample/src/index.xml30
-rw-r--r--sample/src/setup.xml35
-rw-r--r--sample/src/subdir/CIAF_2/img_6803.jpgbin0 -> 170496 bytes
-rw-r--r--sample/src/subdir/CIAF_2/img_6813.jpgbin0 -> 125719 bytes
-rw-r--r--sample/src/subdir/CIAF_2/img_6815.jpgbin0 -> 112015 bytes
-rw-r--r--sample/src/subdir/CIAF_2/img_6830.jpgbin0 -> 111134 bytes
-rw-r--r--sample/src/subdir/CIAF_2/img_6855.jpgbin0 -> 72126 bytes
-rw-r--r--sample/src/subdir/CIAF_2/img_6857.jpgbin0 -> 94683 bytes
-rw-r--r--sample/src/subdir/CIAF_2/index.xml50
-rw-r--r--sample/src/subdir/Tatry/img_7738.jpgbin0 -> 371722 bytes
-rw-r--r--sample/src/subdir/Tatry/img_7755.jpgbin0 -> 321602 bytes
-rw-r--r--sample/src/subdir/Tatry/img_7792.jpgbin0 -> 257911 bytes
-rw-r--r--sample/src/subdir/Tatry/index.xml27
-rw-r--r--sample/src/subdir/index.xml23
-rw-r--r--setup.c225
-rw-r--r--setup.h77
-rw-r--r--templates/scripts-general.js22
-rw-r--r--templates/styles.css357
-rw-r--r--templates/template-album.tmpl70
-rw-r--r--templates/template-index.tmpl73
-rw-r--r--templates/template-view_photo.tmpl71
-rw-r--r--xml-parser.c211
-rw-r--r--xml-parser.h60
46 files changed, 3810 insertions, 0 deletions
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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 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 <tbzatek@users.sourceforge.net>
+
+
+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 <tbzatek@users.sourceforge.net>
+##
+## 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
+<?xml version="1.0" encoding="utf-8"?>
+<gallery type="album">
+ <general>
+ <ID>Album ID</ID>
+ <title>Album Title</title>
+ <description><![CDATA[Album description<br/>
+ ]]></description>
+ </general>
+
+ <items>
+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=" <nofullsize />\n"; fi
+ echo -e " <item src=\"`echo $i | cut -b 3-`\"${INCL}>\n${INCL2} <title> </title>\n <title_description> </title_description>\n </item>\n";
+done
+
+cat << XML_FOOTER_STOP
+ </items>
+</gallery>
+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 <tbzatek@users.sourceforge.net>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include <libxml/xmlmemory.h>
+
+#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 <tbzatek@users.sourceforge.net>
+ *
+ * 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 <tbzatek@users.sourceforge.net>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#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 <tbzatek@users.sourceforge.net>
+ *
+ * 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 <glib.h>
+
+#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 <tbzatek@users.sourceforge.net>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#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, "<!-- $(BEGIN_IMG_LIST_LANDSCAPE) -->"))) {
+ in_img_list_landscape = TRUE;
+ free (b);
+ continue;
+ }
+ if (in_img_list && (strstr (buffer, "<!-- $(END_IMG_LIST_LANDSCAPE) -->"))) {
+ in_img_list_landscape = FALSE;
+ free (b);
+ continue;
+ }
+ if (in_img_list && (strstr (buffer, "<!-- $(BEGIN_IMG_LIST_PORTRAIT) -->"))) {
+ in_img_list_portrait = TRUE;
+ free (b);
+ continue;
+ }
+ if (in_img_list && (strstr (buffer, "<!-- $(END_IMG_LIST_PORTRAIT) -->"))) {
+ 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, "<!-- $(BEGIN_GO_UP) -->")) {
+ memset (buf_go_up_string, 0, BUFFER_SIZE);
+ in_go_up_string = TRUE;
+ free (b);
+ continue;
+ }
+ if (in_go_up_string && (strstr (buffer, "<!-- $(END_GO_UP) -->"))) {
+ 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, "<!-- $(ID) -->") && items->ID)
+ str_replace (&b, "<!-- $(ID) -->", items->ID);
+ if (strstr (b, "<!-- $(TITLE) -->") && items->title)
+ str_replace (&b, "<!-- $(TITLE) -->", items->title);
+ if (strstr (b, "<!-- $(DESCRIPTION) -->") && items->desc)
+ str_replace (&b, "<!-- $(DESCRIPTION) -->", items->desc);
+ if (strstr (b, "<!-- $(FOOTER) -->") && setup->footer)
+ str_replace (&b, "<!-- $(FOOTER) -->", setup->footer);
+ if (strstr (b, "<!-- $(TOTAL_ITEMS) -->")) {
+ s1 = g_strdup_printf ("%d", items->items->len);
+ str_replace (&b, "<!-- $(TOTAL_ITEMS) -->", s1);
+ g_free (s1);
+ }
+ if (strstr (b, "<!-- $(NAV_BAR) -->")) {
+ s1 = g_strdup (items->ID);
+ parent = items->parent_index;
+ level = 1;
+ while (parent) {
+ s3 = make_string ("../", level);
+ s2 = g_strconcat ("<a href=\"", s3, "index.html\">", parent->ID, "</a> &gt; ", s1, NULL);
+ free (s3);
+ g_free (s1);
+ s1 = s2;
+ parent = parent->parent_index;
+ level++;
+ }
+ str_replace (&b, "<!-- $(NAV_BAR) -->", s1);
+ g_free (s1);
+ }
+
+ /* Image list, nested placeholders */
+ if (strstr (buffer, "<!-- $(BEGIN_IMG_LIST) -->")) {
+ 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, "<!-- $(END_IMG_LIST) -->"))) {
+ 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, "<!-- $(ALBUM_SUBPATH) -->")) {
+ s2 = g_strconcat (item->path, "/index.html", NULL);
+ str_replace (&s1, "<!-- $(ALBUM_SUBPATH) -->", s2);
+ g_free (s2);
+ }
+ while (strstr (s1, "<!-- $(IMG_SUBPAGE) -->")) {
+ s2 = g_strconcat (img_src, ".html", NULL);
+ str_replace (&s1, "<!-- $(IMG_SUBPAGE) -->", s2);
+ g_free (s2);
+ }
+ while (strstr (s1, "<!-- $(IMG_TITLE) -->") && item->title)
+ str_replace (&s1, "<!-- $(IMG_TITLE) -->", item->title);
+ while (strstr (s1, "<!-- $(IMG_DESCRIPTION) -->") && item->title_description)
+ str_replace (&s1, "<!-- $(IMG_DESCRIPTION) -->", item->title_description);
+ while (strstr(s1, "<!-- $(ALBUM_NUM_ITEMS) -->")) {
+ s3 = g_strconcat (items->base_dir, "/", item->path, "/index.xml", NULL);
+ s2 = g_strdup_printf ("%d", get_album_objects_count(s3));
+ str_replace (&s1, "<!-- $(ALBUM_NUM_ITEMS) -->", s2);
+ g_free (s2);
+ g_free (s3);
+ }
+ while (strstr (s1, "<!-- $(IMG_THUMBNAIL) -->")) {
+ s3 = g_path_get_basename (img_src);
+ s2 = g_strconcat (THUMBNAIL_DIR, "/", s3, NULL);
+ str_replace (&s1, "<!-- $(IMG_THUMBNAIL) -->", s2);
+ g_free (s2);
+ g_free (s3);
+ }
+ while (strstr (s1, "<!-- $(IMG_FILENAME) -->"))
+ str_replace (&s1, "<!-- $(IMG_FILENAME) -->", 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, "<!-- $(BEGIN_IMG_FULLSIZE_LINK) -->")) {
+ in_img_fullsize_link = TRUE;
+ free (b);
+ continue;
+ }
+ if (strstr (buffer, "<!-- $(END_IMG_FULLSIZE_LINK) -->")) {
+ 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, "<!-- $(FILE_NAME) -->"))
+ str_replace (&b, "<!-- $(FILE_NAME) -->", item->path);
+ if (strstr (b, "<!-- $(TITLE) -->") && item->title)
+ str_replace (&b, "<!-- $(TITLE) -->", item->title);
+ if (strstr (b, "<!-- $(DESCRIPTION) -->") && item->title_description)
+ str_replace (&b, "<!-- $(DESCRIPTION) -->", item->title_description);
+ if (strstr (b, "<!-- $(TOTAL_ITEMS) -->")) {
+ s1 = g_strdup_printf ("%d", parent_items->items->len);
+ str_replace (&b, "<!-- $(TOTAL_ITEMS) -->", s1);
+ g_free (s1);
+ }
+ if (strstr (b, "<!-- $(FILE_NO) -->")) {
+ s1 = g_strdup_printf ("%d", item_index);
+ str_replace(&b, "<!-- $(FILE_NO) -->", s1);
+ g_free (s1);
+ }
+ if (strstr (b, "<!-- $(NAV_BAR) -->")) {
+ s1 = g_strconcat (item->title, " (", item->path, ")", NULL);
+ parent = parent_items;
+ level = 0;
+ while (parent) {
+ s3 = make_string ("../", level);
+ s2 = g_strconcat ("<a href=\"", s3, "index.html\">", parent->ID, "</a> &gt; ", s1, NULL);
+ free (s3);
+ g_free (s1);
+ s1 = s2;
+ parent = parent->parent_index;
+ level++;
+ }
+ str_replace (&b, "<!-- $(NAV_BAR) -->", s1);
+ g_free (s1);
+ }
+ if (strstr (b, "<!-- $(IMG_SRC_BIG) -->")) {
+ s1 = g_strconcat (IMG_BIG_DIR, "/", item->path, NULL);
+ str_replace (&b, "<!-- $(IMG_SRC_BIG) -->", s1);
+ g_free (s1);
+ }
+ if (strstr(b, "<!-- $(IMG_SRC_FULL) -->")) {
+ s1 = g_strconcat (IMG_ORIG_DIR, "/", item->path, NULL);
+ str_replace (&b, "<!-- $(IMG_SRC_FULL) -->", s1);
+ g_free (s1);
+ }
+ if (strstr(b, "<!-- $(IMG_SIZE_BIG_W) -->")) {
+ s1 = g_strdup_printf ("%lu", img_big_w);
+ str_replace (&b, "<!-- $(IMG_SIZE_BIG_W) -->", s1);
+ g_free (s1);
+ }
+ if (strstr(b, "<!-- $(IMG_SIZE_BIG_H) -->")) {
+ s1 = g_strdup_printf ("%lu", img_big_h);
+ str_replace (&b, "<!-- $(IMG_SIZE_BIG_H) -->", s1);
+ g_free (s1);
+ }
+ if (strstr(b, "<!-- $(IMG_SIZE_ORIG_W) -->")) {
+ s1 = g_strdup_printf ("%lu", img_orig_w);
+ str_replace (&b, "<!-- $(IMG_SIZE_ORIG_W) -->", s1);
+ g_free (s1);
+ }
+ if (strstr(b, "<!-- $(IMG_SIZE_ORIG_H) -->")) {
+ s1 = g_strdup_printf ("%lu", img_orig_h);
+ str_replace (&b, "<!-- $(IMG_SIZE_ORIG_H) -->", s1);
+ g_free (s1);
+ }
+
+ if (strstr (b, "<!-- $(EXIF_ISO) -->")) {
+ if (exif->iso)
+ str_replace (&b, "<!-- $(EXIF_ISO) -->", exif->iso);
+ else
+ str_replace (&b, "<!-- $(EXIF_ISO) -->", "??");
+ }
+ if (strstr (b, "<!-- $(EXIF_TIME) -->")) {
+ if (exif->exposure)
+ str_replace (&b, "<!-- $(EXIF_TIME) -->", exif->exposure);
+ else
+ str_replace (&b, "<!-- $(EXIF_TIME) -->", "??");
+ }
+ if (strstr (b, "<!-- $(EXIF_APERTURE) -->")) {
+ if (exif->aperture)
+ str_replace (&b, "<!-- $(EXIF_APERTURE) -->", exif->aperture);
+ else
+ str_replace (&b, "<!-- $(EXIF_APERTURE) -->", "??");
+ }
+ if (strstr (b, "<!-- $(EXIF_FOCAL_LENGTH) -->")) {
+ if (exif->focal_length)
+ str_replace (&b, "<!-- $(EXIF_FOCAL_LENGTH) -->", exif->focal_length);
+ else
+ str_replace (&b, "<!-- $(EXIF_FOCAL_LENGTH) -->", "??");
+ }
+ if (strstr (b, "<!-- $(EXIF_FLASH) -->")) {
+ if (exif->flash)
+ str_replace (&b, "<!-- $(EXIF_FLASH) -->", exif->flash);
+ else
+ str_replace (&b, "<!-- $(EXIF_FLASH) -->", "??");
+ }
+ if (strstr (b, "<!-- $(EXIF_DATE) -->")) {
+ if (exif->datetime)
+ str_replace (&b, "<!-- $(EXIF_DATE) -->", exif->datetime);
+ else
+ str_replace (&b, "<!-- $(EXIF_DATE) -->", "??");
+ }
+ if (strstr (b, "<!-- $(EXIF_CAMERA_MODEL) -->")) {
+ if (exif->camera_model)
+ str_replace (&b, "<!-- $(EXIF_CAMERA_MODEL) -->", exif->camera_model);
+ else
+ str_replace (&b, "<!-- $(EXIF_CAMERA_MODEL) -->", "??");
+ }
+ if (strstr (b, "<!-- $(EXIF_FOCAL_35) -->")) {
+ if (exif->focal_length_35mm) {
+ s1 = g_strconcat ("(", exif->focal_length_35mm, ")", NULL);
+ str_replace (&b, "<!-- $(EXIF_FOCAL_35) -->", s1);
+ g_free (s1);
+ }
+ else
+ str_replace (&b, "<!-- $(EXIF_FOCAL_35) -->", "");
+ }
+
+ if (strstr (b, "<!-- $(LINK_NEXT) -->")) {
+ if (next_item) {
+ s1 = g_strconcat (next_item->path, ".html", NULL);
+ str_replace (&b, "<!-- $(LINK_NEXT) -->", s1);
+ g_free (s1);
+ }
+ else
+ str_replace (&b, "<!-- $(LINK_NEXT) -->", "index.html");
+ }
+ if (strstr(b, "<!-- $(LINK_PREV) -->")) {
+ if (previous_item) {
+ s1 = g_strconcat (previous_item->path, ".html", NULL);
+ str_replace (&b, "<!-- $(LINK_PREV) -->", s1);
+ g_free (s1);
+ }
+ else
+ str_replace (&b, "<!-- $(LINK_PREV) -->", "index.html");
+ }
+
+ if (strstr (b, "<!-- $(FOOTER) -->"))
+ str_replace (&b, "<!-- $(FOOTER) -->", 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 <tbzatek@users.sourceforge.net>
+ *
+ * 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 <tbzatek@users.sourceforge.net>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#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 <tbzatek@users.sourceforge.net>
+ *
+ * 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 <glib.h>
+
+
+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 <tbzatek@users.sourceforge.net>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <libexif/exif-data.h>
+#include <libexif/exif-content.h>
+#include <libexif/exif-entry.h>
+
+#include <wand/magick-wand.h>
+
+#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 <tbzatek@users.sourceforge.net>
+ *
+ * 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 <glib.h>
+
+
+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
--- /dev/null
+++ b/sample/src/CIAF_1/img_6802.jpg
Binary files 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
--- /dev/null
+++ b/sample/src/CIAF_1/img_6850.jpg
Binary files 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
--- /dev/null
+++ b/sample/src/CIAF_1/img_7270.jpg
Binary files 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<gallery type="album">
+ <general>
+ <ID>CIAF 2007</ID>
+ <title>CIAF - Czech International Air Fest 2007</title>
+ <description><![CDATA[8. - 9. září 2007<br/>
+ mezinárodní letiště Brno - Tuřany<br/>
+ <a href="http://www.airshow.cz/18-CZ-CIAF-07-Home.html">http://www.airshow.cz/18-CZ-CIAF-07-Home.html</a><br/>
+ ]]></description>
+
+ <!-- you can override global settings in each album: -->
+ <images quality="60" landscape_w="640" landscape_h="480" portrait_w="480" portrait_h="640" />
+ </general>
+
+ <items>
+ <item src="img_6802b.jpg" quality="95" width="500" height="750">
+ <title>Photo title</title>
+ <title_description>The preview image (and thumbnail of course) are automatically generated from original image.</title_description>
+ </item>
+
+ <item src="img_6802.jpg" preview="preview/img_6802.jpg">
+ <title>Photo title</title>
+ <title_description>This photo uses supplied preview image. Some people (me) can see the difference in sharpness.</title_description>
+ </item>
+
+ <item src="img_6802c.jpg" preview="preview/img_6802.jpg">
+ <nofullsize />
+ <title>Photo title</title>
+ <title_description>This photo doesn't contain link to original image, though it's still needed for EXIF data extraction.</title_description>
+ </item>
+
+ <item src="img_6850.jpg" quality="20">
+ <title>--</title>
+ <title_description>Different quality settings.</title_description>
+ </item>
+
+ <item src="img_6850b.jpg" width="350" height="350">
+ <title>--</title>
+ <title_description>Different preview sizes. Sizes are maximal and aspect ratio is always kept.</title_description>
+ </item>
+
+ <item src="img_7270.jpg">
+ <title><![CDATA[Photo containing <em>CDATA</em> <u>structures</u>]]></title>
+ <title_description><![CDATA[Can contain <a href="http://cgg.bzatek.net/" title="Cataract Gallery Generator" class="footermail">links</a> etc...]]></title_description>
+ </item>
+
+ </items>
+</gallery>
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
--- /dev/null
+++ b/sample/src/CIAF_1/preview/img_6802.jpg
Binary files 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<gallery type="index">
+ <general>
+ <ID>CGG Sample gallery root</ID>
+ <title>Welcome to sample gallery for Cataract Gallery Generator!</title>
+ <description><![CDATA[
+ This demo gallery will show you basic features and configuration possibilities.<br />
+ Please check the source XML documents for detailed syntax and description.<br />
+ <br />
+ 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).
+ ]]>
+ </description>
+ </general>
+
+ <items>
+ <item path="CIAF_1">
+ <title>CIAF 2007</title>
+ <title_description>8. - 9. září 2007</title_description>
+ <thumbnail src="CIAF_1/img_6802.jpg" />
+ </item>
+
+ <item path="subdir">
+ <title>Going deeper...</title>
+ <title_description>nesting</title_description>
+ <thumbnail src="subdir/Tatry/img_7755.jpg" />
+ </item>
+
+ </items>
+</gallery>
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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<gallery_setup>
+ <templates>
+ <!-- path to templates (design) -->
+ <path>../../templates</path>
+
+ <!-- index: top level page -->
+ <index>template-index.tmpl</index>
+ <!-- album: list of photos in directory -->
+ <album>template-album.tmpl</album>
+ <!-- photo: single photo page -->
+ <photo>template-view_photo.tmpl</photo>
+
+ <!-- mandatory files used in templates -->
+ <styles>styles.css</styles>
+ <scripts>scripts-general.js</scripts>
+ </templates>
+
+ <images>
+ <!-- default sizes of thumbnail and preview images -->
+ <thumbnail landscape_w="180" landscape_h="120"
+ portrait_w="120" portrait_h="180"
+ quality="95" />
+ <preview landscape_w="900" landscape_h="600"
+ portrait_w="500" portrait_h="750"
+ quality="95" />
+ </images>
+
+ <footer><![CDATA[
+ <div class="footer">So this is footer, suitable place for copyright, W3C validation links and advertisement! |
+ Generated with <a href="http://cgg.bzatek.net/" title="Cataract Gallery Generator" class="footermail">Cataract Gallery Generator</a>.
+ </div>
+ ]]></footer>
+
+</gallery_setup>
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
--- /dev/null
+++ b/sample/src/subdir/CIAF_2/img_6803.jpg
Binary files 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
--- /dev/null
+++ b/sample/src/subdir/CIAF_2/img_6813.jpg
Binary files 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
--- /dev/null
+++ b/sample/src/subdir/CIAF_2/img_6815.jpg
Binary files 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
--- /dev/null
+++ b/sample/src/subdir/CIAF_2/img_6830.jpg
Binary files 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
--- /dev/null
+++ b/sample/src/subdir/CIAF_2/img_6855.jpg
Binary files 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
--- /dev/null
+++ b/sample/src/subdir/CIAF_2/img_6857.jpg
Binary files 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<gallery type="album">
+ <general>
+ <ID>CIAF 2007</ID>
+ <title>CIAF - Czech International Air Fest 2007</title>
+ <description><![CDATA[8. - 9. září 2007<br/>
+ mezinárodní letiště Brno - Tuřany<br/>
+ <a href="http://www.airshow.cz/18-CZ-CIAF-07-Home.html">http://www.airshow.cz/18-CZ-CIAF-07-Home.html</a><br/>
+ ]]></description>
+ </general>
+
+ <items>
+ <item src="img_6803.jpg">
+ <nofullsize />
+ <title>L-159 ALCA</title>
+ <title_description><![CDATA[<a href="http://www.airshow.cz/115-CZ-Letecka-technika-L159.html">http://www.airshow.cz/115-CZ-Letecka-technika-L159.html</a>]]></title_description>
+ </item>
+
+ <item src="img_6813.jpg">
+ <nofullsize />
+ <title>L-159 ALCA</title>
+ <title_description><![CDATA[<a href="http://www.airshow.cz/115-CZ-Letecka-technika-L159.html">http://www.airshow.cz/115-CZ-Letecka-technika-L159.html</a>]]></title_description>
+ </item>
+
+ <item src="img_6815.jpg">
+ <nofullsize />
+ <title>L-159 ALCA</title>
+ <title_description><![CDATA[<a href="http://www.airshow.cz/115-CZ-Letecka-technika-L159.html">http://www.airshow.cz/115-CZ-Letecka-technika-L159.html</a>]]></title_description>
+ </item>
+
+ <item src="img_6830.jpg">
+ <nofullsize />
+ <title>Bell-412</title>
+ <title_description> </title_description>
+ </item>
+
+ <item src="img_6855.jpg">
+ <nofullsize />
+ <title>Péter Besenyei - EXTRA 300S</title>
+ <title_description><![CDATA[<a href="http://www.airshow.cz/157-CZ-CIAF-07-Besenyei-Peter.html">http://www.airshow.cz/157-CZ-CIAF-07-Besenyei-Peter.html</a>]]></title_description>
+ </item>
+
+ <item src="img_6857.jpg">
+ <nofullsize />
+ <title>Péter Besenyei - EXTRA 300S</title>
+ <title_description><![CDATA[<a href="http://www.airshow.cz/157-CZ-CIAF-07-Besenyei-Peter.html">http://www.airshow.cz/157-CZ-CIAF-07-Besenyei-Peter.html</a>]]></title_description>
+ </item>
+
+ </items>
+</gallery>
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
--- /dev/null
+++ b/sample/src/subdir/Tatry/img_7738.jpg
Binary files 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
--- /dev/null
+++ b/sample/src/subdir/Tatry/img_7755.jpg
Binary files 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
--- /dev/null
+++ b/sample/src/subdir/Tatry/img_7792.jpg
Binary files 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<gallery type="album">
+ <general>
+ <ID>Bystrá</ID>
+ <title>Račkova dolina, Bystrá</title>
+ <description><![CDATA[sobota 29. září 2007<br/>
+ ]]></description>
+ </general>
+
+ <items>
+ <item src="img_7738.jpg">
+ <title></title>
+ <title_description></title_description>
+ </item>
+
+ <item src="img_7755.jpg">
+ <title></title>
+ <title_description></title_description>
+ </item>
+
+ <item src="img_7792.jpg">
+ <title></title>
+ <title_description></title_description>
+ </item>
+
+ </items>
+</gallery>
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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<gallery type="index">
+ <general>
+ <ID>Subfolder</ID>
+ <title>Subfolder</title>
+ <description>Some description...</description>
+ </general>
+
+ <items>
+ <item path="CIAF_2">
+ <title>CIAF 2007 (part II.)</title>
+ <title_description>8. - 9. září 2007</title_description>
+ <thumbnail src="CIAF_2/img_6830.jpg" />
+ </item>
+
+ <item path="Tatry">
+ <title>Tatry 2007</title>
+ <title_description>27. - 30. září 2007</title_description>
+ <thumbnail src="Tatry/img_7792.jpg" />
+ </item>
+
+ </items>
+</gallery>
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 <tbzatek@users.sourceforge.net>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <glib.h>
+
+#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 <tbzatek@users.sourceforge.net>
+ *
+ * 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 <glib.h>
+
+
+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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Browsing album "<!-- $(ID) -->"</title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta name="generator" content="Bluefish 1.0.6" />
+ <meta name="date" content="2007-01-06T22:48:18+0100" />
+ <meta name="description" content="Photo album: <!-- $(ID) -->" />
+ <meta name="ROBOTS" content="NOINDEX, NOFOLLOW" />
+ <meta http-equiv="content-style-type" content="text/css "/>
+ <link href="support/folder-saved-search.png" type="image/png" rel="icon" />
+ <!-- link rel="shortcut icon" href="http://primates.ximian.com/%7Ejimmac/photos/favicon.ico" type="image/x-icon" / -->
+ <link href="styles.css" type="text/css" rel="stylesheet" media="screen, print" />
+ <script type="text/javascript" src="scripts-general.js"> </script>
+</head>
+<body>
+
+
+<!-- ## Navigation bar -->
+<div class="navigation">
+<table cellpadding="0" cellspacing="0" border="0" width="100%"><tr><td>
+ <!-- $(NAV_BAR) -->
+</td>
+<td align="right"><b><!-- $(TOTAL_ITEMS) --> items</b></td></tr></table>
+</div>
+
+
+<!-- ## Description -->
+<div class="desc">
+<!-- $(BEGIN_GO_UP) -->
+<div class="desc_date">:: <a href="../index.html" id="exif_line_a">Go Up</a><br /><br /><br /></div>
+<!-- $(END_GO_UP) -->
+<!-- $(TITLE) -->
+<div class="desc_date"><!-- $(DESCRIPTION) --></div>
+</div>
+
+
+<!-- ## Images -->
+<div class="bigthumbnails">
+<!-- $(BEGIN_IMG_LIST) -->
+ <!-- $(BEGIN_IMG_LIST_LANDSCAPE) -->
+ <div class="one_thumbnail">
+ <a href="<!-- $(IMG_SUBPAGE) -->">
+ <img src="<!-- $(IMG_THUMBNAIL) -->" alt="<!-- $(IMG_TITLE) -->" /><br />
+ <span class="thumb_name"><!-- $(IMG_FILENAME) --></span>
+ </a>
+ </div>
+ <!-- $(END_IMG_LIST_LANDSCAPE) -->
+ <!-- $(BEGIN_IMG_LIST_PORTRAIT) -->
+ <div class="one_thumbnail">
+ <a href="<!-- $(IMG_SUBPAGE) -->">
+ <img src="<!-- $(IMG_THUMBNAIL) -->" alt="<!-- $(IMG_TITLE) -->" class="portrait" /><br />
+ <span class="thumb_name"><!-- $(IMG_FILENAME) --></span>
+ </a>
+ </div>
+ <!-- $(END_IMG_LIST_PORTRAIT) -->
+<!-- $(END_IMG_LIST) -->
+</div>
+
+
+<!-- Dirty hack to add free space at the bottom of the page -->
+<div style="display: block; float: left; height: 290px;"></div>
+
+<!-- ## Footer -->
+<!-- $(FOOTER) -->
+
+
+</body>
+</html>
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title><!-- $(ID) --></title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta name="generator" content="Bluefish 1.0.6" />
+ <meta name="date" content="2007-01-06T22:48:18+0100" />
+ <meta name="description" content="Photo index: <!-- $(ID) -->" />
+ <meta name="ROBOTS" content="NOINDEX, NOFOLLOW" />
+ <meta http-equiv="content-style-type" content="text/css "/>
+ <link href="support/folder-saved-search.png" type="image/png" rel="icon" />
+ <!-- link rel="shortcut icon" href="http://primates.ximian.com/%7Ejimmac/photos/favicon.ico" type="image/x-icon" / -->
+ <link href="styles.css" type="text/css" rel="stylesheet" media="screen, print" />
+ <script type="text/javascript" src="scripts-general.js"> </script>
+</head>
+<body>
+
+
+<!-- ## Navigation bar -->
+<div class="navigation">
+<table cellpadding="0" cellspacing="0" border="0" width="100%"><tr><td>
+ <!-- $(NAV_BAR) -->
+</td>
+<td align="right"><b><!-- $(TOTAL_ITEMS) --> albums</b></td></tr></table>
+</div>
+
+
+<!-- ## Description -->
+<div class="desc">
+<!-- $(BEGIN_GO_UP) -->
+<div class="desc_date">:: <a href="../index.html" id="exif_line_a">Go Up</a><br /><br /><br /></div>
+<!-- $(END_GO_UP) -->
+<!-- $(TITLE) -->
+<div class="desc_date"><!-- $(DESCRIPTION) --></div>
+</div>
+
+
+<!-- ## Albums -->
+<div class="albumlist">
+<!-- $(BEGIN_IMG_LIST) -->
+ <!-- $(BEGIN_IMG_LIST_LANDSCAPE) -->
+ <div class="one_album">
+ <a href="<!-- $(ALBUM_SUBPATH) -->">
+ <img src="<!-- $(IMG_THUMBNAIL) -->" alt="<!-- $(IMG_TITLE) -->" />
+ <span class="album_text"><!-- $(IMG_TITLE) --></span><br /><br /><br />
+ <span class="album_subtext"><!-- $(IMG_DESCRIPTION) --></span><br /><br />
+ <span class="album_note">(<!-- $(ALBUM_NUM_ITEMS) --> items)</span>
+ </a>
+ </div>
+ <!-- $(END_IMG_LIST_LANDSCAPE) -->
+ <!-- $(BEGIN_IMG_LIST_PORTRAIT) -->
+ <div class="one_album">
+ <a href="<!-- $(ALBUM_SUBPATH) -->">
+ <img src="<!-- $(IMG_THUMBNAIL) -->" alt="<!-- $(IMG_TITLE) -->" />
+ <span class="album_text_portrait"><!-- $(IMG_TITLE) --></span><br /><br /><br />
+ <span class="album_subtext_portrait"><!-- $(IMG_DESCRIPTION) --></span><br /><br />
+ <span class="album_note">(<!-- $(ALBUM_NUM_ITEMS) --> items)</span>
+ </a>
+ </div>
+ <!-- $(END_IMG_LIST_PORTRAIT) -->
+<!-- $(END_IMG_LIST) -->
+</div>
+
+<!-- Dirty hack to add free space at the bottom of the page -->
+<div style="display: block; float: left; height: 390px;"></div>
+
+<!-- ## Footer -->
+<!-- $(FOOTER) -->
+
+
+</body>
+</html>
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Viewing photo "<!-- $(FILE_NAME) -->" [<!-- $(FILE_NO) -->/<!-- $(TOTAL_ITEMS) -->]</title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta name="generator" content="Bluefish 1.0.7"/>
+ <meta name="date" content="2007-08-27T23:06:28+0200"/>
+ <meta name="description" content="Viewing photo '<!-- $(FILE_NAME) -->'" />
+ <meta name="ROBOTS" content="NOINDEX, NOFOLLOW" />
+ <meta http-equiv="content-style-type" content="text/css "/>
+ <link href="support/image-x-generic.png" type="image/png" rel="icon" />
+ <!-- link rel="shortcut icon" href="http://primates.ximian.com/%7Ejimmac/photos/favicon.ico" type="image/x-icon" / -->
+ <link href="styles.css" type="text/css" rel="stylesheet" media="screen, print" />
+ <script type="text/javascript" src="scripts-general.js"> </script>
+</head>
+<body>
+
+
+<!-- ## Navigation bar -->
+<div class="navigation">
+<table cellpadding="0" cellspacing="0" border="0" width="100%"><tr><td>
+ <!-- $(NAV_BAR) -->
+</td>
+<td align="center" style="color: #AAAAAA;">ISO <!-- $(EXIF_ISO) --> :: <!-- $(EXIF_TIME) --> :: <!-- $(EXIF_APERTURE) --> :: <!-- $(EXIF_FOCAL_LENGTH) --></td>
+<td align="right"><b><!-- $(FILE_NO) --></b> of <b><!-- $(TOTAL_ITEMS) --></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ <a href="<!-- $(LINK_PREV) -->">&lt; Previous</a> :: <a href="<!-- $(LINK_NEXT) -->">Next &gt;</a>
+</td></tr></table>
+</div>
+
+<div class="content">
+<!-- ## Description -->
+<div class="desc"><!-- $(TITLE) -->
+<div class="desc_date"><!-- $(DESCRIPTION) --></div>
+</div>
+
+
+<!-- ## Image -->
+<div id="img_preview">
+ <a href="<!-- $(LINK_NEXT) -->" id="img_preview_a"><img src="<!-- $(IMG_SRC_BIG) -->" width="<!-- $(IMG_SIZE_BIG_W) -->" height="<!-- $(IMG_SIZE_BIG_H) -->" alt="" id="preview" /></a>
+ <br /><br />
+ <!-- $(BEGIN_IMG_FULLSIZE_LINK) -->
+ <a href="<!-- $(IMG_SRC_FULL) -->">See original size (<!-- $(IMG_SIZE_ORIG_W) -->x<!-- $(IMG_SIZE_ORIG_H) -->)</a>
+ <!-- $(END_IMG_FULLSIZE_LINK) -->
+</div>
+
+
+<!-- ## EXIF -->
+<div id="exif_table" style="display: none;" class="exif">
+:: <a href="javascript:toggle_div('exif_table');toggle_div('exif_line');" class="exif_line_a">Hide EXIF</a>
+<table class="exif">
+<tbody><tr><td align="right">Date: </td><td><b><!-- $(EXIF_DATE) --></b></td></tr>
+<tr><td align="right">Camera: </td><td><b><!-- $(EXIF_CAMERA_MODEL) --></b></td></tr>
+<tr><td align="right">ISO: </td><td><b><!-- $(EXIF_ISO) --></b></td></tr>
+<tr><td align="right">Focal length: </td><td><b><!-- $(EXIF_FOCAL_LENGTH) --></b></td></tr>
+<tr><td align="right">Aperture value: </td><td><b><!-- $(EXIF_APERTURE) --></b></td></tr>
+<tr><td align="right">Exposure time: </td><td><b><!-- $(EXIF_TIME) --></b></td></tr>
+<tr><td align="right">Flash: </td><td><b><!-- $(EXIF_FLASH) --></b></td></tr>
+</tbody></table>
+</div>
+<div style="display: block;" id="exif_line" class="exif">
+:: <a href="javascript:toggle_div('exif_table');toggle_div('exif_line');" class="exif_line_a">Show EXIF</a>
+</div>
+</div>
+
+<!-- ## Footer -->
+<!-- $(FOOTER) -->
+
+
+</body>
+</html>
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 <tbzatek@users.sourceforge.net>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <glib.h>
+
+#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 <tbzatek@users.sourceforge.net>
+ *
+ * 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 <glib.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/xpath.h>
+
+
+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);