summaryrefslogtreecommitdiffstats
path: root/src/text-chemistry-impl.h
blob: c3b1121192ef9420caddd51ed00541da038e5ecb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef SEEN_TEXT_CHEMISTRY_IMPL_H
#define SEEN_TEXT_CHEMISTRY_IMPL_H
/*
 * Text commands template implementation
 *
 * Authors:
 *   Iskren Chernev <iskren.chernev@gmail.com>
 *
 * Copyright (C) 2004 authors
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#include <algorithm>
#include "style.h"
#include "xml/node.h"
#include "document.h"

Glib::ustring text_relink_shapes_str(gchar const *prop,
        std::map<Glib::ustring, Glib::ustring> const &old_to_new);

inline Inkscape::XML::Node *text_obj_or_node_to_node(SPObject *obj) {
    return obj->getRepr();
}

inline Inkscape::XML::Node *text_obj_or_node_to_node(Inkscape::XML::Node *node) {
    return node;
}

template<typename InIter>
text_refs_t text_categorize_refs(SPDocument *doc, InIter begin, InIter end, text_ref_t which) {
    text_refs_t res;
    std::set<Glib::ustring> int_ext;
    auto idVisitor = [which, &res, &int_ext](SPShapeReference *href) {
        auto ref_obj = href->getObject();
        if (!ref_obj)
            return;
        auto id = ref_obj->getId();
        if (sp_repr_is_def(ref_obj->getRepr())) {
            if (which & TEXT_REF_DEF) {
                res.emplace_back(id, TEXT_REF_DEF);
            }
        } else {
            int_ext.insert(id);
        }
    };
    // Visit all shape references, detect the refs, and put internal and
    // external ids in int_ext.
    for (auto it = begin; it != end; ++it) {
        sp_repr_visit_descendants(
                text_obj_or_node_to_node(*it),
                [doc, &idVisitor](Inkscape::XML::Node *crnt) {
            if (!(crnt->name() && strcmp("svg:text", crnt->name()) == 0)) {
                return true;
            }

            auto crnt_obj = doc->getObjectByRepr(crnt);
            assert(crnt_obj == doc->getObjectById(crnt->attribute("id")));
            {
                const auto &inside_ids = crnt_obj->style->shape_inside.hrefs;
                std::for_each(inside_ids.begin(), inside_ids.end(), idVisitor);

                const auto &subtract_ids = crnt_obj->style->shape_subtract.hrefs;
                std::for_each(subtract_ids.begin(), subtract_ids.end(), idVisitor);
            }
            // Do not recurse into svg:text elements children
            return false;
        });
    }

    if (!(which & (TEXT_REF_INTERNAL | TEXT_REF_EXTERNAL))) {
        // We already discovered the defs, bail out if nothing else is
        // required.
        return res;
    }
    // Visit all root elements, recursively and see which ones are in int_ext,
    // therefore discovering the internal ids.
    for (auto it = begin; it != end; ++it) {
        sp_repr_visit_descendants(
                text_obj_or_node_to_node(*it),
                [which, &res, &int_ext](Inkscape::XML::Node *crnt) {

            auto id = crnt->attribute("id");
            auto find_iter = id ? int_ext.find(id) : int_ext.end();
            if (find_iter != int_ext.end()) {
                if (which & TEXT_REF_INTERNAL) {
                    res.emplace_back(id, TEXT_REF_INTERNAL);
                }
                int_ext.erase(find_iter);
                // don't recurse into children of a matched element
                return false;
            }

            return true;
        });
    }

    // What is left in int_ext are the external ones
    if (which & TEXT_REF_EXTERNAL) {
        for (auto const &ext_id : int_ext) {
            res.emplace_back(ext_id, TEXT_REF_EXTERNAL);
        }
    }

    return res;
}

template<typename InIterOrig, typename InIterCopy>
void text_relink_refs(text_refs_t const &refs, InIterOrig origBegin, InIterOrig origEnd, InIterCopy copyBegin) {
    // get all ids of text refs
    std::set<Glib::ustring> all_refs;
    for (auto const &ref : refs) {
        all_refs.insert(ref.first);
    }

    // find a mapping from old ids to new ids
    std::map<Glib::ustring, Glib::ustring> old_to_new;
    for (auto itOrig = origBegin, itCopy = copyBegin; itOrig != origEnd; ++itOrig, ++itCopy) {
        sp_repr_visit_descendants(
                text_obj_or_node_to_node(*itOrig),
                text_obj_or_node_to_node(*itCopy),
                [&all_refs, &old_to_new](Inkscape::XML::Node *a, Inkscape::XML::Node *b) {
            if (a->attribute("id") && all_refs.find(a->attribute("id")) != all_refs.end()) {
                old_to_new[a->attribute("id")] = b->attribute("id");
                return false;
            }
            return true;
        });
    }

    if (all_refs.size() != old_to_new.size()) {
        std::cerr << "text_relink_refs: Failed to match all references! all:" << all_refs.size()
            << " matched:" << old_to_new.size() << std::endl;
    }
    // relink references
    for (auto itOrig = origBegin, itCopy = copyBegin; itOrig != origEnd; ++itOrig, ++itCopy) {
        sp_repr_visit_descendants(
                text_obj_or_node_to_node(*itCopy),
                [&old_to_new](Inkscape::XML::Node *crnt) {
            if (strcmp("svg:text", crnt->name()) == 0) {
                SPCSSAttr* css = sp_repr_css_attr (crnt, "style");
                for (auto &&prop : {"shape-inside", "shape-subtract"}) {
                    if (auto prop_str = sp_repr_css_property(css, prop, nullptr)) {
                        sp_repr_css_set_property(css, prop, text_relink_shapes_str(prop_str, old_to_new).c_str());
                    }
                }
                sp_repr_css_set (crnt, css, "style");
                return false;
            }
            return true;
        });
    }
}

#endif // SEEN_TEXT_CHEMISTRY_IMPL_H