diff options
| author | Tomas Bzatek <tbzatek@users.sourceforge.net> | 2009-02-26 22:50:39 +0100 |
|---|---|---|
| committer | Tomas Bzatek <tbzatek@users.sourceforge.net> | 2009-02-26 22:50:39 +0100 |
| commit | 89c58dc04c264c5778ae34d1428e12483f3ac5ac (patch) | |
| tree | d5aef506841b3b0e2e91016d0c0bbb608535873c /src | |
| parent | 10a77c7a1c4648693ded958d6ac8641afcdf1d34 (diff) | |
| download | cataract-89c58dc04c264c5778ae34d1428e12483f3ac5ac.tar.xz | |
Autotoolize
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 41 | ||||
| -rw-r--r-- | src/cgg-config.h | 30 | ||||
| -rwxr-xr-x | src/cgg-dirgen | 54 | ||||
| -rw-r--r-- | src/cgg.c | 155 | ||||
| -rw-r--r-- | src/gallery-utils.c | 262 | ||||
| -rw-r--r-- | src/gallery-utils.h | 53 | ||||
| -rw-r--r-- | src/generators-replace-table.c | 174 | ||||
| -rw-r--r-- | src/generators-replace-table.h | 63 | ||||
| -rw-r--r-- | src/generators.c | 1037 | ||||
| -rw-r--r-- | src/generators.h | 64 | ||||
| -rw-r--r-- | src/items.c | 320 | ||||
| -rw-r--r-- | src/items.h | 96 | ||||
| -rw-r--r-- | src/jpeg-utils.cpp | 429 | ||||
| -rw-r--r-- | src/jpeg-utils.h | 94 | ||||
| -rw-r--r-- | src/setup.c | 268 | ||||
| -rw-r--r-- | src/setup.h | 92 | ||||
| -rw-r--r-- | src/xml-parser.c | 261 | ||||
| -rw-r--r-- | src/xml-parser.h | 67 |
18 files changed, 3560 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..443d2f1 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,41 @@ +NULL = + +INCLUDES = \ + -DDATADIR=\"$(datadir)\" \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + $(WARN_CFLAGS) \ + $(DISABLE_DEPRECATED_CFLAGS) \ + $(LIBS_CFLAGS) \ + -DG_LOG_DOMAIN=\"cgg\" + +bin_PROGRAMS = \ + cgg \ + $(NULL) + +bin_SCRIPTS = \ + cgg-dirgen \ + $(NULL) + +cgg_SOURCES = \ + cgg.c \ + cgg-config.h \ + gallery-utils.c \ + gallery-utils.h \ + generators.c \ + generators.h \ + generators-replace-table.c \ + generators-replace-table.h \ + items.c \ + items.h \ + jpeg-utils.cpp \ + jpeg-utils.h \ + setup.c \ + setup.h \ + xml-parser.c \ + xml-parser.h \ + $(NULL) + +cgg_LDADD = $(LIBS_LIBS) + +EXTRA_DIST = cgg-dirgen diff --git a/src/cgg-config.h b/src/cgg-config.h new file mode 100644 index 0000000..7a447d7 --- /dev/null +++ b/src/cgg-config.h @@ -0,0 +1,30 @@ +/* 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" + +// #define APP_VERSION "0.99.0" +// #define APP_BUILD_DATE "2008-07-27" + diff --git a/src/cgg-dirgen b/src/cgg-dirgen new file mode 100755 index 0000000..44dd256 --- /dev/null +++ b/src/cgg-dirgen @@ -0,0 +1,54 @@ +#!/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) +## -t Generate thumbnails from the "thumbnails" folder + + +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=""; + INCL3=""; + if [ "$1" = "-d" ]; then INCL=" preview=\"preview/`echo $i | cut -b 3-`\""; fi + if [ "$2" = "-o" ]; then INCL2=" <nofullsize />\n"; fi + if [ "$3" = "-t" ]; then INCL3=" thumbnail=\"thumbnails/`echo $i | cut -b 3-`\""; fi + echo -e " <item src=\"`echo $i | cut -b 3-`\"${INCL}${INCL3}>\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/src/cgg.c b/src/cgg.c new file mode 100644 index 0000000..042e114 --- /dev/null +++ b/src/cgg.c @@ -0,0 +1,155 @@ +/* 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 "cgg-config.h" +#include "setup.h" +#include "generators.h" + + + + + + +/* + * 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) %s Tomas Bzatek", VERSION, APP_BUILD_DATE, APP_COPYRIGHT_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 ("%s", 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 ("%s", 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/src/gallery-utils.c b/src/gallery-utils.c new file mode 100644 index 0000000..6378ae5 --- /dev/null +++ b/src/gallery-utils.c @@ -0,0 +1,262 @@ +/* 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/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <utime.h> + +#include "gallery-utils.h" + + +/* + * str_replace: replace substring 'search' with a 'replace' string + * - multiple occurrences of the string are replaced + * - reallocates the original string + */ +void +str_replace (char **dst, const char *search, const char *replace) +{ + #define REPLACE_MAX_LENGTH 32768 + static char d[REPLACE_MAX_LENGTH]; + char *src; + char *found; + int i; + + /* TODO: add range checking */ + + if (strstr (*dst, search) == NULL || strlen (*dst) == 0 || strlen (search) == 0) + return; + + i = 0; + src = *dst; + while (strstr (src, search)) { + found = strstr (src, search); + + /* copy the data between search string */ + if (found > src) { + memcpy (&d[i], src, found - src); + i += found - src; + } + + /* copy replace string instead */ + if (strlen (replace) > 0) { + memcpy (&d[i], replace, strlen (replace)); + i += strlen (replace); + } + src = found + strlen (search); + } + + /* copy the rest */ + if (src) { + memcpy (&d[i], src, strlen (src)); + i += strlen (src); + } + d[i] = 0x0; + + +#ifdef __DEBUG_ALL__ + printf ("str_replace('%s', '%s') = '%s' --> '%s'\n", search, replace, *dst, &d[0]); +#endif + + /* return fixed string */ + free (*dst); + *dst = g_strdup (&d[0]); +} + + +/* + * 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; + struct stat st; + struct utimbuf ut; + + 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); + + /* copy timestamps */ + memset (&st, 0, sizeof (st)); + if (stat (src, &st) == -1) { + fprintf (stderr, "copy_file: cannot stat source file \"%s\": %s\n", src, strerror (errno)); + return TRUE; + } + + memset (&ut, 0, sizeof (ut)); + ut.actime = st.st_atime; + ut.modtime = st.st_mtime; + if (utime (dst, &ut) == -1) + fprintf (stderr, "copy_file: cannot set timestamps on target file \"%s\": %s\n", dst, strerror (errno)); + + 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; +} + + +/* + * fix_entities: replace all invalid & entities with & + * - returns newly allocated string + */ +void +fix_entities (char **str) +{ + static char d[REPLACE_MAX_LENGTH]; + char *src; + char *found; + char *scan; + int i; + + /* TODO: add range checking */ + + if (! *str || strstr (*str, "&") == NULL) + return; + + i = 0; + src = *str; + while (strstr (src, "&")) { + found = strstr (src, "&"); + + /* copy the data between search string */ + memcpy (&d[i], src, found - src + 1); + i += found - src + 1; + + /* scroll to next whitespace */ + scan = found + 1; + while (scan && ( + (*scan >= 0x41 && *scan <= 0x5a) || (*scan >= 0x61 && *scan <= 0x7a) || /* A-Z, a-z */ + (*scan >= 0x30 && *scan <= 0x39) || (*scan == 0x23) /* 0-9, # */ + )) + scan++; + + if (scan && (*scan == 0x3b)) { + /* this is semicolon, correctly closed entity */ + /* -- ignore */ + } + else { + /* replace with & */ + memcpy (&d[i], "amp;", 4); + i += 4; + } + src = found + 1; + } + + /* copy the rest */ + if (src) { + memcpy (&d[i], src, strlen (src)); + i += strlen (src); + } + d[i] = 0x0; + + +#ifdef __DEBUG_ALL__ + printf ("fix_entities: '%s' --> '%s'\n", *str, &d[0]); +#endif + + /* return fixed string */ + free (*str); + *str = g_strdup (&d[0]); +} + + +/* + * remove_tags: remove all occurences of tags beginning with tag_begin and ending with tag_end + * - e.g. remove_tags (&x, "<!--", "-->") will remove all comments + * - returns newly allocated string + */ +void +remove_tags (char **str, const char *tag_begin, const char *tag_end) +{ + char *src; + char *found; + char *found2; + char *dest; + + if (! *str || ! tag_begin || ! tag_end || strlen (*str) == 0 || strlen (tag_begin) == 0 || strlen (tag_end) == 0) + return; + + src = *str; + + while ((found = strstr (src, tag_begin)) != NULL) { + found2 = strstr (found, tag_end); + if (found2) { + found2 += strlen (tag_end); + dest = malloc (strlen (src) - (found2 - found) + 1); + memcpy (dest, src, found - src); + memcpy (dest + (found - src), found2, strlen (found2) + 1); +#ifdef __DEBUG_ALL__ + printf ("found = %ld, found2 = %ld, strlen = %d, res = %d\n", (long int)found, (long int)found2, strlen (src), strlen (src) - (found2 - found) + 1); + printf ("orig = %s, new = %s, strlen = %d\n", src, dest, strlen (dest)); +#endif + g_free (src); + src = g_strdup (dest); + free (dest); + } + } + *str = src; +} diff --git a/src/gallery-utils.h b/src/gallery-utils.h new file mode 100644 index 0000000..53c82c0 --- /dev/null +++ b/src/gallery-utils.h @@ -0,0 +1,53 @@ +/* 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 + * - multiple occurences of the string are 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); + +/* + * fix_entities: replace all invalid & entities with & + * - returns newly allocated string + */ +void fix_entities (char **str); + +/* + * remove_tags: remove all occurences of tags beginning with tag_begin and ending with tag_end + * - e.g. remove_tags (&x, "<!--", "-->") will remove all comments + * - returns newly allocated string + */ +void remove_tags (char **str, const char *tag_begin, const char *tag_end); diff --git a/src/generators-replace-table.c b/src/generators-replace-table.c new file mode 100644 index 0000000..23771fc --- /dev/null +++ b/src/generators-replace-table.c @@ -0,0 +1,174 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2009 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 <glib/gprintf.h> + +#include "generators-replace-table.h" +#include "gallery-utils.h" + + + + +#if 0 +static void +replace_table_destroy_notify (gpointer data) +{ + g_print ("replace_table_destroy_notify: %s\n", (gchar *) data); + + if ((gchar *) data) + g_free ((gchar *) data); +} +#endif + +ReplaceTable * +replace_table_new () +{ + return (ReplaceTable *) g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); +} + +void +replace_table_free (ReplaceTable *table) +{ + g_return_if_fail (table != NULL); + + g_hash_table_destroy (table); +} + + +/* + * replace_table_add_key: add tag/value pair to replace + * + * tag, value will be referenced inside + * + */ +void +replace_table_add_key (ReplaceTable *table, const gchar *tag, const gchar *value) +{ + g_return_if_fail (table != NULL); + + if (tag != NULL && value != NULL) + g_hash_table_replace (table, g_strdup (tag), g_strdup (value)); +} + +void +replace_table_add_key_int (ReplaceTable *table, const gchar *tag, gint value) +{ + g_return_if_fail (table != NULL); + + if (tag != NULL) + g_hash_table_replace (table, g_strdup (tag), g_strdup_printf ("%d", value)); +} + +void +replace_table_add_key_printf (ReplaceTable *table, const gchar *tag, const gchar *format, ...) +{ + va_list args; + gchar *s = NULL; + + g_return_if_fail (table != NULL); + + if (tag != NULL && format != NULL) { + va_start (args, format); + g_vasprintf (&s, format, args); + g_hash_table_replace (table, g_strdup (tag), s); + va_end (args); + } +} + + + +static void +replace_table_process_value (gpointer key, gpointer value, gpointer user_data) +{ + gchar **buffer = user_data; + gchar *tag; + gchar *tag_value; + + g_return_if_fail (key != NULL); + g_return_if_fail (value != NULL); + g_return_if_fail (user_data != NULL); + + /* replace <!-- $(xxx) --> tags */ + tag = g_strdup_printf ("<!-- $(%s) -->", (gchar *) key); + tag_value = g_strdup ((gchar *) value); + adjust_tags_normal (&tag_value); + while (strstr (*buffer, tag)) { + str_replace (buffer, tag, tag_value); + } + g_free (tag_value); + g_free (tag); + + /* replace $(xxx) tags */ + tag = g_strdup_printf ("$(%s)", (gchar *) key); + tag_value = g_strdup ((gchar *) value); + adjust_tags_parameter (&tag_value); + while (strstr (*buffer, tag)) { + str_replace (buffer, tag, tag_value); + } + g_free (tag_value); + g_free (tag); +} + +/* + * replace_table_process: process buffer and replace all tags filled in the replace table + * + * - reallocates source buffer + * + */ +void +replace_table_process (gchar **buffer, ReplaceTable *table) +{ + g_return_if_fail (table != NULL); + + g_hash_table_foreach (table, replace_table_process_value, buffer); + +/* + gchar *val = g_hash_table_lookup (table, "TAG"); + g_print ("replace_table_process: val = '%s'\n", val); +*/ +} + + +/* + * adjust_tags_normal: adjust string for normal HTML use + * adjust_tags_parameter: adjust string for use as tag parameter value + * - both funtions return newly allocated string + */ +void +adjust_tags_normal (char **str) +{ + fix_entities (str); +} + +void +adjust_tags_parameter (char **str) +{ + /* TODO: replace line endings with single space? */ + remove_tags (str, "<!--", "-->"); /* comments */ + remove_tags (str, "<", ">"); /* tags */ + fix_entities (str); /* entities */ + str_replace (str, "\"", """); /* " */ + str_replace (str, "'", "'"); /* ' */ +} diff --git a/src/generators-replace-table.h b/src/generators-replace-table.h new file mode 100644 index 0000000..d72d150 --- /dev/null +++ b/src/generators-replace-table.h @@ -0,0 +1,63 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2009 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 GHashTable ReplaceTable; + +/* + * 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 + * + */ +ReplaceTable *replace_table_new (); + +void replace_table_free (ReplaceTable *table); + + +/* + * replace_table_add_key: add tag/value pair to replace + * + * tag, value will be referenced inside + * + */ +void replace_table_add_key (ReplaceTable *table, const gchar *tag, const gchar *value); +void replace_table_add_key_int (ReplaceTable *table, const gchar *tag, gint value); +void replace_table_add_key_printf (ReplaceTable *table, const gchar *tag, const gchar *format, ...) G_GNUC_PRINTF (3, 4); + +/* + * replace_table_process: process buffer and replace all tags filled in the replace table + * + * - reallocates source buffer + * + */ +void replace_table_process (gchar **buffer, ReplaceTable *table); + + +/* + * adjust_tags_normal: adjust string for normal HTML use + * adjust_tags_parameter: adjust string for use as tag parameter value + * - both funtions return newly allocated string + */ +void adjust_tags_normal (char **str); +void adjust_tags_parameter (char **str); + diff --git a/src/generators.c b/src/generators.c new file mode 100644 index 0000000..8a2053a --- /dev/null +++ b/src/generators.c @@ -0,0 +1,1037 @@ +/* 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 "cgg-config.h" +#include "setup.h" +#include "items.h" +#include "jpeg-utils.h" +#include "gallery-utils.h" +#include "generators-replace-table.h" + + +/* + * generate_image: auxiliary function for write_html_album + * - img_src and thumb should be freed afterwards + */ +void +generate_image (TGallerySetup *setup, + TAlbum *items, + TIndexItem *item, + unsigned int item_index, + const char *dst, + unsigned long *img_w, unsigned long *img_h, + char **img_src, char **thumb) +{ + unsigned long new_w, new_h; + unsigned long thumb_w, thumb_h; + char *thumb_dst; + char *big_dst; + char *big_src; + char *orig_dst; + char *img_src_full; + char *thumb_src_full; + char *s1; + int bigq; + + + *img_src = NULL; + *thumb = NULL; + *img_w = 0; + *img_h = 0; + + if (items->type == GALLERY_TYPE_INDEX) { + if (item->thumbnail == NULL || strlen (item->thumbnail) == 0) + return; + img_src_full = g_strconcat (items->base_dir, "/", item->thumbnail, NULL); + thumb_src_full = g_strconcat (items->base_dir, "/", item->thumbnail, NULL); + *img_src = g_path_get_basename (item->thumbnail); + s1 = g_path_get_basename (item->thumbnail); + *thumb = g_strdup_printf ("thn_%.3d_%s", item_index, s1); + g_free (s1); + } + else + if (items->type == GALLERY_TYPE_ALBUM) { + *img_src = (item->path == NULL && item->preview) ? item->preview : item->path; + *thumb = (item->thumbnail) ? item->thumbnail : *img_src; + img_src_full = g_strconcat (items->base_dir, "/", *img_src, NULL); + thumb_src_full = g_strconcat (items->base_dir, "/", *thumb, NULL); + *img_src = g_path_get_basename (*img_src); + s1 = g_path_get_basename (*thumb); + *thumb = g_strdup_printf ("thn_%.3d_%s", item_index, s1); + g_free (s1); + } + + 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 */ + get_image_sizes (thumb_src_full, &thumb_w, &thumb_h); + s1 = g_path_get_dirname (dst); + thumb_dst = g_strconcat (s1, "/", THUMBNAIL_DIR, "/", *thumb, NULL); + g_free (s1); + if (setup->verbose) printf (" Generating thumbnail of '%s' ...", *thumb); + + if ((thumb_w > 0) && (thumb_h > 0)) { + if ((thumb_w / thumb_h) >= 1) + calculate_sizes (setup->thumbnail_landscape_width, setup->thumbnail_landscape_height, &thumb_w, &thumb_h); + else + calculate_sizes (setup->thumbnail_portrait_width, setup->thumbnail_portrait_height, &thumb_w, &thumb_h); + if (! resize_image (thumb_src_full, thumb_dst, thumb_w, thumb_h, setup->thumbnail_quality, TRUE)) + fprintf (stderr, "write_html_index: error resizing thumbnail %s\n", thumb_src_full); + else + if (setup->verbose) printf (" done.\n"); + g_free (thumb_dst); + } else + printf (" failed!\n"); + + + /* 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, FALSE)) + 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); + } + modify_exif (big_dst, setup->erase_exif_thumbnail, setup->add_copyright); + g_free (big_dst); + + if (item->force_fullsize || (items->fullsize && ! item->force_nofullsize) || + (! item->force_nofullsize && ! items->nofullsize && ! setup->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"); + modify_exif (orig_dst, setup->erase_exif_thumbnail, setup->add_copyright); + g_free (orig_dst); + } + } + } + g_free (img_src_full); + g_free (thumb_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_img_separator; + char *buf_go_up_string; + gboolean in_img_list; + gboolean in_img_list_landscape; + gboolean in_img_list_portrait; + gboolean in_img_separator; + gboolean in_go_up_string; + char *b; + char *s1, *s2, *s3, *s4, *s5; + TAlbum *parent; + TIndexItem *item; + TIndexItem *tmp_item; + int level, old_parent_item_index; + gboolean res, bb; + int i; + unsigned long img_w, img_h; + char *img_src; + char *thumb; + unsigned int real_total_items; + ReplaceTable *global_replace_table; + ReplaceTable *local_replace_table; + + + 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_img_separator = 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_img_separator = FALSE; + in_go_up_string = FALSE; + res = TRUE; + + global_replace_table = replace_table_new (); + + /* Get number of real pictures in the list */ + real_total_items = 0; + for (i = 0; i < items->items->len; i++) { + tmp_item = g_ptr_array_index (items->items, i); + if (tmp_item->type == INDEX_ITEM_TYPE_PICTURE) + real_total_items++; + } + replace_table_add_key_int (global_replace_table, "TOTAL_ITEMS", real_total_items); + + /* Page title */ + if (items->parent_index == NULL || setup->site_title == NULL) + s1 = g_strdup (setup->site_title ? setup->site_title : items->ID); + else + s1 = g_strdup_printf ("%s | %s", items->title, setup->site_title); + replace_table_add_key (global_replace_table, "PAGE_TITLE", s1); + g_free (s1); + + /* Simple placeholders */ + replace_table_add_key (global_replace_table, "ID", items->ID); + replace_table_add_key (global_replace_table, "TITLE", items->title); + replace_table_add_key (global_replace_table, "DESCRIPTION", items->desc); + replace_table_add_key (global_replace_table, "FOOTNOTE", items->footnote); + replace_table_add_key (global_replace_table, "FOOTER", setup->footer); + s1 = setup->use_inpage_links ? g_strdup_printf ("../index.html#i%d", items->parent_item_index + 1) : g_strdup ("../index.html"); + replace_table_add_key (global_replace_table, "GO_UP_LINK", s1); + g_free (s1); + + /* Navigation bar */ + s1 = g_strdup (items->ID); + old_parent_item_index = items->parent_item_index + 1; + parent = items->parent_index; + level = 1; + while (parent) { + s3 = make_string ("../", level); + s4 = g_strdup (parent->ID); + s5 = setup->use_inpage_links ? g_strdup_printf ("#i%d", old_parent_item_index) : g_strdup (""); + s2 = g_strdup_printf ("<a href=\"%sindex.html%s\">%s</a> > %s", s3, s5, s4, s1); + free (s3); + g_free (s1); + g_free (s4); + g_free (s5); + s1 = s2; + old_parent_item_index = parent->parent_item_index + 1; + parent = parent->parent_index; + level++; + } + replace_table_add_key (global_replace_table, "NAV_BAR", s1); + g_free (s1); + + /* META tags */ + s1 = g_strdup_printf ("\t<meta name=\"generator\" content=\"Cataract Gallery Generator v%s\" />\n", VERSION); + if (setup->meta_author || items->meta_author) { + s3 = g_strdup (items->meta_author ? items->meta_author : setup->meta_author); + adjust_tags_parameter (&s3); + s2 = g_strdup_printf ("%s\t<meta name=\"author\" content=\"%s\" />\n", s1, s3); + g_free (s3); + g_free (s1); + s1 = s2; + } + if (setup->meta_description || items->meta_description) { + s3 = g_strdup (items->meta_description ? items->meta_description : setup->meta_description); + adjust_tags_parameter (&s3); + s2 = g_strdup_printf ("%s\t<meta name=\"description\" content=\"%s\" />\n", s1, s3); + g_free (s3); + g_free (s1); + s1 = s2; + } + if (setup->meta_keywords || items->meta_keywords) { + s3 = g_strdup (items->meta_keywords ? items->meta_keywords : setup->meta_keywords); + adjust_tags_parameter (&s3); + s2 = g_strdup_printf ("%s\t<meta name=\"keywords\" content=\"%s\" />\n", s1, s3); + g_free (s3); + g_free (s1); + s1 = s2; + } + replace_table_add_key (global_replace_table, "CGG_META_TAGS", s1); + g_free (s1); + + + /* 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; + + /* Block placeholders */ + if (in_img_list && (strstr (buffer, "<!-- $(BEGIN_IMG_LIST_LANDSCAPE) -->"))) { + in_img_list_landscape = TRUE; + continue; + } + if (in_img_list && (strstr (buffer, "<!-- $(END_IMG_LIST_LANDSCAPE) -->"))) { + in_img_list_landscape = FALSE; + continue; + } + if (in_img_list && (strstr (buffer, "<!-- $(BEGIN_IMG_LIST_PORTRAIT) -->"))) { + in_img_list_portrait = TRUE; + continue; + } + if (in_img_list && (strstr (buffer, "<!-- $(END_IMG_LIST_PORTRAIT) -->"))) { + in_img_list_portrait = FALSE; + continue; + } + if (in_img_list && (strstr (buffer, "<!-- $(BEGIN_LIST_SEPARATOR) -->"))) { + in_img_separator = TRUE; + continue; + } + if (in_img_list && (strstr (buffer, "<!-- $(END_LIST_SEPARATOR) -->"))) { + in_img_separator = FALSE; + continue; + } + if (in_img_list && in_img_list_landscape) { + buf_img_list_landscape = strncat (buf_img_list_landscape, buffer, BUFFER_SIZE - strlen (buf_img_list_landscape) - 2); + continue; + } + if (in_img_list && in_img_list_portrait) { + buf_img_list_portrait = strncat (buf_img_list_portrait, buffer, BUFFER_SIZE - strlen (buf_img_list_portrait) - 2); + continue; + } + if (in_img_list && in_img_separator) { + buf_img_separator = strncat (buf_img_separator, buffer, BUFFER_SIZE - strlen (buf_img_separator) - 2); + continue; + } + + if (strstr (buffer, "<!-- $(BEGIN_GO_UP) -->")) { + memset (buf_go_up_string, 0, BUFFER_SIZE); + in_go_up_string = TRUE; + continue; + } + if (in_go_up_string && strstr (buffer, "<!-- $(END_GO_UP) -->")) { + in_go_up_string = FALSE; + if (! items->parent_index) + continue; + } + if (in_go_up_string) { + buf_go_up_string = strncat (buf_go_up_string, buffer, BUFFER_SIZE - strlen (buf_go_up_string) - 2); + continue; + } + + /* 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); + memset (buf_img_separator, 0, BUFFER_SIZE); + in_img_list = TRUE; + in_img_list_landscape = FALSE; + in_img_list_portrait = FALSE; + in_img_separator = FALSE; + 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); + continue; + } + + /* Generate the images (preview, original, thumbnail) */ + local_replace_table = replace_table_new (); + img_w = 0; + img_h = 0; + s1 = NULL; + + switch (item->type) { + case INDEX_ITEM_TYPE_PICTURE: + img_src = NULL; + thumb = NULL; + generate_image (setup, items, item, i, dst, &img_w, &img_h, &img_src, &thumb); + /* Skip HTML code generation if it's a hidden item */ + if (! item->hidden) { + if (img_w == 0 || img_h == 0 || (img_w / img_h) >= 1) + s1 = strdup (buf_img_list_landscape); + else + s1 = strdup (buf_img_list_portrait); + + replace_table_add_key_printf (local_replace_table, "ALBUM_SUBPATH", "%s/index.html", item->path); + replace_table_add_key_printf (local_replace_table, "IMG_SUBPAGE", "%s.html", img_src); + replace_table_add_key (local_replace_table, "IMG_TITLE", item->title); + replace_table_add_key (local_replace_table, "IMG_DESCRIPTION", item->title_description); + replace_table_add_key_printf (local_replace_table, "IMG_LIST_ID", "i%d", i + 1); + replace_table_add_key_printf (local_replace_table, "IMG_THUMBNAIL", "%s/%s", THUMBNAIL_DIR, thumb); + replace_table_add_key (local_replace_table, "IMG_FILENAME", img_src); + if (items->type == GALLERY_TYPE_INDEX) { + s3 = g_strconcat (items->base_dir, "/", item->path, "/index.xml", NULL); + replace_table_add_key_int (local_replace_table, "ALBUM_NUM_ITEMS", get_album_objects_count (s3)); + g_free (s3); + } + } + if (img_src) + g_free (img_src); + if (thumb) + g_free (thumb); + break; + + case INDEX_ITEM_TYPE_SEPARATOR: + s1 = strdup (buf_img_separator); + replace_table_add_key (local_replace_table, "LIST_SEPARATOR_TITLE", item->title); + break; + } + + bb = TRUE; + if (s1) { + replace_table_process (&s1, local_replace_table); + bb = fputs (s1, fout); + free (s1); + } + replace_table_free (local_replace_table); + if (! bb) { + fprintf (stderr, "write_html_index: error writing to file \"%s\": %s\n", dst, strerror (errno)); + res = FALSE; + break; + } + } + continue; /* no need to write anything */ + } + + /* Select apropriate line buffer */ + if (strstr (buffer, "<!-- $(END_GO_UP) -->") && items->parent_index) { + b = strdup (buf_go_up_string); + } else + b = strdup (buffer); + + /* Replace all known tags */ + replace_table_process (&b, global_replace_table); + + /* Write to file */ + bb = fputs (b, fout); + free (b); + if (! bb) { + fprintf (stderr, "write_html_index: error writing to file \"%s\": %s\n", dst, strerror (errno)); + res = FALSE; + break; + } + } + + fclose (fout); + fclose (fin); + replace_table_free (global_replace_table); + free (buffer); + free (buf_img_list_landscape); + free (buf_img_list_portrait); + free (buf_img_separator); + 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, real_item_index, real_total_items; + TIndexItem *previous_item; + TIndexItem *next_item; + TIndexItem *tmp_item; + TAlbum *parent; + int i; + char *s1, *s2, *s3, *s4, *s5; + char *imgname, *preload_imgname; + char *title, *title_desc; + char *b; + gboolean res, bb; + int level, old_parent_item_index; + gboolean override_title_meta; + gboolean image_fullsize; + ReplaceTable *replace_table; + + + 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); + preload_imgname = NULL; + + replace_table = replace_table_new (); + + /* Get our index in the album */ + item_index = 0; + real_item_index = 0; + real_total_items = 0; + for (i = 0; i < parent_items->items->len; i++) { + tmp_item = g_ptr_array_index (parent_items->items, i); + if (tmp_item->type == INDEX_ITEM_TYPE_PICTURE) { + if (! item_index) real_item_index++; + real_total_items++; + } + if (tmp_item == item) + item_index = i + 1; + } + + /* Get previous and next items */ + previous_item = NULL; + next_item = NULL; + for (i = item_index - 2; i >= 0 && (previous_item == NULL || previous_item->type != INDEX_ITEM_TYPE_PICTURE); i--) + previous_item = g_ptr_array_index (parent_items->items, i); + if (previous_item && previous_item->type != INDEX_ITEM_TYPE_PICTURE) + previous_item = NULL; + for (i = item_index; item_index < parent_items->items->len && (next_item == NULL || next_item->type != INDEX_ITEM_TYPE_PICTURE); i++) + next_item = g_ptr_array_index (parent_items->items, i); + if (next_item && next_item->type != INDEX_ITEM_TYPE_PICTURE) + next_item = NULL; + + /* Paths setup */ + imgname = g_path_get_basename ((item->path == NULL && item->preview) ? item->preview : item->path); + if (next_item && setup->preload) { + s1 = g_path_get_basename ((next_item->path == NULL && next_item->preview) ? next_item->preview : next_item->path); + preload_imgname = g_strconcat (IMG_BIG_DIR, "/", s1, NULL); + g_free (s1); + } + s1 = g_path_get_dirname (dst); + big_dst = g_strconcat (s1, "/", IMG_BIG_DIR, "/", imgname, NULL); + orig_dst = g_strconcat (s1, "/", IMG_ORIG_DIR, "/", imgname, 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 */ + 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); + image_fullsize = item->force_fullsize || (parent_items->fullsize && ! item->force_nofullsize) || + (! item->force_nofullsize && ! parent_items->nofullsize && ! setup->nofullsize); + if (image_fullsize) + get_image_sizes (orig_dst, &img_orig_w, &img_orig_h); + + /* Get title and description from IPTC/EXIF/JPEG if not defined */ + title = g_strdup (item->title); + title_desc = g_strdup (item->title_description); + if (setup->use_iptc_exif && title == NULL && title_desc == NULL) { + if (exif->iptc_caption) + title = g_strdup (exif->iptc_caption); + if (exif->jpeg_comment) { + if (! title) + title = g_strdup (exif->jpeg_comment); + else + title_desc = g_strdup (exif->jpeg_comment); + } + if (exif->exif_imgdesc) { + if (! title) + title = g_strdup (exif->exif_imgdesc); +/* if (! title_desc) -- disabled + title_desc = g_strdup (exif->exif_imgdesc); */ + } + if (exif->exif_usercomment) { + if (! title) + title = g_strdup (exif->exif_usercomment); + if (! title_desc) + title_desc = g_strdup (exif->exif_usercomment); + } + /* Convert line breaks to be visible in the HTML code */ + if (title) { + str_replace (&title, "\r\n", "<br />"); + str_replace (&title, "\n", "<br />"); + } + if (title_desc) { + str_replace (&title_desc, "\r\n", "<br />"); + str_replace (&title_desc, "\n", "<br />"); + } + } + if (title) title = g_strstrip (title); + if (title_desc) title_desc = g_strstrip (title_desc); + + /* Page title */ + s1 = (title && strlen (title) > 0) ? g_strdup_printf("%s | ", title) : NULL; + s2 = g_strdup_printf ("%s [%d/%d]", parent_items->title ? parent_items->title : parent_items->ID, real_item_index, real_total_items); + s3 = setup->site_title ? g_strdup_printf(" | %s", setup->site_title) : NULL; + s4 = g_strconcat (s1 ? s1 : "", s2, s3 ? s3 : "", NULL); + replace_table_add_key (replace_table, "PAGE_TITLE", s4); + if (s1) g_free (s1); + if (s2) g_free (s2); + if (s3) g_free (s3); + g_free (s4); + + /* Simple placeholders */ + replace_table_add_key (replace_table, "FILE_NAME", imgname); + replace_table_add_key (replace_table, "TITLE", title); + replace_table_add_key (replace_table, "DESCRIPTION", title_desc); + replace_table_add_key (replace_table, "FOOTER", setup->footer); + replace_table_add_key_int (replace_table, "TOTAL_ITEMS", real_total_items); + replace_table_add_key_int (replace_table, "FILE_NO", real_item_index); + replace_table_add_key_int (replace_table, "IMG_SIZE_BIG_W", img_big_w); + replace_table_add_key_int (replace_table, "IMG_SIZE_BIG_H", img_big_h); + replace_table_add_key_int (replace_table, "IMG_SIZE_ORIG_W", img_orig_w); + replace_table_add_key_int (replace_table, "IMG_SIZE_ORIG_H", img_orig_h); + replace_table_add_key_printf (replace_table, "IMG_SRC_BIG", "%s/%s", IMG_BIG_DIR, imgname); + replace_table_add_key_printf (replace_table, "IMG_SRC_FULL", "%s/%s", IMG_ORIG_DIR, imgname); + replace_table_add_key (replace_table, "IMG_SRC_PRELOAD", preload_imgname ? preload_imgname : ""); + + /* Navigation bar */ + s1 = g_strdup (imgname); + parent = parent_items; + level = 0; + while (parent) { + s3 = make_string ("../", level); + s4 = g_strdup (parent->ID); + s5 = setup->use_inpage_links ? g_strdup_printf ("#i%d", parent == parent_items ? item_index : old_parent_item_index) : g_strdup (""); + s2 = g_strdup_printf ("<a href=\"%sindex.html%s\">%s</a> > %s", s3, s5, s4, s1); + free (s3); + g_free (s1); + g_free (s4); + g_free (s5); + s1 = s2; + old_parent_item_index = parent->parent_item_index + 1; + parent = parent->parent_index; + level++; + } + replace_table_add_key (replace_table, "NAV_BAR", s1); + g_free (s1); + + /* EXIF values */ + replace_table_add_key (replace_table, "EXIF_ISO", exif->iso ? exif->iso : "??"); + replace_table_add_key (replace_table, "EXIF_TIME", exif->exposure ? exif->exposure : "??"); + replace_table_add_key (replace_table, "EXIF_APERTURE", exif->aperture ? exif->aperture : "??"); + replace_table_add_key (replace_table, "EXIF_FOCAL_LENGTH", exif->focal_length ? exif->focal_length : "??"); + replace_table_add_key (replace_table, "EXIF_FLASH", exif->flash ? exif->flash : "??"); + replace_table_add_key (replace_table, "EXIF_DATE", exif->datetime ? exif->datetime : "??"); + replace_table_add_key (replace_table, "EXIF_CAMERA_MODEL", exif->camera_model ? exif->camera_model : "??"); + s1 = g_strdup_printf ("(%s)", exif->focal_length_35mm); + replace_table_add_key (replace_table, "EXIF_FOCAL_35", exif->focal_length_35mm ? s1 : ""); + g_free (s1); + + /* Border style */ + s1 = item->border_style; + if (s1 == NULL) + s1 = parent_items->border_style; + if (s1 == NULL) + s1 = setup->border_style; + if (s1 == NULL) + s1 = "border_single"; + replace_table_add_key (replace_table, "IMG_BORDER_STYLE", s1); + + /* Next/Previous links */ + if (next_item) { + s2 = g_path_get_basename ((next_item->path == NULL && next_item->preview) ? next_item->preview : next_item->path); + replace_table_add_key_printf (replace_table, "LINK_NEXT", "%s.html", s2); + g_free (s2); + } + else + replace_table_add_key (replace_table, "LINK_NEXT", "index.html"); + if (previous_item) { + s2 = g_path_get_basename ((previous_item->path == NULL && previous_item->preview) ? previous_item->preview : previous_item->path); + replace_table_add_key_printf (replace_table, "LINK_PREV", "%s.html", s2); + g_free (s2); + } + else + replace_table_add_key (replace_table, "LINK_PREV", "index.html"); + + /* META tags */ + override_title_meta = setup->use_title_as_meta && title && (strlen (title) > 0); + s1 = g_strdup_printf ("\t<meta name=\"generator\" content=\"Cataract Gallery Generator v%s\" />\n", VERSION); + if (setup->meta_author || parent_items->meta_author) { + s3 = g_strdup (parent_items->meta_author ? parent_items->meta_author : setup->meta_author); + adjust_tags_parameter (&s3); + s2 = g_strdup_printf ("%s\t<meta name=\"author\" content=\"%s\" />\n", s1, s3); + g_free (s3); + g_free (s1); + s1 = s2; + } + if (setup->meta_description || parent_items->meta_description || override_title_meta) { + s3 = g_strdup (override_title_meta ? title : (parent_items->meta_description ? parent_items->meta_description : setup->meta_description)); + adjust_tags_parameter (&s3); + s2 = g_strdup_printf ("%s\t<meta name=\"description\" content=\"%s\" />\n", s1, s3); + g_free (s3); + g_free (s1); + s1 = s2; + } + if ((setup->meta_keywords || parent_items->meta_keywords) && (! override_title_meta)) { + s3 = g_strdup (parent_items->meta_keywords ? parent_items->meta_keywords : setup->meta_keywords); + adjust_tags_parameter (&s3); + s2 = g_strdup_printf ("%s\t<meta name=\"keywords\" content=\"%s\" />\n", s1, s3); + g_free (s3); + g_free (s1); + s1 = s2; + } + replace_table_add_key (replace_table, "CGG_META_TAGS", s1); + g_free (s1); + + + /* 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; + + /* Block placeholders */ + if (strstr (buffer, "<!-- $(BEGIN_IMG_FULLSIZE_LINK) -->")) { + in_img_fullsize_link = TRUE; + continue; + } + if (strstr (buffer, "<!-- $(END_IMG_FULLSIZE_LINK) -->")) { + in_img_fullsize_link = FALSE; + if (! image_fullsize) /* write down the block later in this cycle */ + continue; + } + if (in_img_fullsize_link) { + buf_img_fullsize_link = strncat (buf_img_fullsize_link, buffer, BUFFER_SIZE - strlen (buf_img_fullsize_link) - 2); + continue; + } + + /* Select apropriate line buffer */ + if (strstr (buffer, "<!-- $(END_IMG_FULLSIZE_LINK) -->") && image_fullsize) { + b = strdup (buf_img_fullsize_link); + } else + b = strdup (buffer); + + /* Replace all known tags */ + replace_table_process (&b, replace_table); + + /* Write to file */ + bb = fputs (b, fout); + free (b); + if (! bb) { + fprintf (stderr, "write_html_image: error writing to file \"%s\": %s\n", dst, strerror (errno)); + res = FALSE; + break; + } + } + + fclose (fout); + fclose (fin); + if (title) g_free (title); + if (title_desc) g_free (title_desc); + free (buffer); + free (big_dst); + free (orig_dst); + free (buf_img_fullsize_link); + g_free (imgname); + if (preload_imgname) + g_free (preload_imgname); + free_exif_data (exif); + replace_table_free (replace_table); + 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, + int parent_item_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; + char *imgname; + 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)) { + 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 (idx_file, R_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)); + memset (items, 0, 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; + items->parent_item_index = parent_item_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; + } + if (item->type == INDEX_ITEM_TYPE_PICTURE) { + s1 = g_strconcat (src_tree, "/", item->path, "/", NULL); + s2 = g_strconcat (dst_dir, "/", item->path, "/", NULL); + build_tree (setup, s1, s2, items, i); + 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 (item->type == INDEX_ITEM_TYPE_PICTURE) { + imgname = g_path_get_basename ((item->path == NULL && item->preview) ? item->preview : item->path); + if (setup->verbose) printf ("Writing '%s.html' ...", imgname); + s1 = g_strconcat (setup->real_templates_dir, "/", setup->template_photo, NULL); + s2 = g_strconcat (items->base_dir, "/", (item->path == NULL && item->preview) ? item->preview : item->path, NULL); + s3 = g_strconcat (dst_dir, "/", imgname, ".html", NULL); + res = write_html_image (setup, s1, s2, s3, item, items); + g_free (s1); + g_free (s2); + g_free (s3); + g_free (imgname); + 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/src/generators.h b/src/generators.h new file mode 100644 index 0000000..c87e8a3 --- /dev/null +++ b/src/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/src/items.c b/src/items.c new file mode 100644 index 0000000..986fd6c --- /dev/null +++ b/src/items.c @@ -0,0 +1,320 @@ +/* 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, *s2; + char *node_name; + 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->footnote = xml_file_get_node_value (xml, "/gallery/general/footnote/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); + + index->border_style = xml_file_get_node_attribute (xml, "/gallery/general/border", "style"); + index->meta_author = xml_file_get_node_value (xml, "/gallery/general/meta/author/text()"); + index->meta_description = xml_file_get_node_value (xml, "/gallery/general/meta/description/text()"); + index->meta_keywords = xml_file_get_node_value (xml, "/gallery/general/meta/keywords/text()"); + + index->nofullsize = xml_file_get_node_present (xml, "/gallery/general/nofullsize"); + index->fullsize = xml_file_get_node_present (xml, "/gallery/general/fullsize"); + + /* Section Items */ + count = xml_file_node_get_children_count (xml, "/gallery/items/*"); + #ifdef __DEBUG_ALL__ + printf("parse_album_xml: items count = %d\n", count); + #endif + 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/*[%d]", i + 1); + node_name = xml_file_get_node_name (xml, s); + if (! node_name) continue; + #ifdef __DEBUG_ALL__ + printf("parse_album_xml: item[%d] = '%s'\n", i + 1, node_name); + #endif + + if (strcmp (node_name, "item") == 0) { + item->type = INDEX_ITEM_TYPE_PICTURE; + 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); + item->border_style = xml_file_get_node_attribute (xml, s, "border"); + if (index->type == GALLERY_TYPE_ALBUM) + item->thumbnail = xml_file_get_node_attribute (xml, s, "thumbnail"); + g_free (s); + + s = g_strdup_printf ("/gallery/items/*[%d]/title/text()", i + 1); + item->title = xml_file_get_node_value (xml, s); + g_free (s); + + s = g_strdup_printf ("/gallery/items/*[%d]/title_description/text()", i + 1); + item->title_description = xml_file_get_node_value (xml, s); + g_free (s); + + if (index->type == GALLERY_TYPE_INDEX) { + s = g_strdup_printf ("/gallery/items/*[%d]/thumbnail", i + 1); + item->thumbnail = xml_file_get_node_attribute (xml, s, "src"); + g_free (s); + } + + s = g_strdup_printf ("/gallery/items/*[%d]/nofullsize", i + 1); + item->force_nofullsize = (xml_file_get_node_present (xml, s) || item->path == NULL); + g_free (s); + + s = g_strdup_printf ("/gallery/items/*[%d]/fullsize", i + 1); + item->force_fullsize = xml_file_get_node_present (xml, s); + g_free (s); + + s = g_strdup_printf ("/gallery/items/*[%d]/hidden", i + 1); + item->hidden = (xml_file_get_node_present (xml, s)); + g_free (s); + + /* Retrieve title and description from linked album if not defined here */ + if (index->type == GALLERY_TYPE_INDEX && + item->title == NULL && item->title_description == NULL) { + s = g_strconcat (index->base_dir, "/", item->path, "/index.xml", NULL); + get_album_titles (s, &item->title, &item->title_description, NULL); + g_free (s); + } + + /* Retrieve thumbnail from linked album if not defined here */ + if (index->type == GALLERY_TYPE_INDEX && item->thumbnail == NULL) { + s = g_strconcat (index->base_dir, "/", item->path, "/index.xml", NULL); + s2 = NULL; + get_album_titles (s, NULL, NULL, &s2); + if (s2) { + item->thumbnail = g_strconcat (item->path, "/", s2, NULL); + g_free (s2); + } + g_free (s); + } + + if (item->path || item->preview) + { + g_ptr_array_add (index->items, item); + } + else + { + fprintf (stderr, "%s: No image src specified, skipping!\n", filename); + free (item); + } + } + else + if (strcmp (node_name, "separator") == 0) { + item->type = INDEX_ITEM_TYPE_SEPARATOR; + + s = g_strdup_printf ("/gallery/items/*[%d]/text()", i + 1); + item->title = xml_file_get_node_value (xml, s); + g_free (s); + + g_ptr_array_add (index->items, item); + } + else { + /* Free the item if nobody cares */ + free (item); + } + free (node_name); + } + + 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->footnote) + free (album->footnote); + if (album->base_dir) + free (album->base_dir); + if (album->border_style) + free (album->border_style); + if (album->meta_author) + free (album->meta_author); + if (album->meta_description) + free (album->meta_description); + if (album->meta_keywords) + free (album->meta_keywords); + + 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); + if (item->border_style) + free (item->border_style); + 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; +} + +/* + * get_album_titles: retrieve title, description and first thumbnail from specified album + */ +void +get_album_titles (const char *filename, char **title, char **description, char **thumbnail) +{ + TXMLFile *xml; + + xml = xml_parser_load (filename); + if (xml == NULL) + return; + + if (title) + *title = xml_file_get_node_value (xml, "/gallery/general/title/text()"); + if (description) + *description = xml_file_get_node_value (xml, "/gallery/general/description/text()"); + if (thumbnail) { + *thumbnail = xml_file_get_node_attribute (xml, "/gallery/items/item[1]/thumbnail", "src"); + if (! *thumbnail) + *thumbnail = xml_file_get_node_attribute (xml, "/gallery/items/item[1]", "src"); + if (! *thumbnail) + *thumbnail = xml_file_get_node_attribute (xml, "/gallery/items/item[1]", "preview"); + } + + xml_parser_close (xml); +} diff --git a/src/items.h b/src/items.h new file mode 100644 index 0000000..9fd6822 --- /dev/null +++ b/src/items.h @@ -0,0 +1,96 @@ +/* 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 enum { + INDEX_ITEM_TYPE_PICTURE = 1 << 0, + INDEX_ITEM_TYPE_SEPARATOR = 1 << 1 +} TIndexItemType; + +typedef struct { + TGalleryType type; + char *ID; + char *title; + char *desc; + char *footnote; + GPtrArray *items; + char *base_dir; + void *parent_index; /* pointer to the parent TAlbum structure */ + int parent_item_index; /* item index in the parent album */ + int quality; + unsigned long landscape_width; + unsigned long landscape_height; + unsigned long portrait_width; + unsigned long portrait_height; + char *border_style; + char *meta_author; + char *meta_description; + char *meta_keywords; + gboolean nofullsize; + gboolean fullsize; +} TAlbum; + +typedef struct { + char *path; + char *title; + char *title_description; + char *thumbnail; + char *preview; + int quality; + unsigned long width; + unsigned long height; + gboolean force_nofullsize; + gboolean force_fullsize; + char *border_style; + TIndexItemType type; + gboolean hidden; +} 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); + +/* + * get_album_titles: retrieve title, description and first thumbnail from specified album + */ +void get_album_titles (const char *filename, char **title, char **description, char **thumbnail); + +#endif /* __ITEMS_H__ */ diff --git a/src/jpeg-utils.cpp b/src/jpeg-utils.cpp new file mode 100644 index 0000000..501f7ea --- /dev/null +++ b/src/jpeg-utils.cpp @@ -0,0 +1,429 @@ +/* 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 <exiv2/image.hpp> +#include <exiv2/exif.hpp> + +#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 **exif_data) +{ + TExifData *data; + + data = (TExifData*) malloc (sizeof (TExifData)); + memset (data, 0, sizeof (TExifData)); + *exif_data = data; + + try + { + Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(filename); + g_assert (image.get() != 0); + image->readMetadata(); + + Exiv2::ExifData &exifData = image->exifData(); + if (! exifData.empty()) { + /* EXIF::Aperture */ + try { + float val = exifData["Exif.Photo.FNumber"].toFloat(); + if (val >= 0) + data->aperture = g_strdup_printf ("f/%.1f", val); + } catch (...) { } + + /* EXIF::Camera model */ + try { + const char *val = exifData["Exif.Image.Model"].toString().c_str(); + if (val && strlen(val) > 0) + data->camera_model = strdup (val); + } catch (...) { } + + /* EXIF::DateTime */ + try { + const char *val = NULL; + try { + val = exifData["Exif.Photo.DateTimeOriginal"].toString().c_str(); + } catch (...) { } + if ((! val) || (strlen(val) == 0)) + try { + val = exifData["Exif.Image.DateTime"].toString().c_str(); + } catch (...) { } + + if (val && strlen(val) > 0) { + static struct tm tt; + static char conv[1024]; + + if (sscanf (val, "%d:%d:%d %d:%d:%d", &tt.tm_year, &tt.tm_mon, &tt.tm_mday, &tt.tm_hour, &tt.tm_min, &tt.tm_sec) == 6) + { + tt.tm_year -= 1900; + tt.tm_mon--; + if (strftime (&conv[0], sizeof(conv), "%c", &tt)) + data->datetime = strdup (&conv[0]); + } + } + } catch (...) { } + + /* EXIF::Shutter speed */ + try { + float val = exifData["Exif.Photo.ExposureTime"].toFloat(); + if (val > 0) { + if (val < 0.5) + data->exposure = g_strdup_printf ("1/%.0f s", 1/val); + else + data->exposure = g_strdup_printf ("%.1f s", val); + } + } catch (...) { } + + /* EXIF::Flash */ + try { + long int val = exifData["Exif.Photo.Flash"].toLong(); + if ((val > 0) && ((val & 1) == 1)) + data->flash = strdup ((char *) "Flash fired"); + else + data->flash = strdup ((char *) "--"); + } catch (...) { } + + /* EXIF::Focal length */ + try { + float val = exifData["Exif.Photo.FocalLength"].toFloat(); + if (val >= 0) + data->focal_length = g_strdup_printf ("%.0f mm", val); + } catch (...) { } + + /* EXIF::ISO */ + try { + long int val = exifData["Exif.Photo.ISOSpeedRatings"].toLong(); + if (val > 0) + data->iso = g_strdup_printf ("%ld", val); + } catch (...) { } + + /* EXIF::Software */ + try { + data->exif_software = strdup (exifData["Exif.Image.Software"].toString().c_str()); + } catch (...) { } + + /* EXIF::Image description */ + try { + data->exif_imgdesc = strdup (exifData["Exif.Image.ImageDescription"].toString().c_str()); + } catch (...) { } + + /* EXIF::Artist */ + try { + data->exif_artist = strdup (exifData["Exif.Image.Artist"].toString().c_str()); + } catch (...) { } + + /* EXIF::Copyright */ + try { + data->exif_copyright = strdup (exifData["Exif.Image.Copyright"].toString().c_str()); + } catch (...) { } + + /* EXIF::User comment */ + try { + data->exif_usercomment = strdup (exifData["Exif.Photo.UserComment"].toString().c_str()); + } catch (...) { } + } + + Exiv2::IptcData &iptcData = image->iptcData(); + if (! iptcData.empty()) { + /* IPTC::Object name */ + try { + data->iptc_objectname = strdup (iptcData["Iptc.Application2.ObjectName"].toString().c_str()); + } catch (...) { } + + /* IPTC::Copyright */ + try { + data->iptc_copyright = strdup (iptcData["Iptc.Application2.Copyright"].toString().c_str()); + } catch (...) { } + + /* IPTC::Credit */ + try { + data->iptc_credit = strdup (iptcData["Iptc.Application2.Credit"].toString().c_str()); + } catch (...) { } + + /* IPTC::Caption */ + try { + data->iptc_caption = strdup (iptcData["Iptc.Application2.Caption"].toString().c_str()); + } catch (...) { } + + /* IPTC::Author */ + try { + data->iptc_author = strdup (iptcData["Iptc.Application2.Byline"].toString().c_str()); + } catch (...) { } + } + + /* JPEG::Comment */ + try { + data->jpeg_comment = strdup (image->comment().c_str()); + } catch (...) { } + } + catch (Exiv2::AnyError& e) + { + fprintf (stderr, "get_exif: Caught Exiv2 exception: '%s'\n", e.what()); + return -1; + } + +#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.Image.Software = '%s'\n", data->exif_software); + printf("Exif.Image.ImageDescription = '%s'\n", data->exif_imgdesc); + printf("Exif.Image.Artist = '%s'\n", data->exif_artist); + printf("Exif.Image.Copyright = '%s'\n", data->exif_copyright); + printf("Exif.Photo.UserComment = '%s'\n", data->exif_usercomment); + printf("Iptc.Application2.ObjectName = '%s'\n", data->iptc_objectname); + printf("Iptc.Application2.Copyright = '%s'\n", data->iptc_copyright); + printf("Iptc.Application2.Credit = '%s'\n", data->iptc_credit); + printf("Iptc.Application2.Caption = '%s'\n", data->iptc_caption); + printf("Iptc.Application2.Byline = '%s'\n", data->iptc_author); + printf("JPEG comment = '%s'\n", data->jpeg_comment); +#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); + + if (data->exif_software) + free (data->exif_software); + if (data->exif_imgdesc) + free (data->exif_imgdesc); + if (data->exif_artist) + free (data->exif_artist); + if (data->exif_copyright) + free (data->exif_copyright); + if (data->exif_usercomment) + free (data->exif_usercomment); + + if (data->iptc_objectname) + free (data->iptc_objectname); + if (data->iptc_copyright) + free (data->iptc_copyright); + if (data->iptc_credit) + free (data->iptc_credit); + if (data->iptc_caption) + free (data->iptc_caption); + if (data->iptc_author) + free (data->iptc_author); + + if (data->jpeg_comment) + free (data->jpeg_comment); + + 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, + gboolean thumbnail) +{ + #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); + if (thumbnail) + MagickThumbnailImage (magick_wand, size_x, size_y); + else + 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 = (unsigned long) (max_width / real_ratio); + *width = max_width; + } + else + { + *width = (unsigned long) (max_height * real_ratio); + *height = max_height; + } +} + + +/* + * modify_exif: - strip thumbnail stored in EXIF table + * - add copyright to Exif::Image::Copyright and Iptc::Application2::Copyright + */ +void +modify_exif (const char *filename, gboolean strip_thumbnail, const char *add_copyright) +{ + bool was_modified = false; + + if ((! strip_thumbnail) && (add_copyright == NULL)) + return; + + try + { + Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(filename); + g_assert (image.get() != 0); + + image->readMetadata(); + Exiv2::ExifData &exifData = image->exifData(); + if (add_copyright) { + exifData["Exif.Image.Copyright"] = add_copyright; + image->iptcData()["Iptc.Application2.Copyright"] = add_copyright; + was_modified = true; + } + + if (strip_thumbnail && (! exifData.empty())) { +#ifdef HAVE_EXIFTHUMB + Exiv2::ExifThumb exifThumb(image->exifData()); + std::string thumbExt = exifThumb.extension(); +#else + std::string thumbExt = exifData.thumbnailExtension(); +#endif + + if (! thumbExt.empty()) { +#ifdef HAVE_EXIFTHUMB + exifThumb.erase(); +#else + exifData.eraseThumbnail(); +#endif + was_modified = true; + } + } + + if (was_modified) + image->writeMetadata(); + } + catch (Exiv2::AnyError& e) + { + fprintf (stderr, "modify_exif: Caught Exiv2 exception: '%s'\n", e.what()); + } +} diff --git a/src/jpeg-utils.h b/src/jpeg-utils.h new file mode 100644 index 0000000..9b8919f --- /dev/null +++ b/src/jpeg-utils.h @@ -0,0 +1,94 @@ +/* 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. + */ + +#ifdef __cplusplus + extern "C" { +#endif + +#include <glib.h> + + +/* TODO: we want to have numerical values here at some point in the future */ +typedef struct { + char *datetime; + char *camera_model; + char *iso; + char *focal_length; + char *focal_length_35mm; + char *aperture; + char *exposure; + char *flash; + + char *exif_software; + char *exif_imgdesc; + char *exif_artist; + char *exif_copyright; + char *exif_usercomment; + + char *iptc_objectname; + char *iptc_copyright; + char *iptc_credit; + char *iptc_caption; + char *iptc_author; + + char *jpeg_comment; +} TExifData; + + +/* + * get_exif: retrieve EXIF info from a JPEG image + */ +int get_exif (const char *filename, TExifData **exif_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 + * - setting thumbnail flag will remove all profiles and optimize for size + */ +gboolean resize_image (const char *src, const char *dst, + int size_x, int size_y, + int quality, + gboolean thumbnail); + +/* + * 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); + +/* + * modify_exif: - strip thumbnail stored in EXIF table + * - add copyright to Exif::Image::Copyright and Iptc::Application2::Copyright + */ +void modify_exif (const char *filename, gboolean strip_thumbnail, const char *add_copyright); + + +#ifdef __cplusplus + } +#endif diff --git a/src/setup.c b/src/setup.c new file mode 100644 index 0000000..aafd218 --- /dev/null +++ b/src/setup.c @@ -0,0 +1,268 @@ +/* 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 "cgg-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; + char *s; + + 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()"); + setup->border_style = xml_file_get_node_attribute (xml, "/gallery_setup/images/border", "style"); + setup->meta_author = xml_file_get_node_value (xml, "/gallery_setup/meta/author/text()"); + setup->meta_description = xml_file_get_node_value (xml, "/gallery_setup/meta/description/text()"); + setup->meta_keywords = xml_file_get_node_value (xml, "/gallery_setup/meta/keywords/text()"); + + s = xml_file_get_node_attribute (xml, "/gallery_setup/images/preload", "value"); + setup->preload = s ? strcasecmp (s, "yes") == 0 : TRUE; /* default to TRUE */ + if (s) g_free (s); + s = xml_file_get_node_attribute (xml, "/gallery_setup/images/use_iptc_exif", "value"); + setup->use_iptc_exif = s ? strcasecmp (s, "yes") == 0 : FALSE; /* default to FALSE */ + if (s) g_free (s); + s = xml_file_get_node_attribute (xml, "/gallery_setup/images/erase_embed_thumbnail", "value"); + setup->erase_exif_thumbnail = s ? strcasecmp (s, "yes") == 0 : FALSE; /* default to FALSE */ + if (s) g_free (s); + s = xml_file_get_node_attribute (xml, "/gallery_setup/meta/use_title_as_meta", "value"); + setup->use_title_as_meta = s ? strcasecmp (s, "yes") == 0 : TRUE; /* default to TRUE */ + if (s) g_free (s); + + setup->site_title = xml_file_get_node_attribute (xml, "/gallery_setup/meta/site", "title"); + setup->add_copyright = xml_file_get_node_value (xml, "/gallery_setup/meta/add_copyright/text()"); + s = xml_file_get_node_attribute (xml, "/gallery_setup/navigation/use_inpage_links", "value"); + setup->use_inpage_links = s ? strcasecmp (s, "yes") == 0 : TRUE; /* default to TRUE */ + if (s) g_free (s); + + setup->nofullsize = xml_file_get_node_present (xml, "/gallery_setup/images/nofullsize"); + + 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); + printf("setup: border_style = '%s'\n", setup->border_style); + printf("setup: meta_author = '%s'\n", setup->meta_author); + printf("setup: meta_description = '%s'\n", setup->meta_description); + printf("setup: meta_keywords = '%s'\n", setup->meta_keywords); + printf("setup: preload = %d\n", setup->preload); + printf("setup: use_iptc_exif = %d\n", setup->use_iptc_exif); + printf("setup: erase_exif_thumbnail = %d\n", setup->erase_exif_thumbnail); + #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); + if (setup->meta_author) + free (setup->meta_author); + if (setup->border_style) + free (setup->border_style); + if (setup->site_title) + free (setup->site_title); + if (setup->add_copyright) + free (setup->add_copyright); + free (setup); + setup = NULL; + } +} diff --git a/src/setup.h b/src/setup.h new file mode 100644 index 0000000..eea4b17 --- /dev/null +++ b/src/setup.h @@ -0,0 +1,92 @@ +/* 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; + char *meta_author; + char *meta_description; + char *meta_keywords; + gboolean use_title_as_meta; + + 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; + + char *border_style; + gboolean nofullsize; + + gboolean preload; + gboolean use_iptc_exif; + gboolean erase_exif_thumbnail; + + char *site_title; + char *add_copyright; + gboolean use_inpage_links; +} 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/src/xml-parser.c b/src/xml-parser.c new file mode 100644 index 0000000..eb6c86f --- /dev/null +++ b/src/xml-parser.c @@ -0,0 +1,261 @@ +/* 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_name: retrieve name of the XPath node + */ +char * +xml_file_get_node_name (TXMLFile *file, const char *x_path) +{ + xmlXPathObjectPtr xpathObj; + xmlNodePtr cur; + 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]; + if (cur->name) + attrv = strdup ((const char *) cur->name); + } + + xmlXPathFreeObject (xpathObj); + return attrv; +} + + +/* + * xml_file_get_node_value: retrieve string value from XPath node + * - multiple matched nodes will be concatenated into one string + * - otherwise please use [0], [1] etc. quantificators + */ +char * +xml_file_get_node_value (TXMLFile *file, const char *x_path) +{ + xmlXPathObjectPtr xpathObj; + xmlNodePtr cur; + char *val, *valx; + int i; + + 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)) { + #ifdef __DEBUG_ALL__ + printf("Result (%d nodes):\n", xpathObj->nodesetval->nodeNr); + #endif + + for (i = 0; i < xpathObj->nodesetval->nodeNr; i++) + { + cur = xpathObj->nodesetval->nodeTab[i]; + #ifdef __DEBUG_ALL__ + printf(" XPATH matched: element node \"%s[%d]\", value = '%s'\n", cur->name, i, cur->content); + #endif + + if (cur->content) + { + if (val == NULL) + { + val = g_strdup ((char *) cur->content); + } + else + { + valx = g_strconcat (val, (char *) cur->content, NULL); + g_free (val); + val = valx; + } + } + } + } + + 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/src/xml-parser.h b/src/xml-parser.h new file mode 100644 index 0000000..02be628 --- /dev/null +++ b/src/xml-parser.h @@ -0,0 +1,67 @@ +/* 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_name: retrieve name of the XPath node + */ +char * xml_file_get_node_name (TXMLFile *file, const char *x_path); + +/* + * xml_file_get_node_value: retrieve string value from XPath node + * - multiple matched nodes will be concatenated into one string + * - otherwise please use [0], [1] etc. quantificators + */ +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); |
