]> git.street.me.uk Git - andy/viking.git/commitdiff
SF Bugs#138: Fix handling of <link> tags in GPX files.
authorRob Norris <rw_norris@hotmail.com>
Sat, 20 May 2017 14:20:47 +0000 (15:20 +0100)
committerRob Norris <rw_norris@hotmail.com>
Sat, 20 May 2017 14:22:21 +0000 (15:22 +0100)
<link> tags (only in GPX1.1) should be of the form <link href="URI"></link>
Previously Viking simply put the URI inside the tag without the 'href'.

Use the GtkHTML functions as necessary.

Note in order to support relative URI paths,
 the directory path is passed around for both reading and writing files.

src/babel.c
src/file.c
src/gpspoint.c
src/gpspoint.h
src/gpx.c
src/gpx.h
src/viklayer.h
src/vikutils.c
src/vikutils.h

index ea86ae4f8a1c04ebc3d7d3d20782bc76c976412e..48604b5f2d4f5dd7eedf0ce38e402924772750ec 100644 (file)
@@ -248,9 +248,10 @@ static gboolean babel_general_convert_from( VikTrwLayer *vt, BabelStatusFunc cb,
 
     f = g_fopen(name_dst, "r");
     if (f) {
-      ret = a_gpx_read_file ( vt, f );
+      gchar *dirpath = g_path_get_dirname ( name_dst );
+      ret = a_gpx_read_file ( vt, f, dirpath );
+      g_free ( dirpath );
       fclose(f);
-      f = NULL;
     }
   }
     
@@ -424,9 +425,10 @@ gboolean a_babel_convert_from_url_filter ( VikTrwLayer *vt, const char *url, con
         g_debug("%s: directly read GPX file %s", __FUNCTION__, name_src);
         FILE *f = g_fopen(name_src, "r");
         if (f) {
-          ret = a_gpx_read_file ( vt, f );
+          gchar *dirpath = g_path_get_dirname ( name_src );
+          ret = a_gpx_read_file ( vt, f, dirpath );
+          g_free ( dirpath );
           fclose(f);
-          f = NULL;
         }
       }
     }
index b7bff204bbdff45ee323194a3a0a15880013e560..6334676b87aab3682a2a3cc93eb017ddfd5407a3 100644 (file)
@@ -129,7 +129,7 @@ void file_write_layer_param ( FILE *f, const gchar *name, VikLayerParamType type
       }
 }
 
-static void write_layer_params_and_data ( VikLayer *l, FILE *f )
+static void write_layer_params_and_data ( VikLayer *l, FILE *f, const gchar *dirpath )
 {
   VikLayerParam *params = vik_layer_get_interface(l->type)->params;
   VikLayerFuncGetParam get_param = vik_layer_get_interface(l->type)->get_param;
@@ -151,7 +151,7 @@ static void write_layer_params_and_data ( VikLayer *l, FILE *f )
   if ( vik_layer_get_interface(l->type)->write_file_data )
   {
     fprintf ( f, "\n\n~LayerData\n" );
-    vik_layer_get_interface(l->type)->write_file_data ( l, f );
+    vik_layer_get_interface(l->type)->write_file_data ( l, f, dirpath );
     fprintf ( f, "~EndLayerData\n" );
   }
   /* foreach param:
@@ -160,7 +160,7 @@ static void write_layer_params_and_data ( VikLayer *l, FILE *f )
   */
 }
 
-static void file_write ( VikAggregateLayer *top, FILE *f, gpointer vp )
+static void file_write ( VikAggregateLayer *top, FILE *f, gpointer vp, const gchar *dirpath )
 {
   Stack *stack = NULL;
   VikLayer *current_layer;
@@ -202,7 +202,7 @@ static void file_write ( VikAggregateLayer *top, FILE *f, gpointer vp )
   {
     current_layer = VIK_LAYER(((GList *)stack->data)->data);
     fprintf ( f, "\n~Layer %s\n", vik_layer_get_interface(current_layer->type)->fixed_layer_name );
-    write_layer_params_and_data ( current_layer, f );
+    write_layer_params_and_data ( current_layer, f, dirpath );
     if ( current_layer->type == VIK_LAYER_AGGREGATE && !vik_aggregate_layer_is_empty(VIK_AGGREGATE_LAYER(current_layer)) )
     {
       push(&stack);
@@ -679,7 +679,12 @@ VikLoadType_t a_file_load ( VikAggregateLayer *top, VikViewport *vp, VikTrwLayer
 
   VikLoadType_t load_answer = LOAD_TYPE_OTHER_SUCCESS;
 
-  gchar *dirpath = g_path_get_dirname ( filename );
+  gchar *absolute = file_realpath_dup ( filename );
+  gchar *dirpath = NULL;
+  if ( absolute )
+    dirpath = g_path_get_dirname ( absolute );
+  g_free ( absolute );
+
   // Attempt loading the primary file type first - our internal .vik file:
   if ( check_magic ( f, VIK_MAGIC, VIK_MAGIC_LEN ) )
   {
@@ -716,7 +721,7 @@ VikLoadType_t a_file_load ( VikAggregateLayer *top, VikViewport *vp, VikTrwLayer
     // NB use a extension check first, as a GPX file header may have a Byte Order Mark (BOM) in it
     //    - which currently confuses our check_magic function
     else if ( a_file_check_ext ( filename, ".gpx" ) || check_magic ( f, GPX_MAGIC, GPX_MAGIC_LEN ) ) {
-      if ( ! ( success = a_gpx_read_file ( vtl, f ) ) ) {
+      if ( ! ( success = a_gpx_read_file ( vtl, f, dirpath ) ) ) {
         load_answer = LOAD_TYPE_GPX_FAILURE;
       }
     }
@@ -769,10 +774,10 @@ gboolean a_file_save ( VikAggregateLayer *top, gpointer vp, const gchar *filenam
     if ( g_chdir ( dir ) ) {
       g_warning ( "Could not change directory to %s", dir );
     }
-    g_free (dir);
   }
 
-  file_write ( top, f, vp );
+  file_write ( top, f, vp, dir );
+  g_free (dir);
 
   // Restore previous working directory
   if ( cwd ) {
@@ -825,6 +830,7 @@ gboolean a_file_export ( VikTrwLayer *vtl, const gchar *filename, VikFileType_t
   if ( f )
   {
     gboolean result = TRUE;
+    gchar *dirpath = g_path_get_dirname ( filename );
 
     if ( trk ) {
       switch ( file_type ) {
@@ -842,10 +848,10 @@ gboolean a_file_export ( VikTrwLayer *vtl, const gchar *filename, VikFileType_t
           a_gpsmapper_write_file ( vtl, f );
           break;
         case FILE_TYPE_GPX:
-          a_gpx_write_file ( vtl, f, &options );
+          a_gpx_write_file ( vtl, f, &options, dirpath );
           break;
         case FILE_TYPE_GPSPOINT:
-          a_gpspoint_write_file ( vtl, f );
+          a_gpspoint_write_file ( vtl, f, dirpath );
           break;
         case FILE_TYPE_GEOJSON:
           result = a_geojson_write_file ( vtl, f );
@@ -869,6 +875,7 @@ gboolean a_file_export ( VikTrwLayer *vtl, const gchar *filename, VikFileType_t
           g_critical("Houston, we've had a problem. file_type=%d", file_type);
       }
     }
+    g_free ( dirpath );
     fclose ( f );
     return result;
   }
index d16049d4fede2a2945cd8f1b01f40f709da87c14..318436f24be159e3ce3cc2c506efd6369b7891f2 100644 (file)
@@ -28,6 +28,7 @@
 #endif
 
 #include "viking.h"
+#include "vikutils.h"
 
 #include <ctype.h>
 #ifdef HAVE_STRING_H
@@ -44,7 +45,6 @@ typedef struct {
 
 static void a_gpspoint_write_track ( const gpointer id, const VikTrack *t, FILE *f );
 static void a_gpspoint_write_trackpoint ( VikTrackpoint *tp, TP_write_info_type *write_info );
-static void a_gpspoint_write_waypoint ( const gpointer id, const VikWaypoint *wp, FILE *f );
 
 /* outline for file gpspoint.c
 
@@ -254,17 +254,9 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f, const gchar *dirpath )
         vik_waypoint_set_type ( wp, line_xtype );
 
       if ( line_image ) {
-        // Ensure the filename is absolute
-        if ( g_path_is_absolute ( line_image ) )
-          vik_waypoint_set_image ( wp, line_image );
-        else {
-          // Otherwise create the absolute filename from the directory of the .vik file & and the relative filename
-          gchar *full = g_strconcat(dirpath, G_DIR_SEPARATOR_S, line_image, NULL);
-          gchar *absolute = file_realpath_dup ( full ); // resolved into the canonical name
-          vik_waypoint_set_image ( wp, absolute );
-          g_free ( absolute );
-          g_free ( full );
-        }
+        gchar *fn = util_make_absolute_filename ( line_image, dirpath );
+        vik_waypoint_set_image ( wp, fn ? fn : line_image );
+        g_free ( fn );
       }
 
       if ( line_symbol )
@@ -565,7 +557,12 @@ static void gpspoint_process_key_and_value ( const gchar *key, guint key_len, co
   }
 }
 
-static void a_gpspoint_write_waypoint ( const gpointer id, const VikWaypoint *wp, FILE *f )
+typedef struct {
+  FILE *file;
+  const gchar *dirpath;
+} WritingContext;
+
+static void a_gpspoint_write_waypoint ( const gpointer id, const VikWaypoint *wp, WritingContext *wc )
 {
   struct LatLon ll;
   gchar s_lat[COORDS_STR_BUFFER_SIZE];
@@ -575,6 +572,10 @@ static void a_gpspoint_write_waypoint ( const gpointer id, const VikWaypoint *wp
     return;
   if ( !(wp->name) )
     return;
+  if ( !wc )
+    return;
+
+  FILE *f = wc->file;
 
   vik_coord_to_latlon ( &(wp->coord), &ll );
   a_coords_dtostr_buffer ( ll.lat, s_lat );
@@ -617,22 +618,19 @@ static void a_gpspoint_write_waypoint ( const gpointer id, const VikWaypoint *wp
   if ( wp->image )
   {
     gchar *tmp_image = NULL;
-    gchar *cwd = NULL;
     if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
-      cwd = g_get_current_dir();
-      if ( cwd )
-        tmp_image = g_strdup ( file_GetRelativeFilename ( cwd, wp->image ) );
+      if ( wc->dirpath )
+        tmp_image = g_strdup ( file_GetRelativeFilename ( (gchar*)wc->dirpath, wp->image ) );
     }
 
-    // if cwd not available - use image filename as is
+    // if tmp_image not available - use image filename as is
     // this should be an absolute path as set in thumbnails
-    if ( !cwd )
+    if ( !tmp_image )
       tmp_image = slashdup(wp->image);
 
     if ( tmp_image )
       fprintf ( f, " image=\"%s\"", tmp_image );
 
-    g_free ( cwd );
     g_free ( tmp_image );
   }
   if ( wp->symbol )
@@ -771,14 +769,15 @@ static void a_gpspoint_write_track ( const gpointer id, const VikTrack *trk, FIL
   fprintf ( f, "type=\"%send\"\n", trk->is_route ? "route" : "track" );
 }
 
-void a_gpspoint_write_file ( VikTrwLayer *trw, FILE *f )
+void a_gpspoint_write_file ( VikTrwLayer *trw, FILE *f, const gchar *dirpath )
 {
   GHashTable *tracks = vik_trw_layer_get_tracks ( trw );
   GHashTable *routes = vik_trw_layer_get_routes ( trw );
   GHashTable *waypoints = vik_trw_layer_get_waypoints ( trw );
 
+  WritingContext wc = { f, dirpath };
   fprintf ( f, "type=\"waypointlist\"\n" );
-  g_hash_table_foreach ( waypoints, (GHFunc) a_gpspoint_write_waypoint, f );
+  g_hash_table_foreach ( waypoints, (GHFunc) a_gpspoint_write_waypoint, &wc );
   fprintf ( f, "type=\"waypointlistend\"\n" );
   g_hash_table_foreach ( tracks, (GHFunc) a_gpspoint_write_track, f );
   g_hash_table_foreach ( routes, (GHFunc) a_gpspoint_write_track, f );
index 18eb1bb0aff95eab9635226b7aad8f8aaf3d216c..cc77518942a43b2afed0a486e8d41b395a9fdf81 100644 (file)
@@ -27,7 +27,7 @@
 G_BEGIN_DECLS
 
 gboolean a_gpspoint_read_file ( VikTrwLayer *trw, FILE *f, const gchar *dirpath );
-void a_gpspoint_write_file ( VikTrwLayer *trw, FILE *f );
+void a_gpspoint_write_file ( VikTrwLayer *trw, FILE *f, const gchar *dirpath );
 
 G_END_DECLS
 
index dc9c42f739752b06f9a3cf5dc74893abec3c8502..0239829086e973790cf2ceedaceaa072af06112b 100644 (file)
--- a/src/gpx.c
+++ b/src/gpx.c
@@ -34,7 +34,9 @@
 
 #include "gpx.h"
 #include "viking.h"
+#include "vikutils.h"
 #include <expat.h>
+#include "misc/gtkhtml-private.h"
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
@@ -105,6 +107,7 @@ typedef struct tag_mapping {
 typedef struct {
        GpxWritingOptions *options;
        FILE *file;
+       const gchar *dirpath;
 } GpxWritingContext;
 
 /*
@@ -211,10 +214,16 @@ struct LatLon c_ll;
 
 /* specialty flags / etc */
 gboolean f_tr_newseg;
+const gchar *c_link = NULL;
 guint unnamed_waypoints = 0;
 guint unnamed_tracks = 0;
 guint unnamed_routes = 0;
 
+typedef struct {
+       VikTrwLayer *vtl;
+       const gchar *dirpath;
+} UserDataT;
+
 static const char *get_attr ( const char **attr, const char *key )
 {
   while ( *attr ) {
@@ -235,9 +244,10 @@ static gboolean set_c_ll ( const char **attr )
   return FALSE;
 }
 
-static void gpx_start(VikTrwLayer *vtl, const char *el, const char **attr)
+static void gpx_start(UserDataT *ud, const char *el, const char **attr)
 {
   static const gchar *tmp;
+  VikTrwLayer *vtl = ud->vtl;
 
   g_string_append_c ( xpath, '/' );
   g_string_append ( xpath, el );
@@ -286,6 +296,9 @@ static void gpx_start(VikTrwLayer *vtl, const char *el, const char **attr)
        }
        break;
 
+     case tt_wpt_link:
+       c_link = get_attr ( attr, "href" );
+       break;
      case tt_gpx_name:
      case tt_gpx_author:
      case tt_gpx_desc:
@@ -302,7 +315,6 @@ static void gpx_start(VikTrwLayer *vtl, const char *el, const char **attr)
      case tt_wpt_ele:
      case tt_wpt_time:
      case tt_wpt_url:
-     case tt_wpt_link:
      case tt_trk_cmt:
      case tt_trk_desc:
      case tt_trk_src:
@@ -363,10 +375,11 @@ static void track_tidy_processing ( VikTrwLayer *vtl )
   }
 }
 
-static void gpx_end(VikTrwLayer *vtl, const char *el)
+static void gpx_end(UserDataT *ud, const char *el)
 {
   static GTimeVal tp_time;
   static GTimeVal wp_time;
+  VikTrwLayer *vtl = ud->vtl;
 
   g_string_truncate ( xpath, xpath->len - strlen(el) - 1 );
 
@@ -489,7 +502,18 @@ static void gpx_end(VikTrwLayer *vtl, const char *el)
        break;
 
      case tt_wpt_link:
-       vik_waypoint_set_image ( c_wp, c_cdata->str );
+       if ( c_link ) {
+         // Correct <link href="uri"></link> format
+         vu_waypoint_set_image_uri ( c_wp, c_link, ud->dirpath );
+       }
+       else {
+         // Fallback for incorrect GPX <link> format (probably from previous versions of Viking!)
+         //  of the form <link>file</link>
+         gchar *fn = util_make_absolute_filename ( c_cdata->str, ud->dirpath );
+         vik_waypoint_set_image ( c_wp, fn ? fn : c_cdata->str );
+         g_free ( fn );
+       }
+       c_link = NULL;
        g_string_erase ( c_cdata, 0, -1 );
        break;
 
@@ -634,13 +658,17 @@ static void gpx_cdata(void *dta, const XML_Char *s, int len)
 // make like a "stack" of tag names
 // like gpspoint's separated like /gpx/wpt/whatever
 
-gboolean a_gpx_read_file( VikTrwLayer *vtl, FILE *f ) {
+gboolean a_gpx_read_file( VikTrwLayer *vtl, FILE *f, const gchar* dirpath ) {
   XML_Parser parser = XML_ParserCreate(NULL);
   int done=0, len;
   enum XML_Status status = XML_STATUS_ERROR;
 
+  UserDataT *ud = g_malloc (sizeof(UserDataT));
+  ud->vtl     = vtl;
+  ud->dirpath = dirpath;
+
   XML_SetElementHandler(parser, (XML_StartElementHandler) gpx_start, (XML_EndElementHandler) gpx_end);
-  XML_SetUserData(parser, vtl); /* in the future we could remove all global variables */
+  XML_SetUserData(parser, ud);
   XML_SetCharacterDataHandler(parser, (XML_CharacterDataHandler) gpx_cdata);
 
   gchar buf[4096];
@@ -659,8 +687,9 @@ gboolean a_gpx_read_file( VikTrwLayer *vtl, FILE *f ) {
     done = feof(f) || !len;
     status = XML_Parse(parser, buf, len, done);
   }
+
   XML_ParserFree (parser);
+  g_free ( ud );
   g_string_free ( xpath, TRUE );
   g_string_free ( c_cdata, TRUE );
 
@@ -914,8 +943,18 @@ static void gpx_write_waypoint ( VikWaypoint *wp, GpxWritingContext *context )
   }
   if ( wp->image )
   {
-    tmp = entitize(wp->image);
-    fprintf ( f, "  <link>%s</link>\n", tmp );
+    gchar *tmp = NULL;
+    if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
+      if ( context->dirpath ) {
+        gchar *rtmp = g_strdup ( file_GetRelativeFilename ( (gchar*)context->dirpath, wp->image ) );
+        if ( rtmp ) {
+          tmp = gtk_html_filename_to_uri ( rtmp );
+        }
+      }
+    }
+    if ( !tmp )
+      tmp = gtk_html_filename_to_uri ( wp->image );
+    fprintf ( f, "  <link href=\"%s\"></link>\n", tmp );
     g_free ( tmp );
   }
   if ( wp->symbol ) 
@@ -1128,9 +1167,9 @@ static int gpx_track_compare_name(const void *x, const void *y)
   return strcmp(a->name,b->name);
 }
 
-void a_gpx_write_file ( VikTrwLayer *vtl, FILE *f, GpxWritingOptions *options )
+void a_gpx_write_file ( VikTrwLayer *vtl, FILE *f, GpxWritingOptions *options, const gchar* dirpath )
 {
-  GpxWritingContext context = { options, f };
+  GpxWritingContext context = { options, f, dirpath };
 
   gpx_write_header ( f );
 
@@ -1259,7 +1298,7 @@ static gchar* write_tmp_file ( VikTrwLayer *vtl, VikTrack *trk, GpxWritingOption
        if ( trk )
                a_gpx_write_track_file ( trk, ff, options );
        else
-               a_gpx_write_file ( vtl, ff, options );
+               a_gpx_write_file ( vtl, ff, options, NULL );
 
        fclose (ff);
 
index 70f3d97d425ab9224aef150f2d126f43464f7bcb..968f89c0bd529f1fa0d12d3fe200d452ff858101 100644 (file)
--- a/src/gpx.h
+++ b/src/gpx.h
@@ -37,8 +37,8 @@ typedef struct {
        gboolean is_route; /// For internal convenience
 } GpxWritingOptions;
 
-gboolean a_gpx_read_file ( VikTrwLayer *trw, FILE *f );
-void a_gpx_write_file ( VikTrwLayer *trw, FILE *f, GpxWritingOptions *options );
+gboolean a_gpx_read_file ( VikTrwLayer *trw, FILE *f, const gchar* dirpath );
+void a_gpx_write_file ( VikTrwLayer *trw, FILE *f, GpxWritingOptions *options, const gchar *dirpath );
 void a_gpx_write_track_file ( VikTrack *trk, FILE *f, GpxWritingOptions *options );
 
 gchar* a_gpx_write_tmp_file ( VikTrwLayer *vtl, GpxWritingOptions *options );
index dd0c90ba3d460ef11f963625258be8ef5363a03f..8a55f936f51d47997e5c9c3039ee27b29bafd87a 100644 (file)
@@ -110,7 +110,6 @@ struct _VikToolInterface {
 /* Parameters (for I/O and Properties) */
 /* --> moved to uibuilder.h */
 
-
 /* layer interface functions */
 
 /* Create a new layer of a certain type. Should be filled with defaults */
@@ -160,7 +159,7 @@ typedef VikLayerParamData
 typedef void          (*VikLayerFuncChangeParam)           (GtkWidget *, ui_change_values );
 
 typedef gboolean      (*VikLayerFuncReadFileData)          (VikLayer *, FILE *, const gchar *); // gchar* is the directory path. Function should report success or failure
-typedef void          (*VikLayerFuncWriteFileData)         (VikLayer *, FILE *);
+typedef void          (*VikLayerFuncWriteFileData)         (VikLayer *, FILE *, const gchar *); // gchar* is the directory path.
 
 /* item manipulation */
 typedef void          (*VikLayerFuncDeleteItem)            (VikLayer *, gint, gpointer);
index ac5ec59057a174c920f3e3f316a6901b73ad8f4c..6df69ca246151a478b81d08f62ec3fc11f9b1bda 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
  *
- * Copyright (C) 2013, Rob Norris <rw_norris@hotmail.com>
+ * Copyright (C) 2013-2017, Rob Norris <rw_norris@hotmail.com>
  *
  * 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
@@ -38,6 +38,7 @@
 #include "ui_util.h"
 #include "dir.h"
 #include "misc/kdtree.h"
+#include "misc/gtkhtml-private.h"
 
 #define FMT_MAX_NUMBER_CODES 9
 
@@ -927,3 +928,20 @@ void vu_zoom_to_show_latlons ( VikCoordMode mode, VikViewport *vvp, struct LatLo
                vik_viewport_set_zoom ( vvp, zoom );
        }
 }
+
+/**
+ * Set the waypoint image given a URI
+ */
+void vu_waypoint_set_image_uri ( VikWaypoint *wp, const gchar *uri, const gchar *dirpath )
+{
+       gchar *filename = gtk_html_filename_from_uri ( uri );
+       if ( g_path_is_absolute ( filename ) ) {
+               vik_waypoint_set_image ( wp, filename );
+       }
+       else {
+               // Try to form full path
+               gchar *full = g_strconcat ( dirpath, G_DIR_SEPARATOR_S, filename, NULL );
+               vik_waypoint_set_image ( wp, full );
+               g_free ( full );
+       }
+}
index 661161832df59d2ca1de0197d59382dd39273574..9ffed633fe6731adb0d4a89cd1b65fdc7003472a 100644 (file)
@@ -48,6 +48,8 @@ void vu_copy_label_menu ( GtkWidget *widget, guint button );
 
 void vu_zoom_to_show_latlons ( VikCoordMode mode, VikViewport *vvp, struct LatLon maxmin[2] );
 
+void vu_waypoint_set_image_uri ( VikWaypoint *wp, const gchar *uri, const gchar *dirpath );
+
 G_END_DECLS
 
 #endif