~mc.../inkscape/inkscape

« back to all changes in this revision

Viewing changes to src/file.cpp

  • Committer: mental
  • Date: 2006-01-16 02:36:01 UTC
  • Revision ID: mental@users.sourceforge.net-20060116023601-wkr0h7edl5veyudq
moving trunk for module inkscape

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define __SP_FILE_C__
 
2
 
 
3
/*
 
4
 * File/Print operations
 
5
 *
 
6
 * Authors:
 
7
 *   Lauris Kaplinski <lauris@kaplinski.com>
 
8
 *   Chema Celorio <chema@celorio.com>
 
9
 *   bulia byak <buliabyak@users.sf.net>
 
10
 *
 
11
 * Copyright (C) 1999-2005 Authors
 
12
 * Copyright (C) 2001-2002 Ximian, Inc.
 
13
 * Copyright (C) 2004 David Turner
 
14
 *
 
15
 * Released under GNU GPL, read the file 'COPYING' for more information
 
16
 */
 
17
 
 
18
/**
 
19
 * Note: This file needs to be cleaned up extensively.
 
20
 * What it probably needs is to have one .h file for
 
21
 * the API, and two or more .cpp files for the implementations.
 
22
 */
 
23
 
 
24
#ifdef HAVE_CONFIG_H
 
25
# include "config.h"
 
26
#endif
 
27
 
 
28
#include <libnr/nr-pixops.h>
 
29
 
 
30
#include "document-private.h"
 
31
#include "selection-chemistry.h"
 
32
#include "ui/view/view-widget.h"
 
33
#include "dir-util.h"
 
34
#include "helper/png-write.h"
 
35
#include "dialogs/export.h"
 
36
#include <glibmm/i18n.h>
 
37
#include "inkscape.h"
 
38
#include "desktop.h"
 
39
#include "selection.h"
 
40
#include "interface.h"
 
41
#include "style.h"
 
42
#include "print.h"
 
43
#include "file.h"
 
44
#include "message-stack.h"
 
45
#include "dialogs/filedialog.h"
 
46
#include "prefs-utils.h"
 
47
#include "path-prefix.h"
 
48
 
 
49
#include "sp-namedview.h"
 
50
#include "desktop-handles.h"
 
51
 
 
52
#include "extension/db.h"
 
53
#include "extension/input.h"
 
54
#include "extension/output.h"
 
55
/* #include "extension/menu.h"  */
 
56
#include "extension/system.h"
 
57
 
 
58
#include "io/sys.h"
 
59
#include "application/application.h"
 
60
#include "application/editor.h"
 
61
#include "inkscape.h"
 
62
#include "uri.h"
 
63
 
 
64
#ifdef WITH_INKBOARD
 
65
#include "jabber_whiteboard/session-manager.h"
 
66
#endif
 
67
 
 
68
/**
 
69
 * 'Current' paths.  Used to remember which directory
 
70
 * had the last file accessed.
 
71
 * Static globals are evil.  This will be gone soon
 
72
 * as C++ification continues
 
73
 */
 
74
static gchar *import_path = NULL;
 
75
 
 
76
//#define INK_DUMP_FILENAME_CONV 1
 
77
#undef INK_DUMP_FILENAME_CONV
 
78
 
 
79
//#define INK_DUMP_FOPEN 1
 
80
#undef INK_DUMP_FOPEN
 
81
 
 
82
void dump_str(gchar const *str, gchar const *prefix);
 
83
void dump_ustr(Glib::ustring const &ustr);
 
84
 
 
85
 
 
86
/*######################
 
87
## N E W
 
88
######################*/
 
89
 
 
90
/**
 
91
 * Create a blank document and add it to the desktop
 
92
 */
 
93
SPDesktop*
 
94
sp_file_new(gchar const *templ)
 
95
{
 
96
    SPDocument *doc = sp_document_new(templ, TRUE, true);
 
97
    g_return_val_if_fail(doc != NULL, NULL);
 
98
 
 
99
    SPDesktop *dt;
 
100
    if (Inkscape::NSApplication::Application::getNewGui())
 
101
    {
 
102
        dt = Inkscape::NSApplication::Editor::createDesktop (doc);
 
103
    } else {
 
104
        SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
 
105
        g_return_val_if_fail(dtw != NULL, NULL);
 
106
        sp_document_unref(doc);
 
107
 
 
108
        sp_create_window(dtw, TRUE);
 
109
        dt = static_cast<SPDesktop*>(dtw->view);
 
110
        sp_namedview_window_from_document(dt);
 
111
    }
 
112
    return dt;
 
113
}
 
114
 
 
115
SPDesktop*
 
116
sp_file_new_default()
 
117
{
 
118
    std::list<gchar *> sources;
 
119
    sources.push_back( profile_path("templates") ); // first try user's local dir
 
120
    sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
 
121
 
 
122
    while (!sources.empty()) {
 
123
        gchar *dirname = sources.front();
 
124
        if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
 
125
 
 
126
            // TRANSLATORS: default.svg is localizable - this is the name of the default document
 
127
            //  template. This way you can localize the default pagesize, translate the name of
 
128
            //  the default layer, etc. If you wish to localize this file, please create a
 
129
            //  localized share/templates/default.xx.svg file, where xx is your language code.
 
130
            char *default_template = g_build_filename(dirname, _("default.svg"), NULL);
 
131
            if (Inkscape::IO::file_test(default_template, G_FILE_TEST_IS_REGULAR)) {
 
132
                return sp_file_new(default_template);
 
133
            } 
 
134
        }
 
135
        g_free(dirname);
 
136
        sources.pop_front();
 
137
    }
 
138
 
 
139
    return sp_file_new(NULL);
 
140
}
 
141
 
 
142
 
 
143
/*######################
 
144
## D E L E T E
 
145
######################*/
 
146
 
 
147
/**
 
148
 *  Perform document closures preceding an exit()
 
149
 */
 
150
void
 
151
sp_file_exit()
 
152
{
 
153
    sp_ui_close_all();
 
154
    // no need to call inkscape_exit here; last document being closed will take care of that
 
155
}
 
156
 
 
157
 
 
158
/*######################
 
159
## O P E N
 
160
######################*/
 
161
 
 
162
/**
 
163
 *  Open a file, add the document to the desktop
 
164
 *
 
165
 *  \param replace_empty if true, and the current desktop is empty, this document
 
166
 *  will replace the empty one.
 
167
 */
 
168
bool
 
169
sp_file_open(gchar const *uri, Inkscape::Extension::Extension *key, bool add_to_recent, bool replace_empty)
 
