4
using System.Collections;
5
using System.Reflection;
8
using System.Xml.XPath;
15
"Export to HTML", Defines.VERSION,
16
PluginInfoAttribute.OFFICIAL_AUTHOR,
17
"Exports individual notes to HTML.",
18
WebSite = Defines.TOMBOY_WEBSITE
20
public class ExportToHTMLPlugin : NotePlugin
22
const string stylesheet_name = "ExportToHTML.xsl";
23
static XslTransform xsl;
25
Gtk.ImageMenuItem item;
27
static ExportToHTMLPlugin ()
29
Assembly asm = Assembly.GetExecutingAssembly ();
30
string asm_dir = System.IO.Path.GetDirectoryName (asm.Location);
31
string stylesheet_file = Path.Combine (asm_dir, stylesheet_name);
33
xsl = new XslTransform ();
35
if (File.Exists (stylesheet_file)) {
36
Logger.Log ("ExportToHTMLPlugin: Using user-custom {0} file.",
38
xsl.Load (stylesheet_file);
40
Stream resource = asm.GetManifestResourceStream (stylesheet_name);
41
if (resource != null) {
42
XmlTextReader reader = new XmlTextReader (resource);
43
xsl.Load (reader, null, null);
46
Logger.Log ("Unable to find HTML export template '{0}'.",
52
protected override void Initialize ()
55
new Gtk.ImageMenuItem (Catalog.GetString ("Export to HTML"));
56
item.Image = new Gtk.Image (Gtk.Stock.Save, Gtk.IconSize.Menu);
57
item.Activated += ExportButtonClicked;
59
AddPluginMenuItem (item);
62
protected override void Shutdown ()
64
// Disconnect the event handlers so
65
// there aren't any memory leaks.
66
item.Activated -= ExportButtonClicked;
69
protected override void OnNoteOpened ()
74
void ExportButtonClicked (object sender, EventArgs args)
76
ExportToHTMLDialog dialog = new ExportToHTMLDialog (Note.Title + ".html");
77
int response = dialog.Run();
78
string output_path = dialog.Filename;
80
if (response != (int) Gtk.ResponseType.Ok) {
85
Logger.Log ("Exporting Note '{0}' to '{1}'...", Note.Title, output_path);
87
StreamWriter writer = null;
88
string error_message = null;
92
// FIXME: Warn about file existing. Allow overwrite.
93
File.Delete (output_path);
97
writer = new StreamWriter (output_path);
98
WriteHTMLForNote (writer, Note, dialog.ExportLinked, dialog.ExportLinkedAll);
100
// Save the dialog preferences now that the note has
101
// successfully been exported
102
dialog.SavePreferences ();
107
Uri output_uri = new Uri (output_path);
108
Gnome.Url.Show (output_uri.AbsoluteUri);
109
} catch (Exception ex) {
110
Logger.Log ("Could not open exported note in a web browser: {0}",
113
string detail = String.Format (
114
Catalog.GetString ("Your note was exported to \"{0}\"."),
117
// Let the user know the note was saved successfully
118
// even though showing the note in a web browser failed.
119
HIGMessageDialog msg_dialog =
120
new HIGMessageDialog (
122
Gtk.DialogFlags.DestroyWithParent,
123
Gtk.MessageType.Info,
125
Catalog.GetString ("Note exported successfully"),
128
msg_dialog.Destroy ();
130
} catch (UnauthorizedAccessException) {
131
error_message = Catalog.GetString ("Access denied.");
132
} catch (DirectoryNotFoundException) {
133
error_message = Catalog.GetString ("Folder does not exist.");
134
} catch (Exception e) {
135
Logger.Log ("Could not export: {0}", e);
137
error_message = e.Message;
143
if (error_message != null)
145
Logger.Log ("Could not export: {0}", error_message);
147
string msg = String.Format (
148
Catalog.GetString ("Could not save the file \"{0}\""),
151
HIGMessageDialog msg_dialog =
152
new HIGMessageDialog (
154
Gtk.DialogFlags.DestroyWithParent,
155
Gtk.MessageType.Error,
160
msg_dialog.Destroy ();
167
public void WriteHTMLForNote (TextWriter writer,
170
bool export_linked_all)
172
// NOTE: Don't use the XmlDocument version, which strips
173
// whitespace between elements for some reason. Also,
174
// XPathDocument is faster.
175
StringWriter s_writer = new StringWriter ();
176
NoteArchiver.Write (s_writer, note.Data);
177
StringReader reader = new StringReader (s_writer.ToString ());
179
XPathDocument doc = new XPathDocument (reader);
182
XsltArgumentList args = new XsltArgumentList ();
183
args.AddParam ("export-linked", "", export_linked);
184
args.AddParam ("export-linked-all", "", export_linked_all);
185
args.AddParam ("root-note", "", note.Title);
187
if ((bool) Preferences.Get (Preferences.ENABLE_CUSTOM_FONT)) {
188
string font_face = (string) Preferences.Get (Preferences.CUSTOM_FONT_FACE);
189
Pango.FontDescription font_desc =
190
Pango.FontDescription.FromString (font_face);
191
string font = String.Format ("font-family:'{0}';", font_desc.Family);
193
args.AddParam ("font", "", font);
196
NoteNameResolver resolver = new NoteNameResolver (note.Manager);
197
xsl.Transform (doc, args, writer, resolver);
201
class NoteNameResolver : XmlResolver
205
public NoteNameResolver (NoteManager manager)
207
this.manager = manager;
210
public override System.Net.ICredentials Credentials
215
public override object GetEntity (Uri absolute_uri, string role, Type of_object_to_return)
217
Note note = manager.FindByUri (absolute_uri.ToString ());
221
StringWriter writer = new StringWriter ();
222
NoteArchiver.Write (writer, note.Data);
223
Stream stream = WriterToStream (writer);
226
Logger.Log ("GetEntity: Returning Stream");
230
// Using UTF-16 does not work - the document is not processed.
231
// Also, the byte order marker (BOM in short, locate at U+FEFF,
232
// 0xef 0xbb 0xbf in UTF-8) must be included, otherwise parsing fails
233
// as well. This way the buffer contains an exact representation of
234
// the on-disk representation of notes.
236
// See http://en.wikipedia.org/wiki/Byte_Order_Mark for more
237
// information about the BOM.
238
MemoryStream WriterToStream (TextWriter writer)
240
UTF8Encoding encoding = new UTF8Encoding ();
241
string s = writer.ToString ();
242
int bytes_required = 3 + encoding.GetByteCount (s);
243
byte[] buffer = new byte [bytes_required];
247
encoding.GetBytes (s, 0, s.Length, buffer, 3);
248
return new MemoryStream (buffer);
251
public override Uri ResolveUri (Uri baseUri, string relativeUri)
253
Note note = manager.Find (relativeUri);
255
return new Uri (note.Uri);
261
class ExportToHTMLDialog : Gtk.FileChooserDialog
263
Gtk.CheckButton export_linked;
264
Gtk.CheckButton export_linked_all;
266
public ExportToHTMLDialog (string default_file) :
267
base (Catalog.GetString ("Destination for HTML Export"),
268
null, Gtk.FileChooserAction.Save, new object[] {})
270
AddButton (Gtk.Stock.Cancel, Gtk.ResponseType.Cancel);
271
AddButton (Gtk.Stock.Save, Gtk.ResponseType.Ok);
273
DefaultResponse = Gtk.ResponseType.Ok;
275
Gtk.Table table = new Gtk.Table (2, 2, false);
277
export_linked = new Gtk.CheckButton (Catalog.GetString ("Export linked notes"));
278
export_linked.Toggled += OnExportLinkedToggled;
279
table.Attach (export_linked, 0, 2, 0, 1, Gtk.AttachOptions.Fill, 0, 0, 0);
282
new Gtk.CheckButton (Catalog.GetString ("Include all other linked notes"));
283
table.Attach (export_linked_all,
284
1, 2, 1, 2, Gtk.AttachOptions.Expand | Gtk.AttachOptions.Fill, 0, 20, 0);
288
DoOverwriteConfirmation = true;
292
LoadPreferences (default_file);
295
public bool ExportLinked
297
get { return export_linked.Active; }
298
set { export_linked.Active = value; }
301
public bool ExportLinkedAll
303
get { return export_linked_all.Active; }
304
set { export_linked_all.Active = value; }
307
public void SavePreferences ()
309
string dir = System.IO.Path.GetDirectoryName (Filename);
310
Preferences.Set (Preferences.EXPORTHTML_LAST_DIRECTORY, dir);
312
Preferences.Set (Preferences.EXPORTHTML_EXPORT_LINKED, ExportLinked);
313
Preferences.Set (Preferences.EXPORTHTML_EXPORT_LINKED_ALL, ExportLinkedAll);
316
protected void LoadPreferences (string default_file)
318
string last_dir = (string) Preferences.Get (Preferences.EXPORTHTML_LAST_DIRECTORY);
320
last_dir = Environment.GetEnvironmentVariable ("HOME");
321
SetCurrentFolder (last_dir);
322
CurrentName = default_file;
324
ExportLinked = (bool) Preferences.Get (Preferences.EXPORTHTML_EXPORT_LINKED);
325
ExportLinkedAll = (bool) Preferences.Get (Preferences.EXPORTHTML_EXPORT_LINKED_ALL);
328
protected void OnExportLinkedToggled (object sender, EventArgs args)
330
if (export_linked.Active)
331
export_linked_all.Sensitive = true;
333
export_linked_all.Sensitive = false;