diff --git a/configure.ac b/configure.ac index ebd3401..f50537a 100644 --- a/configure.ac +++ b/configure.ac @@ -37,6 +37,8 @@ PKG_CHECK_MODULES([LIBXML], [libxml-2.0]) PKG_CHECK_MODULES([LIBXSLT], [libexslt]) PKG_CHECK_MODULES([WEBKIT], [webkit2gtk-4.0]) PKG_CHECK_MODULES([GDA], [libgda-5.0 libgda-sqlite-5.0]) +PKG_CHECK_MODULES([PIXBUF], [gdk-pixbuf-2.0]) +PKG_CHECK_MODULES([RSVG], [librsvg-2.0]) PKG_CHECK_MODULES([SWE_GLIB], [swe-glib >= 2.1.0]) LIBGD_INIT([ diff --git a/src/Makefile.am b/src/Makefile.am index e681b70..f7438cf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,7 +32,7 @@ AM_CPPFLAGS = -DG_LOG_DOMAIN=\"Astrognome\" -DLOCALEDIR=\"$(localedir)\" -DPKGDA bin_PROGRAMS = astrognome astrognome_SOURCES = $(astrognome_source_files) $(BUILT_SOURCES) -astrognome_LDADD = $(SWE_GLIB_LIBS) $(GTK_LIBS) $(LIBXML_LIBS) $(LIBXSLT_LIBS) $(WEBKIT_LIBS) $(GDA_LIBS) $(top_builddir)/libgd/libgd.la +astrognome_LDADD = $(SWE_GLIB_LIBS) $(GTK_LIBS) $(LIBXML_LIBS) $(LIBXSLT_LIBS) $(WEBKIT_LIBS) $(GDA_LIBS) $(PIXBUF_LIBS) $(RSVG_LIBS) $(top_builddir)/libgd/libgd.la astrognome_LDFLAGS = -rdynamic -astrognome_CFLAGS = $(SWE_GLIB_CFLAGS) $(CFLAGS) $(GTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBXSLT_CFLAGS) $(WEBKIT_CFLAGS) $(GDA_CFLAGS) -Wall +astrognome_CFLAGS = $(SWE_GLIB_CFLAGS) $(CFLAGS) $(GTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBXSLT_CFLAGS) $(WEBKIT_CFLAGS) $(GDA_CFLAGS) $(PIXBUF_CFLAGS) $(RSVG_CFLAGS) -Wall diff --git a/src/ag-chart.c b/src/ag-chart.c index 2d73769..e25cf7e 100644 --- a/src/ag-chart.c +++ b/src/ag-chart.c @@ -10,6 +10,9 @@ #include #include #include +#include +#include +#include #include "config.h" #include "astrognome.h" @@ -41,6 +44,10 @@ typedef enum { XML_CONVERT_INT } XmlConvertType; +#if !LIBRSVG_HAVE_CSS +# error "We need RSVG CSS support to export charts as images!" +#endif + G_DEFINE_QUARK(ag_chart_error_quark, ag_chart_error); G_DEFINE_TYPE_WITH_PRIVATE(AgChart, ag_chart, GSWE_TYPE_MOMENT); @@ -1388,7 +1395,11 @@ ag_chart_sort_planets_by_position(GswePlanetData *planet1, } gchar * -ag_chart_create_svg(AgChart *chart, gsize *length, GError **err) +ag_chart_create_svg( + AgChart *chart, + gsize *length, + gboolean rendering, + GError **err) { xmlDocPtr doc = create_save_doc(chart), xslt_doc, @@ -1401,7 +1412,8 @@ ag_chart_create_svg(AgChart *chart, gsize *length, GError **err) antiscia_node = NULL, node = NULL; gchar *value, - *save_content = NULL; + *save_content = NULL, + **params; const gchar *xslt_content; GList *houses, *house, @@ -1705,15 +1717,22 @@ ag_chart_create_svg(AgChart *chart, gsize *length, GError **err) return NULL; } + params = g_new0(gchar *, 3); + params[0] = "rendering"; + params[1] = (rendering) ? "'yes'" : "'no'"; + // libxml2 messes up the output, as it prints decimal floating point // numbers in a localized format. It is not good in locales that use a // character for decimal separator other than a dot. So let's just use the // C locale until the SVG is generated. current_locale = uselocale(newlocale(LC_ALL, "C", 0)); - svg_doc = xsltApplyStylesheet(xslt_proc, doc, NULL); + + svg_doc = xsltApplyStylesheet(xslt_proc, doc, (const char **)params); + uselocale(current_locale); xsltFreeStylesheet(xslt_proc); xmlFreeDoc(doc); + g_free(params); // Now, svg_doc contains the generated SVG file @@ -1747,7 +1766,7 @@ ag_chart_export_svg_to_file(AgChart *chart, GFile *file, GError **err) gchar *svg; gsize length; - if ((svg = ag_chart_create_svg(chart, &length, err)) == NULL) { + if ((svg = ag_chart_create_svg(chart, &length, TRUE, err)) == NULL) { return; } @@ -1764,6 +1783,72 @@ ag_chart_export_svg_to_file(AgChart *chart, GFile *file, GError **err) ); } +void +ag_chart_export_jpg_to_file(AgChart *chart, GFile *file, GError **err) +{ + gchar *svg, + *jpg; + gsize svg_length, + jpg_length; + RsvgHandle *svg_handle; + GdkPixbuf *pixbuf; + + if ((svg = ag_chart_create_svg(chart, &svg_length, TRUE, err)) == NULL) { + return; + } + + if ((svg_handle = rsvg_handle_new_from_data( + (const guint8 *)svg, + svg_length, + err + )) == NULL) { + g_free(svg); + + return; + } + + g_free(svg); + + if ((pixbuf = rsvg_handle_get_pixbuf(svg_handle)) == NULL) { + g_set_error( + err, + AG_CHART_ERROR, AG_CHART_ERROR_RENDERING_ERROR, + _("Unknown rendering error") + ); + + return; + } + + if (!gdk_pixbuf_save_to_buffer( + pixbuf, + &jpg, + &jpg_length, + "jpeg", + err, + NULL + )) { + g_object_unref(pixbuf); + + return; + } + + g_object_unref(pixbuf); + + g_file_replace_contents( + file, + (const gchar *)jpg, + jpg_length, + NULL, + FALSE, + G_FILE_CREATE_NONE, + NULL, + NULL, + err + ); + + g_free(jpg); +} + void ag_chart_set_note(AgChart *chart, const gchar *note) { diff --git a/src/ag-chart.h b/src/ag-chart.h index 12e97ec..517ae43 100644 --- a/src/ag-chart.h +++ b/src/ag-chart.h @@ -17,6 +17,7 @@ typedef enum { AG_CHART_ERROR_NOT_IMPLEMENTED, AG_CHART_ERROR_INVALID_PLAC_FILE, AG_CHART_ERROR_UNSUPPORTED_PLAC_FILE, + AG_CHART_ERROR_RENDERING_ERROR, } AgChartError; #define AG_TYPE_CHART (ag_chart_get_type()) @@ -43,6 +44,8 @@ struct _AgChartClass { GsweMomentClass parent_class; }; +typedef void (*AgChartSaveImageFunc)(AgChart *, GFile *, GError **); + GType ag_chart_get_type(void) G_GNUC_CONST; AgChart *ag_chart_new_full(GsweTimestamp *timestamp, @@ -67,6 +70,10 @@ void ag_chart_export_svg_to_file(AgChart *chart, GFile *file, GError **err); +void ag_chart_export_jpg_to_file(AgChart *chart, + GFile *file, + GError **err); + void ag_chart_set_name(AgChart *chart, const gchar *name); @@ -82,9 +89,10 @@ void ag_chart_set_city(AgChart *chart, const gchar *ag_chart_get_city(AgChart *chart); -gchar *ag_chart_create_svg(AgChart *chart, - gsize *length, - GError **err); +gchar *ag_chart_create_svg(AgChart *chart, + gsize *length, + gboolean rendering, + GError **err); GList *ag_chart_get_planets(AgChart *chart); diff --git a/src/ag-window.c b/src/ag-window.c index 5be051f..731e86f 100644 --- a/src/ag-window.c +++ b/src/ag-window.c @@ -496,6 +496,7 @@ ag_window_redraw_chart(AgWindow *window) gchar *svg_content = ag_chart_create_svg( priv->chart, &length, + FALSE, &err ); @@ -808,12 +809,12 @@ ag_window_recalculate_chart(AgWindow *window, gboolean set_everything) } static void -ag_window_export_svg(AgWindow *window, GError **err) +ag_window_export_image(AgWindow *window, GError **err) { const gchar *name; - gchar *file_name; GtkWidget *fs; gint response; + GError *local_err = NULL; AgWindowPrivate *priv = ag_window_get_instance_private(window); ag_window_recalculate_chart(window, TRUE); @@ -851,41 +852,200 @@ ag_window_export_svg(AgWindow *window, GError **err) return; } - file_name = g_strdup_printf("%s.svg", name); - fs = gtk_file_chooser_dialog_new(_("Export Chart as SVG"), GTK_WINDOW(window), GTK_FILE_CHOOSER_ACTION_SAVE, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Save"), GTK_RESPONSE_ACCEPT, NULL); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fs), filter_svg); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fs), filter_jpg); + gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fs), filter_svg); gtk_dialog_set_default_response(GTK_DIALOG(fs), GTK_RESPONSE_ACCEPT); gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), FALSE); - gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fs), TRUE); - gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fs), file_name); - g_free(file_name); + // Due to file name modifying later (depending on the file type selection), + // we must do this manually + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fs), FALSE); + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fs), name); - response = gtk_dialog_run(GTK_DIALOG(fs)); - gtk_widget_hide(fs); + while (TRUE) { + response = gtk_dialog_run(GTK_DIALOG(fs)); + gtk_widget_hide(fs); - if (response == GTK_RESPONSE_ACCEPT) { - GFile *file = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(fs)); + if (response == GTK_RESPONSE_ACCEPT) { + GFile *file = gtk_file_chooser_get_file( + GTK_FILE_CHOOSER(fs) + ); + GtkFileFilter *filter = gtk_file_chooser_get_filter( + GTK_FILE_CHOOSER(fs) + ); + gchar *filename = g_file_get_uri(file), + *extension, + *current_extension; + AgChartSaveImageFunc save_func = NULL; + gboolean can_save = FALSE; - ag_chart_export_svg_to_file(priv->chart, file, err); + if (filter == filter_svg) { + extension = ".svg"; + save_func = &ag_chart_export_svg_to_file; + } else if (filter == filter_jpg) { + extension = ".jpg"; + save_func = &ag_chart_export_jpg_to_file; + } else { + g_warning("Unknown file type"); + gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fs), filter_svg); + } + + current_extension = g_utf8_strrchr(filename, -1, '.'); + + if (current_extension == NULL) { + gchar *tmp; + + tmp = filename; + filename = g_strdup_printf("%s%s", tmp, extension); + g_free(tmp); + } else { + GFileInfo *fileinfo; + GFile *tmp_file; + gboolean valid; + GtkFileFilterInfo filter_info; + + tmp_file = g_file_new_for_uri(filename); + fileinfo = g_file_query_info( + tmp_file, + G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, + G_FILE_QUERY_INFO_NONE, + NULL, + NULL + ); + g_object_unref(tmp_file); + + filter_info.contains = + GTK_FILE_FILTER_URI + | GTK_FILE_FILTER_DISPLAY_NAME; + filter_info.uri = filename; + filter_info.display_name = g_file_info_get_display_name( + fileinfo + ); + + valid = gtk_file_filter_filter(filter, &filter_info); + g_object_unref(fileinfo); + + if (!valid) { + GtkResponseType response; + gchar *message, + *new_filename; + + new_filename = g_strdup_printf("%s%s", filename, extension); + + message = g_strdup_printf( + "File extension doesn’t match the chosen format. " \ + "Do you want Astrognome to append the correct " \ + "extension (the new filename will be %s) or " \ + "choose a new name?", + new_filename + ); + + response = ag_app_buttoned_dialog( + GTK_WINDOW(window), + GTK_MESSAGE_QUESTION, + message, + "Cancel", GTK_RESPONSE_CANCEL, + "Append extension", GTK_RESPONSE_APPLY, + "Choose new file", GTK_RESPONSE_NO, + NULL + ); + + if (response == GTK_RESPONSE_APPLY) { + g_free(filename); + filename = new_filename; + } else { + g_free(filename); + g_clear_object(&file); + + if (response == GTK_RESPONSE_NO) { + continue; + } + + break; + } + } + } + + g_clear_object(&file); + file = g_file_new_for_uri(filename); + g_free(filename); + + // Now check if a file under the modified name exists + if (g_file_query_exists(file, NULL)) { + GtkResponseType sub_response; + gchar *message; + GFileInfo *fileinfo; + + fileinfo = g_file_query_info( + file, + G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, + G_FILE_QUERY_INFO_NONE, + NULL, + NULL + ); + message = g_strdup_printf( + "File %s already exists. Do you want to overwrite it?", + g_file_info_get_display_name(fileinfo) + ); + g_object_unref(fileinfo); + + sub_response = ag_app_buttoned_dialog( + GTK_WINDOW(window), GTK_MESSAGE_QUESTION, + message, + _("No"), GTK_RESPONSE_NO, + _("Yes"), GTK_RESPONSE_YES, + NULL + ); + + g_free(message); + + can_save = (sub_response == GTK_RESPONSE_YES); + } else { + can_save = TRUE; + } + + if (can_save) { + g_clear_error(&local_err); + save_func(priv->chart, file, &local_err); + + if (local_err) { + ag_app_message_dialog( + GTK_WINDOW(window), + GTK_MESSAGE_ERROR, + "%s", + local_err->message + ); + } + + g_clear_object(&file); + + break; + } + + g_clear_object(&file); + } else { + break; + } } gtk_widget_destroy(fs); } static void -ag_window_export_svg_action(GSimpleAction *action, - GVariant *parameter, - gpointer user_data) +ag_window_export_image_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data) { AgWindow *window = AG_WINDOW(user_data); - GError *err = NULL; + GError *err = NULL; - ag_window_export_svg(window, &err); + ag_window_export_image(window, &err); if (err) { ag_app_message_dialog( @@ -898,7 +1058,7 @@ ag_window_export_svg_action(GSimpleAction *action, } static void -ag_window_export(AgWindow *window, GError **err) +ag_window_export_agc(AgWindow *window, GError **err) { const gchar *name; gchar *file_name; @@ -968,15 +1128,15 @@ ag_window_export(AgWindow *window, GError **err) } static void -ag_window_export_action(GSimpleAction *action, - GVariant *parameter, - gpointer user_data) +ag_window_export_agc_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data) { AgWindow *window = AG_WINDOW(user_data); GError *err = NULL; ag_window_recalculate_chart(window, TRUE); - ag_window_export(window, &err); + ag_window_export_agc(window, &err); if (err) { ag_app_message_dialog( @@ -1612,19 +1772,19 @@ ag_window_connection_action(GSimpleAction *action, } static GActionEntry win_entries[] = { - { "close", ag_window_close_action, NULL, NULL, NULL }, - { "save", ag_window_save_action, NULL, NULL, NULL }, - { "export", ag_window_export_action, NULL, NULL, NULL }, - { "export-svg", ag_window_export_svg_action, NULL, NULL, NULL }, - { "view-menu", ag_window_view_menu_action, NULL, "false", NULL }, - { "gear-menu", ag_window_gear_menu_action, NULL, "false", NULL }, - { "change-tab", ag_window_change_tab_action, "s", "'edit'", NULL }, - { "new-chart", ag_window_new_chart_action, NULL, NULL, NULL }, - { "back", ag_window_back_action, NULL, NULL, NULL }, - { "refresh", ag_window_refresh_action, NULL, NULL, NULL }, - { "selection", ag_window_selection_mode_action, NULL, "false", NULL }, - { "delete", ag_window_delete_action, NULL, NULL, NULL }, - { "connection", ag_window_connection_action, "s", "'aspects'", NULL }, + { "close", ag_window_close_action, NULL, NULL, NULL }, + { "save", ag_window_save_action, NULL, NULL, NULL }, + { "export-agc", ag_window_export_agc_action, NULL, NULL, NULL }, + { "export-image", ag_window_export_image_action, NULL, NULL, NULL }, + { "view-menu", ag_window_view_menu_action, NULL, "false", NULL }, + { "gear-menu", ag_window_gear_menu_action, NULL, "false", NULL }, + { "change-tab", ag_window_change_tab_action, "s", "'edit'", NULL }, + { "new-chart", ag_window_new_chart_action, NULL, NULL, NULL }, + { "back", ag_window_back_action, NULL, NULL, NULL }, + { "refresh", ag_window_refresh_action, NULL, NULL, NULL }, + { "selection", ag_window_selection_mode_action, NULL, "false", NULL }, + { "delete", ag_window_delete_action, NULL, NULL, NULL }, + { "connection", ag_window_connection_action, "s", "'aspects'", NULL }, }; static void diff --git a/src/astrognome.c b/src/astrognome.c index 85e71db..039a783 100644 --- a/src/astrognome.c +++ b/src/astrognome.c @@ -24,6 +24,8 @@ GtkBuilder *builder; GtkFileFilter *filter_all = NULL; GtkFileFilter *filter_chart = NULL; GtkFileFilter *filter_hor = NULL; +GtkFileFilter *filter_svg = NULL; +GtkFileFilter *filter_jpg = NULL; GtkTreeModel *country_list = NULL; GtkTreeModel *city_list = NULL; GHashTable *xinclude_positions; @@ -84,6 +86,18 @@ init_filters(void) gtk_file_filter_set_name(filter_hor, _("Placidus charts")); gtk_file_filter_add_pattern(filter_hor, "*.hor"); g_object_ref_sink(filter_hor); + + filter_svg = gtk_file_filter_new(); + gtk_file_filter_set_name(filter_svg, _("SVG image")); + gtk_file_filter_add_pattern(filter_svg, "*.svg"); + g_object_ref_sink(filter_svg); + + filter_jpg = gtk_file_filter_new(); + gtk_file_filter_set_name(filter_jpg, _("JPEG image")); + gtk_file_filter_add_pattern(filter_jpg, "*.jpg"); + gtk_file_filter_add_pattern(filter_jpg, "*.jpe"); + gtk_file_filter_add_pattern(filter_jpg, "*.jpeg"); + g_object_ref_sink(filter_jpg); } static int diff --git a/src/astrognome.h b/src/astrognome.h index 9697c32..e087a09 100644 --- a/src/astrognome.h +++ b/src/astrognome.h @@ -13,6 +13,8 @@ typedef struct { extern GtkFileFilter *filter_all; extern GtkFileFilter *filter_chart; extern GtkFileFilter *filter_hor; +extern GtkFileFilter *filter_svg; +extern GtkFileFilter *filter_jpg; extern GtkTreeModel *country_list; extern GtkTreeModel *city_list; extern const GswePlanet used_planets[]; diff --git a/src/resources/ui/ag-window.ui b/src/resources/ui/ag-window.ui index f4f4189..9f0701f 100644 --- a/src/resources/ui/ag-window.ui +++ b/src/resources/ui/ag-window.ui @@ -39,14 +39,14 @@ Export… - win.export + win.export-agc <Primary><Shift>e
- Export as SVG - win.export-svg + Export as image… + win.export-image
diff --git a/src/resources/ui/chart-default.css b/src/resources/ui/chart-default.css index cb74e88..dd3413f 100644 --- a/src/resources/ui/chart-default.css +++ b/src/resources/ui/chart-default.css @@ -134,3 +134,7 @@ line.antiscion { stroke: #000000; stroke-dasharray: 20,10; } + +#background { + fill: #ffffff; +} diff --git a/src/resources/ui/chart-default.xsl b/src/resources/ui/chart-default.xsl index 168d77c..552d744 100644 --- a/src/resources/ui/chart-default.xsl +++ b/src/resources/ui/chart-default.xsl @@ -8,7 +8,7 @@ method="xml" media-type="image/svg+xml" doctype-public="-//W3C//DTD SVG 1.0//EN" - doctype-system="http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd" + doctype-system="http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" version="1.0" encoding="UTF-8" indent="yes"/> @@ -69,6 +69,15 @@ version="1.0"> + + + + + + + <xsl:value-of select="concat( 'Natal chart of ', @@ -116,6 +125,16 @@ <polygon points="0.0,0.0 7.0,-2.0 5.0,0.0 7.0,2.0" /> </marker> </defs> + + <xsl:choose> + <xsl:when test="$rendering='yes'"> + <rect id="background" x="0" y="0"> + <xsl:attribute name="width"><xsl:value-of select="$image_size"/></xsl:attribute> + <xsl:attribute name="height"><xsl:value-of select="$image_size"/></xsl:attribute> + </rect> + </xsl:when> + </xsl:choose> + <g id="chart"> <xsl:attribute name="transform"><xsl:value-of select="concat('translate(', $image_size div 2, ',', $image_size div 2, ')')" /></xsl:attribute> <g id="moonless_chart"> @@ -185,41 +204,41 @@ <xsl:attribute name="x1"><xsl:value-of select="$image_size * 0.3"/></xsl:attribute> <xsl:attribute name="x2"><xsl:value-of select="$image_size * 0.3125"/></xsl:attribute> </line> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_15" transform="rotate(-10,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_25" transform="rotate(-20,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_35" transform="rotate(-30,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_45" transform="rotate(-40,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_55" transform="rotate(-50,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_65" transform="rotate(-60,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_75" transform="rotate(-70,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_85" transform="rotate(-80,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_95" transform="rotate(-90,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_105" transform="rotate(-100,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_115" transform="rotate(-110,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_125" transform="rotate(-120,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_135" transform="rotate(-130,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_145" transform="rotate(-140,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_155" transform="rotate(-150,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_165" transform="rotate(-160,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_175" transform="rotate(-170,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_185" transform="rotate(-180,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_195" transform="rotate(-190,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_205" transform="rotate(-200,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_215" transform="rotate(-210,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_225" transform="rotate(-220,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_235" transform="rotate(-230,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_245" transform="rotate(-240,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_255" transform="rotate(-250,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_265" transform="rotate(-260,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_275" transform="rotate(-270,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_285" transform="rotate(-280,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_295" transform="rotate(-290,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_305" transform="rotate(-300,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_315" transform="rotate(-310,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_325" transform="rotate(-320,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_335" transform="rotate(-330,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_345" transform="rotate(-340,0,0)" class="degree-thin" /> - <use x="0" y="0" xlink:href="#deg_5" id="#deg_355" transform="rotate(-350,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_15" transform="rotate(-10,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_25" transform="rotate(-20,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_35" transform="rotate(-30,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_45" transform="rotate(-40,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_55" transform="rotate(-50,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_65" transform="rotate(-60,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_75" transform="rotate(-70,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_85" transform="rotate(-80,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_95" transform="rotate(-90,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_105" transform="rotate(-100,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_115" transform="rotate(-110,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_125" transform="rotate(-120,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_135" transform="rotate(-130,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_145" transform="rotate(-140,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_155" transform="rotate(-150,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_165" transform="rotate(-160,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_175" transform="rotate(-170,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_185" transform="rotate(-180,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_195" transform="rotate(-190,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_205" transform="rotate(-200,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_215" transform="rotate(-210,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_225" transform="rotate(-220,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_235" transform="rotate(-230,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_245" transform="rotate(-240,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_255" transform="rotate(-250,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_265" transform="rotate(-260,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_275" transform="rotate(-270,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_285" transform="rotate(-280,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_295" transform="rotate(-290,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_305" transform="rotate(-300,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_315" transform="rotate(-310,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_325" transform="rotate(-320,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_335" transform="rotate(-330,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_345" transform="rotate(-340,0,0)" class="degree-thin" /> + <use x="0" y="0" xlink:href="#deg_5" id="deg_355" transform="rotate(-350,0,0)" class="degree-thin" /> <line id="deg_1" y1="0" y2="0" transform="rotate(-1,0,0)" class="degree-thin"> <xsl:attribute name="x1"><xsl:value-of select="$image_size * 0.3"/></xsl:attribute>