170
{
 
171
    SPDocument *doc;
 
172
    try {
 
173
        doc = Inkscape::Extension::open(key, uri);
 
174
    } catch (Inkscape::Extension::Input::no_extension_found &e) {
 
175
        doc = NULL;
 
176
    } catch (Inkscape::Extension::Input::open_failed &e) {
 
177
        doc = NULL;
 
178
    }
 
179
 
 
180
    if (doc) {
 
181
        SPDesktop *desktop = SP_ACTIVE_DESKTOP;
 
182
        SPDocument *existing = desktop ? SP_DT_DOCUMENT(desktop) : NULL;
 
183
        
 
184
        if (existing && existing->virgin && replace_empty) {
 
185
            // If the current desktop is empty, open the document there
 
186
            desktop->change_document(doc);
 
187
        } else {
 
188
            if (!Inkscape::NSApplication::Application::getNewGui()) {
 
189
                // create a whole new desktop and window
 
190
                SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
 
191
                sp_create_window(dtw, TRUE);
 
192
                desktop = static_cast<SPDesktop*>(dtw->view);
 
193
            } else {
 
194
                desktop = Inkscape::NSApplication::Editor::createDesktop (doc);
 
195
            }
 
196
        }
 
197
        
 
198
        doc->virgin = FALSE;
 
199
        // everyone who cares now has a reference, get rid of ours
 
200
        sp_document_unref(doc);
 
201
        // resize the window to match the document properties
 
202
        // (this may be redundant for new windows... if so, move to the "virgin"
 
203
        //  section above)
 
204
#ifdef WITH_INKBOARD
 
205
                desktop->whiteboard_session_manager()->setDesktop(desktop);
 
206
#endif
 
207
        sp_namedview_window_from_document(desktop);
 
208
 
 
209
        if (add_to_recent) {
 
210
            prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
 
211
        }
 
212
 
 
213
        return TRUE;
 
214
    } else {
 
215
        gchar *safeUri = Inkscape::IO::sanitizeString(uri);
 
216
        gchar *text = g_strdup_printf(_("Failed to load the requested file %s"), safeUri);
 
217
        sp_ui_error_dialog(text);
 
218
        g_free(text);
 
219
        g_free(safeUri);
 
220
        return FALSE;
 
221
    }
 
222
}
 
223
 
 
224
/**
 
225
 *  Handle prompting user for "do you want to revert"?  Revert on "OK"
 
226
 */
 
227
void
 
228
sp_file_revert_dialog()
 
229
{
 
230
    SPDesktop  *desktop = SP_ACTIVE_DESKTOP;
 
231
    g_assert(desktop != NULL);
 
232
 
 
233
    SPDocument *doc = SP_DT_DOCUMENT(desktop);
 
234
    g_assert(doc != NULL);
 
235
 
 
236
    Inkscape::XML::Node     *repr = sp_document_repr_root(doc);
 
237
    g_assert(repr != NULL);
 
238
 
 
239
    gchar const *uri = doc->uri;
 
240
    if (!uri) {
 
241
        desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved yet.  Cannot revert."));
 
242
        return;
 
243
    }
 
244
 
 
245
    bool do_revert = true;
 
246
    if (repr->attribute("sodipodi:modified") != NULL) {
 
247
        gchar *text = g_strdup_printf(_("Changes will be lost!  Are you sure you want to reload document %s?"), uri);
 
248
 
 
249
        bool response = desktop->warnDialog (text);
 
250
        g_free(text);
 
251
 
 
252
        if (!response) {
 
253
            do_revert = false;
 
254
        }
 
255
    }
 
256
 
 
257
    bool reverted;
 
258
    if (do_revert) {
 
259
        // Allow overwriting of current document.
 
260
        doc->virgin = TRUE;
 
261
        reverted = sp_file_open(uri,NULL);
 
262
    } else {
 
263
        reverted = false;
 
264
    }
 
265
 
 
266
    if (reverted) {
 
267
        desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Document reverted."));
 
268
    } else {
 
269
        desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not reverted."));
 
270
    }
 
271
}
 
272
 
 
273
void dump_str(gchar const *str, gchar const *prefix)
 
274
{
 
275
    Glib::ustring tmp;
 
276
    tmp = prefix;
 
277
    tmp += " [";
 
278
    size_t const total = strlen(str);
 
279
    for (unsigned i = 0; i < total; i++) {
 
280
        gchar *const tmp2 = g_strdup_printf(" %02x", (0x0ff & str[i]));
 
281
        tmp += tmp2;
 
282
        g_free(tmp2);
 
283
    }
 
284
 
 
285
    tmp += "]";
 
286
    g_message(tmp.c_str());
 
287
}
 
288
 
 
289
void dump_ustr(Glib::ustring const &ustr)
 
290
{
 
291
    char const *cstr = ustr.c_str();
 
292
    char const *data = ustr.data();
 
293
    Glib::ustring::size_type const byteLen = ustr.bytes();
 
294
    Glib::ustring::size_type const dataLen = ustr.length();
 
295
    Glib::ustring::size_type const cstrLen = strlen(cstr);
 
296
 
 
297
    g_message("   size: %lu\n   length: %lu\n   bytes: %lu\n    clen: %lu",
 
298
              gulong(ustr.size()), gulong(dataLen), gulong(byteLen), gulong(cstrLen) );
 
299
    g_message( "  ASCII? %s", (ustr.is_ascii() ? "yes":"no") );
 
300
    g_message( "  UTF-8? %s", (ustr.validate() ? "yes":"no") );
 
301
 
 
302
    try {
 
303
        Glib::ustring tmp;
 
304
        for (Glib::ustring::size_type i = 0; i < ustr.bytes(); i++) {
 
305
            tmp = "    ";
 
306
            if (i < dataLen) {
 
307
                Glib::ustring::value_type val = ustr.at(i);
 
308
                gchar* tmp2 = g_strdup_printf( (((val & 0xff00) == 0) ? "  %02x" : "%04x"), val );
 
309
                tmp += tmp2;
 
310
                g_free( tmp2 );
 
311
            } else {
 
312
                tmp += "    ";
 
313
            }
 
314
 
 
315
            if (i < byteLen) {
 
316
                int val = (0x0ff & data[i]);
 
317
                gchar *tmp2 = g_strdup_printf("    %02x", val);
 
318
                tmp += tmp2;
 
319
                g_free( tmp2 );
 
320
                if ( val > 32 && val < 127 ) {
 
321
                    tmp2 = g_strdup_printf( "   '%c'", (gchar)val );
 
322
                    tmp += tmp2;
 
323
                    g_free( tmp2 );
 
324
                } else {
 
325
                    tmp += "    . ";
 
326
                }
 
327
            } else {
 
328
                tmp += "       ";
 
329
            }
 
330
 
 
331
            if ( i < cstrLen ) {
 
332
                int val = (0x0ff & cstr[i]);
 
333
                gchar* tmp2 = g_strdup_printf("    %02x", val);
 
334
                tmp += tmp2;
 
335
                g_free(tmp2);
 
336
                if ( val > 32 && val < 127 ) {
 
337
                    tmp2 = g_strdup_printf("   '%c'", (gchar) val);
 
338
                    tmp += tmp2;
 
339
                    g_free( tmp2 );
 
340
                } else {
 
341
                    tmp += "    . ";
 
342
                }
 
343
            } else {
 
344
                tmp += "            ";
 
345
            }
 
346
 
 
347
            g_message( tmp.c_str() );
 
348
        }
 
349
    } catch (...) {
 
350
        g_message("XXXXXXXXXXXXXXXXXX Exception" );
 
351
    }
 
352
    g_message("---------------");
 
353
}
 
