Code

Added replace font extension
authorCraig Marshall <craig9@gmail.com>
Sun, 26 Sep 2010 14:42:29 +0000 (15:42 +0100)
committerCraig Marshall <craig9@gmail.com>
Sun, 26 Sep 2010 14:42:29 +0000 (15:42 +0100)
po/POTFILES.in
share/extensions/Makefile.am
share/extensions/replace_font.inx [new file with mode: 0644]
share/extensions/replace_font.py [new file with mode: 0644]

index ab7222d176da36fd37df1aa93a4764721f3feb3a..83da4321b0fe5f85b499ad81212d25375e9efa45 100644 (file)
 [type: gettext/xml] share/extensions/radiusrand.inx
 [type: gettext/xml] share/extensions/render_alphabetsoup.inx
 [type: gettext/xml] share/extensions/render_barcode.inx
+[type: gettext/xml] share/extensions/replace_font.inx
 [type: gettext/xml] share/extensions/restack.inx
 [type: gettext/xml] share/extensions/rtree.inx
 [type: gettext/xml] share/extensions/rubberstretch.inx
@@ -544,6 +545,7 @@ share/extensions/web-transmit-att.py
 [type: gettext/xml] share/extensions/render_alphabetsoup.inx
 [type: gettext/xml] share/extensions/render_barcode.inx
 [type: gettext/xml] share/extensions/render_barcode_datamatrix.inx
+[type: gettext/xml] share/extensions/replace_font.inx
 [type: gettext/xml] share/extensions/restack.inx
 [type: gettext/xml] share/extensions/rtree.inx
 [type: gettext/xml] share/extensions/rubberstretch.inx
index 36a25d56f84b0d6add117390aa370409a632d38e..f621837f7226c0e53369a4aca4661386b8765aa7 100644 (file)
@@ -67,7 +67,7 @@ extensions = \
        grid_cartesian.py \
        grid_polar.py \
        guides_creator.py \
-    guillotine.py \
+       guillotine.py \
        handles.py \
        hpgl_output.py \
        ill2svg.pl \
@@ -113,6 +113,7 @@ extensions = \
        ps2pdf-ext.py \
        pturtle.py \
        radiusrand.py \
+       replace_font.py \
        restack.py \
        render_barcode.py \
        render_barcode_datamatrix.py \
@@ -222,7 +223,7 @@ modules = \
        grid_cartesian.inx \
        grid_polar.inx \
        guides_creator.inx \
-    guillotine.inx \
+       guillotine.inx \
        handles.inx \
        hpgl_output.inx \
        inkscape_help_askaquestion.inx \
@@ -268,6 +269,7 @@ modules = \
        render_barcode.inx \
        render_barcode_datamatrix.inx \
        render_alphabetsoup.inx \
+       replace_font.inx \
        restack.inx \
        rubberstretch.inx \
        rtree.inx \
