Merge pull request #79 from gergelypolonkai/export-image

Export chart as JPEG and viewable SVG
This commit is contained in:
Gergely Polonkai 2014-09-17 11:26:49 +02:00
commit e07d818724
10 changed files with 377 additions and 83 deletions

View File

@ -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([

View File

@ -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

View File

@ -10,6 +10,9 @@
#include <locale.h>
#include <math.h>
#include <string.h>
#include <librsvg/rsvg.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <glib/gi18n.h>
#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)
{

View File

@ -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);

View File

@ -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 doesnt 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

View File

@ -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

View File

@ -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[];

View File

@ -39,14 +39,14 @@
</item>
<item>
<attribute name="label" translatable="yes">Export…</attribute>
<attribute name="action">win.export</attribute>
<attribute name="action">win.export-agc</attribute>
<attribute name="accel">&lt;Primary&gt;&lt;Shift&gt;e</attribute>
</item>
</section>
<section>
<item>
<attribute name="label" translatable="yes">Export as SVG</attribute>
<attribute name="action">win.export-svg</attribute>
<attribute name="label" translatable="yes">Export as image…</attribute>
<attribute name="action">win.export-image</attribute>
</item>
</section>
<section>

View File

@ -134,3 +134,7 @@ line.antiscion {
stroke: #000000;
stroke-dasharray: 20,10;
}
#background {
fill: #ffffff;
}

View File

@ -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:attribute name="width"><xsl:value-of select="$image_size"/></xsl:attribute>
<xsl:attribute name="height"><xsl:value-of select="$image_size"/></xsl:attribute>
<xsl:choose>
<xsl:when test="$rendering='yes'">
<style type="text/css">
<xi:include href="gres://ui/chart-default.css" parse="text"/>
</style>
</xsl:when>
</xsl:choose>
<title>
<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>