354
 
 
355
static Inkscape::UI::Dialogs::FileOpenDialog *openDialogInstance = NULL;
 
356
 
 
357
/**
 
358
 *  Display an file Open selector.  Open a document if OK is pressed.
 
359
 *  Can select single or multiple files for opening.
 
360
 */
 
361
void
 
362
sp_file_open_dialog(gpointer object, gpointer data)
 
363
{
 
364
    gchar *open_path2 = NULL;
 
365
 
 
366
    gchar *open_path = g_strdup(prefs_get_string_attribute("dialogs.open", "path"));
 
367
    if (open_path != NULL && open_path[0] == '\0') {
 
368
        g_free(open_path);
 
369
        open_path = NULL;
 
370
    }
 
371
    if (open_path && !Inkscape::IO::file_test(open_path, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
 
372
        g_free(open_path);
 
373
        open_path = NULL;
 
374
    }
 
375
    if (open_path == NULL)
 
376
        open_path = g_strconcat(g_get_home_dir(), G_DIR_SEPARATOR_S, NULL);
 
377
 
 
378
    if (!openDialogInstance) {
 
379
        openDialogInstance =
 
380
              Inkscape::UI::Dialogs::FileOpenDialog::create(
 
381
                 (char const *)open_path,
 
382
                 Inkscape::UI::Dialogs::SVG_TYPES,
 
383
                 (char const *)_("Select file to open"));
 
384
    }
 
385
    bool const success = openDialogInstance->show();
 
386
    gchar *fileName = ( success
 
387
                        ? g_strdup(openDialogInstance->getFilename())
 
388
                        : NULL );
 
389
    Inkscape::Extension::Extension *selection =
 
390
            openDialogInstance->getSelectionType();
 
391
    g_free(open_path);
 
392
 
 
393
    if (!success) return;
 
394
 
 
395
    // Code to check & open iff multiple files.
 
396
    Glib::SListHandle<Glib::ustring> flist=openDialogInstance->getFilenames();
 
397
    GSList *list=flist.data();
 
398
 
 
399
    if(g_slist_length(list)>1)
 
400
    {
 
401
        gchar *fileName=NULL;
 
402
 
 
403
        while(list!=NULL)
 
404
        {
 
405
 
 
406
#ifdef INK_DUMP_FILENAME_CONV
 
407
            g_message(" FileName: %s",(const char *)list->data);           
 
408
#endif
 
409
 
 
410
            fileName=(gchar *)g_strdup((gchar *)list->data);
 
411
 
 
412
            if (fileName && !g_file_test(fileName,G_FILE_TEST_IS_DIR)) {
 
413
                gsize bytesRead = 0;
 
414
                gsize bytesWritten = 0;
 
415
                GError *error = NULL;
 
416
#ifdef INK_DUMP_FILENAME_CONV
 
417
                dump_str( fileName, "A file pre  is " );
 
418
#endif
 
419
                gchar *newFileName = g_filename_to_utf8(fileName,
 
420
                                                -1,
 
421
                                                        &bytesRead,
 
422
                                                        &bytesWritten,
 
423
                                                        &error);
 
424
                if ( newFileName != NULL ) {
 
425
                    g_free(fileName);
 
426
                    fileName = newFileName;
 
427
#ifdef INK_DUMP_FILENAME_CONV
 
428
                    dump_str( fileName, "A file post is " );
 
429
#endif
 
430
                } else {
 
431
                    // TODO: bulia, please look over
 
432
                    g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
 
433
                }
 
434
 
 
435
#ifdef INK_DUMP_FILENAME_CONV                
 
436
                g_message("Opening File %s\n",fileName);
 
437
#endif
 
438
 
 
439
                sp_file_open(fileName, selection);
 
440
                g_free(fileName);
 
441
            }
 
442
            else
 
443
            {
 
444
                g_message("Cannot Open Directory %s\n",fileName);
 
445
            }
 
446
            
 
447
            list=list->next;
 
448
        }
 
449
 
 
450
        return;
 
451
    }
 
452
 
 
453
 
 
454
    if (fileName) {
 
455
        gsize bytesRead = 0;
 
456
        gsize bytesWritten = 0;
 
457
        GError *error = NULL;
 
458
#ifdef INK_DUMP_FILENAME_CONV
 
459
        dump_str( fileName, "A file pre  is " );
 
460
#endif
 
461
        gchar *newFileName = g_filename_to_utf8(fileName,
 
462
                                                -1,
 
463
                                                &bytesRead,
 
464
                                                &bytesWritten,
 
465
                                                &error);
 
466
        if ( newFileName != NULL ) {
 
467
            g_free(fileName);
 
468
            fileName = newFileName;
 
469
#ifdef INK_DUMP_FILENAME_CONV
 
470
            dump_str( fileName, "A file post is " );
 
471
#endif
 
472
        } else {
 
473
            // TODO: bulia, please look over
 
474
            g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
 
475
        }
 
476
 
 
477
 
 
478
        if ( !g_utf8_validate(fileName, -1, NULL) ) {
 
479
            // TODO: bulia, please look over
 
480
            g_warning( "INPUT FILENAME IS NOT UTF-8" );
 
481
        }
 
482
 
 
483
 
 
484
        open_path = g_dirname(fileName);
 
485
        open_path2 = g_strconcat(open_path, G_DIR_SEPARATOR_S, NULL);
 
486
        prefs_set_string_attribute("dialogs.open", "path", open_path2);
 
487
        g_free(open_path);
 
488
        g_free(open_path2);
 
489
 
 
490
        sp_file_open(fileName, selection);
 
491
        g_free(fileName);
 
492
    }
 
493
 
 
494
    return;
 
495
}
 
496
 
 
497
 
 
498
/*######################
 
499
## V A C U U M
 
500
######################*/
 
501
 
 
502
/**
 
503
 * Remove unreferenced defs from the defs section of the document.
 
504
 */
 
505
 
 
506
 
 
507
void
 
508
sp_file_vacuum()
 
509
{
 
510
    SPDocument *doc = SP_ACTIVE_DOCUMENT;
 
511
 
 
512
    unsigned int diff = vacuum_document (doc);
 
513
 
 
514
    sp_document_done(doc);
 
515
 
 
516
    SPDesktop *dt = SP_ACTIVE_DESKTOP;
 
517
    if (diff > 0) {
 
518
        dt->messageStack()->flashF(Inkscape::NORMAL_MESSAGE,
 
519
                ngettext("Removed <b>%i</b> unused definition in &lt;defs&gt;.",
 
520
                         "Removed <b>%i</b> unused definitions in &lt;defs&gt;.",
 
521
                         diff),
 
522
                diff);
 
523
    } else {
 
524
        dt->messageStack()->flash(Inkscape::NORMAL_MESSAGE,  _("No unused definitions in &lt;defs&gt;."));
 
525
    }
 
526
}
 
527
 
 
528
 
 
529
 
 
530
/*######################
 
531
## S A V E
 
532
######################*/
 
533
 
 
534
/**
 
535
 * This 'save' function called by the others below
 
536
 */
 
537
static bool
 
538
file_save(SPDocument *doc, gchar const *uri, Inkscape::Extension::Extension *key, bool saveas)
 
539
{
 
540
    if (!doc || !uri) //Safety check
 
541
        return FALSE;
 
542
 
 
543
    try {
 
544
        Inkscape::Extension::save(key, doc, uri,
 
545
                                  saveas && prefs_get_int_attribute("dialogs.save_as", "append_extension", 1),
 
546
                                  saveas, TRUE); // save officially, with inkscape: attributes set
 
547
    } catch (Inkscape::Extension::Output::no_extension_found &e) {
 
548
        gchar *safeUri = Inkscape::IO::sanitizeString(uri);
 
549
        gchar *text = g_strdup_printf(_("No Inkscape extension found to save document (%s).  This may have been caused by an unknown filename extension."), safeUri);
 
550
        SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved."));
 
551
        sp_ui_error_dialog(text);
 
552
        g_free(text);
 
553
        g_free(safeUri);
 
554
        return FALSE;
 
555
    } catch (Inkscape::Extension::Output::save_failed &e) {
 
556
        gchar *safeUri = Inkscape::IO::sanitizeString(uri);
 
557
        gchar *text = g_strdup_printf(_("File %s could not be saved."), safeUri);
 
558
        SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved."));
 
559
        sp_ui_error_dialog(text);
 
560
        g_free(text);
 
561
        g_free(safeUri);
 
562
        return FALSE;
 
563
    } catch (Inkscape::Extension::Output::no_overwrite &e) {
 
564
        return sp_file_save_dialog(doc);
 
565
    }
 
566
 
 
567
    SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Document saved."));
 
568
    return TRUE;
 
569
}
 
570
 
 
571
static Inkscape::UI::Dialogs::FileSaveDialog *saveDialogInstance = NULL;
 
572
 
 
573
/**
 
574
 *  Display a SaveAs dialog.  Save the document if OK pressed.
 
575
 */
 
576
gboolean
 
577
sp_file_save_dialog(SPDocument *doc)
 
578
{
 
579
    Inkscape::XML::Node *repr = sp_document_repr_root(doc);
 
580
    gchar const *default_extension = NULL;
 
581
    gchar *save_loc;
 
582
    Inkscape::Extension::Output *extension;
 
583
    gchar *save_path = NULL;
 
584
 
 
585
    default_extension = repr->attribute("inkscape:output_extension");
 
586
    if (default_extension == NULL) {
 
587
        default_extension = prefs_get_string_attribute("dialogs.save_as", "default");
 
588
    }
 
589
    //g_warning("%s: extension name: '%s'", __FUNCTION__, default_extension);
 
590
 
 
591
    if (doc->uri == NULL) {
 
592
        int i = 1;
 
593
        char const *filename_extension;
 
594
        char *temp_filename;
 
595
 
 
596
        extension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(default_extension));
 
597
        //g_warning("%s: extension ptr: 0x%x", __FUNCTION__, (unsigned int)extension);
 
598
        if (extension == NULL) {
 
599
            filename_extension = ".svg";
 
600
        } else {
 
601
            filename_extension = extension->get_extension();
 
602
        }
 
603
 
 
604
        save_path = g_strdup(prefs_get_string_attribute("dialogs.save_as", "path"));
 
605
        if (save_path != NULL && save_path[0] == '\0') {
 
606
            g_free(save_path);
 
607
            save_path = NULL;
 
608
        }
 
609
        if (save_path && !Inkscape::IO::file_test(save_path, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
 
610
            g_free(save_path);
 
611
            save_path = NULL;
 
612
        }
 
613
        if (save_path == NULL)
 
614
            save_path = g_strdup(g_get_home_dir());
 
615
        temp_filename = g_strdup_printf(_("drawing%s"), filename_extension);
 
616
        save_loc = g_build_filename(save_path, temp_filename, NULL);
 
617
        g_free(temp_filename);
 
618
 
 
619
        while (Inkscape::IO::file_test(save_loc, G_FILE_TEST_EXISTS)) {
 
620
            g_free(save_loc);
 
621
            temp_filename = g_strdup_printf(_("drawing-%d%s"), i++, filename_extension);
 
622
            save_loc = g_build_filename(save_path, temp_filename, NULL);
 
623
            g_free(temp_filename);
 
624
        }
 
625
    } else {
 
626
        save_loc = g_path_get_dirname(doc->uri); /* \todo should use a getter */
 
627
    }
 
628
 
 
629
    { // convert save_loc from utf-8 to locale
 
630
      // is this needed any more, now that everything is handled in
 
631
      // Inkscape::IO?
 
632
        gsize bytesRead = 0;
 
633
        gsize bytesWritten = 0;
 
634
        GError* error = NULL;
 
635
#ifdef INK_DUMP_FILENAME_CONV
 
636
        dump_str( save_loc, "B file pre  is " );
 
637
#endif
 
638
        gchar* save_loc_local = g_filename_from_utf8( save_loc, -1, &bytesRead, &bytesWritten, &error);
 
639
 
 
640
        if ( save_loc_local != NULL ) {
 
641
            g_free(save_loc);
 
642
            save_loc = save_loc_local;
 
643
#ifdef INK_DUMP_FILENAME_CONV
 
644
            dump_str( save_loc, "B file post is " );
 
645
#endif
 
646
        } else {
 
647
            //g_warning( "Error converting save filename stored in the file to locale encoding.");
 
648
        }
 
649
    }
 
650
 
 
651
    if (!saveDialogInstance) {
 
652
        saveDialogInstance =
 
653
             Inkscape::UI::Dialogs::FileSaveDialog::create(
 
654
                 (char const *) save_loc,
 
655
                 Inkscape::UI::Dialogs::SVG_TYPES,
 
656
                 (char const *) _("Select file to save to"),
 
657
                 default_extension
 
658
            );
 
659
    } // FIXME: else (i.e. if reshowing an already shown dialog) save_loc is not used, it thus always displays the previously opened dir
 
660
    bool success = saveDialogInstance->show();
 
661
    char *fileName = ( success
 
662
                       ? g_strdup(saveDialogInstance->getFilename())
 
663
                       : NULL );
 
664
    Inkscape::Extension::Extension *selectionType =
 
665
        saveDialogInstance->getSelectionType();
 
666
    g_free(save_loc);
 
667
    g_free(save_path);
 
668
    if (!success) {
 
669
        return success;
 
670
    }
 
671
 
 
672
    if (fileName && *fileName) {
 
673
        gsize bytesRead = 0;
 
674
        gsize bytesWritten = 0;
 
675
        GError *error = NULL;
 
676
#ifdef INK_DUMP_FILENAME_CONV
 
677
        dump_str( fileName, "C file pre  is " );
 
678
#endif
 
679
        gchar *newFileName = g_filename_to_utf8(fileName,
 
680
                                                -1,
 
681
                                                &bytesRead,
 
682
                                                &bytesWritten,
 
683
                                                &error);
 
684
        if ( newFileName != NULL ) {
 
685
            g_free(fileName);
 
686
            fileName = newFileName;
 
687
#ifdef INK_DUMP_FILENAME_CONV
 
688
            dump_str( fileName, "C file post is " );
 
689
#endif
 
690
        } else {
 
691
            g_warning( "Error converting save filename to UTF-8." );
 
692
        }
 
693
 
 
694
        if (!g_utf8_validate(fileName, -1, NULL)) {
 
695
            // TODO: bulia, please look over
 
696
            g_warning( "The filename is not UTF-8." );
 
697
        }
 
698
 
 
699
        success = file_save(doc, fileName, selectionType, TRUE);
 
700
 
 
701
        if (success) {
 
702
            prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
 
703
        }
 
704
 
 
705
        save_path = g_dirname(fileName);
 
706
        prefs_set_string_attribute("dialogs.save_as", "path", save_path);
 
707
        g_free(save_path);
 
708
 
 
709
        g_free(fileName);
 
710
        return success;
 
711
    } else {
 
712
        return FALSE;
 
713
    }
 
714
}
 
715
 
 
716
 
 
717
/**
 
718
 * Save a document, displaying a SaveAs dialog if necessary.
 
719
 */
 
720
gboolean
 
721
sp_file_save_document(SPDocument *doc)
 
722
{
 
723
    gboolean success = TRUE;
 
724
 
 
725
    Inkscape::XML::Node *repr = sp_document_repr_root(doc);
 
726
 
 
727
    gchar const *fn = repr->attribute("sodipodi:modified");
 
728
    if (fn != NULL) {
 
729
        if (doc->uri == NULL
 
730
            || repr->attribute("inkscape:output_extension") == NULL)
 
731
        {
 
732
            return sp_file_save_dialog(doc);
 
733
        } else {
 
734
            fn = g_strdup(doc->uri);
 
735
            gchar const *ext = repr->attribute("inkscape:output_extension");
 
736
            success = file_save(doc, fn, Inkscape::Extension::db.get(ext), FALSE);
 
737
            g_free((void *) fn);
 
738
        }
 
739
    } else {
 
740
        SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No changes need to be saved."));
 
741
        success = TRUE;
 
742
    }
 
743
 
 
744
    return success;
 
745
}
 
746
 
 
747
 
 
748
/**
 
749
 * Save a document.
 
750
 */
 
751
bool
 
752
sp_file_save(gpointer object, gpointer data)
 
753
{
 
754
    if (!SP_ACTIVE_DOCUMENT)
 
755
        return false;
 
756
    sp_namedview_document_from_window(SP_ACTIVE_DESKTOP);
 
757
    return sp_file_save_document(SP_ACTIVE_DOCUMENT);
 
758
}
 
759
 
 
760
 
 
761
/**
 
762
 *  Save a document, always displaying the SaveAs dialog.
 
763
 */
 
764
bool
 
765
sp_file_save_as(gpointer object, gpointer data)
 
766
{
 
767
    if (!SP_ACTIVE_DOCUMENT)
 
768
        return false;
 
769
    sp_namedview_document_from_window(SP_ACTIVE_DESKTOP);
 
770
    return sp_file_save_dialog(SP_ACTIVE_DOCUMENT);
 
771
}
 
772
 
 
773
 
 
774
 
 
775
 
 
776
/*######################
 
777
## I M P O R T
 
778
######################*/
 
779
 
 
780
/**
 
781
 *  Import a resource.  Called by sp_file_import()
 
782
 */
 
783
void
 
784
file_import(SPDocument *in_doc, gchar const *uri, Inkscape::Extension::Extension *key)
 
785
{
 
786
    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
 
787
 
 
788
    //DEBUG_MESSAGE( fileImport, "file_import( in_doc:%p uri:[%s], key:%p", in_doc, uri, key );
 
789
    SPDocument *doc;
 
790
    try {
 
791
        doc = Inkscape::Extension::open(key, uri);
 
792
    } catch (Inkscape::Extension::Input::no_extension_found &e) {
 
793
        doc = NULL;
 
794
    } catch (Inkscape::Extension::Input::open_failed &e) {
 
795
        doc = NULL;
 
796
    }
 
797
 
 
798
    if (doc != NULL) {
 
799
        // the import extension has passed us a document, now we need to embed it into our document
 
800
        if ( 0 ) {
 
801
//            const gchar *docbase = (sp_repr_document_root( sp_repr_document( repr ))->attribute("sodipodi:docbase" );
 
802
            g_message(" settings  uri  [%s]", doc->uri );
 
803
            g_message("           base [%s]", doc->base );
 
804
            g_message("           name [%s]", doc->name );
 
805
            Inkscape::IO::fixupHrefs( doc, doc->base, TRUE );
 
806
            g_message("        mid-fixup");
 
807
            Inkscape::IO::fixupHrefs( doc, in_doc->base, TRUE );
 
808
        }
 
809
 
 
810
        // move imported defs to our document's defs
 
811
        SPObject *in_defs = SP_DOCUMENT_DEFS(in_doc);
 
812
        SPObject *defs = SP_DOCUMENT_DEFS(doc);
 
813
        Inkscape::XML::Node *last_def = SP_OBJECT_REPR(in_defs)->lastChild();
 
814
        for (SPObject *child = sp_object_first_child(defs);
 
815
             child != NULL; child = SP_OBJECT_NEXT(child))
 
816
        {
 
817
            // FIXME: in case of id conflict, newly added thing will be re-ided and thus likely break a reference to it from imported stuff
 
818
            SP_OBJECT_REPR(in_defs)->addChild(SP_OBJECT_REPR(child)->duplicate(), last_def);
 
819
        }
 
820
 
 
821
        guint items_count = 0;
 
822
        for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc));
 
823
             child != NULL; child = SP_OBJECT_NEXT(child)) {
 
824
            if (SP_IS_ITEM(child))
 
825
                items_count ++;
 
826
        }
 