diff --git a/share/extensions/replace_font.inx b/share/extensions/replace_font.inx
new file mode 100644 (file)
index 0000000..8ec9090
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+    <_name>Replace font</_name>
+    <id>org.inkscape.replace_font</id>
+
+    <dependency type="extension">org.inkscape.output.svg.inkscape</dependency>
+    <dependency type="executable" location="extensions">replace_font.py</dependency>
+    <dependency type="executable" location="extensions">inkex.py</dependency>
+
+     <param name="action" type="notebook">
+        <page name="find_replace" _gui-text="Find and Replace font">
+            <param name="fr_find" type="string" _gui-text="Find this font: "></param>
+            <param name="fr_replace" type="string" _gui-text="And replace with: "></param>
+        </page>
+        <page name="replace_all" _gui-text="Replace font">
+            <param name="r_replace" type="string" _gui-text="Replace all fonts with: "></param>
+        </page>
+        <page name="list_only" _gui-text="List all fonts">
+            <_param name="d" type="description">Choose this tab if you would like to see a list of the fonts used/found.</_param>
+        </page>
+    </param>
+
+    <param name="scope" type="enum" _gui-text="Work on:">
+        <_item value="entire_document">Entire drawing</_item>
+        <_item value="selection_only">Selected objects only</_item>
+    </param>
+
+    <effect needs-live-preview="false">
+                <object-type>all</object-type>
+                <effects-menu>
+                      <submenu _name="Text"/>
+                </effects-menu>
+    </effect>
+
+    <script>
+        <command reldir="extensions" interpreter="python">replace_font.py</command>
+    </script>
+</inkscape-extension>
diff --git a/share/extensions/replace_font.py b/share/extensions/replace_font.py
new file mode 100644 (file)
index 0000000..f0f25ab
--- /dev/null
@@ -0,0 +1,260 @@
+#!/usr/bin/env python
+'''
+replace_font.py
+
+Copyright (C) 2010 Craig Marshall, craig9 [at] gmail.com
+
+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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+
+-----------------------
+
+This script finds all fonts in the current drawing that match the
+specified find font, and replaces them with the specified replacement
+font.
+
+It can also replace all fonts indiscriminately, and list all fonts
+currently being used.
+'''
+
+import os
+import sys
+import inkex
+import simplestyle
+import gettext
+_ = gettext.gettext
+
+text_tags = ['{http://www.w3.org/2000/svg}tspan',
+                            '{http://www.w3.org/2000/svg}text',
+                            '{http://www.w3.org/2000/svg}flowRoot',
+                            '{http://www.w3.org/2000/svg}flowPara',
+                            '{http://www.w3.org/2000/svg}flowSpan']
+font_attributes = ['font-family', '-inkscape-font-specification']
+
+def set_font(node, new_font, style=None):
+    '''
+    Sets the font attribute in the style attribute of node, using the
+    font name stored in new_font. If the style dict is open already,
+    it can be passed in, otherwise it will be optned anyway.
+
+    Returns a dirty boolean flag
+    '''
+    dirty = False
+    if not style:
+        style = get_style(node)
+    if style:
+        for att in font_attributes:
+            if att in style:
+                style[att] = new_font
+                set_style(node, style)
+                dirty = True
+    return dirty
+
+def find_replace_font(node, find, replace):
+    '''
+    Searches the relevant font attributes/styles of node for find, and
+    replaces them with replace.
+
+    Returns a dirty boolean flag
+    '''
+    dirty = False
+    style = get_style(node)
+    if style:
+        for att in font_attributes:
+            if att in style and style[att].strip().lower() == find:
+                set_font(node, replace, style)
+                dirty = True
+    return dirty
+
+def is_styled_text(node):
+    '''
+    Returns true if the tag in question is a "styled" element that
+    can hold text.
+    '''
+    return node.tag in text_tags and 'style' in node.attrib
+
+def is_text(node):
+    '''
+    Returns true if the tag in question is an element that
+    can hold text.
+    '''
+    return node.tag in text_tags
+
+
+def get_style(node):
+    '''
+    Sugar coated way to get style dict from a node
+    '''
+    if 'style' in node.attrib:
+        return simplestyle.parseStyle(node.attrib['style'])
+
+def set_style(node, style):
+    '''
+    Sugar coated way to set the style dict, for node
+    '''
+    node.attrib['style'] = simplestyle.formatStyle(style)
+
+def get_fonts(node):
+    '''
+    Given a node, returns a list containing all the fonts that
+    the node is using.
+    '''
+    fonts = []
+    s = get_style(node)
+    if not s:
+        return fonts
+    for a in font_attributes:
+        if a in s:
+            fonts.append(s[a])
+    return fonts
+
+def die(msg = "Dying!"):
+    inkex.errormsg(msg)
+    sys.exit(0)
+
+def report_replacements(num):
+    '''
+    Sends a message to the end user showing success of failure
+    of the font replacement
+    '''
+    if num == 0:
+        die(_('Couldn\'t find anything using that font, please ensure the spelling and spacing is correct.'))
+
+def report_findings(findings):
+    '''
+    Tells the user which fonts were found, if any
+    '''
+    if len(findings) == 0:
+        inkex.errormsg(_("Didn't find any fonts in this document/selection."))
+    else:
+        if len(findings) == 1:
+            inkex.errormsg(_("Found the following font only: %s") % findings[0])
+        else:
+            inkex.errormsg(_("Found the following fonts:\n%s") % '\n'.join(findings))
+
+class ReplaceFont(inkex.Effect):
+    '''
+    Replaces all instances of one font with another
+    '''
+    def __init__(self):
+        inkex.Effect.__init__(self)
+        self.OptionParser.add_option("--fr_find", action="store",
+                                        type="string", dest="fr_find",
+                                        default=None, help="")
+
+        self.OptionParser.add_option("--fr_replace", action="store",
+                                        type="string", dest="fr_replace",
+                                        default=None, help="")
+
+        self.OptionParser.add_option("--r_replace", action="store",
+                                        type="string", dest="r_replace",
+                                        default=None, help="")
+
+        self.OptionParser.add_option("--action", action="store",
+                                        type="string", dest="action",
+                                        default=None, help="")
+
+        self.OptionParser.add_option("--scope", action="store",
+                                        type="string", dest="scope",
+                                        default=None, help="")
+
+    def find_child_text_items(self, node):
+        '''
+        Recursive method for appending all text-type elements
+        to self.selected_items
+        '''
+        if is_text(node):
+            self.selected_items.append(node)
+            for child in node:
+                self.find_child_text_items(child)
+
+    def relevant_items(self, scope):
+        '''
+        Depending on the scope, returns all text elements, or all
+        selected text elements including nested children
+        '''
+        items = []
+        to_return = []
+        if scope == "selection_only":
+            self.selected_items = []
+            for item in self.selected.iteritems():
+                self.find_child_text_items(item[1])
+            items = self.selected_items
+            if len(items) == 0:
+                die(_("There was nothing selected"))
+        else:
+            items = self.document.getroot().getiterator()
+        to_return.extend(filter(is_text, items))
+        return to_return
+
+    def find_replace(self, nodes, find, replace):
+        '''
+        Walks through nodes, replacing fonts as it goes according
+        to find and replace
+        '''
+        replacements = 0
+        for node in nodes:
+            if find_replace_font(node, find, replace):
+                replacements += 1
+        report_replacements(replacements)
+
+    def replace_all(self, nodes, replace):
+        '''
+        Walks through nodes, setting fonts indiscriminately.
+        '''
+        replacements = 0
+        for node in nodes:
+            if set_font(node, replace):
+                replacements += 1
+        report_replacements(replacements)
+
+    def list_all(self, nodes):
+        '''
+        Walks through nodes, building a list of all fonts found, then
+        reports to the user with that list
+        '''
+        fonts_found = []
+        for node in nodes:
+            for f in get_fonts(node):
+                if not f in fonts_found:
+                    fonts_found.append(f)
+        report_findings(sorted(fonts_found))
+
+    def effect(self):
+        action = self.options.action.strip("\"") # TODO Is this a bug? (Extra " characters)
+        scope = self.options.scope
+
+        relevant_items = self.relevant_items(scope)
+
+        if action == "find_replace":
+            find = self.options.fr_find
+            if find is None or find == "":
+                die(_("Please enter a search string in the find box."));
+            find = find.strip().lower()
+            replace = self.options.fr_replace
+            if replace is None or replace == "":
+                die(_("Please enter a replacement font in the replace with box."));
+            self.find_replace(relevant_items, find, replace)
+        elif action == "replace_all":
+            replace = self.options.r_replace
+            if replace is None or replace == "":
+                die(_("Please enter a replacement font in the replace all box."));
+            self.replace_all(relevant_items, replace)
+        elif action == "list_only":
+            self.list_all(relevant_items)
+            sys.exit(0)
+
+if __name__ == "__main__":
+    e = ReplaceFont()
+    e.affect()