1526af4068
And so remove the current year to ease maintenance and since it is not strictly necessary. Also remove individual copyright holders (where appropriate) and replace the name with "The Geany contributors". The detailed authorship information is still available in the GIT history. Also remove copyright notice and author names from READMEs.
777 lines
21 KiB
C
777 lines
21 KiB
C
/*
|
|
* export.c - this file is part of Geany, a fast and lightweight IDE
|
|
*
|
|
* Copyright 2007 The Geany contributors
|
|
*
|
|
* 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
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
/* Export plugin. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
|
|
#include "geanyplugin.h"
|
|
|
|
|
|
GeanyData *geany_data;
|
|
|
|
PLUGIN_VERSION_CHECK(GEANY_API_VERSION)
|
|
PLUGIN_SET_INFO(_("Export"), _("Exports the current file into different formats."), VERSION,
|
|
_("The Geany developer team"))
|
|
|
|
|
|
static GtkWidget *main_menu_item = NULL;
|
|
|
|
|
|
#define ROTATE_RGB(color) \
|
|
(((color) & 0xFF0000) >> 16) + ((color) & 0x00FF00) + (((color) & 0x0000FF) << 16)
|
|
#define TEMPLATE_HTML "\
|
|
<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n\
|
|
\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n\
|
|
<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n\
|
|
\n\
|
|
<head>\n\
|
|
<title>{export_filename}</title>\n\
|
|
<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\" />\n\
|
|
<meta name=\"generator\" content=\"Geany " VERSION "\" />\n\
|
|
<meta name=\"date\" content=\"{export_date}\" />\n\
|
|
<style type=\"text/css\">\n\
|
|
{export_styles}\n\
|
|
</style>\n\
|
|
</head>\n\
|
|
\n\
|
|
<body>\n\
|
|
<p>\n\
|
|
{export_content}\n\
|
|
</p>\n\
|
|
</body>\n\
|
|
</html>\n"
|
|
|
|
#define TEMPLATE_LATEX "\
|
|
% {export_filename} (LaTeX code generated by Geany " VERSION " on {export_date})\n\
|
|
\\documentclass[a4paper]{article}\n\
|
|
\\usepackage[a4paper,margin=2cm]{geometry}\n\
|
|
\\usepackage[utf8]{inputenc}\n\
|
|
\\usepackage[T1]{fontenc}\n\
|
|
\\usepackage{color}\n\
|
|
\\setlength{\\parindent}{0em}\n\
|
|
\\setlength{\\parskip}{2ex plus1ex minus0.5ex}\n\
|
|
{export_styles}\n\
|
|
\\begin{document}\
|
|
\n\
|
|
\\ttfamily\n\
|
|
\\setlength{\\fboxrule}{0pt}\n\
|
|
\\setlength{\\fboxsep}{0pt}\n\
|
|
{export_content}\
|
|
\\end{document}\n"
|
|
|
|
|
|
enum
|
|
{
|
|
FORE = 0,
|
|
BACK,
|
|
BOLD,
|
|
ITALIC,
|
|
USED,
|
|
MAX_TYPES
|
|
};
|
|
|
|
enum
|
|
{
|
|
DATE_TYPE_DEFAULT,
|
|
DATE_TYPE_HTML
|
|
};
|
|
|
|
typedef void (*ExportFunc) (GeanyDocument *doc, const gchar *filename,
|
|
gboolean use_zoom, gboolean insert_line_numbers);
|
|
typedef struct
|
|
{
|
|
GeanyDocument *doc;
|
|
gboolean have_zoom_level_checkbox;
|
|
ExportFunc export_func;
|
|
} ExportInfo;
|
|
|
|
static void on_file_save_dialog_response(GtkDialog *dialog, gint response, gpointer user_data);
|
|
static void write_html_file(GeanyDocument *doc, const gchar *filename,
|
|
gboolean use_zoom, gboolean insert_line_numbers);
|
|
static void write_latex_file(GeanyDocument *doc, const gchar *filename,
|
|
gboolean use_zoom, gboolean insert_line_numbers);
|
|
|
|
|
|
/* converts a RGB colour into a LaTeX compatible representation, taken from SciTE */
|
|
static gchar* get_tex_rgb(gint rgb_colour)
|
|
{
|
|
/* texcolor[rgb]{0,0.5,0}{....} */
|
|
gdouble rf = (rgb_colour % 256) / 256.0;
|
|
gdouble gf = ((rgb_colour & - 16711936) / 256) / 256.0;
|
|
gdouble bf = ((rgb_colour & 0xff0000) / 65536) / 256.0;
|
|
gint r = (gint) (rf * 10 + 0.5);
|
|
gint g = (gint) (gf * 10 + 0.5);
|
|
gint b = (gint) (bf * 10 + 0.5);
|
|
|
|
return g_strdup_printf("%d.%d, %d.%d, %d.%d", r / 10, r % 10, g / 10, g % 10, b / 10, b % 10);
|
|
}
|
|
|
|
|
|
/* convert a style number (0..127) into a string representation (aa, ab, .., ba, bb, .., zy, zz) */
|
|
static gchar *get_tex_style(gint style)
|
|
{
|
|
static gchar buf[4];
|
|
int i = 0;
|
|
|
|
do
|
|
{
|
|
buf[i] = (style % 26) + 'a';
|
|
style /= 26;
|
|
i++;
|
|
} while (style > 0);
|
|
buf[i] = '\0';
|
|
|
|
return buf;
|
|
}
|
|
|
|
|
|
static void create_file_save_as_dialog(const gchar *extension, ExportFunc func,
|
|
gboolean show_zoom_level_checkbox)
|
|
{
|
|
GtkWidget *dialog, *vbox;
|
|
GeanyDocument *doc;
|
|
ExportInfo *exi;
|
|
|
|
g_return_if_fail(extension != NULL);
|
|
|
|
doc = document_get_current();
|
|
g_return_if_fail(doc != NULL);
|
|
|
|
exi = g_new(ExportInfo, 1);
|
|
exi->doc = doc;
|
|
exi->export_func = func;
|
|
exi->have_zoom_level_checkbox = FALSE;
|
|
|
|
dialog = gtk_file_chooser_dialog_new(_("Export File"), GTK_WINDOW(geany->main_widgets->window),
|
|
GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL);
|
|
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
|
|
gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
|
|
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), TRUE);
|
|
gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
|
|
gtk_widget_set_name(dialog, "GeanyExportDialog");
|
|
|
|
gtk_dialog_add_buttons(GTK_DIALOG(dialog),
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
|
|
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
|
|
|
|
/* file chooser extra widget */
|
|
vbox = gtk_vbox_new(FALSE, 0);
|
|
gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), vbox);
|
|
{
|
|
GtkWidget *check_line_numbers;
|
|
|
|
check_line_numbers = gtk_check_button_new_with_mnemonic(_("_Insert line numbers"));
|
|
gtk_widget_set_tooltip_text(check_line_numbers,
|
|
_("Insert line numbers before each line in the exported document"));
|
|
gtk_box_pack_start(GTK_BOX(vbox), check_line_numbers, FALSE, FALSE, 0);
|
|
gtk_widget_show_all(vbox);
|
|
|
|
ui_hookup_widget(dialog, check_line_numbers, "check_line_numbers");
|
|
}
|
|
if (show_zoom_level_checkbox)
|
|
{
|
|
GtkWidget *check_zoom_level;
|
|
|
|
check_zoom_level = gtk_check_button_new_with_mnemonic(_("_Use current zoom level"));
|
|
gtk_widget_set_tooltip_text(check_zoom_level,
|
|
_("Renders the font size of the document together with the current zoom level"));
|
|
gtk_box_pack_start(GTK_BOX(vbox), check_zoom_level, FALSE, FALSE, 0);
|
|
gtk_widget_show_all(vbox);
|
|
|
|
ui_hookup_widget(dialog, check_zoom_level, "check_zoom_level");
|
|
exi->have_zoom_level_checkbox = TRUE;
|
|
}
|
|
|
|
g_signal_connect(dialog, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
|
g_signal_connect(dialog, "response", G_CALLBACK(on_file_save_dialog_response), exi);
|
|
|
|
gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(geany->main_widgets->window));
|
|
|
|
/* if the current document has a filename we use it as the default. */
|
|
gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(dialog));
|
|
if (doc->file_name != NULL)
|
|
{
|
|
gchar *base_name = g_path_get_basename(doc->file_name);
|
|
gchar *file_name;
|
|
gchar *locale_filename;
|
|
gchar *locale_dirname;
|
|
const gchar *suffix = "";
|
|
|
|
if (g_str_has_suffix(doc->file_name, extension))
|
|
suffix = "_export";
|
|
|
|
file_name = g_strconcat(base_name, suffix, extension, NULL);
|
|
locale_filename = utils_get_locale_from_utf8(doc->file_name);
|
|
locale_dirname = g_path_get_dirname(locale_filename);
|
|
/* set the current name to base_name.html which probably doesn't exist yet so
|
|
* gtk_file_chooser_set_filename() can't be used and we need
|
|
* gtk_file_chooser_set_current_folder() additionally */
|
|
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), locale_dirname);
|
|
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), file_name);
|
|
g_free(locale_dirname);
|
|
g_free(locale_filename);
|
|
g_free(file_name);
|
|
g_free(base_name);
|
|
}
|
|
else
|
|
{
|
|
const gchar *default_open_path = geany->prefs->default_open_path;
|
|
gchar *fname = g_strconcat(GEANY_STRING_UNTITLED, extension, NULL);
|
|
|
|
gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(dialog));
|
|
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), fname);
|
|
|
|
/* use default startup directory(if set) if no files are open */
|
|
if (!EMPTY(default_open_path) && g_path_is_absolute(default_open_path))
|
|
{
|
|
gchar *locale_path = utils_get_locale_from_utf8(default_open_path);
|
|
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), locale_path);
|
|
g_free(locale_path);
|
|
}
|
|
g_free(fname);
|
|
}
|
|
gtk_dialog_run(GTK_DIALOG(dialog));
|
|
}
|
|
|
|
|
|
static void on_menu_create_latex_activate(GtkMenuItem *menuitem, gpointer user_data)
|
|
{
|
|
create_file_save_as_dialog(".tex", write_latex_file, FALSE);
|
|
}
|
|
|
|
|
|
static void on_menu_create_html_activate(GtkMenuItem *menuitem, gpointer user_data)
|
|
{
|
|
create_file_save_as_dialog(".html", write_html_file, TRUE);
|
|
}
|
|
|
|
|
|
static void write_data(const gchar *filename, const gchar *data)
|
|
{
|
|
gint error_nr = utils_write_file(filename, data);
|
|
gchar *utf8_filename = utils_get_utf8_from_locale(filename);
|
|
|
|
if (error_nr == 0)
|
|
ui_set_statusbar(TRUE, _("Document successfully exported as '%s'."), utf8_filename);
|
|
else
|
|
ui_set_statusbar(TRUE, _("File '%s' could not be written (%s)."),
|
|
utf8_filename, g_strerror(error_nr));
|
|
|
|
g_free(utf8_filename);
|
|
}
|
|
|
|
|
|
static gchar *get_date(gint type)
|
|
{
|
|
const gchar *format;
|
|
|
|
if (type == DATE_TYPE_HTML)
|
|
/* needs testing */
|
|
#ifdef _GNU_SOURCE
|
|
format = "%Y-%m-%dT%H:%M:%S%z";
|
|
#else
|
|
format = "%Y-%m-%dT%H:%M:%S";
|
|
#endif
|
|
else
|
|
format = "%c";
|
|
|
|
return utils_get_date_time(format, NULL);
|
|
}
|
|
|
|
|
|
static void on_file_save_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
|
|
{
|
|
ExportInfo *exi = user_data;
|
|
|
|
if (response == GTK_RESPONSE_ACCEPT && exi != NULL)
|
|
{
|
|
gchar *new_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
|
|
gchar *utf8_filename;
|
|
gboolean insert_line_numbers;
|
|
gboolean use_zoom_level = FALSE;
|
|
|
|
if (exi->have_zoom_level_checkbox)
|
|
{
|
|
use_zoom_level = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
|
|
ui_lookup_widget(GTK_WIDGET(dialog), "check_zoom_level")));
|
|
}
|
|
insert_line_numbers = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
|
|
ui_lookup_widget(GTK_WIDGET(dialog), "check_line_numbers")));
|
|
|
|
utf8_filename = utils_get_utf8_from_locale(new_filename);
|
|
|
|
/* check if file exists and ask whether to overwrite or not */
|
|
if (g_file_test(new_filename, G_FILE_TEST_EXISTS))
|
|
{
|
|
if (dialogs_show_question(
|
|
_("The file '%s' already exists. Do you want to overwrite it?"),
|
|
utf8_filename) == FALSE)
|
|
return;
|
|
}
|
|
|
|
exi->export_func(exi->doc, new_filename, use_zoom_level, insert_line_numbers);
|
|
|
|
g_free(utf8_filename);
|
|
g_free(new_filename);
|
|
}
|
|
g_free(exi);
|
|
gtk_widget_destroy(GTK_WIDGET(dialog));
|
|
}
|
|
|
|
|
|
/* returns the "width" (count of needed characters) for the given number */
|
|
static gint get_line_numbers_arity(gint line_number)
|
|
{
|
|
gint a = 0;
|
|
while ((line_number /= 10) != 0)
|
|
a++;
|
|
return a;
|
|
}
|
|
|
|
|
|
static gint get_line_number_width(GeanyDocument *doc)
|
|
{
|
|
gint line_count = sci_get_line_count(doc->editor->sci);
|
|
return get_line_numbers_arity(line_count);
|
|
}
|
|
|
|
|
|
static void write_latex_file(GeanyDocument *doc, const gchar *filename,
|
|
gboolean use_zoom, gboolean insert_line_numbers)
|
|
{
|
|
GeanyEditor *editor = doc->editor;
|
|
ScintillaObject *sci = doc->editor->sci;
|
|
gint i, doc_len, style = -1, old_style = 0, column = 0;
|
|
gint k, line_number, line_number_width, line_number_max_width = 0, pad;
|
|
gchar c, c_next, *tmp, *date;
|
|
/* 0 - fore, 1 - back, 2 - bold, 3 - italic, 4 - font size, 5 - used(0/1) */
|
|
gint styles[STYLE_MAX + 1][MAX_TYPES];
|
|
gboolean block_open = FALSE;
|
|
GString *body;
|
|
GString *cmds;
|
|
GString *latex;
|
|
|
|
/* first read all styles from Scintilla */
|
|
for (i = 0; i < STYLE_MAX; i++)
|
|
{
|
|
styles[i][FORE] = scintilla_send_message(sci, SCI_STYLEGETFORE, i, 0);
|
|
styles[i][BACK] = scintilla_send_message(sci, SCI_STYLEGETBACK, i, 0);
|
|
styles[i][BOLD] = scintilla_send_message(sci, SCI_STYLEGETBOLD, i, 0);
|
|
styles[i][ITALIC] = scintilla_send_message(sci, SCI_STYLEGETITALIC, i, 0);
|
|
styles[i][USED] = 0;
|
|
}
|
|
|
|
if (insert_line_numbers)
|
|
line_number_max_width = get_line_number_width(doc);
|
|
|
|
/* read the document and write the LaTeX code */
|
|
body = g_string_new("");
|
|
doc_len = sci_get_length(sci);
|
|
for (i = 0; i < doc_len; i++)
|
|
{
|
|
style = sci_get_style_at(sci, i);
|
|
c = sci_get_char_at(sci, i);
|
|
c_next = sci_get_char_at(sci, i + 1);
|
|
|
|
/* line numbers */
|
|
if (insert_line_numbers && column == 0)
|
|
{
|
|
line_number = sci_get_line_from_position(sci, i) + 1;
|
|
line_number_width = get_line_numbers_arity(line_number);
|
|
/* padding */
|
|
pad = line_number_max_width - line_number_width;
|
|
for (k = 0; k < pad; k++)
|
|
{
|
|
g_string_append(body, " ");
|
|
}
|
|
g_string_append_printf(body, "%d ", line_number);
|
|
}
|
|
|
|
if (style != old_style || ! block_open)
|
|
{
|
|
old_style = style;
|
|
styles[style][USED] = 1;
|
|
if (block_open)
|
|
{
|
|
g_string_append(body, "}\n");
|
|
block_open = FALSE;
|
|
}
|
|
if (i < doc_len)
|
|
{
|
|
g_string_append_printf(body, "\\style%s{", get_tex_style(style));
|
|
block_open = TRUE;
|
|
}
|
|
}
|
|
/* escape the current character if necessary else just add it */
|
|
switch (c)
|
|
{
|
|
case '\r':
|
|
case '\n':
|
|
{
|
|
if (c == '\r' && c_next == '\n')
|
|
continue; /* when using CR/LF skip CR and add the line break with LF */
|
|
|
|
if (block_open)
|
|
{
|
|
g_string_append(body, "}");
|
|
block_open = FALSE;
|
|
}
|
|
g_string_append(body, " \\\\\n");
|
|
column = -1;
|
|
break;
|
|
}
|
|
case '\t':
|
|
{
|
|
gint tab_width = sci_get_tab_width(editor->sci);
|
|
gint tab_stop = tab_width - (column % tab_width);
|
|
|
|
column += tab_stop - 1; /* -1 because we add 1 at the end of the loop */
|
|
g_string_append_printf(body, "\\hspace*{%dem}", tab_stop);
|
|
break;
|
|
}
|
|
case ' ':
|
|
{
|
|
if (c_next == ' ')
|
|
{
|
|
g_string_append(body, "{\\hspace*{1em}}");
|
|
i++; /* skip the next character */
|
|
}
|
|
else
|
|
g_string_append_c(body, ' ');
|
|
break;
|
|
}
|
|
case '{':
|
|
case '}':
|
|
case '_':
|
|
case '&':
|
|
case '$':
|
|
case '#':
|
|
case '%':
|
|
{
|
|
g_string_append_printf(body, "\\%c", c);
|
|
break;
|
|
}
|
|
case '\\':
|
|
{
|
|
g_string_append(body, "\\symbol{92}");
|
|
break;
|
|
}
|
|
case '~':
|
|
{
|
|
g_string_append(body, "\\symbol{126}");
|
|
break;
|
|
}
|
|
case '^':
|
|
{
|
|
g_string_append(body, "\\symbol{94}");
|
|
break;
|
|
}
|
|
/* mask "--", "<<" and ">>" */
|
|
case '-':
|
|
case '<':
|
|
case '>':
|
|
{
|
|
g_string_append_c(body, c);
|
|
if (c_next == c)
|
|
g_string_append(body, "\\/");
|
|
|
|
break;
|
|
}
|
|
default: g_string_append_c(body, c);
|
|
}
|
|
column++;
|
|
}
|
|
if (block_open)
|
|
{
|
|
g_string_append(body, "}\n");
|
|
block_open = FALSE;
|
|
}
|
|
|
|
/* force writing of style 0 (used at least for line breaks) */
|
|
styles[0][USED] = 1;
|
|
|
|
/* write used styles in the header */
|
|
cmds = g_string_new("");
|
|
for (i = 0; i < STYLE_MAX; i++)
|
|
{
|
|
if (styles[i][USED])
|
|
{
|
|
g_string_append_printf(cmds,
|
|
"\\newcommand{\\style%s}[1]{\\noindent{", get_tex_style(i));
|
|
if (styles[i][BOLD])
|
|
g_string_append(cmds, "\\textbf{");
|
|
if (styles[i][ITALIC])
|
|
g_string_append(cmds, "\\textit{");
|
|
|
|
tmp = get_tex_rgb(styles[i][FORE]);
|
|
g_string_append_printf(cmds, "\\textcolor[rgb]{%s}{", tmp);
|
|
g_free(tmp);
|
|
tmp = get_tex_rgb(styles[i][BACK]);
|
|
g_string_append_printf(cmds, "\\fcolorbox[rgb]{0, 0, 0}{%s}{", tmp);
|
|
g_string_append(cmds, "#1}}");
|
|
g_free(tmp);
|
|
|
|
if (styles[i][BOLD])
|
|
g_string_append_c(cmds, '}');
|
|
if (styles[i][ITALIC])
|
|
g_string_append_c(cmds, '}');
|
|
g_string_append(cmds, "}}\n");
|
|
}
|
|
}
|
|
|
|
date = get_date(DATE_TYPE_DEFAULT);
|
|
/* write all */
|
|
latex = g_string_new(TEMPLATE_LATEX);
|
|
utils_string_replace_all(latex, "{export_content}", body->str);
|
|
utils_string_replace_all(latex, "{export_styles}", cmds->str);
|
|
utils_string_replace_all(latex, "{export_date}", date);
|
|
utils_string_replace_all(latex, "{export_filename}", DOC_FILENAME(doc));
|
|
|
|
write_data(filename, latex->str);
|
|
|
|
g_string_free(body, TRUE);
|
|
g_string_free(cmds, TRUE);
|
|
g_string_free(latex, TRUE);
|
|
g_free(date);
|
|
}
|
|
|
|
|
|
static void write_html_file(GeanyDocument *doc, const gchar *filename,
|
|
gboolean use_zoom, gboolean insert_line_numbers)
|
|
{
|
|
GeanyEditor *editor = doc->editor;
|
|
ScintillaObject *sci = doc->editor->sci;
|
|
gint i, doc_len, style = -1, old_style = 0, column = 0;
|
|
gint k, line_number, line_number_width, line_number_max_width = 0, pad;
|
|
gchar c, c_next, *date, *doc_filename;
|
|
/* 0 - fore, 1 - back, 2 - bold, 3 - italic, 4 - font size, 5 - used(0/1) */
|
|
gint styles[STYLE_MAX + 1][MAX_TYPES];
|
|
gboolean span_open = FALSE;
|
|
const gchar *font_name;
|
|
gint font_size;
|
|
PangoFontDescription *font_desc;
|
|
GString *body;
|
|
GString *css;
|
|
GString *html;
|
|
|
|
/* first read all styles from Scintilla */
|
|
for (i = 0; i < STYLE_MAX; i++)
|
|
{
|
|
styles[i][FORE] = ROTATE_RGB(scintilla_send_message(sci, SCI_STYLEGETFORE, i, 0));
|
|
styles[i][BACK] = ROTATE_RGB(scintilla_send_message(sci, SCI_STYLEGETBACK, i, 0));
|
|
styles[i][BOLD] = scintilla_send_message(sci, SCI_STYLEGETBOLD, i, 0);
|
|
styles[i][ITALIC] = scintilla_send_message(sci, SCI_STYLEGETITALIC, i, 0);
|
|
styles[i][USED] = 0;
|
|
}
|
|
|
|
/* read Geany's font and font size */
|
|
font_desc = pango_font_description_from_string(geany->interface_prefs->editor_font);
|
|
font_name = pango_font_description_get_family(font_desc);
|
|
/*font_size = pango_font_description_get_size(font_desc) / PANGO_SCALE;*/
|
|
/* take the zoom level also into account */
|
|
font_size = scintilla_send_message(sci, SCI_STYLEGETSIZE, 0, 0);
|
|
if (use_zoom)
|
|
font_size += scintilla_send_message(sci, SCI_GETZOOM, 0, 0);
|
|
|
|
if (insert_line_numbers)
|
|
line_number_max_width = get_line_number_width(doc);
|
|
|
|
/* read the document and write the HTML body */
|
|
body = g_string_new("");
|
|
doc_len = sci_get_length(sci);
|
|
for (i = 0; i < doc_len; i++)
|
|
{
|
|
style = sci_get_style_at(sci, i);
|
|
c = sci_get_char_at(sci, i);
|
|
/* sci_get_char_at() takes care of index boundaries and return 0 if i is too high */
|
|
c_next = sci_get_char_at(sci, i + 1);
|
|
|
|
/* line numbers */
|
|
if (insert_line_numbers && column == 0)
|
|
{
|
|
line_number = sci_get_line_from_position(sci, i) + 1;
|
|
line_number_width = get_line_numbers_arity(line_number);
|
|
/* padding */
|
|
pad = line_number_max_width - line_number_width;
|
|
for (k = 0; k < pad; k++)
|
|
{
|
|
g_string_append(body, " ");
|
|
}
|
|
g_string_append_printf(body, "%d ", line_number);
|
|
}
|
|
|
|
if ((style != old_style || ! span_open) && ! isspace(c))
|
|
{
|
|
old_style = style;
|
|
styles[style][USED] = 1;
|
|
if (span_open)
|
|
{
|
|
g_string_append(body, "</span>");
|
|
}
|
|
if (i < doc_len)
|
|
{
|
|
g_string_append_printf(body, "<span class=\"style_%d\">", style);
|
|
span_open = TRUE;
|
|
}
|
|
}
|
|
/* escape the current character if necessary else just add it */
|
|
switch (c)
|
|
{
|
|
case '\r':
|
|
case '\n':
|
|
{
|
|
if (c == '\r' && c_next == '\n')
|
|
continue; /* when using CR/LF skip CR and add the line break with LF */
|
|
|
|
if (span_open)
|
|
{
|
|
g_string_append(body, "</span>");
|
|
span_open = FALSE;
|
|
}
|
|
g_string_append(body, "<br />\n");
|
|
column = -1;
|
|
break;
|
|
}
|
|
case '\t':
|
|
{
|
|
gint j;
|
|
gint tab_width = sci_get_tab_width(editor->sci);
|
|
gint tab_stop = tab_width - (column % tab_width);
|
|
|
|
column += tab_stop - 1; /* -1 because we add 1 at the end of the loop */
|
|
for (j = 0; j < tab_stop; j++)
|
|
{
|
|
g_string_append(body, " ");
|
|
}
|
|
break;
|
|
}
|
|
case ' ':
|
|
{
|
|
g_string_append(body, " ");
|
|
break;
|
|
}
|
|
case '<':
|
|
{
|
|
g_string_append(body, "<");
|
|
break;
|
|
}
|
|
case '>':
|
|
{
|
|
g_string_append(body, ">");
|
|
break;
|
|
}
|
|
case '&':
|
|
{
|
|
g_string_append(body, "&");
|
|
break;
|
|
}
|
|
default: g_string_append_c(body, c);
|
|
}
|
|
column++;
|
|
}
|
|
if (span_open)
|
|
{
|
|
g_string_append(body, "</span>");
|
|
span_open = FALSE;
|
|
}
|
|
|
|
/* write used styles in the header */
|
|
css = g_string_new("");
|
|
g_string_append_printf(css,
|
|
"\tbody\n\t{\n\t\tfont-family: %s, monospace;\n\t\tfont-size: %dpt;\n\t}\n",
|
|
font_name, font_size);
|
|
|
|
for (i = 0; i < STYLE_MAX; i++)
|
|
{
|
|
if (styles[i][USED])
|
|
{
|
|
g_string_append_printf(css,
|
|
"\t.style_%d\n\t{\n\t\tcolor: #%06x;\n\t\tbackground-color: #%06x;\n%s%s\t}\n",
|
|
i, styles[i][FORE], styles[i][BACK],
|
|
(styles[i][BOLD]) ? "\t\tfont-weight: bold;\n" : "",
|
|
(styles[i][ITALIC]) ? "\t\tfont-style: italic;\n" : "");
|
|
}
|
|
}
|
|
|
|
date = get_date(DATE_TYPE_HTML);
|
|
doc_filename = g_markup_escape_text(DOC_FILENAME(doc), -1);
|
|
/* write all */
|
|
html = g_string_new(TEMPLATE_HTML);
|
|
utils_string_replace_all(html, "{export_date}", date);
|
|
utils_string_replace_all(html, "{export_content}", body->str);
|
|
utils_string_replace_all(html, "{export_styles}", css->str);
|
|
utils_string_replace_all(html, "{export_filename}", doc_filename);
|
|
|
|
write_data(filename, html->str);
|
|
|
|
pango_font_description_free(font_desc);
|
|
g_string_free(body, TRUE);
|
|
g_string_free(css, TRUE);
|
|
g_string_free(html, TRUE);
|
|
g_free(doc_filename);
|
|
g_free(date);
|
|
}
|
|
|
|
|
|
void plugin_init(GeanyData *data)
|
|
{
|
|
GtkWidget *menu_export;
|
|
GtkWidget *menu_export_menu;
|
|
GtkWidget *menu_create_html;
|
|
GtkWidget *menu_create_latex;
|
|
|
|
menu_export = gtk_image_menu_item_new_with_mnemonic(_("_Export"));
|
|
gtk_container_add(GTK_CONTAINER(geany->main_widgets->tools_menu), menu_export);
|
|
|
|
menu_export_menu = gtk_menu_new ();
|
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_export), menu_export_menu);
|
|
|
|
/* HTML */
|
|
menu_create_html = gtk_menu_item_new_with_mnemonic(_("As _HTML..."));
|
|
gtk_container_add(GTK_CONTAINER (menu_export_menu), menu_create_html);
|
|
|
|
g_signal_connect(menu_create_html, "activate", G_CALLBACK(on_menu_create_html_activate), NULL);
|
|
|
|
/* LaTeX */
|
|
menu_create_latex = gtk_menu_item_new_with_mnemonic(_("As _LaTeX..."));
|
|
gtk_container_add(GTK_CONTAINER (menu_export_menu), menu_create_latex);
|
|
|
|
g_signal_connect(menu_create_latex, "activate",
|
|
G_CALLBACK(on_menu_create_latex_activate), NULL);
|
|
|
|
/* disable menu_item when there are no documents open */
|
|
ui_add_document_sensitive(menu_export);
|
|
main_menu_item = menu_export;
|
|
|
|
gtk_widget_show_all(menu_export);
|
|
}
|
|
|
|
|
|
void plugin_cleanup(void)
|
|
{
|
|
gtk_widget_destroy(main_menu_item);
|
|
}
|