827
        SPCSSAttr *style = sp_css_attr_from_object (SP_DOCUMENT_ROOT (doc));
 
828
 
 
829
        SPObject *new_obj = NULL;
 
830
 
 
831
        if ((style && style->firstChild()) || items_count > 1) {
 
832
            // create group
 
833
            Inkscape::XML::Node *newgroup = sp_repr_new("svg:g");
 
834
            sp_repr_css_set (newgroup, style, "style");
 
835
 
 
836
            for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc)); child != NULL; child = SP_OBJECT_NEXT(child) ) {
 
837
                if (SP_IS_ITEM(child)) {
 
838
                    Inkscape::XML::Node *newchild = SP_OBJECT_REPR(child)->duplicate();
 
839
 
 
840
                    // convert layers to groups; FIXME: add "preserve layers" mode where each layer
 
841
                    // from impot is copied to the same-named layer in host
 
842
                    newchild->setAttribute("inkscape:groupmode", NULL);
 
843
 
 
844
                    newgroup->appendChild(newchild);
 
845
                }
 
846
            }
 
847
 
 
848
            if (desktop) {
 
849
                // Add it to the current layer
 
850
                new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
 
851
            } else {
 
852
                // There's no desktop (command line run?)
 
853
                // FIXME: For such cases we need a document:: method to return the current layer
 
854
                new_obj = SP_DOCUMENT_ROOT(in_doc)->appendChildRepr(newgroup);
 
855
            }
 
