1 // SPDX-License-Identifier: GPL-2.0-or-later
2 #ifndef SEEN_SP_DOCUMENT_H
3 #define SEEN_SP_DOCUMENT_H
6 * SPDocument: Typed SVG document implementation
9 * Lauris Kaplinski <lauris@kaplinski.com>
10 * MenTaLguY <mental@rydia.net>
11 * Jon A. Cruz <jon@joncruz.org>
14 * Copyright (C) 2004-2005 MenTaLguY
15 * Copyright (C) 1999-2002 Lauris Kaplinski
16 * Copyright (C) 2000-2001 Ximian, Inc.
18 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
21 #include <cstddef> // for size_t
22 #include <deque> // for deque
23 #include <map> // for map
24 #include <memory> // for unique_ptr, default_de...
25 #include <queue> // for queue
27 #include <string> // for string
28 #include <vector> // for vector
30 #include <boost/ptr_container/ptr_list.hpp> // for ptr_list
32 #include <giomm/simpleactiongroup.h> // for SimpleActionGroup
33 #include <glib.h> // for GQuark, gboolean, gchar
34 #include <glibmm/refptr.h> // for RefPtr
35 #include <glibmm/ustring.h> // for ustring
36 #include <sigc++/connection.h> // for connection
37 #include <sigc++/signal.h> // for signal
39 #include <2geom/affine.h> // for Affine
40 #include <2geom/rect.h> // for Rect, OptRect
41 #include <2geom/transforms.h> // for Scale
43 #include "3rdparty/libcroco/src/cr-cascade.h" // for CRCascade
45 #include "composite-undo-stack-observer.h"
46 // XXX only for testing!
47 #include "console-output-undo-observer.h"
49 // This variable is introduced with 0.92.1
50 // with the introduction of automatic fix
51 // for files detected to have been created
52 // with previous versions to have a similar
54 extern bool sp_no_convert_text_baseline_spacing
;
57 // This variable is introduced with 0.92.1
58 // with the introduction of automatic fix
59 // for files detected to have been created
60 // with previous versions to have a similar
62 extern bool sp_do_not_fix_pre_92
;
88 class UndoStackObserver
;
98 } // namespace Inkscape
100 /// Typed SVG document implementation.
104 /// For sanity check in SPObject::requestDisplayUpdate
105 unsigned update_in_progress
= 0;
109 SPDocument(SPDocument
const &) = delete;
110 SPDocument
&operator=(SPDocument
const &) = delete;
112 static int get_new_doc_number();
114 // Document creation ------------------
115 static std::unique_ptr
<SPDocument
> createDoc(Inkscape::XML::Document
*rdoc
, char const *filename
,
116 char const *base
, char const *name
, bool keepalive
, SPDocument
*parent
= nullptr);
117 static std::unique_ptr
<SPDocument
> createNewDoc(char const *filename
, bool keepalive
,
118 bool make_new
= false, SPDocument
*parent
= nullptr);
119 static std::unique_ptr
<SPDocument
> createNewDocFromMem(std::span
<char const> buffer
, bool keepalive
,
120 std::string
const &filename
= "");
121 SPDocument
*createChildDoc(std::string
const &filename
);
123 void setPages(bool enabled
);
124 void prunePages(const std::string
&page_nums
, bool invert
= false);
127 std::unique_ptr
<SPDocument
> copy() const;
128 // Substitute doc root
129 void rebase(Inkscape::XML::Document
* new_xmldoc
, bool keep_namedview
= true);
130 // Substitute doc root with a file
131 void rebase(const gchar
* file
, bool keep_namedview
= true);
132 // Substitute doc root with file in disk
133 void rebase(bool keep_namedview
= true);
134 // Document status --------------------
135 void setVirgin(bool Virgin
) { virgin
= Virgin
; }
136 bool getVirgin() { return virgin
; }
137 const SPDocument
*getOriginalDocument() const { return _original_document
; }
139 bool isModifiedSinceSave() const { return modified_since_save
; }
140 bool isModifiedSinceAutoSave() const { return modified_since_autosave
; }
141 void setModifiedSinceSave(bool const modified
= true);
142 void setModifiedSinceAutoSaveFalse() { modified_since_autosave
= false; };
145 bool rerouting_handler();
147 void requestModified();
148 bool _updateDocument(int flags
, unsigned int object_modified_tag
= 0); // Used by stand-alone sp_document_idle_handler
149 int ensureUpToDate(unsigned int object_modified_tag
= 0);
151 bool addResource(char const *key
, SPObject
*object
);
152 bool removeResource(char const *key
, SPObject
*object
);
153 std::vector
<SPObject
*> const getResourceList(char const *key
);
154 void process_pending_resource_changes();
156 void do_change_filename(char const *const filename
, bool const rebase
);
157 void changeFilenameAndHrefs(char const *filename
);
158 void setXMLDialogSelectedObject(SPObject
*activexmltree
) { _activexmltree
= activexmltree
; }
159 SPObject
*getXMLDialogSelectedObject() { return _activexmltree
; }
161 Inkscape::EventLog
*get_event_log() { return _event_log
.get(); }
163 Inkscape::PageManager
& getPageManager() { return *_page_manager
; }
164 const Inkscape::PageManager
& getPageManager() const { return *_page_manager
; }
166 Inkscape::Colors::DocumentCMS
&getDocumentCMS() { return *_cms_manager
; }
167 const Inkscape::Colors::DocumentCMS
&getDocumentCMS() const { return *_cms_manager
; }
170 void _importDefsNode(SPDocument
*source
, Inkscape::XML::Node
*defs
, Inkscape::XML::Node
*target_defs
);
171 SPObject
*_activexmltree
;
173 std::unique_ptr
<Inkscape::PageManager
> _page_manager
;
174 std::unique_ptr
<Inkscape::Colors::DocumentCMS
> _cms_manager
;
176 std::queue
<GQuark
> pending_resource_changes
;
178 // Find items by geometry --------------------
179 std::deque
<SPItem
*> const &get_flat_item_list(unsigned int dkey
, bool into_groups
, bool active_only
) const;
181 SPDocument
*_searchForChild(std::string
const &filename
, SPDocument
const *avoid
= nullptr);
182 /** Detect Y-axis orientation change.
183 * \return true if change has been detected */
184 bool has_yaxis_orientation_changed();
185 /** Update desktop transform after Y-axis orientation change.
186 * \return shift to apply to display to keep content from scrolling */
187 double update_desktop_affine();
190 void clearNodeCache() { _node_cache
.clear(); }
191 void importDefs(SPDocument
*source
);
193 unsigned int vacuumDocument();
195 /******** Getters and Setters **********/
197 // Document structure -----------------
198 Avoid::Router
* getRouter() const { return _router
.get(); }
200 /** Returns our SPRoot */
201 SPRoot
*getRoot() { return root
; }
202 SPRoot
const *getRoot() const { return root
; }
204 /** Return the main defs object for the document. */
207 Inkscape::XML::Node
*getReprRoot() { return rroot
; }
208 Inkscape::XML::Node
*getReprNamedView();
209 SPNamedView
*getNamedView();
211 /** Our Inkscape::XML::Document. */
212 Inkscape::XML::Document
*getReprDoc() { return rdoc
; }
213 Inkscape::XML::Document
const *getReprDoc() const { return rdoc
; }
216 std::vector
<Glib::ustring
> getLanguages() const;
218 SPDocument
*getParent() { return _parent_document
; }
219 SPDocument
const *getParent() const { return _parent_document
; }
221 Inkscape::Selection
*getSelection() { return _selection
.get(); }
224 CRCascade
*getStyleCascade() { return style_cascade
; }
226 // File information --------------------
228 /** A filename, or NULL */
229 void setDocumentFilename(char const *filename
);
230 char const *getDocumentFilename() const { return document_filename
; }
232 /** To be used for resolving relative hrefs. */
233 void setDocumentBase( char const* document_base
);
234 char const *getDocumentBase() const { return document_base
; };
236 /** basename or other human-readable label for the document. */
237 char const* getDocumentName() const { return document_name
; }
240 // Document geometry ------------------------
241 Inkscape::Util::Unit
const* getDisplayUnit();
243 void setDocumentScale( const double scaleX
, const double scaleY
);
244 void setDocumentScale( const double scale
);
245 Geom::Scale
getDocumentScale(bool computed
= true) const;
246 void scaleContentBy(Geom::Scale
const &delta
);
248 void setWidthAndHeight(const Inkscape::Util::Quantity
&width
, const Inkscape::Util::Quantity
&height
, bool changeSize
=true);
249 Geom::Point
getDimensions() const;
251 void setWidth(const Inkscape::Util::Quantity
&width
, bool changeSize
=true);
252 void setHeight(const Inkscape::Util::Quantity
&height
, bool changeSize
=true);
253 Inkscape::Util::Quantity
getWidth() const;
254 Inkscape::Util::Quantity
getHeight() const;
257 void setViewBox(const Geom::Rect
&viewBox
);
258 Geom::Rect
getViewBox() const;
260 Geom::OptRect
preferredBounds() const;
261 Geom::OptRect
pageBounds();
262 void fitToRect(Geom::Rect
const &rect
, bool with_margins
= false);
263 void setupViewport(SPItemCtx
*ctx
);
265 // Desktop geometry ------------------------
266 /// Document to desktop coordinate transformation.
267 const Geom::Affine
&doc2dt() const;
268 /// Desktop to document coordinate transformation.
269 const Geom::Affine
&dt2doc() const
271 // Note: doc2dt().inverse() happens to be identical to doc2dt()
274 /// True if the desktop Y-axis points down, false if it points up.
275 bool is_yaxisdown() const { return yaxisdir() > 0; }
276 /// "1" if the desktop Y-axis points down, "-1" if it points up.
277 double yaxisdir() const { return _doc2dt
[3]; }
278 // return true if coordinate system origin needs to move to current page
279 bool get_origin_follows_page();
280 void set_origin_follows_page(bool on
);
281 // signal emitted when Y-axis orientation gets flipped
282 sigc::signal
<void (double)> get_y_axis_flipped() { return _y_axis_flipped
; }
284 // Find items -----------------------------
285 void bindObjectToId(char const *id
, SPObject
*object
);
286 SPObject
*getObjectById(std::string
const &id
) const;
287 SPObject
*getObjectById(char const *id
) const;
288 SPObject
*getObjectByHref(std::string
const &href
) const;
289 SPObject
*getObjectByHref(char const *href
) const;
291 void bindObjectToRepr(Inkscape::XML::Node
*repr
, SPObject
*object
);
292 SPObject
*getObjectByRepr(Inkscape::XML::Node
*repr
) const;
294 std::vector
<SPObject
*> getObjectsByClass(Glib::ustring
const &klass
) const;
295 std::vector
<SPObject
*> getObjectsByElement(Glib::ustring
const &element
, bool custom
= false) const;
296 std::vector
<SPObject
*> getObjectsBySelector(Glib::ustring
const &selector
) const;
299 * @brief Generate a document-wide unique id.
301 * Generates an id string not in use by any object in the document.
302 * The generated string is based on the given prefix by appending a number.
304 std::string
generate_unique_id(char const *prefix
);
307 * @brief Set the reference document object.
308 * Use this function to extend functionality of getObjectById() - it will search in reference document.
309 * This is useful when rendering objects that have been copied from this document into a sandbox document.
310 * Setting reference will allow sandbox document to find gradients, or linked objects that may have been
311 * referenced by copied object.
314 void set_reference_document(SPDocument
* document
);
315 SPDocument
* get_reference_document();
318 * @brief Object used to temporarily set and then automatically clear reference document.
320 struct install_reference_document
{
321 install_reference_document(SPDocument
* inject_into
, SPDocument
* reference
);
322 ~install_reference_document();
327 std::vector
<SPItem
*> getItemsInBox (unsigned int dkey
, Geom::Rect
const &box
, bool take_hidden
= false, bool take_insensitive
= false, bool take_groups
= true, bool enter_groups
= false, bool enter_layers
= true) const;
328 std::vector
<SPItem
*> getItemsPartiallyInBox(unsigned int dkey
, Geom::Rect
const &box
, bool take_hidden
= false, bool take_insensitive
= false, bool take_groups
= true, bool enter_groups
= false, bool enter_layers
= true) const;
329 SPItem
*getItemAtPoint(unsigned int key
, Geom::Point
const &p
, bool into_groups
, SPItem
*upto
= nullptr) const;
330 std::vector
<SPItem
*> getItemsAtPoints(unsigned const key
, std::vector
<Geom::Point
> points
, bool all_layers
= true, bool topmost_only
= true, size_t limit
= 0, bool active_only
= true) const;
331 SPItem
*getGroupAtPoint(unsigned int key
, Geom::Point
const &p
) const;
334 * Returns the bottommost item from the list which is at the point, or NULL if none.
336 static SPItem
*getItemFromListAtPointBottom(unsigned int dkey
, SPGroup
*group
, const std::vector
<SPItem
*> &list
, Geom::Point
const &p
, bool take_insensitive
= false);
339 // Box tool -------------------------------
340 void setCurrentPersp3D(Persp3D
* const persp
);
342 * getCurrentPersp3D returns current_persp3d (if non-NULL) or the first
343 * perspective in the defs. If no perspective exists, returns NULL.
345 Persp3D
* getCurrentPersp3D();
346 void update_lpobjs();
347 void setCurrentPersp3DImpl(Persp3DImpl
* const persp_impl
) { current_persp3d_impl
= persp_impl
; }
348 Persp3DImpl
* getCurrentPersp3DImpl() { return current_persp3d_impl
; }
350 void getPerspectivesInDefs(std::vector
<Persp3D
*> &list
) const;
351 unsigned int numPerspectivesInDefs() const {
352 std::vector
<Persp3D
*> list
;
353 getPerspectivesInDefs(list
);
358 // Document undo/redo ----------------------
359 unsigned long serial() const { return _serial
; } // Returns document's unique number.
360 bool isSeeking() const {return seeking
;} // In a transition between two "good" states of document?
361 bool isPartial() const {return partial
!= nullptr;} // In partianl undo/redo transaction
362 void reset_key(void *dummy
) { actionkey
.clear(); }
363 bool isSensitive() const { return sensitive
; }
365 // Garbage collecting ----------------------
366 void queueForOrphanCollection(SPObject
*object
);
367 void collectOrphans();
370 // Actions ---------------------------------
371 Glib::RefPtr
<Gio::SimpleActionGroup
> getActionGroup() { return action_group
; }
373 /************* Data ***************/
376 // Document ------------------------------
377 std::unique_ptr
<Avoid::Router
> _router
; // Instance of the connector router
378 std::unique_ptr
<Inkscape::Selection
> _selection
;
380 // Document status -----------------------
382 bool keepalive
; ///< false if temporary document (e.g. to generate a PNG for display in a dialog).
383 bool virgin
; ///< Has the document never been touched?
384 bool modified_since_save
= false;
385 bool modified_since_autosave
= false;
386 sigc::connection modified_connection
;
387 sigc::connection rerouting_connection
;
389 // Document structure --------------------
390 Inkscape::XML::Document
*rdoc
; ///< Our Inkscape::XML::Document
391 Inkscape::XML::Node
*rroot
; ///< Root element of Inkscape::XML::Document
393 SPRoot
*root
; ///< Our SPRoot
395 // A list of svg documents being used or shown within this document
396 std::vector
<std::unique_ptr
<SPDocument
>> _child_documents
;
397 // Conversely this is a parent document because this is a child.
398 SPDocument
*_parent_document
= nullptr;
399 // When copying documents, this can refer to its original
400 SPDocument
const *_original_document
= nullptr;
401 // Reference document to fall back to when getObjectById cannot find element in '*this' document
402 SPDocument
*_ref_document
= nullptr;
405 CRCascade
*style_cascade
;
408 mutable Geom::Affine _doc2dt
;
410 // File information ----------------------
411 char *document_filename
; ///< A filename, or NULL
412 char *document_base
; ///< To be used for resolving relative hrefs.
413 char *document_name
; ///< basename or other human-readable label for the document.
415 // Find items ----------------------------
416 std::map
<std::string
, SPObject
*> iddef
;
417 std::map
<Inkscape::XML::Node
*, SPObject
*> reprdef
;
419 // Find items by geometry --------------------
420 mutable std::map
<unsigned long, std::deque
<SPItem
*>> _node_cache
; // Used to speed up search.
422 // Box tool ----------------------------
423 Persp3D
*current_persp3d
; /**< Currently 'active' perspective (to which, e.g., newly created boxes are attached) */
424 Persp3DImpl
*current_persp3d_impl
;
426 // Document undo/redo ----------------------
427 friend Inkscape::DocumentUndo
;
428 std::unique_ptr
<Inkscape::EventLog
> _event_log
;
430 /* Undo/Redo state */
431 bool sensitive
; /* If we save actions to undo stack */
432 Inkscape::XML::Event
* partial
; /* partial undo log when interrupted */
433 std::deque
<Inkscape::Event
*> undo
; /* Undo stack of reprs */
434 std::deque
<Inkscape::Event
*> redo
; /* Redo stack of reprs */
436 Inkscape::CompositeUndoStackObserver undoStackObservers
;
438 // XXX only for testing!
439 Inkscape::ConsoleOutputUndoObserver console_output_undo_observer
;
441 bool seeking
; // Related to undo/redo/unique id
442 unsigned long _serial
; // Unique document number (used by undo/redo).
443 Glib::ustring actionkey
; // Last action key, used to combine actions in undo.
444 unsigned long object_id_counter
; // Steadily-incrementing counter used to assign unique ids to objects.
446 // Garbage collecting ----------------------
448 std::vector
<SPObject
*> _collection_queue
; ///< Orphans
450 // Actions ---------------------------------
451 Glib::RefPtr
<Gio::SimpleActionGroup
> action_group
;
453 /*********** Signals **************/
455 typedef sigc::signal
<void (SPObject
*)> IDChangedSignal
;
456 typedef sigc::signal
<void ()> ResourcesChangedSignal
;
457 typedef sigc::signal
<void (unsigned)> ModifiedSignal
;
458 typedef sigc::signal
<void (char const *)> FilenameSetSignal
;
459 typedef sigc::signal
<void (double, double)> ResizedSignal
;
460 typedef sigc::signal
<void ()> ReconstructionStart
;
461 typedef sigc::signal
<void ()> ReconstructionFinish
;
462 typedef sigc::signal
<void ()> CommitSignal
;
463 typedef sigc::signal
<void ()> BeforeCommitSignal
; // allow to add actions berfore commit to include in undo
465 typedef std::map
<GQuark
, SPDocument::IDChangedSignal
> IDChangedSignalMap
;
466 typedef std::map
<GQuark
, SPDocument::ResourcesChangedSignal
> ResourcesChangedSignalMap
;
468 /** Dictionary of signals for id changes */
469 IDChangedSignalMap id_changed_signals
;
471 SPDocument::ModifiedSignal modified_signal
;
472 SPDocument::FilenameSetSignal filename_set_signal
;
473 SPDocument::ReconstructionStart _reconstruction_start_signal
;
474 SPDocument::ReconstructionFinish _reconstruction_finish_signal
;
475 SPDocument::CommitSignal commit_signal
; // Used by friend Inkscape::DocumentUndo
476 SPDocument::BeforeCommitSignal before_commit_signal
; // Used by friend Inkscape::DocumentUndo
478 sigc::connection _desktop_activated_connection
;
480 sigc::signal
<void ()> destroySignal
;
481 sigc::signal
<void ()> _saved_or_modified_signal
;
482 sigc::signal
<void (double)> _y_axis_flipped
;
486 * @brief Add the observer to the document's undo listener
487 * The caller is in charge of freeing any memory allocated to the observer
490 void addUndoObserver(Inkscape::UndoStackObserver
& observer
);
491 void removeUndoObserver(Inkscape::UndoStackObserver
& observer
);
493 sigc::connection
connectDestroy(sigc::signal
<void ()>::slot_type slot
);
494 sigc::connection
connectModified(ModifiedSignal::slot_type slot
);
495 sigc::connection
connectFilenameSet(FilenameSetSignal::slot_type slot
);
496 sigc::connection
connectCommit(CommitSignal::slot_type slot
);
497 sigc::connection
connectBeforeCommit(BeforeCommitSignal::slot_type slot
);
498 sigc::connection
connectIdChanged(const char *id
, IDChangedSignal::slot_type slot
);
499 sigc::connection
connectResourcesChanged(char const *key
, SPDocument::ResourcesChangedSignal::slot_type slot
);
500 sigc::connection
connectReconstructionStart(ReconstructionStart::slot_type slot
);
501 sigc::connection
connectReconstructionFinish(ReconstructionFinish::slot_type slot
);
502 sigc::connection
connectSavedOrModified(sigc::slot
<void ()> &&slot
);
505 std::map
<std::string
, std::vector
<SPObject
*> > resources
;
506 ResourcesChangedSignalMap resources_changed_signals
; // Used by Extension::Internal::Filter
508 void _emitModified(unsigned int object_modified_tag
= 0); // Used by SPItem
509 void emitReconstructionStart();
510 void emitReconstructionFinish();
514 * Ideas: How to overcome style invalidation nightmare
516 * 1. There is reference request dictionary, that contains
517 * objects (styles) needing certain id. Object::build checks
518 * final id against it, and invokes necessary methods
520 * 2. Removing referenced object is simply prohibited -
521 * needs analyse, how we can deal with situations, where
522 * we simply want to ungroup etc. - probably we need
523 * Repr::reparent method :( [Or was it ;)]
527 #endif // SEEN_SP_DOCUMENT_H
532 c-file-style:"stroustrup"
533 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
538 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :