From: Rob Norris Date: Sat, 20 May 2017 14:20:47 +0000 (+0100) Subject: SF Bugs#138: Fix handling of tags in GPX files. X-Git-Url: https://git.street.me.uk/andy/viking.git/commitdiff_plain/8a439f317d60281cdb3f6dab57dd51b7315a90de SF Bugs#138: Fix handling of tags in GPX files. tags (only in GPX1.1) should be of the form 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. --- diff --git a/src/babel.c b/src/babel.c index ea86ae4f..48604b5f 100644 --- a/src/babel.c +++ b/src/babel.c @@ -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; } } } diff --git a/src/file.c b/src/file.c index b7bff204..6334676b 100644 --- a/src/file.c +++ b/src/file.c @@ -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; } diff --git a/src/gpspoint.c b/src/gpspoint.c index d16049d4..318436f2 100644 --- a/src/gpspoint.c +++ b/src/gpspoint.c @@ -28,6 +28,7 @@ #endif #include "viking.h" +#include "vikutils.h" #include #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 ); diff --git a/src/gpspoint.h b/src/gpspoint.h index 18eb1bb0..cc775189 100644 --- a/src/gpspoint.h +++ b/src/gpspoint.h @@ -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 diff --git a/src/gpx.c b/src/gpx.c index dc9c42f7..02398290 100644 --- a/src/gpx.c +++ b/src/gpx.c @@ -34,7 +34,9 @@ #include "gpx.h" #include "viking.h" +#include "vikutils.h" #include +#include "misc/gtkhtml-private.h" #ifdef HAVE_STRING_H #include #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 format + vu_waypoint_set_image_uri ( c_wp, c_link, ud->dirpath ); + } + else { + // Fallback for incorrect GPX format (probably from previous versions of Viking!) + // of the form file + 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, " %s\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, " \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); diff --git a/src/gpx.h b/src/gpx.h index 70f3d97d..968f89c0 100644 --- 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 ); diff --git a/src/viklayer.h b/src/viklayer.h index dd0c90ba..8a55f936 100644 --- a/src/viklayer.h +++ b/src/viklayer.h @@ -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); diff --git a/src/vikutils.c b/src/vikutils.c index ac5ec590..6df69ca2 100644 --- a/src/vikutils.c +++ b/src/vikutils.c @@ -2,7 +2,7 @@ /* * viking -- GPS Data and Topo Analyzer, Explorer, and Manager * - * Copyright (C) 2013, Rob Norris + * Copyright (C) 2013-2017, Rob Norris * * 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 ); + } +} diff --git a/src/vikutils.h b/src/vikutils.h index 66116183..9ffed633 100644 --- a/src/vikutils.h +++ b/src/vikutils.h @@ -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