856
 
 
857
            Inkscape::GC::release(newgroup);
 
858
        } else {
 
859
            // just add one item
 
860
            for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc)); child != NULL; child = SP_OBJECT_NEXT(child) ) {
 
861
                if (SP_IS_ITEM(child)) {
 
862
                    Inkscape::XML::Node *newitem = SP_OBJECT_REPR(child)->duplicate();
 
863
                    newitem->setAttribute("inkscape:groupmode", NULL);
 
864
 
 
865
                    if (desktop) {
 
866
                        // Add it to the current layer
 
867
                        new_obj = desktop->currentLayer()->appendChildRepr(newitem);
 
868
                    } else {
 
869
                        // There's no desktop (command line run?)
 
870
                        // FIXME: For such cases we need a document:: method to return the current layer
 
871
                        new_obj = SP_DOCUMENT_ROOT(in_doc)->appendChildRepr(newitem);
 
872
                    }
 
873
 
 
874
                }
 
875
            }
 
876
        }
 
877
 
 
878
        if (style) sp_repr_css_attr_unref (style);
 
879
 
 
880
        // select and move the imported item
 
881
        if (new_obj && SP_IS_ITEM(new_obj)) {
 
882
            Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
 
883
            selection->set(SP_ITEM(new_obj));
 
884
 
 
885
            // To move the imported object, we must temporarily set the "transform pattern with
 
886
            // object" option.
 
887
            {
 
888
                int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
 
889
                prefs_set_int_attribute("options.transform", "pattern", 1);
 
890
                sp_document_ensure_up_to_date(SP_DT_DOCUMENT(desktop));
 
891
                NR::Point m( desktop->point() - selection->bounds().midpoint() );
 
892
                sp_selection_move_relative(selection, m);
 
893
                prefs_set_int_attribute("options.transform", "pattern", saved_pref);
 
894
            }
 
895
        }
 
