[yelp] * src/yelp-info-parser.c:



commit d0cd51f252fd08347a381b4f53bcd49115299b26
Author: Jan Nieuwenhuizen <janneke gnu org>
Date:   Thu Apr 30 20:15:56 2009 +0100

    * src/yelp-info-parser.c:
    * stylesheets/info2html:
    Add support for displaying images in info files
    Bug #568209 - patch by Jan Nieuwenhuizen
---
 ChangeLog                 |    8 +++
 src/yelp-info-parser.c    |  154 ++++++++++++++++++++++++++++++++++++++-------
 stylesheets/info2html.xsl |   10 +++
 3 files changed, 148 insertions(+), 24 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 501af5b..b68249c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2009-04-30  Don Scorgie  <dscorgie src gnome org>
 
+	* src/yelp-info-parser.c: 
+	* stylesheets/info2html: 
+	Add support for displaying images in info
+	files
+	Bug #568209 - patch by Jan Nieuwenhuizen
+
+2009-04-30  Don Scorgie  <dscorgie src gnome org>
+
 	* src/yelp-window.c: 
 	* src/yelp-utils.c:
 	Fix a crash when requesting bad URIs from the
diff --git a/src/yelp-info-parser.c b/src/yelp-info-parser.c
index 2d06798..a1b3e55 100644
--- a/src/yelp-info-parser.c
+++ b/src/yelp-info-parser.c
@@ -59,6 +59,124 @@ void                  fix_tag_table                      (gchar *offset,
 void   		      info_process_text_notes            (xmlNodePtr *node, 
 							  gchar *content,
 							  GtkTreeStore *tree);
+
+
+static GHashTable *
+info_image_get_attributes (gchar const* string)
+{
+  GMatchInfo *match_info;
+  GRegex *regex;
+  GHashTable *h;
+
+  h = 0;
+  regex = g_regex_new ("([^\\s][^\\s=]+)=(?:([^\\s \"]+)|(?:\"((?:[^\\\"]|\\\\[\\\\\"])*)\"))", 0, 0, NULL);
+  g_regex_match (regex, string, 0, &match_info);
+  while (g_match_info_matches (match_info))
+    {
+      gchar *key;
+      gchar *value;
+
+      if (!h)
+	h = g_hash_table_new (g_str_hash, g_str_equal);
+      key = g_match_info_fetch (match_info, 1);
+      value = g_match_info_fetch (match_info, 2);
+      if (!*value)
+	value = g_match_info_fetch (match_info, 3);
+      g_hash_table_insert (h, key, value);
+      //fprintf (stderr, "Found: %s -> %s\n", key, value);
+      g_match_info_next (match_info, NULL);
+    }
+  g_match_info_free (match_info);
+  g_regex_unref (regex);
+
+  return h;
+}
+
+/*
+  info elements look like \0\b[<TAGNAME>\0\b] and take attribute=value
+  pairs, i.e. for image: \0\b[image src="foo.png" \0\b]
+*/
+#define INFO_TAG_0 "\0"
+#define INFO_TAG_1 "\b"
+#define INFO_TAG_OPEN_2 INFO_TAG_1 "["
+#define INFO_TAG_CLOSE_2 INFO_TAG_1 "]"
+#define INFO_TAG_OPEN_2_RE INFO_TAG_1 "[[]"
+#define INFO_TAG_CLOSE_2_RE INFO_TAG_1 "[]]"
+#define INFO_TAG_OPEN INFO_TAG_0 INFO_TAG_1 INFO_TAG_OPEN_2
+#define INFO_TAG_CLOSE INFO_TAG_0 INFO_TAG_1 INFO_TAG_CLOSE_2
+#define INFO_TAG_OPEN_RE INFO_TAG_0 INFO_TAG_1 INFO_TAG_OPEN_2_RE
+#define INFO_TAG_CLOSE_RE INFO_TAG_0 INFO_TAG_1 INFO_TAG_CLOSE_2_RE
+/* C/glib * cannot really handle \0 in strings, convert to '@' */
+#define INFO_C_TAG_0 "@"
+#define INFO_C_TAG_OPEN INFO_C_TAG_0 INFO_TAG_OPEN_2
+#define INFO_C_TAG_CLOSE INFO_C_TAG_0 INFO_TAG_CLOSE_2
+#define INFO_C_TAG_OPEN_RE INFO_C_TAG_0 INFO_TAG_OPEN_2_RE
+#define INFO_C_TAG_CLOSE_RE INFO_C_TAG_0 INFO_TAG_CLOSE_2_RE
+#define INFO_C_IMAGE_TAG_OPEN INFO_C_TAG_OPEN "image"
+#define INFO_C_IMAGE_TAG_OPEN_RE INFO_C_TAG_OPEN_RE "image"
+
+static xmlNodePtr
+info_insert_image (xmlNodePtr parent, GMatchInfo *match_info)
+{
+  GHashTable *h = info_image_get_attributes (g_match_info_fetch (match_info, 1));
+  gchar *source;
+  if (h)
+    source = (gchar*)g_hash_table_lookup (h, "src");
+
+  if (!h || !source || !*source)
+    return xmlNewTextChild (parent, NULL, BAD_CAST "para1", BAD_CAST "[broken image]");
+
+  gchar *title = (gchar*)g_hash_table_lookup (h, "title");
+  gchar *text = (gchar*)g_hash_table_lookup (h, "text");
+  gchar *alt = (gchar*)g_hash_table_lookup (h, "alt");
+  g_hash_table_destroy (h);
+  xmlNodePtr img = xmlNewChild (parent, NULL, BAD_CAST "img", NULL);
+  xmlNewProp (img, BAD_CAST "src", BAD_CAST source);
+  xmlNewProp (img, BAD_CAST "title", BAD_CAST (title ? title : ""));
+  xmlNewProp (img, BAD_CAST "text", BAD_CAST (text ? text : ""));
+  xmlNewProp (img, BAD_CAST "alt", BAD_CAST (alt ? alt : ""));
+  g_free (source);
+  g_free (title);
+  g_free (alt);
+  return parent;
+}
+
+/*
+  Convert body text CONTENT to xml nodes, processing info image tags
+  when found.  IWBN add a regex match for *Note: here and call the
+  *Note ==> <a href> logic of info_process_text_notes from here.
+ */
+static xmlNodePtr
+info_body_text (xmlNodePtr parent, xmlNsPtr ns, gchar const *name, gchar const *content)
+{
+  if (!strstr (content, INFO_C_IMAGE_TAG_OPEN))
+    return xmlNewTextChild (parent, ns, BAD_CAST name, BAD_CAST content);
+
+  gint content_len = strlen (content);
+  gint pos = 0;
+  GRegex *regex = g_regex_new ("(" INFO_C_IMAGE_TAG_OPEN_RE "((?:[^" INFO_TAG_1 "]|[^" INFO_C_TAG_0 "]+" INFO_TAG_1 ")*)" INFO_C_TAG_CLOSE_RE ")", 0, 0, NULL);
+  GMatchInfo *match_info;
+  g_regex_match (regex, content, 0, &match_info);
+  while (g_match_info_matches (match_info))
+    {
+      gint image_start;
+      gint image_end;
+      gboolean image_found = g_match_info_fetch_pos (match_info, 0,
+						     &image_start, &image_end);
+      gchar *before = g_strndup (&content[pos], image_start - pos);
+      pos = image_end + 1;
+      xmlNewTextChild (parent, NULL, BAD_CAST "para1", BAD_CAST (before));
+      g_free (before);
+      if (image_found)
+	info_insert_image (parent, match_info);
+      g_match_info_next (match_info, NULL);
+    }
+  gchar *after = g_strndup (&content[pos], content_len - pos);
+  xmlNewTextChild (parent, NULL, BAD_CAST "para1", BAD_CAST (after));
+  g_free (after);
+  return 0;
+}
+
 /* Part 1: Parse File Into Tree Store */
 
 enum
@@ -116,14 +234,10 @@ static char
 	g_io_channel_shutdown (channel, FALSE, NULL);
 	g_io_channel_unref (channel);
 
+	/* C/glib * cannot really handle \0 in strings, convert. */
 	for (i = 0; i < (len - 1); i++)
-	{
-		if (str[i] == '\0' && str[i+1] == '\b')
-		{
-		  debug_print (DB_WARN, "=> got a NULL, replacing\n");
-		  str[i] = ' '; str[i+1] = ' ';
-		}
-	}
+	  if (str[i] == INFO_TAG_OPEN[0] && str[i+1] == INFO_TAG_OPEN[1])
+	    str[i] = INFO_C_TAG_OPEN[0];
 
 	return str;
 }