896
 
 
897
        sp_document_unref(doc);
 
898
        sp_document_done(in_doc);
 
899
 
 
900
    } else {
 
901
        gchar *text = g_strdup_printf(_("Failed to load the requested file %s"), uri);
 
902
        sp_ui_error_dialog(text);
 
903
        g_free(text);
 
904
    }
 
905
 
 
906
    return;
 
907
}
 
908
 
 
909
 
 
910
static Inkscape::UI::Dialogs::FileOpenDialog *importDialogInstance = NULL;
 
911
 
 
912
/**
 
913
 *  Display an Open dialog, import a resource if OK pressed.
 
914
 */
 
915
void
 
916
sp_file_import(GtkWidget *widget)
 
917
{
 
918
    SPDocument *doc = SP_ACTIVE_DOCUMENT;
 
919
    if (!doc)
 
920
        return;
 
921
 
 
922
    if (!importDialogInstance) {
 
923
        importDialogInstance =
 
924
             Inkscape::UI::Dialogs::FileOpenDialog::create(
 
925
                 (char const *)import_path,
 
926
                 Inkscape::UI::Dialogs::IMPORT_TYPES,
 
927
                 (char const *)_("Select file to import"));
 
928
    }
 
929
    bool success = importDialogInstance->show();
 
930
    char *fileName = ( success
 
931
                       ? g_strdup(importDialogInstance->getFilename())
 
932
                       : NULL );
 
933
    Inkscape::Extension::Extension *selection =
 
934
        importDialogInstance->getSelectionType();
 
935
 
 
936
    if (!success) return;
 
937
    if (fileName) {
 
938
        gsize bytesRead = 0;
 
939
        gsize bytesWritten = 0;
 
940
        GError *error = NULL;
 
941
#ifdef INK_DUMP_FILENAME_CONV
 
942
        dump_str( fileName, "D file pre  is " );
 
943
#endif
 
944
        gchar *newFileName = g_filename_to_utf8( fileName,
 
945
                                                 -1,
 
946
                                                 &bytesRead,
 
947
                                                 &bytesWritten,
 
948
                                                 &error);
 
949
        if ( newFileName != NULL ) {
 
950
            g_free(fileName);
 
951
            fileName = newFileName;
 
952
#ifdef INK_DUMP_FILENAME_CONV
 
953
            dump_str( fileName, "D file post is " );
 
954
#endif
 
955
        } else {
 
956
            // TODO: bulia, please look over
 
957
            g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
 
958
        }
 
959
 
 
960
 
 
961
        if (!g_utf8_validate(fileName, -1, NULL)) {
 
962
            // TODO: bulia, please look over
 
963
            g_warning( "INPUT FILENAME IS NOT UTF-8" );
 
964
        }
 
965
 
 
966
        g_free(import_path);
 
967
        import_path = g_dirname(fileName);
 
968
        if (import_path) import_path = g_strconcat(import_path, G_DIR_SEPARATOR_S, NULL);
 
969
 
 
970
        file_import(doc, fileName, selection);
 
971
        g_free(fileName);
 
972
    }
 
973
 
 
974
    return;
 
975
}
 
976
 
 
977
 
 
978
 
 
979
/*######################
 
980
## E X P O R T
 
981
######################*/
 
982
 
 
983
/**
 
984
 *
 
985
 */
 
986
void
 
987
sp_file_export_dialog(void *widget)
 
988
{
 
989
    sp_export_dialog();
 
990
}
 
991
 
 
992
#include <display/nr-arena-item.h>
 
993
#include <display/nr-arena.h>
 
994
 
 
995
struct SPEBP {
 
996
    int width, height, sheight;
 
997
    guchar r, g, b, a;
 
998
    NRArenaItem *root; // the root arena item to show; it is assumed that all unneeded items are hidden
 
999
    guchar *px;
 
1000
    unsigned (*status)(float, void *);
 
1001
    void *data;
 
1002
};
 
1003
 
 
1004
 
 
1005
/**
 
1006
 *
 
1007
 */
 
1008
static int
 
1009
sp_export_get_rows(guchar const **rows, int row, int num_rows, void *data)
 
1010
{
 
1011
    struct SPEBP *ebp = (struct SPEBP *) data;
 
1012
 
 
1013
    if (ebp->status) {
 
1014
        if (!ebp->status((float) row / ebp->height, ebp->data)) return 0;
 
1015
    }
 
1016
 
 
1017
    num_rows = MIN(num_rows, ebp->sheight);
 
1018
    num_rows = MIN(num_rows, ebp->height - row);
 
1019
 
 
1020
    /* Set area of interest */
 
1021
    NRRectL bbox;
 
1022
    bbox.x0 = 0;
 
1023
    bbox.y0 = row;
 
1024
    bbox.x1 = ebp->width;
 
1025
    bbox.y1 = row + num_rows;
 
1026
    /* Update to renderable state */
 
1027
    NRGC gc(NULL);
 
1028
    nr_matrix_set_identity(&gc.transform);
 
1029
 
 
1030
    nr_arena_item_invoke_update(ebp->root, &bbox, &gc, NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_NONE);
 
1031
 
 
1032
    NRPixBlock pb;
 
1033
    nr_pixblock_setup_extern(&pb, NR_PIXBLOCK_MODE_R8G8B8A8N,
 
1034
                             bbox.x0, bbox.y0, bbox.x1, bbox.y1,
 
1035
                             ebp->px, 4 * ebp->width, FALSE, FALSE);
 
1036
 
 
1037
    for (int r = 0; r < num_rows; r++) {
 
1038
        guchar *p = NR_PIXBLOCK_PX(&pb) + r * pb.rs;
 
1039
        for (int c = 0; c < ebp->width; c++) {
 
1040
            *p++ = ebp->r;
 
1041
            *p++ = ebp->g;
 
1042
            *p++ = ebp->b;
 
1043
            *p++ = ebp->a;
 
1044
        }
 
1045
    }
 
1046
 
 
1047
    /* Render */
 
1048
    nr_arena_item_invoke_render(ebp->root, &bbox, &pb, 0);
 
1049
 
 
1050
    for (int r = 0; r < num_rows; r++) {
 
1051
        rows[r] = NR_PIXBLOCK_PX(&pb) + r * pb.rs;
 
1052
    }
 
1053
 
 
1054
    nr_pixblock_release(&pb);
 
1055
 
 
1056
    return num_rows;
 
1057
}
 
1058
 
 
1059
/**
 
1060
Hide all items which are not listed in list, recursively, skipping groups and defs
 
1061
*/
 
1062
void
 
1063
hide_other_items_recursively(SPObject *o, GSList *list, unsigned dkey)
 
1064
{
 
1065
    if (SP_IS_ITEM(o)
 
1066
        && !SP_IS_DEFS(o)
 
1067
        && !SP_IS_ROOT(o)
 
1068
        && !SP_IS_GROUP(o)
 
1069
        && !g_slist_find(list, o))
 
1070
    {
 
1071
        sp_item_invoke_hide(SP_ITEM(o), dkey);
 
1072
    }
 
1073
 
 
1074
     // recurse
 
1075
    if (!g_slist_find(list, o)) {
 
1076
        for (SPObject *child = sp_object_first_child(o) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
 
1077
            hide_other_items_recursively(child, list, dkey);
 
1078
        }
 
1079
    }
 
1080
}
 
1081
 
 
1082
 
 
1083
/**
 
1084
 *  Render the SVG drawing onto a PNG raster image, then save to
 
1085
 *  a file.  Returns TRUE if succeeded in writing the file,
 
1086
 *  FALSE otherwise.
 
1087
 */
 
1088
int
 
1089
sp_export_png_file(SPDocument *doc, gchar const *filename,
 
1090
                   double x0, double y0, double x1, double y1,
 
1091
                   unsigned width, unsigned height,
 
1092
                   unsigned long bgcolor,
 
1093
                   unsigned (*status)(float, void *),
 
1094
                   void *data, bool force_overwrite,
 
1095
                   GSList *items_only)
 
1096
{
 
1097
    int write_status = TRUE;
 
1098
    g_return_val_if_fail(doc != NULL, FALSE);
 
1099
    g_return_val_if_fail(filename != NULL, FALSE);
 
1100
    g_return_val_if_fail(width >= 1, FALSE);
 
1101
    g_return_val_if_fail(height >= 1, FALSE);
 
1102
 
 
1103
    if (!force_overwrite && !sp_ui_overwrite_file(filename)) {
 
1104
        return FALSE;
 
1105
    }
 
1106
 
 
1107
    sp_document_ensure_up_to_date(doc);
 
1108
 
 
1109
    /* Go to document coordinates */
 
1110
    gdouble t = y0;
 
1111
    y0 = sp_document_height(doc) - y1;
 
1112
    y1 = sp_document_height(doc) - t;
 
1113
 
 
1114
    /*
 
1115
     * 1) a[0] * x0 + a[2] * y1 + a[4] = 0.0
 
1116
     * 2) a[1] * x0 + a[3] * y1 + a[5] = 0.0
 
1117
     * 3) a[0] * x1 + a[2] * y1 + a[4] = width
 
1118
     * 4) a[1] * x0 + a[3] * y0 + a[5] = height
 
1119
     * 5) a[1] = 0.0;
 
1120
     * 6) a[2] = 0.0;
 
1121
     *
 
1122
     * (1,3) a[0] * x1 - a[0] * x0 = width
 
1123
     * a[0] = width / (x1 - x0)
 
1124
     * (2,4) a[3] * y0 - a[3] * y1 = height
 
1125
     * a[3] = height / (y0 - y1)
 
1126
     * (1) a[4] = -a[0] * x0
 
1127
     * (2) a[5] = -a[3] * y1
 
1128
     */
 
1129
 
 
1130
    NRMatrix affine;
 
1131
    affine.c[0] = width / (x1 - x0);
 
1132
    affine.c[1] = 0.0;
 
1133
    affine.c[2] = 0.0;
 
1134
    affine.c[3] = height / (y1 - y0);
 
1135
    affine.c[4] = -affine.c[0] * x0;
 
1136
    affine.c[5] = -affine.c[3] * y0;
 
1137
 
 
1138
    //SP_PRINT_MATRIX("SVG2PNG", &affine);
 
1139
 
 
1140
    struct SPEBP ebp;
 
1141
    ebp.width  = width;
 
1142
    ebp.height = height;
 
1143
    ebp.r      = NR_RGBA32_R(bgcolor);
 
1144
    ebp.g      = NR_RGBA32_G(bgcolor);
 
1145
    ebp.b      = NR_RGBA32_B(bgcolor);
 
1146
    ebp.a      = NR_RGBA32_A(bgcolor);
 
1147
 
 
1148
    /* Create new arena */
 
1149
    NRArena *arena = NRArena::create();
 
1150
    unsigned dkey = sp_item_display_key_new(1);
 
1151
 
 
1152
    /* Create ArenaItems and set transform */
 
1153
    ebp.root = sp_item_invoke_show(SP_ITEM(sp_document_root(doc)), arena, dkey, SP_ITEM_SHOW_DISPLAY);
 
1154
    nr_arena_item_set_transform(NR_ARENA_ITEM(ebp.root), NR::Matrix(&affine));
 
1155
 
 
1156
    // We show all and then hide all items we don't want, instead of showing only requested items,
 
1157
    // because that would not work if the shown item references something in defs
 
1158
    if (items_only) {
 
1159
        hide_other_items_recursively(sp_document_root(doc), items_only, dkey);
 
1160
    }
 
1161
 
 
1162
    ebp.status = status;
 
1163
    ebp.data   = data;
 
1164
 
 
1165
    if ((width < 256) || ((width * height) < 32768)) {
 
1166
        ebp.px = nr_pixelstore_64K_new(FALSE, 0);
 
1167
        ebp.sheight = 65536 / (4 * width);
 
1168
        write_status = sp_png_write_rgba_striped(filename, width, height, sp_export_get_rows, &ebp);
 
1169
        nr_pixelstore_64K_free(ebp.px);
 
1170
    } else {
 
1171
        ebp.px = nr_new(guchar, 4 * 64 * width);
 
1172
        ebp.sheight = 64;
 
1173
        write_status = sp_png_write_rgba_striped(filename, width, height, sp_export_get_rows, &ebp);
 
1174
        nr_free(ebp.px);
 
1175
    }
 
1176
 
 
1177
    // Hide items
 
1178
    sp_item_invoke_hide(SP_ITEM(sp_document_root(doc)), dkey);
 
1179
 
 
1180
    /* Free Arena and ArenaItem */
 
1181
    nr_arena_item_unref(ebp.root);
 
1182
    nr_object_unref((NRObject *) arena);
 
1183
    return write_status;
 
1184
}
 