@@ -731,9 +845,7 @@ parse_tree_level (GtkTreeStore *tree, xmlNodePtr *node, GtkTreeIter iter)
 					     BAD_CAST "Section",
 					     NULL);
 		  if (!notes)
-		    xmlNewTextChild (newnode, NULL,
-				     BAD_CAST "para",
-				     BAD_CAST page_content);
+		    info_body_text (newnode, NULL, "para", page_content);
 		  
 		  else {
 		    /* Handle notes here */
@@ -889,8 +1001,7 @@ yelp_info_parse_menu (GtkTreeStore *tree, xmlNodePtr *node,
 
   tmp = g_strconcat (split[0], "\n* Menu:", NULL);
   if (!notes)
-    xmlNewTextChild (newnode, NULL,
-		     BAD_CAST "para", BAD_CAST tmp);
+    info_body_text (newnode, NULL, "para", tmp);
   else {
     info_process_text_notes (&newnode, tmp, tree);
   }
@@ -1004,8 +1115,7 @@ info_process_text_notes (xmlNodePtr *node, gchar *content, GtkTreeStore *tree)
 	 * start, so we can just add it and forget about it.
 	 */
 	first = FALSE;
-	xmlNewTextChild (holder, NULL, BAD_CAST "para1",
-			 BAD_CAST (*current_real));
+	info_body_text (holder, NULL, "para1", (*current_real));
 	continue;
       }
       /* If we got to here, we now gotta parse the note reference */
@@ -1014,14 +1124,13 @@ info_process_text_notes (xmlNodePtr *node, gchar *content, GtkTreeStore *tree)
 	/* Special type of note that isn't really a note, but pretends
 	 * it is
 	 */
-	xmlNewTextChild (holder, NULL, BAD_CAST "para1",
-			 BAD_CAST g_strconcat ("*Note", *current_real, NULL));
+	info_body_text (holder, NULL, "para1",
+			g_strconcat ("*Note", *current_real, NULL));
 	continue;
       }
       append = strchr (*current_real, ':');
       if (!append) {
-	xmlNewTextChild (holder, NULL, BAD_CAST "para1",
-			 BAD_CAST *current_real);
+	info_body_text (holder, NULL, "para1", *current_real);
 	continue;
       }
       append++;
@@ -1036,8 +1145,7 @@ info_process_text_notes (xmlNodePtr *node, gchar *content, GtkTreeStore *tree)
       }
       alt_append1 = strchr (alt_append1, ',');
       if (!append && !alt_append && !alt_append1) {
-	xmlNewTextChild (holder, NULL, BAD_CAST "para1",
-			 BAD_CAST *current_real);
+	info_body_text (holder, NULL, "para1", *current_real);
 	continue;
       }
       if (!append || alt_append || alt_append1) {
@@ -1173,16 +1281,14 @@ info_process_text_notes (xmlNodePtr *node, gchar *content, GtkTreeStore *tree)
 	ref1 = xmlNewTextChild (holder, NULL, BAD_CAST "a",
 				BAD_CAST link_text);
 	if (*(ulink+1) != NULL)
-	  xmlNewTextChild (holder, NULL, BAD_CAST "para",
-			   BAD_CAST "");
+	  info_body_text (holder, NULL, "para", "");
 
 	g_free (link_text);
 	xmlNewProp (ref1, BAD_CAST "href", BAD_CAST href);
       }
       g_strfreev (urls);
       /* Finally, we can add the text as required */
-      xmlNewTextChild (holder, NULL, BAD_CAST "para1",
-		       BAD_CAST append);
+      info_body_text (holder, NULL, "para1", append);
       g_free (url);
       g_free (href);
     }
diff --git a/stylesheets/info2html.xsl b/stylesheets/info2html.xsl
index 0916420..d8340fa 100644
--- a/stylesheets/info2html.xsl
+++ b/stylesheets/info2html.xsl
@@ -76,6 +76,15 @@
   </xsl:element>
 </xsl:template>
 
+<xsl:template match="img">
+  <xsl:element name="a">
+    <xsl:attribute name="href"> <xsl:value-of select="@src"/></xsl:attribute>
+      <xsl:element name="img">
+        <xsl:attribute name="src"> <xsl:value-of select="@src"/></xsl:attribute>
+      </xsl:element>
+  </xsl:element>
+</xsl:template>
+
 <xsl:template match="menuholder">
   <xsl:apply-templates select="node()[not(self::menuholder)]"/>
 </xsl:template>
@@ -112,6 +121,7 @@ dl dd {
 ol { margin-left: 2em; }
 ul { margin-left: 2em; }
 ul li { margin-right: 1em; }
+img { border: none; }
 </xsl:text></xsl:template>
 
 </xsl:stylesheet>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]