1185
 
 
1186
 
 
1187
/*######################
 
1188
## P R I N T
 
1189
######################*/
 
1190
 
 
1191
 
 
1192
/**
 
1193
 *  Print the current document, if any.
 
1194
 */
 
1195
void
 
1196
sp_file_print()
 
1197
{
 
1198
    SPDocument *doc = SP_ACTIVE_DOCUMENT;
 
1199
    if (doc)
 
1200
        sp_print_document(doc, FALSE);
 
1201
}
 
1202
 
 
1203
 
 
1204
/**
 
1205
 *  Print the current document, if any.  Do not use
 
1206
 *  the machine's print drivers.
 
1207
 */
 
1208
void
 
1209
sp_file_print_direct()
 
1210
{
 
1211
    SPDocument *doc = SP_ACTIVE_DOCUMENT;
 
1212
    if (doc)
 
1213
        sp_print_document(doc, TRUE);
 
1214
}
 
1215
 
 
1216
 
 
1217
/**
 
1218
 * Display what the drawing would look like, if
 
1219
 * printed.
 
1220
 */
 
1221
void
 
1222
sp_file_print_preview(gpointer object, gpointer data)
 
1223
{
 
1224
 
 
1225
    SPDocument *doc = SP_ACTIVE_DOCUMENT;
 
1226
    if (doc)
 
1227
        sp_print_preview_document(doc);
 
1228
 
 
1229
}
 
1230
 
 
1231
void Inkscape::IO::fixupHrefs( SPDocument *doc, const gchar *base, gboolean spns )
 
1232
{
 
1233
    //g_message("Inkscape::IO::fixupHrefs( , [%s], )", base );
 
1234
 
 
1235
    if ( 0 ) {
 
1236
        gchar const* things[] = {
 
1237
            "data:foo,bar",
 
1238
            "http://www.google.com/image.png",
 
1239
            "ftp://ssd.com/doo",
 
1240
            "/foo/dee/bar.svg",
 
1241
            "foo.svg",
 
1242
            "file:/foo/dee/bar.svg",
 
1243
            "file:///foo/dee/bar.svg",
 
1244
            "file:foo.svg",
 
1245
            "/foo/bar\xe1\x84\x92.svg",
 
1246
            "file:///foo/bar\xe1\x84\x92.svg",
 
1247
            "file:///foo/bar%e1%84%92.svg",
 
1248
            "/foo/bar%e1%84%92.svg",
 
1249
            "bar\xe1\x84\x92.svg",
 
1250
            "bar%e1%84%92.svg",
 
1251
            NULL
 
1252
        };
 
1253
        g_message("+------");
 
1254
        for ( int i = 0; things[i]; i++ )
 
1255
        {
 
1256
            try
 
1257
            {
 
1258
                URI uri(things[i]);
 
1259
                gboolean isAbs = g_path_is_absolute( things[i] );
 
1260
                gchar *str = uri.toString();
 
1261
                g_message( "abs:%d  isRel:%d  scheme:[%s]  path:[%s][%s]   uri[%s] / [%s]", (int)isAbs,
 
1262
                           (int)uri.isRelative(),
 
1263
                           uri.getScheme(),
 
1264
                           uri.getPath(),
 
1265
                           uri.getOpaque(),
 
1266
                           things[i],
 
1267
                           str );
 
1268
                g_free(str);
 
1269
            }
 
1270
            catch ( MalformedURIException err )
 
1271
            {
 
1272
                dump_str( things[i], "MalformedURIException" );
 
1273
                xmlChar *redo = xmlURIEscape((xmlChar const *)things[i]);
 
1274
                g_message("    gone from [%s] to [%s]", things[i], redo );
 
1275
                if ( redo == NULL )
 
1276
                {
 
1277
                    URI again = URI::fromUtf8( things[i] );
 
1278
                    gboolean isAbs = g_path_is_absolute( things[i] );
 
1279
                    gchar *str = again.toString();
 
1280
                    g_message( "abs:%d  isRel:%d  scheme:[%s]  path:[%s][%s]   uri[%s] / [%s]", (int)isAbs,
 
1281
                               (int)again.isRelative(),
 
1282
                               again.getScheme(),
 
1283
                               again.getPath(),
 
1284
                               again.getOpaque(),
 
1285
                               things[i],
 
1286
                               str );
 
1287
                    g_free(str);
 
1288
                    g_message("    ----");
 
1289
                }
 
1290
            }
 
1291
        }
 
1292
        g_message("+------");
 
1293
    }
 
1294
 
 
1295
    GSList const *images = sp_document_get_resource_list(doc, "image");
 
1296
    for (GSList const *l = images; l != NULL; l = l->next) {
 
1297
        Inkscape::XML::Node *ir = SP_OBJECT_REPR(l->data);
 
1298
 
 
1299
        const gchar *href = ir->attribute("xlink:href");
 
1300
 
 
1301
        // First try to figure out an absolute path to the asset
 
1302
        //g_message("image href [%s]", href );
 
1303
        if (spns && !g_path_is_absolute(href)) {
 
1304
            const gchar *absref = ir->attribute("sodipodi:absref");
 
1305
            const gchar *base_href = g_build_filename(base, href, NULL);
 
1306
            //g_message("      absr [%s]", absref );
 
1307
 
 
1308
            if ( absref && Inkscape::IO::file_test(absref, G_FILE_TEST_EXISTS) && !Inkscape::IO::file_test(base_href, G_FILE_TEST_EXISTS))
 
1309
            {
 
1310
                // only switch over if the absref is valid while href is not
 
1311
                href = absref;
 
1312
                //g_message("     copied absref to href");
 
1313
            }
 
1314
        }
 
1315
 
 
1316
        // Once we have an absolute path, convert it relative to the new location
 
1317
        if (href && g_path_is_absolute(href)) {
 
1318
            const gchar *relname = sp_relative_path_from_path(href, base);
 
1319
            //g_message("     setting to [%s]", relname );
 
1320
            ir->setAttribute("xlink:href", relname);
 
1321
        }
 
1322
// TODO next refinement is to make the first choice keeping the relative path as-is if
 
1323
//      based on the new location it gives us a valid file.
 
1324
    }
 
1325
}
 
1326
 
 
1327
 
 
1328
/*
 
1329
  Local Variables:
 
1330
  mode:c++
 
1331
  c-file-style:"stroustrup"
 
1332
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
1333
  indent-tabs-mode:nil
 
1334
  fill-column:99
 
1335
  End:
 
1336
*/
 
1337
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :