diff options
Diffstat (limited to 'share/extensions')
44 files changed, 6561 insertions, 412 deletions
diff --git a/share/extensions/Inkscape.pm b/share/extensions/Inkscape.pm index b87762c0d..3e9027baa 100644 --- a/share/extensions/Inkscape.pm +++ b/share/extensions/Inkscape.pm @@ -166,14 +166,14 @@ sub generateSearchRegExp { =head3 setElAttribute - # Setting an atribute in a tag localizated it's id: + # Setting an attribute in a tag localizated it's id: $svg->setElAttribute {id=>'myDrawing'}, 'width', $w * 2; - # Setting an atribute in a tag localizated it's position: + # Setting an attribute in a tag localizated it's position: $svg->setElAttribute {tag=>'svg',pos=>0}, 'width', $w * 2; - # Setting an atribute in some tags localizated by atributes: + # Setting an attribute in some tags localizated by attributes: $svg->setElAttribute {tag=>'circle',r=>10}, 'r', 50; -This method will set an atribute in a tag or in a colection of tags, selected +This method will set an attribute in a tag or in a colection of tags, selected by the localizator hash. =cut diff --git a/share/extensions/Makefile.am b/share/extensions/Makefile.am index 7327ffbd5..78532f939 100644 --- a/share/extensions/Makefile.am +++ b/share/extensions/Makefile.am @@ -76,6 +76,21 @@ extensions = \ inkweb.js \ interp.py \ interp_att_g.py \ + jessyInk.js \ + jessyInk_autoTexts.py \ + jessyInk_core_mouseHandler_noclick.js \ + jessyInk_core_mouseHandler_zoomControl.js \ + jessyInk_effects.py \ + jessyInk_export.py \ + jessyInk_install.py \ + jessyInk_keyBindings.py \ + jessyInk_masterSlide.py \ + jessyInk_mouseHandler.py \ + jessyInk_summary.py \ + jessyInk_transitions.py \ + jessyInk_uninstall.py \ + jessyInk_video.py \ + jessyInk_view.py \ launch_webbrowser.py \ lindenmayer.py \ lorem_ipsum.py \ @@ -148,6 +163,7 @@ extensions = \ otherstuff = \ aisvg.xslt \ colors.xml \ + jessyInk_video.svg \ svg2xaml.xsl \ xaml2svg.xsl @@ -217,6 +233,18 @@ modules = \ inkscape_help_keys.inx \ interp.inx \ interp_att_g.inx \ + jessyInk_autoTexts.inx \ + jessyInk_effects.inx \ + jessyInk_export.inx \ + jessyInk_install.inx \ + jessyInk_keyBindings.inx \ + jessyInk_masterSlide.inx \ + jessyInk_mouseHandler.inx \ + jessyInk_summary.inx \ + jessyInk_transitions.inx \ + jessyInk_uninstall.inx \ + jessyInk_video.inx \ + jessyInk_view.inx \ lindenmayer.inx \ lorem_ipsum.inx \ markers_strokepaint.inx \ diff --git a/share/extensions/generate_voronoi.inx b/share/extensions/generate_voronoi.inx index 4a04ffe1d..dc0dde07d 100644 --- a/share/extensions/generate_voronoi.inx +++ b/share/extensions/generate_voronoi.inx @@ -7,8 +7,8 @@ <dependency type="executable" location="extensions">inkex.py</dependency> <_param name="title1" type="description">Generate a random pattern of Voronoi cells. The pattern will be accessible in the Fill and Stroke dialog. You must select an object or a group.</_param> <_param name="title2" type="description">If border is zero, the pattern will be discontinuous at the edges. Use a positive border, preferably greater than the cell size, to produce a smooth join of the pattern at the edges. Use a negative border to reduce the size of the pattern and get an empty border.</_param> - <param name="size" type="int" min="2" max="200" _gui-text=" Average size of cell (px) ">10</param> - <param name="border" type="int" min="-200" max="200" _gui-text=" Size of Border (px) ">0</param> + <param name="size" type="int" min="2" max="200" _gui-text="Average size of cell (px)">10</param> + <param name="border" type="int" min="-200" max="200" _gui-text="Size of Border (px)">0</param> <effect> <object-type>all</object-type> <effects-menu> diff --git a/share/extensions/inkscape_help_keys.inx b/share/extensions/inkscape_help_keys.inx index c7894a81a..938a11077 100644 --- a/share/extensions/inkscape_help_keys.inx +++ b/share/extensions/inkscape_help_keys.inx @@ -3,7 +3,7 @@ <_name>Keys and Mouse Reference</_name> <id>org.inkscape.help.keys</id> <dependency type="executable" location="extensions">launch_webbrowser.py</dependency> - <param name="url" gui-hidden="true" type="string">http://inkscape.org/doc/keys047.html</param> + <param name="url" gui-hidden="true" type="string">http://inkscape.org/doc/keys048.html</param> <effect needs-document="false"> <object-type>all</object-type> <effects-menu hidden="true"/> diff --git a/share/extensions/inkscape_help_relnotes.inx b/share/extensions/inkscape_help_relnotes.inx index ab790bd9f..881eada0c 100644 --- a/share/extensions/inkscape_help_relnotes.inx +++ b/share/extensions/inkscape_help_relnotes.inx @@ -3,7 +3,7 @@ <_name>New in This Version</_name> <id>org.inkscape.help.relnotes</id> <dependency type="executable" location="extensions">launch_webbrowser.py</dependency> - <param name="url" gui-hidden="true" type="string">http://wiki.inkscape.org/wiki/index.php/ReleaseNotes047</param> + <param name="url" gui-hidden="true" type="string">http://wiki.inkscape.org/wiki/index.php/Release_notes/0.48</param> <effect needs-document="false"> <object-type>all</object-type> <effects-menu hidden="true"/> diff --git a/share/extensions/interp_att_g.py b/share/extensions/interp_att_g.py index 503e4ec60..f23edaf36 100755 --- a/share/extensions/interp_att_g.py +++ b/share/extensions/interp_att_g.py @@ -29,7 +29,7 @@ class InterpAttG(inkex.Effect): self.OptionParser.add_option("-o", "--att-other", action="store", type="string", dest="att_other", - help="Other atribute (for a limited UI).") + help="Other attribute (for a limited UI).") self.OptionParser.add_option("-t", "--att-other-type", action="store", type="string", dest="att_other_type", diff --git a/share/extensions/jessyInk.js b/share/extensions/jessyInk.js new file mode 100755 index 000000000..51736ad4e --- /dev/null +++ b/share/extensions/jessyInk.js @@ -0,0 +1,2722 @@ +// Copyright 2008, 2009 Hannes Hochreiner +// 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 3 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, see http://www.gnu.org/licenses/. + +// Set onload event handler. +window.onload = jessyInkInit; + +// Creating a namespace dictionary. The standard Inkscape namespaces are taken from inkex.py. +var NSS = new Object(); +NSS['sodipodi']='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd'; +NSS['cc']='http://web.resource.org/cc/'; +NSS['svg']='http://www.w3.org/2000/svg'; +NSS['dc']='http://purl.org/dc/elements/1.1/'; +NSS['rdf']='http://www.w3.org/1999/02/22-rdf-syntax-ns#'; +NSS['inkscape']='http://www.inkscape.org/namespaces/inkscape'; +NSS['xlink']='http://www.w3.org/1999/xlink'; +NSS['xml']='http://www.w3.org/XML/1998/namespace'; +NSS['jessyink']='https://launchpad.net/jessyink'; + +// Keycodes. +var LEFT_KEY = 37; // cursor left keycode +var UP_KEY = 38; // cursor up keycode +var RIGHT_KEY = 39; // cursor right keycode +var DOWN_KEY = 40; // cursor down keycode +var PAGE_UP_KEY = 33; // page up keycode +var PAGE_DOWN_KEY = 34; // page down keycode +var HOME_KEY = 36; // home keycode +var END_KEY = 35; // end keycode +var ENTER_KEY = 13; // next slide +var SPACE_KEY = 32; + +// Presentation modes. +var SLIDE_MODE = 1; +var INDEX_MODE = 2; +var DRAWING_MODE = 3; + +// Mouse handler actions. +var MOUSE_UP = 1; +var MOUSE_DOWN = 2; +var MOUSE_MOVE = 3; +var MOUSE_WHEEL = 4; + +// Parameters. +var ROOT_NODE = document.getElementsByTagNameNS(NSS["svg"], "svg")[0]; +var HEIGHT = 0; +var WIDTH = 0; +var INDEX_COLUMNS_DEFAULT = 4; +var INDEX_COLUMNS = INDEX_COLUMNS_DEFAULT; +var INDEX_OFFSET = 0; +var STATE_START = -1; +var STATE_END = -2; +var BACKGROUND_COLOR = null; +var slides = new Array(); + +// Initialisation. +var currentMode = SLIDE_MODE; +var masterSlide = null; +var activeSlide = 0; +var activeEffect = 0; +var timeStep = 30; // 40 ms equal 25 frames per second. +var lastFrameTime = null; +var processingEffect = false; +var transCounter = 0; +var effectArray = 0; +var defaultTransitionInDict = new Object(); +defaultTransitionInDict["name"] = "appear"; +var defaultTransitionOutDict = new Object(); +defaultTransitionOutDict["name"] = "appear"; +var jessyInkInitialised = false; + +// Initialise char and key code dictionaries. +var charCodeDictionary = getDefaultCharCodeDictionary(); +var keyCodeDictionary = getDefaultKeyCodeDictionary(); + +// Initialise mouse handler dictionary. +var mouseHandlerDictionary = getDefaultMouseHandlerDictionary(); + +var progress_bar_visible = false; +var timer_elapsed = 0; +var timer_start = timer_elapsed; +var timer_duration = 15; // 15 minutes + +var history_counter = 0; +var history_original_elements = new Array(); +var history_presentation_elements = new Array(); + +var mouse_original_path = null; +var mouse_presentation_path = null; +var mouse_last_x = -1; +var mouse_last_y = -1; +var mouse_min_dist_sqr = 3 * 3; +var path_colour = "red"; +var path_width_default = 3; +var path_width = path_width_default; +var path_paint_width = path_width; + +var number_of_added_slides = 0; + +/** Initialisation function. + * The whole presentation is set-up in this function. + */ +function jessyInkInit() +{ + // Make sure we only execute this code once. Double execution can occur if the onload event handler is set + // in the main svg tag as well (as was recommended in earlier versions). Executing this function twice does + // not lead to any problems, but it takes more time. + if (jessyInkInitialised) + return; + + // Making the presentation scaleable. + var VIEWBOX = ROOT_NODE.getAttribute("viewBox"); + + if (VIEWBOX) + { + WIDTH = ROOT_NODE.viewBox.animVal.width; + HEIGHT = ROOT_NODE.viewBox.animVal.height; + } + else + { + HEIGHT = parseFloat(ROOT_NODE.getAttribute("height")); + WIDTH = parseFloat(ROOT_NODE.getAttribute("width")); + ROOT_NODE.setAttribute("viewBox", "0 0 " + WIDTH + " " + HEIGHT); + } + + ROOT_NODE.setAttribute("width", "100%"); + ROOT_NODE.setAttribute("height", "100%"); + + // Setting the background color. + var namedViews = document.getElementsByTagNameNS(NSS["sodipodi"], "namedview"); + + for (var counter = 0; counter < namedViews.length; counter++) + { + if (namedViews[counter].hasAttribute("id") && namedViews[counter].hasAttribute("pagecolor")) + { + if (namedViews[counter].getAttribute("id") == "base") + { + BACKGROUND_COLOR = namedViews[counter].getAttribute("pagecolor"); + var newAttribute = "background-color:" + BACKGROUND_COLOR + ";"; + + if (ROOT_NODE.hasAttribute("style")) + newAttribute += ROOT_NODE.getAttribute("style"); + + ROOT_NODE.setAttribute("style", newAttribute); + } + } + } + + // Defining clip-path. + var defsNodes = document.getElementsByTagNameNS(NSS["svg"], "defs"); + + if (defsNodes.length > 0) + { + var existingClipPath = document.getElementById("jessyInkSlideClipPath"); + + if (!existingClipPath) + { + var rectNode = document.createElementNS(NSS["svg"], "rect"); + var clipPath = document.createElementNS(NSS["svg"], "clipPath"); + + rectNode.setAttribute("x", 0); + rectNode.setAttribute("y", 0); + rectNode.setAttribute("width", WIDTH); + rectNode.setAttribute("height", HEIGHT); + + clipPath.setAttribute("id", "jessyInkSlideClipPath"); + clipPath.setAttribute("clipPathUnits", "userSpaceOnUse"); + + clipPath.appendChild(rectNode); + defsNodes[0].appendChild(clipPath); + } + } + + // Making a list of the slide and finding the master slide. + var nodes = document.getElementsByTagNameNS(NSS["svg"], "g"); + var tempSlides = new Array(); + var existingJessyInkPresentationLayer = null; + + for (var counter = 0; counter < nodes.length; counter++) + { + if (nodes[counter].getAttributeNS(NSS["inkscape"], "groupmode") && (nodes[counter].getAttributeNS(NSS["inkscape"], "groupmode") == "layer")) + { + if (nodes[counter].getAttributeNS(NSS["inkscape"], "label") && nodes[counter].getAttributeNS(NSS["jessyink"], "masterSlide") == "masterSlide") + masterSlide = nodes[counter]; + else if (nodes[counter].getAttributeNS(NSS["inkscape"], "label") && nodes[counter].getAttributeNS(NSS["jessyink"], "presentationLayer") == "presentationLayer") + existingJessyInkPresentationLayer = nodes[counter]; + else + tempSlides.push(nodes[counter].getAttribute("id")); + } + else if (nodes[counter].getAttributeNS(NSS['jessyink'], 'element')) + { + handleElement(nodes[counter]); + } + } + + // Hide master slide set default transitions. + if (masterSlide) + { + masterSlide.style.display = "none"; + + if (masterSlide.hasAttributeNS(NSS["jessyink"], "transitionIn")) + defaultTransitionInDict = propStrToDict(masterSlide.getAttributeNS(NSS["jessyink"], "transitionIn")); + + if (masterSlide.hasAttributeNS(NSS["jessyink"], "transitionOut")) + defaultTransitionOutDict = propStrToDict(masterSlide.getAttributeNS(NSS["jessyink"], "transitionOut")); + } + + if (existingJessyInkPresentationLayer != null) + { + existingJessyInkPresentationLayer.parentNode.removeChild(existingJessyInkPresentationLayer); + } + + // Set start slide. + var hashObj = new LocationHash(window.location.hash); + + activeSlide = hashObj.slideNumber; + activeEffect = hashObj.effectNumber; + + if (activeSlide < 0) + activeSlide = 0; + else if (activeSlide >= tempSlides.length) + activeSlide = tempSlides.length - 1; + + var originalNode = document.getElementById(tempSlides[counter]); + + var JessyInkPresentationLayer = document.createElementNS(NSS["svg"], "g"); + JessyInkPresentationLayer.setAttributeNS(NSS["inkscape"], "groupmode", "layer"); + JessyInkPresentationLayer.setAttributeNS(NSS["inkscape"], "label", "JessyInk Presentation Layer"); + JessyInkPresentationLayer.setAttributeNS(NSS["jessyink"], "presentationLayer", "presentationLayer"); + JessyInkPresentationLayer.setAttribute("id", "jessyink_presentation_layer"); + JessyInkPresentationLayer.style.display = "inherit"; + ROOT_NODE.appendChild(JessyInkPresentationLayer); + + // Gathering all the information about the transitions and effects of the slides, set the background + // from the master slide and substitute the auto-texts. + for (var counter = 0; counter < tempSlides.length; counter++) + { + var originalNode = document.getElementById(tempSlides[counter]); + originalNode.style.display = "none"; + var node = suffixNodeIds(originalNode.cloneNode(true), "_" + counter); + JessyInkPresentationLayer.appendChild(node); + slides[counter] = new Object(); + slides[counter]["original_element"] = originalNode; + slides[counter]["element"] = node; + + // Set build in transition. + slides[counter]["transitionIn"] = new Object(); + + var dict; + + if (node.hasAttributeNS(NSS["jessyink"], "transitionIn")) + dict = propStrToDict(node.getAttributeNS(NSS["jessyink"], "transitionIn")); + else + dict = defaultTransitionInDict; + + slides[counter]["transitionIn"]["name"] = dict["name"]; + slides[counter]["transitionIn"]["options"] = new Object(); + + for (key in dict) + if (key != "name") + slides[counter]["transitionIn"]["options"][key] = dict[key]; + + // Set build out transition. + slides[counter]["transitionOut"] = new Object(); + + if (node.hasAttributeNS(NSS["jessyink"], "transitionOut")) + dict = propStrToDict(node.getAttributeNS(NSS["jessyink"], "transitionOut")); + else + dict = defaultTransitionOutDict; + + slides[counter]["transitionOut"]["name"] = dict["name"]; + slides[counter]["transitionOut"]["options"] = new Object(); + + for (key in dict) + if (key != "name") + slides[counter]["transitionOut"]["options"][key] = dict[key]; + + // Copy master slide content. + if (masterSlide) + { + var clonedNode = suffixNodeIds(masterSlide.cloneNode(true), "_" + counter); + clonedNode.removeAttributeNS(NSS["inkscape"], "groupmode"); + clonedNode.removeAttributeNS(NSS["inkscape"], "label"); + clonedNode.style.display = "inherit"; + + node.insertBefore(clonedNode, node.firstChild); + } + + // Setting clip path. + node.setAttribute("clip-path", "url(#jessyInkSlideClipPath)"); + + // Substitute auto texts. + substituteAutoTexts(node, node.getAttributeNS(NSS["inkscape"], "label"), counter + 1, tempSlides.length); + + node.removeAttributeNS(NSS["inkscape"], "groupmode"); + node.removeAttributeNS(NSS["inkscape"], "label"); + + // Set effects. + var tempEffects = new Array(); + var groups = new Object(); + + for (var IOCounter = 0; IOCounter <= 1; IOCounter++) + { + var propName = ""; + var dir = 0; + + if (IOCounter == 0) + { + propName = "effectIn"; + dir = 1; + } + else if (IOCounter == 1) + { + propName = "effectOut"; + dir = -1; + } + + var effects = getElementsByPropertyNS(node, NSS["jessyink"], propName); + + for (var effectCounter = 0; effectCounter < effects.length; effectCounter++) + { + var element = document.getElementById(effects[effectCounter]); + var dict = propStrToDict(element.getAttributeNS(NSS["jessyink"], propName)); + + // Put every element that has an effect associated with it, into its own group. + // Unless of course, we already put it into its own group. + if (!(groups[element.id])) + { + var newGroup = document.createElementNS(NSS["svg"], "g"); + + element.parentNode.insertBefore(newGroup, element); + newGroup.appendChild(element.parentNode.removeChild(element)); + groups[element.id] = newGroup; + } + + var effectDict = new Object(); + + effectDict["effect"] = dict["name"]; + effectDict["dir"] = dir; + effectDict["element"] = groups[element.id]; + + for (var option in dict) + { + if ((option != "name") && (option != "order")) + { + if (!effectDict["options"]) + effectDict["options"] = new Object(); + + effectDict["options"][option] = dict[option]; + } + } + + if (!tempEffects[dict["order"]]) + tempEffects[dict["order"]] = new Array(); + + tempEffects[dict["order"]][tempEffects[dict["order"]].length] = effectDict; + } + } + + // Make invisible, but keep in rendering tree to ensure that bounding box can be calculated. + node.setAttribute("opacity",0); + node.style.display = "inherit"; + + // Create a transform group. + var transformGroup = document.createElementNS(NSS["svg"], "g"); + + // Add content to transform group. + while (node.firstChild) + transformGroup.appendChild(node.firstChild); + + // Transfer the transform attribute from the node to the transform group. + if (node.getAttribute("transform")) + { + transformGroup.setAttribute("transform", node.getAttribute("transform")); + node.removeAttribute("transform"); + } + + // Create a view group. + var viewGroup = document.createElementNS(NSS["svg"], "g"); + + viewGroup.appendChild(transformGroup); + slides[counter]["viewGroup"] = node.appendChild(viewGroup); + + // Insert background. + if (BACKGROUND_COLOR != null) + { + var rectNode = document.createElementNS(NSS["svg"], "rect"); + + rectNode.setAttribute("x", 0); + rectNode.setAttribute("y", 0); + rectNode.setAttribute("width", WIDTH); + rectNode.setAttribute("height", HEIGHT); + rectNode.setAttribute("id", "jessyInkBackground" + counter); + rectNode.setAttribute("fill", BACKGROUND_COLOR); + + slides[counter]["viewGroup"].insertBefore(rectNode, slides[counter]["viewGroup"].firstChild); + } + + // Set views. + var tempViews = new Array(); + var views = getElementsByPropertyNS(node, NSS["jessyink"], "view"); + var matrixOld = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1); + + // Set initial view even if there are no other views. + slides[counter]["viewGroup"].setAttribute("transform", matrixOld.toAttribute()); + slides[counter].initialView = matrixOld.toAttribute(); + + for (var viewCounter = 0; viewCounter < views.length; viewCounter++) + { + var element = document.getElementById(views[viewCounter]); + var dict = propStrToDict(element.getAttributeNS(NSS["jessyink"], "view")); + + if (dict["order"] == 0) + { + matrixOld = pointMatrixToTransformation(rectToMatrix(element)).mult((new matrixSVG()).fromSVGMatrix(slides[counter].viewGroup.getScreenCTM()).inv().mult((new matrixSVG()).fromSVGMatrix(element.parentNode.getScreenCTM())).inv()); + slides[counter].initialView = matrixOld.toAttribute(); + } + else + { + var effectDict = new Object(); + + effectDict["effect"] = dict["name"]; + effectDict["dir"] = 1; + effectDict["element"] = slides[counter]["viewGroup"]; + effectDict["order"] = dict["order"]; + + for (var option in dict) + { + if ((option != "name") && (option != "order")) + { + if (!effectDict["options"]) + effectDict["options"] = new Object(); + + effectDict["options"][option] = dict[option]; + } + } + + effectDict["options"]["matrixNew"] = pointMatrixToTransformation(rectToMatrix(element)).mult((new matrixSVG()).fromSVGMatrix(slides[counter].viewGroup.getScreenCTM()).inv().mult((new matrixSVG()).fromSVGMatrix(element.parentNode.getScreenCTM())).inv()); + + tempViews[dict["order"]] = effectDict; + } + + // Remove element. + element.parentNode.removeChild(element); + } + + // Consolidate view array and append it to the effect array. + if (tempViews.length > 0) + { + for (var viewCounter = 0; viewCounter < tempViews.length; viewCounter++) + { + if (tempViews[viewCounter]) + { + tempViews[viewCounter]["options"]["matrixOld"] = matrixOld; + matrixOld = tempViews[viewCounter]["options"]["matrixNew"]; + + if (!tempEffects[tempViews[viewCounter]["order"]]) + tempEffects[tempViews[viewCounter]["order"]] = new Array(); + + tempEffects[tempViews[viewCounter]["order"]][tempEffects[tempViews[viewCounter]["order"]].length] = tempViews[viewCounter]; + } + } + } + + // Set consolidated effect array. + if (tempEffects.length > 0) + { + slides[counter]["effects"] = new Array(); + + for (var effectCounter = 0; effectCounter < tempEffects.length; effectCounter++) + { + if (tempEffects[effectCounter]) + slides[counter]["effects"][slides[counter]["effects"].length] = tempEffects[effectCounter]; + } + } + + node.setAttribute("onmouseover", "if ((currentMode == INDEX_MODE) && ( activeSlide != " + counter + ")) { indexSetActiveSlide(" + counter + "); };"); + + // Set visibility for initial state. + if (counter == activeSlide) + { + node.style.display = "inherit"; + node.setAttribute("opacity",1); + } + else + { + node.style.display = "none"; + node.setAttribute("opacity",0); + } + } + + // Set key handler. + var jessyInkObjects = document.getElementsByTagNameNS(NSS["svg"], "g"); + + for (var counter = 0; counter < jessyInkObjects.length; counter++) + { + var elem = jessyInkObjects[counter]; + + if (elem.getAttributeNS(NSS["jessyink"], "customKeyBindings")) + { + if (elem.getCustomKeyBindings != undefined) + keyCodeDictionary = elem.getCustomKeyBindings(); + + if (elem.getCustomCharBindings != undefined) + charCodeDictionary = elem.getCustomCharBindings(); + } + } + + // Set mouse handler. + var jessyInkMouseHandler = document.getElementsByTagNameNS(NSS["jessyink"], "mousehandler"); + + for (var counter = 0; counter < jessyInkMouseHandler.length; counter++) + { + var elem = jessyInkMouseHandler[counter]; + + if (elem.getMouseHandler != undefined) + { + var tempDict = elem.getMouseHandler(); + + for (mode in tempDict) + { + if (!mouseHandlerDictionary[mode]) + mouseHandlerDictionary[mode] = new Object(); + + for (handler in tempDict[mode]) + mouseHandlerDictionary[mode][handler] = tempDict[mode][handler]; + } + } + } + + // Check effect number. + if ((activeEffect < 0) || (!slides[activeSlide].effects)) + { + activeEffect = 0; + } + else if (activeEffect > slides[activeSlide].effects.length) + { + activeEffect = slides[activeSlide].effects.length; + } + + createProgressBar(JessyInkPresentationLayer); + hideProgressBar(); + setProgressBarValue(activeSlide); + setTimeIndicatorValue(0); + setInterval("updateTimer()", 1000); + setSlideToState(activeSlide, activeEffect); + jessyInkInitialised = true; +} + +/** Function to subtitute the auto-texts. + * + * @param node the node + * @param slideName name of the slide the node is on + * @param slideNumber number of the slide the node is on + * @param numberOfSlides number of slides in the presentation + */ +function substituteAutoTexts(node, slideName, slideNumber, numberOfSlides) +{ + var texts = node.getElementsByTagNameNS(NSS["svg"], "tspan"); + + for (var textCounter = 0; textCounter < texts.length; textCounter++) + { + if (texts[textCounter].getAttributeNS(NSS["jessyink"], "autoText") == "slideNumber") + texts[textCounter].firstChild.nodeValue = slideNumber; + else if (texts[textCounter].getAttributeNS(NSS["jessyink"], "autoText") == "numberOfSlides") + texts[textCounter].firstChild.nodeValue = numberOfSlides; + else if (texts[textCounter].getAttributeNS(NSS["jessyink"], "autoText") == "slideTitle") + texts[textCounter].firstChild.nodeValue = slideName; + } +} + +/** Convenience function to get an element depending on whether it has a property with a particular name. + * This function emulates some dearly missed XPath functionality. + * + * @param node the node + * @param namespace namespace of the attribute + * @param name attribute name + */ +function getElementsByPropertyNS(node, namespace, name) +{ + var elems = new Array(); + + if (node.getAttributeNS(namespace, name)) + elems.push(node.getAttribute("id")); + + for (var counter = 0; counter < node.childNodes.length; counter++) + { + if (node.childNodes[counter].nodeType == 1) + elems = elems.concat(getElementsByPropertyNS(node.childNodes[counter], namespace, name)); + } + + return elems; +} + +/** Function to dispatch the next effect, if there is none left, change the slide. + * + * @param dir direction of the change (1 = forwards, -1 = backwards) + */ +function dispatchEffects(dir) +{ + if (slides[activeSlide]["effects"] && (((dir == 1) && (activeEffect < slides[activeSlide]["effects"].length)) || ((dir == -1) && (activeEffect > 0)))) + { + processingEffect = true; + + if (dir == 1) + { + effectArray = slides[activeSlide]["effects"][activeEffect]; + activeEffect += dir; + } + else if (dir == -1) + { + activeEffect += dir; + effectArray = slides[activeSlide]["effects"][activeEffect]; + } + + transCounter = 0; + startTime = (new Date()).getTime(); + lastFrameTime = null; + effect(dir); + } + else if (((dir == 1) && (activeSlide < (slides.length - 1))) || (((dir == -1) && (activeSlide > 0)))) + { + changeSlide(dir); + } +} + +/** Function to skip effects and directly either put the slide into start or end state or change slides. + * + * @param dir direction of the change (1 = forwards, -1 = backwards) + */ +function skipEffects(dir) +{ + if (slides[activeSlide]["effects"] && (((dir == 1) && (activeEffect < slides[activeSlide]["effects"].length)) || ((dir == -1) && (activeEffect > 0)))) + { + processingEffect = true; + + if (slides[activeSlide]["effects"] && (dir == 1)) + activeEffect = slides[activeSlide]["effects"].length; + else + activeEffect = 0; + + if (dir == 1) + setSlideToState(activeSlide, STATE_END); + else + setSlideToState(activeSlide, STATE_START); + + processingEffect = false; + } + else if (((dir == 1) && (activeSlide < (slides.length - 1))) || (((dir == -1) && (activeSlide > 0)))) + { + changeSlide(dir); + } +} + +/** Function to change between slides. + * + * @param dir direction (1 = forwards, -1 = backwards) + */ +function changeSlide(dir) +{ + processingEffect = true; + effectArray = new Array(); + + effectArray[0] = new Object(); + if (dir == 1) + { + effectArray[0]["effect"] = slides[activeSlide]["transitionOut"]["name"]; + effectArray[0]["options"] = slides[activeSlide]["transitionOut"]["options"]; + effectArray[0]["dir"] = -1; + } + else if (dir == -1) + { + effectArray[0]["effect"] = slides[activeSlide]["transitionIn"]["name"]; + effectArray[0]["options"] = slides[activeSlide]["transitionIn"]["options"]; + effectArray[0]["dir"] = 1; + } + effectArray[0]["element"] = slides[activeSlide]["element"]; + + activeSlide += dir; + setProgressBarValue(activeSlide); + + effectArray[1] = new Object(); + + if (dir == 1) + { + effectArray[1]["effect"] = slides[activeSlide]["transitionIn"]["name"]; + effectArray[1]["options"] = slides[activeSlide]["transitionIn"]["options"]; + effectArray[1]["dir"] = 1; + } + else if (dir == -1) + { + effectArray[1]["effect"] = slides[activeSlide]["transitionOut"]["name"]; + effectArray[1]["options"] = slides[activeSlide]["transitionOut"]["options"]; + effectArray[1]["dir"] = -1; + } + + effectArray[1]["element"] = slides[activeSlide]["element"]; + + if (slides[activeSlide]["effects"] && (dir == -1)) + activeEffect = slides[activeSlide]["effects"].length; + else + activeEffect = 0; + + if (dir == -1) + setSlideToState(activeSlide, STATE_END); + else + setSlideToState(activeSlide, STATE_START); + + transCounter = 0; + startTime = (new Date()).getTime(); + lastFrameTime = null; + effect(dir); +} + +/** Function to toggle between index and slide mode. +*/ +function toggleSlideIndex() +{ + var suspendHandle = ROOT_NODE.suspendRedraw(500); + + if (currentMode == SLIDE_MODE) + { + hideProgressBar(); + INDEX_OFFSET = -1; + indexSetPageSlide(activeSlide); + currentMode = INDEX_MODE; + } + else if (currentMode == INDEX_MODE) + { + for (var counter = 0; counter < slides.length; counter++) + { + slides[counter]["element"].setAttribute("transform","scale(1)"); + + if (counter == activeSlide) + { + slides[counter]["element"].style.display = "inherit"; + slides[counter]["element"].setAttribute("opacity",1); + activeEffect = 0; + } + else + { + slides[counter]["element"].setAttribute("opacity",0); + slides[counter]["element"].style.display = "none"; + } + } + currentMode = SLIDE_MODE; + setSlideToState(activeSlide, STATE_START); + setProgressBarValue(activeSlide); + + if (progress_bar_visible) + { + showProgressBar(); + } + } + + ROOT_NODE.unsuspendRedraw(suspendHandle); + ROOT_NODE.forceRedraw(); +} + +/** Function to run an effect. + * + * @param dir direction in which to play the effect (1 = forwards, -1 = backwards) + */ +function effect(dir) +{ + var done = true; + + var suspendHandle = ROOT_NODE.suspendRedraw(200); + + for (var counter = 0; counter < effectArray.length; counter++) + { + if (effectArray[counter]["effect"] == "fade") + done &= fade(parseInt(effectArray[counter]["dir"]) * dir, effectArray[counter]["element"], transCounter, effectArray[counter]["options"]); + else if (effectArray[counter]["effect"] == "appear") + done &= appear(parseInt(effectArray[counter]["dir"]) * dir, effectArray[counter]["element"], transCounter, effectArray[counter]["options"]); + else if (effectArray[counter]["effect"] == "pop") + done &= pop(parseInt(effectArray[counter]["dir"]) * dir, effectArray[counter]["element"], transCounter, effectArray[counter]["options"]); + else if (effectArray[counter]["effect"] == "view") + done &= view(parseInt(effectArray[counter]["dir"]) * dir, effectArray[counter]["element"], transCounter, effectArray[counter]["options"]); + } + + ROOT_NODE.unsuspendRedraw(suspendHandle); + ROOT_NODE.forceRedraw(); + + if (!done) + { + var currentTime = (new Date()).getTime(); + var timeDiff = 1; + + transCounter = currentTime - startTime; + + if (lastFrameTime != null) + { + timeDiff = timeStep - (currentTime - lastFrameTime); + + if (timeDiff <= 0) + timeDiff = 1; + } + + lastFrameTime = currentTime; + + window.setTimeout("effect(" + dir + ")", timeDiff); + } + else + { + window.location.hash = (activeSlide + 1) + '_' + activeEffect; + processingEffect = false; + } +} + +/** Function to display the index sheet. + * + * @param offsetNumber offset number + */ +function displayIndex(offsetNumber) +{ + var offsetX = 0; + var offsetY = 0; + + if (offsetNumber < 0) + offsetNumber = 0; + else if (offsetNumber >= slides.length) + offsetNumber = slides.length - 1; + + for (var counter = 0; counter < slides.length; counter++) + { + if ((counter < offsetNumber) || (counter > offsetNumber + INDEX_COLUMNS * INDEX_COLUMNS - 1)) + { + slides[counter]["element"].setAttribute("opacity",0); + slides[counter]["element"].style.display = "none"; + } + else + { + offsetX = ((counter - offsetNumber) % INDEX_COLUMNS) * WIDTH; + offsetY = Math.floor((counter - offsetNumber) / INDEX_COLUMNS) * HEIGHT; + + slides[counter]["element"].setAttribute("transform","scale("+1/INDEX_COLUMNS+") translate("+offsetX+","+offsetY+")"); + slides[counter]["element"].style.display = "inherit"; + slides[counter]["element"].setAttribute("opacity",0.5); + } + + setSlideToState(counter, STATE_END); + } + + //do we need to save the current offset? + if (INDEX_OFFSET != offsetNumber) + INDEX_OFFSET = offsetNumber; +} + +/** Function to set the active slide in the slide view. + * + * @param nbr index of the active slide + */ +function slideSetActiveSlide(nbr) +{ + if (nbr >= slides.length) + nbr = slides.length - 1; + else if (nbr < 0) + nbr = 0; + + slides[activeSlide]["element"].setAttribute("opacity",0); + slides[activeSlide]["element"].style.display = "none"; + + activeSlide = parseInt(nbr); + + setSlideToState(activeSlide, STATE_START); + slides[activeSlide]["element"].style.display = "inherit"; + slides[activeSlide]["element"].setAttribute("opacity",1); + + activeEffect = 0; + setProgressBarValue(nbr); +} + +/** Function to set the active slide in the index view. + * + * @param nbr index of the active slide + */ +function indexSetActiveSlide(nbr) +{ + if (nbr >= slides.length) + nbr = slides.length - 1; + else if (nbr < 0) + nbr = 0; + + slides[activeSlide]["element"].setAttribute("opacity",0.5); + + activeSlide = parseInt(nbr); + window.location.hash = (activeSlide + 1) + '_0'; + + slides[activeSlide]["element"].setAttribute("opacity",1); +} + +/** Function to set the page and active slide in index view. + * + * @param nbr index of the active slide + * + * NOTE: To force a redraw, + * set INDEX_OFFSET to -1 before calling indexSetPageSlide(). + * + * This is necessary for zooming (otherwise the index might not + * get redrawn) and when switching to index mode. + * + * INDEX_OFFSET = -1 + * indexSetPageSlide(activeSlide); + */ +function indexSetPageSlide(nbr) +{ + if (nbr >= slides.length) + nbr = slides.length - 1; + else if (nbr < 0) + nbr = 0; + + //calculate the offset + var offset = nbr - nbr % (INDEX_COLUMNS * INDEX_COLUMNS); + + if (offset < 0) + offset = 0; + + //if different from kept offset, then record and change the page + if (offset != INDEX_OFFSET) + { + INDEX_OFFSET = offset; + displayIndex(INDEX_OFFSET); + } + + //set the active slide + indexSetActiveSlide(nbr); +} + +/** Event handler for key press. + * + * @param e the event + */ +function keydown(e) +{ + if (!e) + e = window.event; + + code = e.keyCode || e.charCode; + + if (!processingEffect && keyCodeDictionary[currentMode] && keyCodeDictionary[currentMode][code]) + keyCodeDictionary[currentMode][code](); + else + document.onkeypress = keypress; +} +// Set event handler for key down. +document.onkeydown = keydown; + +/** Event handler for key press. + * + * @param e the event + */ +function keypress(e) +{ + document.onkeypress = null; + + if (!e) + e = window.event; + + str = String.fromCharCode(e.keyCode || e.charCode); + + if (!processingEffect && charCodeDictionary[currentMode] && charCodeDictionary[currentMode][str]) + charCodeDictionary[currentMode][str](); +} + +/** Function to supply the default char code dictionary. + * + * @returns default char code dictionary + */ +function getDefaultCharCodeDictionary() +{ + var charCodeDict = new Object(); + + charCodeDict[SLIDE_MODE] = new Object(); + charCodeDict[INDEX_MODE] = new Object(); + charCodeDict[DRAWING_MODE] = new Object(); + + charCodeDict[SLIDE_MODE]["i"] = function () { toggleSlideIndex(); }; + charCodeDict[SLIDE_MODE]["d"] = function () { slideSwitchToDrawingMode(); }; + charCodeDict[SLIDE_MODE]["D"] = function () { slideQueryDuration(); }; + charCodeDict[SLIDE_MODE]["n"] = function () { slideAddSlide(activeSlide); }; + charCodeDict[SLIDE_MODE]["p"] = function () { slideToggleProgressBarVisibility(); }; + charCodeDict[SLIDE_MODE]["t"] = function () { slideResetTimer(); }; + charCodeDict[SLIDE_MODE]["e"] = function () { slideUpdateExportLayer(); }; + + charCodeDict[DRAWING_MODE]["d"] = function () { drawingSwitchToSlideMode(); }; + charCodeDict[DRAWING_MODE]["0"] = function () { drawingResetPathWidth(); }; + charCodeDict[DRAWING_MODE]["1"] = function () { drawingSetPathWidth(1.0); }; + charCodeDict[DRAWING_MODE]["3"] = function () { drawingSetPathWidth(3.0); }; + charCodeDict[DRAWING_MODE]["5"] = function () { drawingSetPathWidth(5.0); }; + charCodeDict[DRAWING_MODE]["7"] = function () { drawingSetPathWidth(7.0); }; + charCodeDict[DRAWING_MODE]["9"] = function () { drawingSetPathWidth(9.0); }; + charCodeDict[DRAWING_MODE]["b"] = function () { drawingSetPathColour("blue"); }; + charCodeDict[DRAWING_MODE]["c"] = function () { drawingSetPathColour("cyan"); }; + charCodeDict[DRAWING_MODE]["g"] = function () { drawingSetPathColour("green"); }; + charCodeDict[DRAWING_MODE]["k"] = function () { drawingSetPathColour("black"); }; + charCodeDict[DRAWING_MODE]["m"] = function () { drawingSetPathColour("magenta"); }; + charCodeDict[DRAWING_MODE]["o"] = function () { drawingSetPathColour("orange"); }; + charCodeDict[DRAWING_MODE]["r"] = function () { drawingSetPathColour("red"); }; + charCodeDict[DRAWING_MODE]["w"] = function () { drawingSetPathColour("white"); }; + charCodeDict[DRAWING_MODE]["y"] = function () { drawingSetPathColour("yellow"); }; + charCodeDict[DRAWING_MODE]["z"] = function () { drawingUndo(); }; + + charCodeDict[INDEX_MODE]["i"] = function () { toggleSlideIndex(); }; + charCodeDict[INDEX_MODE]["-"] = function () { indexDecreaseNumberOfColumns(); }; + charCodeDict[INDEX_MODE]["="] = function () { indexIncreaseNumberOfColumns(); }; + charCodeDict[INDEX_MODE]["+"] = function () { indexIncreaseNumberOfColumns(); }; + charCodeDict[INDEX_MODE]["0"] = function () { indexResetNumberOfColumns(); }; + + return charCodeDict; +} + +/** Function to supply the default key code dictionary. + * + * @returns default key code dictionary + */ +function getDefaultKeyCodeDictionary() +{ + var keyCodeDict = new Object(); + + keyCodeDict[SLIDE_MODE] = new Object(); + keyCodeDict[INDEX_MODE] = new Object(); + keyCodeDict[DRAWING_MODE] = new Object(); + + keyCodeDict[SLIDE_MODE][LEFT_KEY] = function() { dispatchEffects(-1); }; + keyCodeDict[SLIDE_MODE][RIGHT_KEY] = function() { dispatchEffects(1); }; + keyCodeDict[SLIDE_MODE][UP_KEY] = function() { skipEffects(-1); }; + keyCodeDict[SLIDE_MODE][DOWN_KEY] = function() { skipEffects(1); }; + keyCodeDict[SLIDE_MODE][PAGE_UP_KEY] = function() { dispatchEffects(-1); }; + keyCodeDict[SLIDE_MODE][PAGE_DOWN_KEY] = function() { dispatchEffects(1); }; + keyCodeDict[SLIDE_MODE][HOME_KEY] = function() { slideSetActiveSlide(0); }; + keyCodeDict[SLIDE_MODE][END_KEY] = function() { slideSetActiveSlide(slides.length - 1); }; + keyCodeDict[SLIDE_MODE][SPACE_KEY] = function() { dispatchEffects(1); }; + + keyCodeDict[INDEX_MODE][LEFT_KEY] = function() { indexSetPageSlide(activeSlide - 1); }; + keyCodeDict[INDEX_MODE][RIGHT_KEY] = function() { indexSetPageSlide(activeSlide + 1); }; + keyCodeDict[INDEX_MODE][UP_KEY] = function() { indexSetPageSlide(activeSlide - INDEX_COLUMNS); }; + keyCodeDict[INDEX_MODE][DOWN_KEY] = function() { indexSetPageSlide(activeSlide + INDEX_COLUMNS); }; + keyCodeDict[INDEX_MODE][PAGE_UP_KEY] = function() { indexSetPageSlide(activeSlide - INDEX_COLUMNS * INDEX_COLUMNS); }; + keyCodeDict[INDEX_MODE][PAGE_DOWN_KEY] = function() { indexSetPageSlide(activeSlide + INDEX_COLUMNS * INDEX_COLUMNS); }; + keyCodeDict[INDEX_MODE][HOME_KEY] = function() { indexSetPageSlide(0); }; + keyCodeDict[INDEX_MODE][END_KEY] = function() { indexSetPageSlide(slides.length - 1); }; + keyCodeDict[INDEX_MODE][ENTER_KEY] = function() { toggleSlideIndex(); }; + + return keyCodeDict; +} + +/** Function to handle all mouse events. + * + * @param evnt event + * @param action type of event (e.g. mouse up, mouse wheel) + */ +function mouseHandlerDispatch(evnt, action) +{ + if (!evnt) + evnt = window.event; + + var retVal = true; + + if (!processingEffect && mouseHandlerDictionary[currentMode] && mouseHandlerDictionary[currentMode][action]) + { + var subRetVal = mouseHandlerDictionary[currentMode][action](evnt); + + if (subRetVal != null && subRetVal != undefined) + retVal = subRetVal; + } + + if (evnt.preventDefault && !retVal) + evnt.preventDefault(); + + evnt.returnValue = retVal; + + return retVal; +} + +// Set mouse event handler. +document.onmousedown = function(e) { return mouseHandlerDispatch(e, MOUSE_DOWN); }; +document.onmouseup = function(e) { return mouseHandlerDispatch(e, MOUSE_UP); }; +document.onmousemove = function(e) { return mouseHandlerDispatch(e, MOUSE_MOVE); }; + +// Moz +if (window.addEventListener) +{ + window.addEventListener('DOMMouseScroll', function(e) { return mouseHandlerDispatch(e, MOUSE_WHEEL); }, false); +} + +// Opera Safari OK - may not work in IE +window.onmousewheel = function(e) { return mouseHandlerDispatch(e, MOUSE_WHEEL); }; + +/** Function to supply the default mouse handler dictionary. + * + * @returns default mouse handler dictionary + */ +function getDefaultMouseHandlerDictionary() +{ + var mouseHandlerDict = new Object(); + + mouseHandlerDict[SLIDE_MODE] = new Object(); + mouseHandlerDict[INDEX_MODE] = new Object(); + mouseHandlerDict[DRAWING_MODE] = new Object(); + + mouseHandlerDict[SLIDE_MODE][MOUSE_DOWN] = function(evnt) { dispatchEffects(1); }; + mouseHandlerDict[SLIDE_MODE][MOUSE_WHEEL] = function(evnt) { slideMousewheel(evnt); }; + + mouseHandlerDict[INDEX_MODE][MOUSE_DOWN] = function(evnt) { toggleSlideIndex(); }; + + mouseHandlerDict[DRAWING_MODE][MOUSE_DOWN] = function(evnt) { drawingMousedown(evnt); }; + mouseHandlerDict[DRAWING_MODE][MOUSE_UP] = function(evnt) { drawingMouseup(evnt); }; + mouseHandlerDict[DRAWING_MODE][MOUSE_MOVE] = function(evnt) { drawingMousemove(evnt); }; + + return mouseHandlerDict; +} + +/** Function to switch from slide mode to drawing mode. +*/ +function slideSwitchToDrawingMode() +{ + currentMode = DRAWING_MODE; + + var tempDict; + + if (ROOT_NODE.hasAttribute("style")) + tempDict = propStrToDict(ROOT_NODE.getAttribute("style")); + else + tempDict = new Object(); + + tempDict["cursor"] = "crosshair"; + ROOT_NODE.setAttribute("style", dictToPropStr(tempDict)); +} + +/** Function to switch from drawing mode to slide mode. +*/ +function drawingSwitchToSlideMode() +{ + currentMode = SLIDE_MODE; + + var tempDict; + + if (ROOT_NODE.hasAttribute("style")) + tempDict = propStrToDict(ROOT_NODE.getAttribute("style")); + else + tempDict = new Object(); + + tempDict["cursor"] = "auto"; + ROOT_NODE.setAttribute("style", dictToPropStr(tempDict)); +} + +/** Function to decrease the number of columns in index mode. +*/ +function indexDecreaseNumberOfColumns() +{ + if (INDEX_COLUMNS >= 3) + { + INDEX_COLUMNS -= 1; + INDEX_OFFSET = -1 + indexSetPageSlide(activeSlide); + } +} + +/** Function to increase the number of columns in index mode. +*/ +function indexIncreaseNumberOfColumns() +{ + if (INDEX_COLUMNS < 7) + { + INDEX_COLUMNS += 1; + INDEX_OFFSET = -1 + indexSetPageSlide(activeSlide); + } +} + +/** Function to reset the number of columns in index mode. +*/ +function indexResetNumberOfColumns() +{ + if (INDEX_COLUMNS != INDEX_COLUMNS_DEFAULT) + { + INDEX_COLUMNS = INDEX_COLUMNS_DEFAULT; + INDEX_OFFSET = -1 + indexSetPageSlide(activeSlide); + } +} + +/** Function to reset path width in drawing mode. +*/ +function drawingResetPathWidth() +{ + path_width = path_width_default; + set_path_paint_width(); +} + +/** Function to set path width in drawing mode. + * + * @param width new path width + */ +function drawingSetPathWidth(width) +{ + path_width = width; + set_path_paint_width(); +} + +/** Function to set path colour in drawing mode. + * + * @param colour new path colour + */ +function drawingSetPathColour(colour) +{ + path_colour = colour; +} + +/** Function to query the duration of the presentation from the user in slide mode. +*/ +function slideQueryDuration() +{ + var new_duration = prompt("Length of presentation in minutes?", timer_duration); + + if ((new_duration != null) && (new_duration != '')) + { + timer_duration = new_duration; + } + + updateTimer(); +} + +/** Function to add new slide in slide mode. + * + * @param afterSlide after which slide to insert the new one + */ +function slideAddSlide(afterSlide) +{ + addSlide(afterSlide); + slideSetActiveSlide(afterSlide + 1); + updateTimer(); +} + +/** Function to toggle the visibility of the progress bar in slide mode. +*/ +function slideToggleProgressBarVisibility() +{ + if (progress_bar_visible) + { + progress_bar_visible = false; + hideProgressBar(); + } + else + { + progress_bar_visible = true; + showProgressBar(); + } +} + +/** Function to reset the timer in slide mode. +*/ +function slideResetTimer() +{ + timer_start = timer_elapsed; + updateTimer(); +} + +/** Convenience function to pad a string with zero in front up to a certain length. + */ +function padString(str, len) +{ + var outStr = str; + + while (outStr.length < len) + { + outStr = '0' + outStr; + } + + return outStr; +} + +/** Function to update the export layer. + */ +function slideUpdateExportLayer() +{ + // Suspend redraw since we are going to mess with the slides. + var suspendHandle = ROOT_NODE.suspendRedraw(2000); + + var tmpActiveSlide = activeSlide; + var tmpActiveEffect = activeEffect; + var exportedLayers = new Array(); + + for (var counterSlides = 0; counterSlides < slides.length; counterSlides++) + { + var exportNode; + + setSlideToState(counterSlides, STATE_START); + + var maxEffect = 0; + + if (slides[counterSlides].effects) + { + maxEffect = slides[counterSlides].effects.length; + } + + exportNode = slides[counterSlides].element.cloneNode(true); + exportNode.setAttributeNS(NSS["inkscape"], "groupmode", "layer"); + exportNode.setAttributeNS(NSS["inkscape"], "label", "slide_" + padString((counterSlides + 1).toString(), slides.length.toString().length) + "_effect_" + padString("0", maxEffect.toString().length)); + + exportedLayers.push(exportNode); + + if (slides[counterSlides]["effects"]) + { + for (var counter = 0; counter < slides[counterSlides]["effects"].length; counter++) + { + for (var subCounter = 0; subCounter < slides[counterSlides]["effects"][counter].length; subCounter++) + { + var effect = slides[counterSlides]["effects"][counter][subCounter]; + if (effect["effect"] == "fade") + fade(parseInt(effect["dir"]), effect["element"], STATE_END, effect["options"]); + else if (effect["effect"] == "appear") + appear(parseInt(effect["dir"]), effect["element"], STATE_END, effect["options"]); + else if (effect["effect"] == "pop") + pop(parseInt(effect["dir"]), effect["element"], STATE_END, effect["options"]); + else if (effect["effect"] == "view") + view(parseInt(effect["dir"]), effect["element"], STATE_END, effect["options"]); + } + + var layerName = "slide_" + padString((counterSlides + 1).toString(), slides.length.toString().length) + "_effect_" + padString((counter + 1).toString(), maxEffect.toString().length); + exportNode = slides[counterSlides].element.cloneNode(true); + exportNode.setAttributeNS(NSS["inkscape"], "groupmode", "layer"); + exportNode.setAttributeNS(NSS["inkscape"], "label", layerName); + exportNode.setAttribute("id", layerName); + + exportedLayers.push(exportNode); + } + } + } + + activeSlide = tmpActiveSlide; + activeEffect = tmpActiveEffect; + setSlideToState(activeSlide, activeEffect); + + // Copy image. + var newDoc = document.documentElement.cloneNode(true); + + // Delete viewbox form new imag and set width and height. + newDoc.removeAttribute('viewbox'); + newDoc.setAttribute('width', WIDTH); + newDoc.setAttribute('height', HEIGHT); + + // Delete all layers and script elements. + var nodesToBeRemoved = new Array(); + + for (var childCounter = 0; childCounter < newDoc.childNodes.length; childCounter++) + { + var child = newDoc.childNodes[childCounter]; + + if (child.nodeType == 1) + { + if ((child.nodeName.toUpperCase() == 'G') || (child.nodeName.toUpperCase() == 'SCRIPT')) + { + nodesToBeRemoved.push(child); + } + } + } + + for (var ndCounter = 0; ndCounter < nodesToBeRemoved.length; ndCounter++) + { + var nd = nodesToBeRemoved[ndCounter]; + + nd.parentNode.removeChild(nd); + } + + // Set current layer. + if (exportedLayers[0]) + { + var namedView; + + for (var nodeCounter = 0; nodeCounter < newDoc.childNodes.length; nodeCounter++) + { + if ((newDoc.childNodes[nodeCounter].nodeType == 1) && (newDoc.childNodes[nodeCounter].getAttribute('id') == 'base')) + { + namedView = newDoc.childNodes[nodeCounter]; + } + } + + if (namedView) + { + namedView.setAttributeNS(NSS['inkscape'], 'current-layer', exportedLayers[0].getAttributeNS(NSS['inkscape'], 'label')); + } + } + + // Add exported layers. + while (exportedLayers.length > 0) + { + var nd = exportedLayers.pop(); + + nd.setAttribute("opacity",1); + nd.style.display = "inherit"; + + newDoc.appendChild(nd); + } + + // Serialise the new document. + var serializer = new XMLSerializer(); + var xml = serializer.serializeToString(newDoc); + + window.open('data:image/svg+xml;base64,' + window.btoa(xml), '_blank'); + + // Unsuspend redraw. + ROOT_NODE.unsuspendRedraw(suspendHandle); + ROOT_NODE.forceRedraw(); +} + +/** Function to undo last drawing operation. +*/ +function drawingUndo() +{ + mouse_presentation_path = null; + mouse_original_path = null; + + if (history_presentation_elements.length > 0) + { + var p = history_presentation_elements.pop(); + var parent = p.parentNode.removeChild(p); + + p = history_original_elements.pop(); + parent = p.parentNode.removeChild(p); + } +} + +/** Event handler for mouse down in drawing mode. + * + * @param e the event + */ +function drawingMousedown(e) +{ + var value = 0; + + if (e.button) + value = e.button; + else if (e.which) + value = e.which; + + if (value == 1) + { + history_counter++; + + var p = calcCoord(e); + + mouse_last_x = e.clientX; + mouse_last_y = e.clientY; + mouse_original_path = document.createElementNS(NSS["svg"], "path"); + mouse_original_path.setAttribute("stroke", path_colour); + mouse_original_path.setAttribute("stroke-width", path_paint_width); + mouse_original_path.setAttribute("fill", "none"); + mouse_original_path.setAttribute("id", "path " + Date()); + mouse_original_path.setAttribute("d", "M" + p.x + "," + p.y); + slides[activeSlide]["original_element"].appendChild(mouse_original_path); + history_original_elements.push(mouse_original_path); + + mouse_presentation_path = document.createElementNS(NSS["svg"], "path"); + mouse_presentation_path.setAttribute("stroke", path_colour); + mouse_presentation_path.setAttribute("stroke-width", path_paint_width); + mouse_presentation_path.setAttribute("fill", "none"); + mouse_presentation_path.setAttribute("id", "path " + Date() + " presentation copy"); + mouse_presentation_path.setAttribute("d", "M" + p.x + "," + p.y); + + if (slides[activeSlide]["viewGroup"]) + slides[activeSlide]["viewGroup"].appendChild(mouse_presentation_path); + else + slides[activeSlide]["element"].appendChild(mouse_presentation_path); + + history_presentation_elements.push(mouse_presentation_path); + + return false; + } + + return true; +} + +/** Event handler for mouse up in drawing mode. + * + * @param e the event + */ +function drawingMouseup(e) +{ + if(!e) + e = window.event; + + if (mouse_presentation_path != null) + { + var p = calcCoord(e); + var d = mouse_presentation_path.getAttribute("d"); + d += " L" + p.x + "," + p.y; + mouse_presentation_path.setAttribute("d", d); + mouse_presentation_path = null; + mouse_original_path.setAttribute("d", d); + mouse_original_path = null; + + return false; + } + + return true; +} + +/** Event handler for mouse move in drawing mode. + * + * @param e the event + */ +function drawingMousemove(e) +{ + if(!e) + e = window.event; + + var dist = (mouse_last_x - e.clientX) * (mouse_last_x - e.clientX) + (mouse_last_y - e.clientY) * (mouse_last_y - e.clientY); + + if (mouse_presentation_path == null) + { + return true; + } + + if (dist >= mouse_min_dist_sqr) + { + var p = calcCoord(e); + var d = mouse_presentation_path.getAttribute("d"); + d += " L" + p.x + "," + p.y; + mouse_presentation_path.setAttribute("d", d); + mouse_original_path.setAttribute("d", d); + mouse_last_x = e.clientX; + mouse_last_y = e.clientY; + } + + return false; +} + +/** Event handler for mouse wheel events in slide mode. + * based on http://adomas.org/javascript-mouse-wheel/ + * + * @param e the event + */ +function slideMousewheel(e) +{ + var delta = 0; + + if (!e) + e = window.event; + + if (e.wheelDelta) + { // IE Opera + delta = e.wheelDelta/120; + } + else if (e.detail) + { // MOZ + delta = -e.detail/3; + } + + if (delta > 0) + skipEffects(-1); + else if (delta < 0) + skipEffects(1); + + if (e.preventDefault) + e.preventDefault(); + + e.returnValue = false; +} + +/** Event handler for mouse wheel events in index mode. + * based on http://adomas.org/javascript-mouse-wheel/ + * + * @param e the event + */ +function indexMousewheel(e) +{ + var delta = 0; + + if (!e) + e = window.event; + + if (e.wheelDelta) + { // IE Opera + delta = e.wheelDelta/120; + } + else if (e.detail) + { // MOZ + delta = -e.detail/3; + } + + if (delta > 0) + indexSetPageSlide(activeSlide - INDEX_COLUMNS * INDEX_COLUMNS); + else if (delta < 0) + indexSetPageSlide(activeSlide + INDEX_COLUMNS * INDEX_COLUMNS); + + if (e.preventDefault) + e.preventDefault(); + + e.returnValue = false; +} + +/** Function to set the path paint width. +*/ +function set_path_paint_width() +{ + var svgPoint1 = document.documentElement.createSVGPoint(); + var svgPoint2 = document.documentElement.createSVGPoint(); + + svgPoint1.x = 0.0; + svgPoint1.y = 0.0; + svgPoint2.x = 1.0; + svgPoint2.y = 0.0; + + var matrix = slides[activeSlide]["element"].getTransformToElement(ROOT_NODE); + + if (slides[activeSlide]["viewGroup"]) + matrix = slides[activeSlide]["viewGroup"].getTransformToElement(ROOT_NODE); + + svgPoint1 = svgPoint1.matrixTransform(matrix); + svgPoint2 = svgPoint2.matrixTransform(matrix); + + path_paint_width = path_width / Math.sqrt((svgPoint2.x - svgPoint1.x) * (svgPoint2.x - svgPoint1.x) + (svgPoint2.y - svgPoint1.y) * (svgPoint2.y - svgPoint1.y)); +} + +/** The view effect. + * + * @param dir direction the effect should be played (1 = forwards, -1 = backwards) + * @param element the element the effect should be applied to + * @param time the time that has elapsed since the beginning of the effect + * @param options a dictionary with additional options (e.g. length of the effect); for the view effect the options need to contain the old and the new matrix. + */ +function view(dir, element, time, options) +{ + var length = 250; + var fraction; + + if (!options["matrixInitial"]) + { + var tempString = slides[activeSlide]["viewGroup"].getAttribute("transform"); + + if (tempString) + options["matrixInitial"] = (new matrixSVG()).fromAttribute(tempString); + else + options["matrixInitial"] = (new matrixSVG()).fromSVGElements(1, 0, 0, 1, 0, 0); + } + + if ((time == STATE_END) || (time == STATE_START)) + fraction = 1; + else + { + if (options && options["length"]) + length = options["length"]; + + fraction = time / length; + } + + if (dir == 1) + { + if (fraction <= 0) + { + element.setAttribute("transform", options["matrixInitial"].toAttribute()); + } + else if (fraction >= 1) + { + element.setAttribute("transform", options["matrixNew"].toAttribute()); + + set_path_paint_width(); + + options["matrixInitial"] = null; + return true; + } + else + { + element.setAttribute("transform", options["matrixInitial"].mix(options["matrixNew"], fraction).toAttribute()); + } + } + else if (dir == -1) + { + if (fraction <= 0) + { + element.setAttribute("transform", options["matrixInitial"].toAttribute()); + } + else if (fraction >= 1) + { + element.setAttribute("transform", options["matrixOld"].toAttribute()); + set_path_paint_width(); + + options["matrixInitial"] = null; + return true; + } + else + { + element.setAttribute("transform", options["matrixInitial"].mix(options["matrixOld"], fraction).toAttribute()); + } + } + + return false; +} + +/** The fade effect. + * + * @param dir direction the effect should be played (1 = forwards, -1 = backwards) + * @param element the element the effect should be applied to + * @param time the time that has elapsed since the beginning of the effect + * @param options a dictionary with additional options (e.g. length of the effect) + */ +function fade(dir, element, time, options) +{ + var length = 250; + var fraction; + + if ((time == STATE_END) || (time == STATE_START)) + fraction = 1; + else + { + if (options && options["length"]) + length = options["length"]; + + fraction = time / length; + } + + if (dir == 1) + { + if (fraction <= 0) + { + element.style.display = "none"; + element.setAttribute("opacity", 0); + } + else if (fraction >= 1) + { + element.style.display = "inherit"; + element.setAttribute("opacity", 1); + return true; + } + else + { + element.style.display = "inherit"; + element.setAttribute("opacity", fraction); + } + } + else if (dir == -1) + { + if (fraction <= 0) + { + element.style.display = "inherit"; + element.setAttribute("opacity", 1); + } + else if (fraction >= 1) + { + element.setAttribute("opacity", 0); + element.style.display = "none"; + return true; + } + else + { + element.style.display = "inherit"; + element.setAttribute("opacity", 1 - fraction); + } + } + return false; +} + +/** The appear effect. + * + * @param dir direction the effect should be played (1 = forwards, -1 = backwards) + * @param element the element the effect should be applied to + * @param time the time that has elapsed since the beginning of the effect + * @param options a dictionary with additional options (e.g. length of the effect) + */ +function appear(dir, element, time, options) +{ + if (dir == 1) + { + element.style.display = "inherit"; + element.setAttribute("opacity",1); + } + else if (dir == -1) + { + element.style.display = "none"; + element.setAttribute("opacity",0); + } + return true; +} + +/** The pop effect. + * + * @param dir direction the effect should be played (1 = forwards, -1 = backwards) + * @param element the element the effect should be applied to + * @param time the time that has elapsed since the beginning of the effect + * @param options a dictionary with additional options (e.g. length of the effect) + */ +function pop(dir, element, time, options) +{ + var length = 500; + var fraction; + + if ((time == STATE_END) || (time == STATE_START)) + fraction = 1; + else + { + if (options && options["length"]) + length = options["length"]; + + fraction = time / length; + } + + if (dir == 1) + { + if (fraction <= 0) + { + element.setAttribute("opacity", 0); + element.setAttribute("transform", "scale(0)"); + element.style.display = "none"; + } + else if (fraction >= 1) + { + element.setAttribute("opacity", 1); + element.removeAttribute("transform"); + element.style.display = "inherit"; + return true; + } + else + { + element.style.display = "inherit"; + var opacityFraction = fraction * 3; + if (opacityFraction > 1) + opacityFraction = 1; + element.setAttribute("opacity", opacityFraction); + var offsetX = WIDTH * (1.0 - fraction) / 2.0; + var offsetY = HEIGHT * (1.0 - fraction) / 2.0; + element.setAttribute("transform", "translate(" + offsetX + "," + offsetY + ") scale(" + fraction + ")"); + } + } + else if (dir == -1) + { + if (fraction <= 0) + { + element.setAttribute("opacity", 1); + element.setAttribute("transform", "scale(1)"); + element.style.display = "inherit"; + } + else if (fraction >= 1) + { + element.setAttribute("opacity", 0); + element.removeAttribute("transform"); + element.style.display = "none"; + return true; + } + else + { + element.setAttribute("opacity", 1 - fraction); + element.setAttribute("transform", "scale(" + 1 - fraction + ")"); + element.style.display = "inherit"; + } + } + return false; +} + +/** Function to set a slide either to the start or the end state. + * + * @param slide the slide to use + * @param state the state into which the slide should be set + */ +function setSlideToState(slide, state) +{ + slides[slide]["viewGroup"].setAttribute("transform", slides[slide].initialView); + + if (slides[slide]["effects"]) + { + if (state == STATE_END) + { + for (var counter = 0; counter < slides[slide]["effects"].length; counter++) + { + for (var subCounter = 0; subCounter < slides[slide]["effects"][counter].length; subCounter++) + { + var effect = slides[slide]["effects"][counter][subCounter]; + if (effect["effect"] == "fade") + fade(effect["dir"], effect["element"], STATE_END, effect["options"]); + else if (effect["effect"] == "appear") + appear(effect["dir"], effect["element"], STATE_END, effect["options"]); + else if (effect["effect"] == "pop") + pop(effect["dir"], effect["element"], STATE_END, effect["options"]); + else if (effect["effect"] == "view") + view(effect["dir"], effect["element"], STATE_END, effect["options"]); + } + } + } + else if (state == STATE_START) + { + for (var counter = slides[slide]["effects"].length - 1; counter >= 0; counter--) + { + for (var subCounter = 0; subCounter < slides[slide]["effects"][counter].length; subCounter++) + { + var effect = slides[slide]["effects"][counter][subCounter]; + if (effect["effect"] == "fade") + fade(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]); + else if (effect["effect"] == "appear") + appear(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]); + else if (effect["effect"] == "pop") + pop(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]); + else if (effect["effect"] == "view") + view(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]); + } + } + } + else + { + setSlideToState(slide, STATE_START); + + for (var counter = 0; counter < slides[slide]["effects"].length && counter < state; counter++) + { + for (var subCounter = 0; subCounter < slides[slide]["effects"][counter].length; subCounter++) + { + var effect = slides[slide]["effects"][counter][subCounter]; + if (effect["effect"] == "fade") + fade(effect["dir"], effect["element"], STATE_END, effect["options"]); + else if (effect["effect"] == "appear") + appear(effect["dir"], effect["element"], STATE_END, effect["options"]); + else if (effect["effect"] == "pop") + pop(effect["dir"], effect["element"], STATE_END, effect["options"]); + else if (effect["effect"] == "view") + view(effect["dir"], effect["element"], STATE_END, effect["options"]); + } + } + } + } + + window.location.hash = (activeSlide + 1) + '_' + activeEffect; +} + +/** Convenience function to translate a attribute string into a dictionary. + * + * @param str the attribute string + * @return a dictionary + * @see dictToPropStr + */ +function propStrToDict(str) +{ + var list = str.split(";"); + var obj = new Object(); + + for (var counter = 0; counter < list.length; counter++) + { + var subStr = list[counter]; + var subList = subStr.split(":"); + if (subList.length == 2) + { + obj[subList[0]] = subList[1]; + } + } + + return obj; +} + +/** Convenience function to translate a dictionary into a string that can be used as an attribute. + * + * @param dict the dictionary to convert + * @return a string that can be used as an attribute + * @see propStrToDict + */ +function dictToPropStr(dict) +{ + var str = ""; + + for (var key in dict) + { + str += key + ":" + dict[key] + ";"; + } + + return str; +} + +/** Sub-function to add a suffix to the ids of the node and all its children. + * + * @param node the node to change + * @param suffix the suffix to add + * @param replace dictionary of replaced ids + * @see suffixNodeIds + */ +function suffixNoneIds_sub(node, suffix, replace) +{ + if (node.nodeType == 1) + { + if (node.getAttribute("id")) + { + var id = node.getAttribute("id") + replace["#" + id] = id + suffix; + node.setAttribute("id", id + suffix); + } + + if ((node.nodeName == "use") && (node.getAttributeNS(NSS["xlink"], "href")) && (replace[node.getAttribute(NSS["xlink"], "href")])) + node.setAttribute(NSS["xlink"], "href", node.getAttribute(NSS["xlink"], "href") + suffix); + + if (node.childNodes) + { + for (var counter = 0; counter < node.childNodes.length; counter++) + suffixNoneIds_sub(node.childNodes[counter], suffix, replace); + } + } +} + +/** Function to add a suffix to the ids of the node and all its children. + * + * @param node the node to change + * @param suffix the suffix to add + * @return the changed node + * @see suffixNodeIds_sub + */ +function suffixNodeIds(node, suffix) +{ + var replace = new Object(); + + suffixNoneIds_sub(node, suffix, replace); + + return node; +} + +/** Function to build a progress bar. + * + * @param parent node to attach the progress bar to + */ +function createProgressBar(parent_node) +{ + var g = document.createElementNS(NSS["svg"], "g"); + g.setAttribute("clip-path", "url(#jessyInkSlideClipPath)"); + g.setAttribute("id", "layer_progress_bar"); + g.setAttribute("style", "display: none;"); + + var rect_progress_bar = document.createElementNS(NSS["svg"], "rect"); + rect_progress_bar.setAttribute("style", "marker: none; fill: rgb(128, 128, 128); stroke: none;"); + rect_progress_bar.setAttribute("id", "rect_progress_bar"); + rect_progress_bar.setAttribute("x", 0); + rect_progress_bar.setAttribute("y", 0.99 * HEIGHT); + rect_progress_bar.setAttribute("width", 0); + rect_progress_bar.setAttribute("height", 0.01 * HEIGHT); + g.appendChild(rect_progress_bar); + + var circle_timer_indicator = document.createElementNS(NSS["svg"], "circle"); + circle_timer_indicator.setAttribute("style", "marker: none; fill: rgb(255, 0, 0); stroke: none;"); + circle_timer_indicator.setAttribute("id", "circle_timer_indicator"); + circle_timer_indicator.setAttribute("cx", 0.005 * HEIGHT); + circle_timer_indicator.setAttribute("cy", 0.995 * HEIGHT); + circle_timer_indicator.setAttribute("r", 0.005 * HEIGHT); + g.appendChild(circle_timer_indicator); + + parent_node.appendChild(g); +} + +/** Function to hide the progress bar. + * + */ +function hideProgressBar() +{ + var progress_bar = document.getElementById("layer_progress_bar"); + + if (!progress_bar) + { + return; + } + + progress_bar.setAttribute("style", "display: none;"); +} + +/** Function to show the progress bar. + * + */ +function showProgressBar() +{ + var progress_bar = document.getElementById("layer_progress_bar"); + + if (!progress_bar) + { + return; + } + + progress_bar.setAttribute("style", "display: inherit;"); +} + +/** Set progress bar value. + * + * @param value the current slide number + * + */ +function setProgressBarValue(value) +{ + var rect_progress_bar = document.getElementById("rect_progress_bar"); + + if (!rect_progress_bar) + { + return; + } + + if (value < 1) + { + // First slide, assumed to be the title of the presentation + var x = 0; + var w = 0.01 * HEIGHT; + } + else if (value >= slides.length - 1) + { + // Last slide, assumed to be the end of the presentation + var x = WIDTH - 0.01 * HEIGHT; + var w = 0.01 * HEIGHT; + } + else + { + value -= 1; + value /= (slides.length - 2); + + var x = WIDTH * value; + var w = WIDTH / (slides.length - 2); + } + + rect_progress_bar.setAttribute("x", x); + rect_progress_bar.setAttribute("width", w); +} + +/** Set time indicator. + * + * @param value the percentage of time elapse so far between 0.0 and 1.0 + * + */ +function setTimeIndicatorValue(value) +{ + var circle_timer_indicator = document.getElementById("circle_timer_indicator"); + + if (!circle_timer_indicator) + { + return; + } + + if (value < 0.0) + { + value = 0.0; + } + + if (value > 1.0) + { + value = 1.0; + } + + var cx = (WIDTH - 0.01 * HEIGHT) * value + 0.005 * HEIGHT; + circle_timer_indicator.setAttribute("cx", cx); +} + +/** Update timer. + * + */ +function updateTimer() +{ + timer_elapsed += 1; + setTimeIndicatorValue((timer_elapsed - timer_start) / (60 * timer_duration)); +} + +/** Convert screen coordinates to document coordinates. + * + * @param e event with screen coordinates + * + * @return coordinates in SVG file coordinate system + */ +function calcCoord(e) +{ + var svgPoint = document.documentElement.createSVGPoint(); + svgPoint.x = e.clientX + window.pageXOffset; + svgPoint.y = e.clientY + window.pageYOffset; + + // The following is needed for Google Chrome, but causes problems + // with Firefox, as viewport is not implemented in Firefox 3.5. + try + { + svgPoint.x += document.rootElement.viewport.x; + svgPoint.y += document.rootElement.viewport.y; + } + catch (e) + { + } + + var matrix = slides[activeSlide]["element"].getScreenCTM(); + + if (slides[activeSlide]["viewGroup"]) + matrix = slides[activeSlide]["viewGroup"].getScreenCTM(); + + svgPoint = svgPoint.matrixTransform(matrix.inverse()); + return svgPoint; +} + +/** Add slide. + * + * @param after_slide after which slide the new slide should be inserted into the presentation + */ +function addSlide(after_slide) +{ + number_of_added_slides++; + + var g = document.createElementNS(NSS["svg"], "g"); + g.setAttribute("clip-path", "url(#jessyInkSlideClipPath)"); + g.setAttribute("id", "Whiteboard " + Date() + " presentation copy"); + g.setAttribute("style", "display: none;"); + + var new_slide = new Object(); + new_slide["element"] = g; + + // Set build in transition. + new_slide["transitionIn"] = new Object(); + var dict = defaultTransitionInDict; + new_slide["transitionIn"]["name"] = dict["name"]; + new_slide["transitionIn"]["options"] = new Object(); + + for (key in dict) + if (key != "name") + new_slide["transitionIn"]["options"][key] = dict[key]; + + // Set build out transition. + new_slide["transitionOut"] = new Object(); + dict = defaultTransitionOutDict; + new_slide["transitionOut"]["name"] = dict["name"]; + new_slide["transitionOut"]["options"] = new Object(); + + for (key in dict) + if (key != "name") + new_slide["transitionOut"]["options"][key] = dict[key]; + + // Copy master slide content. + if (masterSlide) + { + var clonedNode = suffixNodeIds(masterSlide.cloneNode(true), "_" + Date() + " presentation_copy"); + clonedNode.removeAttributeNS(NSS["inkscape"], "groupmode"); + clonedNode.removeAttributeNS(NSS["inkscape"], "label"); + clonedNode.style.display = "inherit"; + + g.appendChild(clonedNode); + } + + // Substitute auto texts. + substituteAutoTexts(g, "Whiteboard " + number_of_added_slides, "W" + number_of_added_slides, slides.length); + + g.setAttribute("onmouseover", "if ((currentMode == INDEX_MODE) && ( activeSlide != " + (after_slide + 1) + ")) { indexSetActiveSlide(" + (after_slide + 1) + "); };"); + + // Create a transform group. + var transformGroup = document.createElementNS(NSS["svg"], "g"); + + // Add content to transform group. + while (g.firstChild) + transformGroup.appendChild(g.firstChild); + + // Transfer the transform attribute from the node to the transform group. + if (g.getAttribute("transform")) + { + transformGroup.setAttribute("transform", g.getAttribute("transform")); + g.removeAttribute("transform"); + } + + // Create a view group. + var viewGroup = document.createElementNS(NSS["svg"], "g"); + + viewGroup.appendChild(transformGroup); + new_slide["viewGroup"] = g.appendChild(viewGroup); + + // Insert background. + if (BACKGROUND_COLOR != null) + { + var rectNode = document.createElementNS(NSS["svg"], "rect"); + + rectNode.setAttribute("x", 0); + rectNode.setAttribute("y", 0); + rectNode.setAttribute("width", WIDTH); + rectNode.setAttribute("height", HEIGHT); + rectNode.setAttribute("id", "jessyInkBackground" + Date()); + rectNode.setAttribute("fill", BACKGROUND_COLOR); + + new_slide["viewGroup"].insertBefore(rectNode, new_slide["viewGroup"].firstChild); + } + + // Set initial view even if there are no other views. + var matrixOld = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1); + + new_slide["viewGroup"].setAttribute("transform", matrixOld.toAttribute()); + new_slide.initialView = matrixOld.toAttribute(); + + // Insert slide + var node = slides[after_slide]["element"]; + var next_node = node.nextSibling; + var parent_node = node.parentNode; + + if (next_node) + { + parent_node.insertBefore(g, next_node); + } + else + { + parent_node.appendChild(g); + } + + g = document.createElementNS(NSS["svg"], "g"); + g.setAttributeNS(NSS["inkscape"], "groupmode", "layer"); + g.setAttributeNS(NSS["inkscape"], "label", "Whiteboard " + number_of_added_slides); + g.setAttribute("clip-path", "url(#jessyInkSlideClipPath)"); + g.setAttribute("id", "Whiteboard " + Date()); + g.setAttribute("style", "display: none;"); + + new_slide["original_element"] = g; + + node = slides[after_slide]["original_element"]; + next_node = node.nextSibling; + parent_node = node.parentNode; + + if (next_node) + { + parent_node.insertBefore(g, next_node); + } + else + { + parent_node.appendChild(g); + } + + before_new_slide = slides.slice(0, after_slide + 1); + after_new_slide = slides.slice(after_slide + 1); + slides = before_new_slide.concat(new_slide, after_new_slide); + + //resetting the counter attributes on the slides that follow the new slide... + for (var counter = after_slide+2; counter < slides.length; counter++) + { + slides[counter]["element"].setAttribute("onmouseover", "if ((currentMode == INDEX_MODE) && ( activeSlide != " + counter + ")) { indexSetActiveSlide(" + counter + "); };"); + } +} + +/** Convenience function to obtain a transformation matrix from a point matrix. + * + * @param mPoints Point matrix. + * @return A transformation matrix. + */ +function pointMatrixToTransformation(mPoints) +{ + mPointsOld = (new matrixSVG()).fromElements(0, WIDTH, WIDTH, 0, 0, HEIGHT, 1, 1, 1); + + return mPointsOld.mult(mPoints.inv()); +} + +/** Convenience function to obtain a matrix with three corners of a rectangle. + * + * @param rect an svg rectangle + * @return a matrixSVG containing three corners of the rectangle + */ +function rectToMatrix(rect) +{ + rectWidth = rect.getBBox().width; + rectHeight = rect.getBBox().height; + rectX = rect.getBBox().x; + rectY = rect.getBBox().y; + rectXcorr = 0; + rectYcorr = 0; + + scaleX = WIDTH / rectWidth; + scaleY = HEIGHT / rectHeight; + + if (scaleX > scaleY) + { + scaleX = scaleY; + rectXcorr -= (WIDTH / scaleX - rectWidth) / 2; + rectWidth = WIDTH / scaleX; + } + else + { + scaleY = scaleX; + rectYcorr -= (HEIGHT / scaleY - rectHeight) / 2; + rectHeight = HEIGHT / scaleY; + } + + if (rect.transform.baseVal.numberOfItems < 1) + { + mRectTrans = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1); + } + else + { + mRectTrans = (new matrixSVG()).fromSVGMatrix(rect.transform.baseVal.consolidate().matrix); + } + + newBasePoints = (new matrixSVG()).fromElements(rectX, rectX, rectX, rectY, rectY, rectY, 1, 1, 1); + newVectors = (new matrixSVG()).fromElements(rectXcorr, rectXcorr + rectWidth, rectXcorr + rectWidth, rectYcorr, rectYcorr, rectYcorr + rectHeight, 0, 0, 0); + + return mRectTrans.mult(newBasePoints.add(newVectors)); +} + +/** Function to handle JessyInk elements. + * + * @param node Element node. + */ +function handleElement(node) +{ + if (node.getAttributeNS(NSS['jessyink'], 'element') == 'core.video') + { + var url; + var width; + var height; + var x; + var y; + var transform; + + var tspans = node.getElementsByTagNameNS("http://www.w3.org/2000/svg", "tspan"); + + for (var tspanCounter = 0; tspanCounter < tspans.length; tspanCounter++) + { + if (tspans[tspanCounter].getAttributeNS("https://launchpad.net/jessyink", "video") == "url") + { + url = tspans[tspanCounter].firstChild.nodeValue; + } + } + + var rects = node.getElementsByTagNameNS("http://www.w3.org/2000/svg", "rect"); + + for (var rectCounter = 0; rectCounter < rects.length; rectCounter++) + { + if (rects[rectCounter].getAttributeNS("https://launchpad.net/jessyink", "video") == "rect") + { + x = rects[rectCounter].getAttribute("x"); + y = rects[rectCounter].getAttribute("y"); + width = rects[rectCounter].getAttribute("width"); + height = rects[rectCounter].getAttribute("height"); + transform = rects[rectCounter].getAttribute("transform"); + } + } + + for (var childCounter = 0; childCounter < node.childNodes.length; childCounter++) + { + if (node.childNodes[childCounter].nodeType == 1) + { + if (node.childNodes[childCounter].style) + { + node.childNodes[childCounter].style.display = 'none'; + } + else + { + node.childNodes[childCounter].setAttribute("style", "display: none;"); + } + } + } + + var foreignNode = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject"); + foreignNode.setAttribute("x", x); + foreignNode.setAttribute("y", y); + foreignNode.setAttribute("width", width); + foreignNode.setAttribute("height", height); + foreignNode.setAttribute("transform", transform); + + var videoNode = document.createElementNS("http://www.w3.org/1999/xhtml", "video"); + videoNode.setAttribute("src", url); + + foreignNode.appendChild(videoNode); + node.appendChild(foreignNode); + } +} + +/** Class processing the location hash. + * + * @param str location hash + */ +function LocationHash(str) +{ + this.slideNumber = 0; + this.effectNumber = 0; + + str = str.substr(1, str.length - 1); + + var parts = str.split('_'); + + // Try to extract slide number. + if (parts.length >= 1) + { + try + { + var slideNumber = parseInt(parts[0]); + + if (!isNaN(slideNumber)) + { + this.slideNumber = slideNumber - 1; + } + } + catch (e) + { + } + } + + // Try to extract effect number. + if (parts.length >= 2) + { + try + { + var effectNumber = parseInt(parts[1]); + + if (!isNaN(effectNumber)) + { + this.effectNumber = effectNumber; + } + } + catch (e) + { + } + } +} + +/** Class representing an svg matrix. +*/ +function matrixSVG() +{ + this.e11 = 0; // a + this.e12 = 0; // c + this.e13 = 0; // e + this.e21 = 0; // b + this.e22 = 0; // d + this.e23 = 0; // f + this.e31 = 0; + this.e32 = 0; + this.e33 = 0; +} + +/** Constructor function. + * + * @param a element a (i.e. 1, 1) as described in the svg standard. + * @param b element b (i.e. 2, 1) as described in the svg standard. + * @param c element c (i.e. 1, 2) as described in the svg standard. + * @param d element d (i.e. 2, 2) as described in the svg standard. + * @param e element e (i.e. 1, 3) as described in the svg standard. + * @param f element f (i.e. 2, 3) as described in the svg standard. + */ +matrixSVG.prototype.fromSVGElements = function(a, b, c, d, e, f) +{ + this.e11 = a; + this.e12 = c; + this.e13 = e; + this.e21 = b; + this.e22 = d; + this.e23 = f; + this.e31 = 0; + this.e32 = 0; + this.e33 = 1; + + return this; +} + +/** Constructor function. + * + * @param matrix an svg matrix as described in the svg standard. + */ +matrixSVG.prototype.fromSVGMatrix = function(m) +{ + this.e11 = m.a; + this.e12 = m.c; + this.e13 = m.e; + this.e21 = m.b; + this.e22 = m.d; + this.e23 = m.f; + this.e31 = 0; + this.e32 = 0; + this.e33 = 1; + + return this; +} + +/** Constructor function. + * + * @param e11 element 1, 1 of the matrix. + * @param e12 element 1, 2 of the matrix. + * @param e13 element 1, 3 of the matrix. + * @param e21 element 2, 1 of the matrix. + * @param e22 element 2, 2 of the matrix. + * @param e23 element 2, 3 of the matrix. + * @param e31 element 3, 1 of the matrix. + * @param e32 element 3, 2 of the matrix. + * @param e33 element 3, 3 of the matrix. + */ +matrixSVG.prototype.fromElements = function(e11, e12, e13, e21, e22, e23, e31, e32, e33) +{ + this.e11 = e11; + this.e12 = e12; + this.e13 = e13; + this.e21 = e21; + this.e22 = e22; + this.e23 = e23; + this.e31 = e31; + this.e32 = e32; + this.e33 = e33; + + return this; +} + +/** Constructor function. + * + * @param attrString string value of the "transform" attribute (currently only "matrix" is accepted) + */ +matrixSVG.prototype.fromAttribute = function(attrString) +{ + str = attrString.substr(7, attrString.length - 8); + + str = str.trim(); + + strArray = str.split(","); + + // Opera does not use commas to separate the values of the matrix, only spaces. + if (strArray.length != 6) + strArray = str.split(" "); + + this.e11 = parseFloat(strArray[0]); + this.e21 = parseFloat(strArray[1]); + this.e31 = 0; + this.e12 = parseFloat(strArray[2]); + this.e22 = parseFloat(strArray[3]); + this.e32 = 0; + this.e13 = parseFloat(strArray[4]); + this.e23 = parseFloat(strArray[5]); + this.e33 = 1; + + return this; +} + +/** Output function + * + * @return a string that can be used as the "transform" attribute. + */ +matrixSVG.prototype.toAttribute = function() +{ + return "matrix(" + this.e11 + ", " + this.e21 + ", " + this.e12 + ", " + this.e22 + ", " + this.e13 + ", " + this.e23 + ")"; +} + +/** Matrix nversion. + * + * @return the inverse of the matrix + */ +matrixSVG.prototype.inv = function() +{ + out = new matrixSVG(); + + det = this.e11 * (this.e33 * this.e22 - this.e32 * this.e23) - this.e21 * (this.e33 * this.e12 - this.e32 * this.e13) + this.e31 * (this.e23 * this.e12 - this.e22 * this.e13); + + out.e11 = (this.e33 * this.e22 - this.e32 * this.e23) / det; + out.e12 = -(this.e33 * this.e12 - this.e32 * this.e13) / det; + out.e13 = (this.e23 * this.e12 - this.e22 * this.e13) / det; + out.e21 = -(this.e33 * this.e21 - this.e31 * this.e23) / det; + out.e22 = (this.e33 * this.e11 - this.e31 * this.e13) / det; + out.e23 = -(this.e23 * this.e11 - this.e21 * this.e13) / det; + out.e31 = (this.e32 * this.e21 - this.e31 * this.e22) / det; + out.e32 = -(this.e32 * this.e11 - this.e31 * this.e12) / det; + out.e33 = (this.e22 * this.e11 - this.e21 * this.e12) / det; + + return out; +} + +/** Matrix multiplication. + * + * @param op another svg matrix + * @return this * op + */ +matrixSVG.prototype.mult = function(op) +{ + out = new matrixSVG(); + + out.e11 = this.e11 * op.e11 + this.e12 * op.e21 + this.e13 * op.e31; + out.e12 = this.e11 * op.e12 + this.e12 * op.e22 + this.e13 * op.e32; + out.e13 = this.e11 * op.e13 + this.e12 * op.e23 + this.e13 * op.e33; + out.e21 = this.e21 * op.e11 + this.e22 * op.e21 + this.e23 * op.e31; + out.e22 = this.e21 * op.e12 + this.e22 * op.e22 + this.e23 * op.e32; + out.e23 = this.e21 * op.e13 + this.e22 * op.e23 + this.e23 * op.e33; + out.e31 = this.e31 * op.e11 + this.e32 * op.e21 + this.e33 * op.e31; + out.e32 = this.e31 * op.e12 + this.e32 * op.e22 + this.e33 * op.e32; + out.e33 = this.e31 * op.e13 + this.e32 * op.e23 + this.e33 * op.e33; + + return out; +} + +/** Matrix addition. + * + * @param op another svg matrix + * @return this + op + */ +matrixSVG.prototype.add = function(op) +{ + out = new matrixSVG(); + + out.e11 = this.e11 + op.e11; + out.e12 = this.e12 + op.e12; + out.e13 = this.e13 + op.e13; + out.e21 = this.e21 + op.e21; + out.e22 = this.e22 + op.e22; + out.e23 = this.e23 + op.e23; + out.e31 = this.e31 + op.e31; + out.e32 = this.e32 + op.e32; + out.e33 = this.e33 + op.e33; + + return out; +} + +/** Matrix mixing. + * + * @param op another svg matrix + * @parma contribOp contribution of the other matrix (0 <= contribOp <= 1) + * @return (1 - contribOp) * this + contribOp * op + */ +matrixSVG.prototype.mix = function(op, contribOp) +{ + contribThis = 1.0 - contribOp; + out = new matrixSVG(); + + out.e11 = contribThis * this.e11 + contribOp * op.e11; + out.e12 = contribThis * this.e12 + contribOp * op.e12; + out.e13 = contribThis * this.e13 + contribOp * op.e13; + out.e21 = contribThis * this.e21 + contribOp * op.e21; + out.e22 = contribThis * this.e22 + contribOp * op.e22; + out.e23 = contribThis * this.e23 + contribOp * op.e23; + out.e31 = contribThis * this.e31 + contribOp * op.e31; + out.e32 = contribThis * this.e32 + contribOp * op.e32; + out.e33 = contribThis * this.e33 + contribOp * op.e33; + + return out; +} + +/** Trimming function for strings. +*/ +String.prototype.trim = function() +{ + return this.replace(/^\s+|\s+$/g, ''); +} + diff --git a/share/extensions/jessyInk_autoTexts.inx b/share/extensions/jessyInk_autoTexts.inx new file mode 100644 index 000000000..93e19b301 --- /dev/null +++ b/share/extensions/jessyInk_autoTexts.inx @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> + <_name>Auto-texts</_name> + <id>jessyink.autotexts</id> + <dependency type="executable" location="extensions">inkex.py</dependency> + <dependency type="executable" location="extensions">jessyInk_autoTexts.py</dependency> + <param name="tab" type="notebook"> + <page name="settings" _gui-text="Settings"> + <param name="autoText" type="optiongroup" _gui-text="Auto-Text:"> + <_option value="none">None (remove)</_option> + <_option value="slideTitle">Slide title</_option> + <_option value="slideNumber">Slide number</_option> + <_option value="numberOfSlides">Number of slides</_option> + </param> + </page> + <page name="help" _gui-text="Help"> + <_param name="help_text" type="description">This extension allows you to install, update and remove auto-texts for a JessyInk presentation. Please see code.google.com/p/jessyink for more details.</_param> + </page> + </param> + <effect> + <object-type>all</object-type> + <effects-menu> + <submenu _name="JessyInk"/> + </effects-menu> + </effect> + <script> + <command reldir="extensions" interpreter="python">jessyInk_autoTexts.py</command> + </script> +</inkscape-extension> + diff --git a/share/extensions/jessyInk_autoTexts.py b/share/extensions/jessyInk_autoTexts.py new file mode 100755 index 000000000..a9873e2fc --- /dev/null +++ b/share/extensions/jessyInk_autoTexts.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# Copyright 2008, 2009 Hannes Hochreiner +# 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 3 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, see http://www.gnu.org/licenses/. + +# These lines are only needed if you don't put the script directly into +# the installation directory +import sys +# Unix +sys.path.append('/usr/share/inkscape/extensions') +# OS X +sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions') +# Windows +sys.path.append('C:\Program Files\Inkscape\share\extensions') + +# We will use the inkex module with the predefined Effect base class. +import inkex + +class JessyInk_AutoTexts(inkex.Effect): + def __init__(self): + # Call the base class constructor. + inkex.Effect.__init__(self) + + self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what') + self.OptionParser.add_option('--autoText', action = 'store', type = 'string', dest = 'autoText', default = 'none') + + inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink" + + def effect(self): + # Check version. + scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS) + + if len(scriptNodes) != 1: + sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n") + + if len(self.selected) == 0: + sys.stderr.write("To assign an effect, please select an object.\n") + + for id, node in self.selected.items(): + nodes = node.xpath("./svg:tspan", namespaces=inkex.NSS) + + if len(nodes) != 1: + sys.stderr.write("Node with id '" + str(id) + "' is not a suitable text node and was therefore ignored.\n") + else: + if self.options.autoText == "slideTitle": + nodes[0].set("{" + inkex.NSS["jessyink"] + "}autoText","slideTitle") + elif self.options.autoText == "slideNumber": + nodes[0].set("{" + inkex.NSS["jessyink"] + "}autoText","slideNumber") + elif self.options.autoText == "numberOfSlides": + nodes[0].set("{" + inkex.NSS["jessyink"] + "}autoText","numberOfSlides") + else: + if nodes[0].attrib.has_key("{" + inkex.NSS["jessyink"] + "}autoText"): + del nodes[0].attrib["{" + inkex.NSS["jessyink"] + "}autoText"] + +# Create effect instance +effect = JessyInk_AutoTexts() +effect.affect() + diff --git a/share/extensions/jessyInk_core_mouseHandler_noclick.js b/share/extensions/jessyInk_core_mouseHandler_noclick.js new file mode 100644 index 000000000..88579754a --- /dev/null +++ b/share/extensions/jessyInk_core_mouseHandler_noclick.js @@ -0,0 +1,53 @@ +// Copyright 2008, 2009 Hannes Hochreiner +// 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 3 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, see http://www.gnu.org/licenses/. + +// Add event listener for initialisation. +document.addEventListener("DOMContentLoaded", jessyInk_core_mouseHandler_noclick_init, false); + +/** Initialisation function. + * + * This function looks for the objects of the appropriate sub-type and hands them to another function that will add the required methods. + */ +function jessyInk_core_mouseHandler_noclick_init() +{ + var elems = document.getElementsByTagNameNS("https://launchpad.net/jessyink", "mousehandler"); + + for (var counter = 0; counter < elems.length; counter++) + { + if (elems[counter].getAttributeNS("https://launchpad.net/jessyink", "subtype") == "jessyInk_core_mouseHandler_noclick") + jessyInk_core_mouseHandler_noclick(elems[counter]); + } +} + +/** Function to initialise an object. + * + * @param obj Object to be initialised. + */ +function jessyInk_core_mouseHandler_noclick(obj) +{ + /** Function supplying a custom mouse handler. + * + * @returns A dictionary containing the new mouse handler functions. + */ + obj.getMouseHandler = function () + { + var handlerDictio = new Object(); + + handlerDictio[SLIDE_MODE] = new Object(); + handlerDictio[SLIDE_MODE][MOUSE_DOWN] = null; + + return handlerDictio; + } +} + diff --git a/share/extensions/jessyInk_core_mouseHandler_zoomControl.js b/share/extensions/jessyInk_core_mouseHandler_zoomControl.js new file mode 100644 index 000000000..7944ae82f --- /dev/null +++ b/share/extensions/jessyInk_core_mouseHandler_zoomControl.js @@ -0,0 +1,443 @@ +// Copyright 2008, 2009 Hannes Hochreiner +// 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 3 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, see http://www.gnu.org/licenses/. + +// Add event listener for initialisation. +document.addEventListener("DOMContentLoaded", jessyInk_core_mouseHandler_zoomControl_init, false); + +/** Initialisation function. + * + * This function looks for the objects of the appropriate sub-type and hands them to another function that will add the required methods. + */ +function jessyInk_core_mouseHandler_zoomControl_init() +{ + var elems = document.getElementsByTagNameNS("https://launchpad.net/jessyink", "mousehandler"); + + for (var counter = 0; counter < elems.length; counter++) + { + if (elems[counter].getAttributeNS("https://launchpad.net/jessyink", "subtype") == "jessyInk_core_mouseHandler_zoomControl") + jessyInk_core_mouseHandler_zoomControl(elems[counter]); + } +} + +/** Function to initialise an object. + * + * @param obj Object to be initialised. + */ +function jessyInk_core_mouseHandler_zoomControl(obj) +{ + // Last dragging position. + obj.dragging_last; + // Flag to indicate whether dragging is active currently. + obj.dragging_active = false; + // Flag to indicate whether dragging is working currently. + obj.dragging_working = false; + // Flag to indicate whether the user clicked. + obj.click = false; + + /** Function supplying a custom mouse handler. + * + * @returns A dictionary containing the new mouse handler functions. + */ + obj.getMouseHandler = function () + { + var handlerDictio = new Object(); + + handlerDictio[SLIDE_MODE] = new Object(); + handlerDictio[SLIDE_MODE][MOUSE_DOWN] = obj.mousedown; + handlerDictio[SLIDE_MODE][MOUSE_MOVE] = obj.mousemove; + handlerDictio[SLIDE_MODE][MOUSE_UP] = obj.mouseup; + handlerDictio[SLIDE_MODE][MOUSE_WHEEL] = obj.mousewheel; + + return handlerDictio; + } + + /** Event handler for mouse clicks. + * + * @param e Event object. + */ + obj.mouseclick = function (e) + { + var elem = obj.getAdHocViewBbox(slides[activeSlide]["viewGroup"], obj.getCoords(e)); + + processingEffect = true; + + effectArray = new Array(); + + effectArray[0] = new Object(); + effectArray[0]["effect"] = "view"; + effectArray[0]["dir"] = 1; + effectArray[0]["element"] = slides[activeSlide]["viewGroup"]; + effectArray[0]["options"] = new Object(); + effectArray[0]["options"]["length"] = 200; + + if (elem == null) + effectArray[0]["options"]["matrixNew"] = (new matrixSVG()).fromSVGElements(1, 0, 0, 1, 0, 0); + else + effectArray[0]["options"]["matrixNew"] = obj.pointMatrixToTransformation(obj.rectToMatrix(elem)).mult((new matrixSVG()).fromSVGMatrix(slides[activeSlide].viewGroup.getScreenCTM()).inv().mult((new matrixSVG()).fromSVGMatrix(elem.parentNode.getScreenCTM())).inv()); + + transCounter = 0; + startTime = (new Date()).getTime(); + lastFrameTime = null; + effect(1); + } + + /** Function to search for the element the user clicked on. + * + * This function searches for the element with the highest z-order, which encloses the point the user clicked on + * and which view box fits entierly into the currently visible part of the slide. + * + * @param elem Element to start the search from. + * @param pnt Point where the user clicked. + * @returns The element the user clicked on or null, if no element could be found. + */ + obj.getAdHocViewBbox = function (elem, pnt) + { + var children = elem.childNodes; + + for (var counter = 0; counter < children.length; counter++) + { + if (children[counter].getBBox) + { + var childPointList = obj.projectRect(children[counter].getBBox(), children[counter].getScreenCTM()); + + var viewBbox = document.documentElement.createSVGRect(); + + viewBbox.x = 0.0; + viewBbox.y = 0.0; + viewBbox.width = WIDTH; + viewBbox.height = HEIGHT; + + var screenPointList = obj.projectRect(viewBbox, slides[activeSlide]["element"].getScreenCTM()); + + if (obj.pointsWithinRect([pnt], childPointList) && obj.pointsWithinRect(childPointList, screenPointList)) + return children[counter]; + + child = obj.getAdHocViewBbox(children[counter], pnt); + + if (child != null) + return child; + } + } + + return null; + } + + /** Function to project a rectangle using the projection matrix supplied. + * + * @param rect The rectangle to project. + * @param projectionMatrix The projection matrix. + * @returns A list of the four corners of the projected rectangle starting from the upper left corner and going counter-clockwise. + */ + obj.projectRect = function (rect, projectionMatrix) + { + var pntUL = document.documentElement.createSVGPoint(); + pntUL.x = rect.x; + pntUL.y = rect.y; + pntUL = pntUL.matrixTransform(projectionMatrix); + + var pntLL = document.documentElement.createSVGPoint(); + pntLL.x = rect.x; + pntLL.y = rect.y + rect.height; + pntLL = pntLL.matrixTransform(projectionMatrix); + + var pntUR = document.documentElement.createSVGPoint(); + pntUR.x = rect.x + rect.width; + pntUR.y = rect.y; + pntUR = pntUR.matrixTransform(projectionMatrix); + + var pntLR = document.documentElement.createSVGPoint(); + pntLR.x = rect.x + rect.width; + pntLR.y = rect.y + rect.height; + pntLR = pntLR.matrixTransform(projectionMatrix); + + return [pntUL, pntLL, pntUR, pntLR]; + } + + /** Function to determine whether all the points supplied in a list are within a rectangle. + * + * @param pnts List of points to check. + * @param pointList List of points representing the four corners of the rectangle. + * @return True, if all points are within the rectangle; false, otherwise. + */ + obj.pointsWithinRect = function (pnts, pointList) + { + var pntUL = pointList[0]; + var pntLL = pointList[1]; + var pntUR = pointList[2]; + + var matrixOrig = (new matrixSVG()).fromElements(pntUL.x, pntLL.x, pntUR.x, pntUL.y, pntLL.y, pntUR.y, 1, 1, 1); + var matrixProj = (new matrixSVG()).fromElements(0, 0, 1, 0, 1, 0, 1, 1, 1); + + var matrixProjection = matrixProj.mult(matrixOrig.inv()); + + for (var blockCounter = 0; blockCounter < Math.ceil(pnts.length / 3.0); blockCounter++) + { + var subPnts = new Array(); + + for (var pntCounter = 0; pntCounter < 3.0; pntCounter++) + { + if (blockCounter * 3.0 + pntCounter < pnts.length) + subPnts[pntCounter] = pnts[blockCounter * 3.0 + pntCounter]; + else + { + var tmpPnt = document.documentElement.createSVGPoint(); + + tmpPnt.x = 0.0; + tmpPnt.y = 0.0; + + subPnts[pntCounter] = tmpPnt; + } + } + + var matrixPnt = (new matrixSVG).fromElements(subPnts[0].x, subPnts[1].x, subPnts[2].x, subPnts[0].y, subPnts[1].y, subPnts[2].y, 1, 1, 1); + var matrixTrans = matrixProjection.mult(matrixPnt); + + for (var pntCounter = 0; pntCounter < 3.0; pntCounter++) + { + if (blockCounter * 3.0 + pntCounter < pnts.length) + { + if ((pntCounter == 0) && !((matrixTrans.e11 > 0.01) && (matrixTrans.e11 < 0.99) && (matrixTrans.e21 > 0.01) && (matrixTrans.e21 < 0.99))) + return false; + else if ((pntCounter == 1) && !((matrixTrans.e12 > 0.01) && (matrixTrans.e12 < 0.99) && (matrixTrans.e22 > 0.01) && (matrixTrans.e22 < 0.99))) + return false; + else if ((pntCounter == 2) && !((matrixTrans.e13 > 0.01) && (matrixTrans.e13 < 0.99) && (matrixTrans.e23 > 0.01) && (matrixTrans.e23 < 0.99))) + return false; + } + } + } + + return true; + } + + /** Event handler for mouse movements. + * + * @param e Event object. + */ + obj.mousemove = function (e) + { + obj.click = false; + + if (!obj.dragging_active || obj.dragging_working) + return false; + + obj.dragging_working = true; + + var p = obj.getCoords(e); + + if (slides[activeSlide].viewGroup.transform.baseVal.numberOfItems < 1) + { + var matrix = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1); + } + else + { + var matrix = (new matrixSVG()).fromSVGMatrix(slides[activeSlide].viewGroup.transform.baseVal.consolidate().matrix); + } + + matrix.e13 += p.x - obj.dragging_last.x; + matrix.e23 += p.y - obj.dragging_last.y; + + slides[activeSlide]["viewGroup"].setAttribute("transform", matrix.toAttribute()); + + obj.dragging_last = p; + obj.dragging_working = false; + + return false; + } + + /** Event handler for mouse down. + * + * @param e Event object. + */ + obj.mousedown = function (e) + { + if (obj.dragging_active) + return false; + + var value = 0; + + if (e.button) + value = e.button; + else if (e.which) + value = e.which; + + if (value == 1) + { + obj.dragging_last = obj.getCoords(e); + obj.dragging_active = true; + obj.click = true; + } + + return false; + } + + /** Event handler for mouse up. + * + * @param e Event object. + */ + obj.mouseup = function (e) + { + obj.dragging_active = false; + + if (obj.click) + return obj.mouseclick(e); + else + return false; + } + + /** Function to get the coordinates of a point corrected for the offset of the viewport. + * + * @param e Point. + * @returns Coordinates of the point corrected for the offset of the viewport. + */ + obj.getCoords = function (e) + { + var svgPoint = document.documentElement.createSVGPoint(); + svgPoint.x = e.clientX + window.pageXOffset; + svgPoint.y = e.clientY + window.pageYOffset; + + // The following is needed for Google Chrome, but causes problems + // with Firefox, as viewport is not implemented in Firefox 3.5. + try + { + svgPoint.x += document.rootElement.viewport.x; + svgPoint.y += document.rootElement.viewport.y; + } + catch (ex) + { + } + + return svgPoint; + } + + /** Event handler for scrolling. + * + * @param e Event object. + */ + obj.mousewheel = function(e) + { + var p = obj.projectCoords(obj.getCoords(e)); + + if (slides[activeSlide].viewGroup.transform.baseVal.numberOfItems < 1) + { + var matrix = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1); + } + else + { + var matrix = (new matrixSVG()).fromSVGMatrix(slides[activeSlide].viewGroup.transform.baseVal.consolidate().matrix); + } + + if (e.wheelDelta) + { // IE Opera + delta = e.wheelDelta/120; + } + else if (e.detail) + { // MOZ + delta = -e.detail/3; + } + + var widthOld = p.x * matrix.e11 + p.y * matrix.e12; + var heightOld = p.x * matrix.e21 + p.y * matrix.e22; + + matrix.e11 *= (1.0 - delta / 20.0); + matrix.e12 *= (1.0 - delta / 20.0); + matrix.e21 *= (1.0 - delta / 20.0); + matrix.e22 *= (1.0 - delta / 20.0); + + var widthNew = p.x * matrix.e11 + p.y * matrix.e12; + var heightNew = p.x * matrix.e21 + p.y * matrix.e22; + + matrix.e13 += (widthOld - widthNew); + matrix.e23 += (heightOld - heightNew); + + slides[activeSlide]["viewGroup"].setAttribute("transform", matrix.toAttribute()); + + return false; + } + + /** Function to project a point to screen coordinates. + * + * @param Point. + * @returns The point projected to screen coordinates. + */ + obj.projectCoords = function(pnt) + { + var matrix = slides[activeSlide]["element"].getScreenCTM(); + + if (slides[activeSlide]["viewGroup"]) + matrix = slides[activeSlide]["viewGroup"].getScreenCTM(); + + pnt = pnt.matrixTransform(matrix.inverse()); + return pnt; + } + + /** Function to convert a rectangle into a point matrix. + * + * The function figures out a rectangle that encloses the rectangle given and has the same width/height ratio as the viewport of the presentation. + * + * @param rect Rectangle. + * @return The upper left, upper right and lower right corner of the rectangle in a point matrix. + */ + obj.rectToMatrix = function(rect) + { + rectWidth = rect.getBBox().width; + rectHeight = rect.getBBox().height; + rectX = rect.getBBox().x; + rectY = rect.getBBox().y; + rectXcorr = 0; + rectYcorr = 0; + + scaleX = WIDTH / rectWidth; + scaleY = HEIGHT / rectHeight; + + if (scaleX > scaleY) + { + scaleX = scaleY; + rectXcorr -= (WIDTH / scaleX - rectWidth) / 2; + rectWidth = WIDTH / scaleX; + } + else + { + scaleY = scaleX; + rectYcorr -= (HEIGHT / scaleY - rectHeight) / 2; + rectHeight = HEIGHT / scaleY; + } + + if (rect.transform.baseVal.numberOfItems < 1) + { + mRectTrans = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1); + } + else + { + mRectTrans = (new matrixSVG()).fromSVGMatrix(rect.transform.baseVal.consolidate().matrix); + } + + newBasePoints = (new matrixSVG()).fromElements(rectX, rectX, rectX, rectY, rectY, rectY, 1, 1, 1); + newVectors = (new matrixSVG()).fromElements(rectXcorr, rectXcorr + rectWidth, rectXcorr + rectWidth, rectYcorr, rectYcorr, rectYcorr + rectHeight, 0, 0, 0); + + return mRectTrans.mult(newBasePoints.add(newVectors)); + } + + /** Function to return a transformation matrix from a point matrix. + * + * @param mPoints The point matrix. + * @returns The transformation matrix. + */ + obj.pointMatrixToTransformation = function(mPoints) + { + mPointsOld = (new matrixSVG()).fromElements(0, WIDTH, WIDTH, 0, 0, HEIGHT, 1, 1, 1); + + return mPointsOld.mult(mPoints.inv()); + } +} + diff --git a/share/extensions/jessyInk_effects.inx b/share/extensions/jessyInk_effects.inx new file mode 100644 index 000000000..3a8a03df8 --- /dev/null +++ b/share/extensions/jessyInk_effects.inx @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> + <_name>Effects</_name> + <id>jessyink.effects</id> + <dependency type="executable" location="extensions">inkex.py</dependency> + <dependency type="executable" location="extensions">jessyInk_effects.py</dependency> + <param name="tab" type="notebook"> + <page name="settings" _gui-text="Settings"> + <_param name="effectInLabel" type="description">Build-in effect</_param> + <param name="effectInOrder" type="int" min="1" max="100" _gui-text="Order:">1</param> + <param name="effectInDuration" precision="1" type="float" min="0.1" max="10.0" _gui-text="Duration in seconds:">0.8</param> + <param name="effectIn" type="optiongroup" _gui-text="Type:"> + <_option value="none">None (default)</_option> + <_option value="appear">Appear</_option> + <_option value="fade">Fade</_option> + <_option value="pop">Pop</_option> + </param> + <_param name="effectOutLabel" type="description">Build-out effect</_param> + <param name="effectOutOrder" type="int" min="1" max="100" _gui-text="Order:">1</param> + <param name="effectOutDuration" precision="1" type="float" min="0.1" max="10.0" _gui-text="Duration in seconds:">0.8</param> + <param name="effectOut" type="optiongroup" _gui-text="Type:"> + <_option value="none">None (default)</_option> + <_option value="appear">Appear</_option> + <_option value="fade">Fade</_option> + <_option value="pop">Pop</_option> + </param> + </page> + <page name="help" _gui-text="Help"> + <_param name="help_text" type="description">This extension allows you to install, update and remove object effects for a JessyInk presentation. Please see code.google.com/p/jessyink for more details.</_param> + </page> + </param> + <effect> + <object-type>all</object-type> + <effects-menu> + <submenu _name="JessyInk"/> + </effects-menu> + </effect> + <script> + <command reldir="extensions" interpreter="python">jessyInk_effects.py</command> + </script> +</inkscape-extension> + diff --git a/share/extensions/jessyInk_effects.py b/share/extensions/jessyInk_effects.py new file mode 100755 index 000000000..862e55001 --- /dev/null +++ b/share/extensions/jessyInk_effects.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# Copyright 2008, 2009 Hannes Hochreiner +# 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 3 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, see http://www.gnu.org/licenses/. + +# These lines are only needed if you don't put the script directly into +# the installation directory +import sys +# Unix +sys.path.append('/usr/share/inkscape/extensions') +# OS X +sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions') +# Windows +sys.path.append('C:\Program Files\Inkscape\share\extensions') + +# We will use the inkex module with the predefined Effect base class. +import inkex + +class JessyInk_Effects(inkex.Effect): + def __init__(self): + # Call the base class constructor. + inkex.Effect.__init__(self) + + self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what') + self.OptionParser.add_option('--effectInOrder', action = 'store', type = 'string', dest = 'effectInOrder', default = 1) + self.OptionParser.add_option('--effectInDuration', action = 'store', type = 'float', dest = 'effectInDuration', default = 0.8) + self.OptionParser.add_option('--effectIn', action = 'store', type = 'string', dest = 'effectIn', default = 'none') + self.OptionParser.add_option('--effectOutOrder', action = 'store', type = 'string', dest = 'effectOutOrder', default = 2) + self.OptionParser.add_option('--effectOutDuration', action = 'store', type = 'float', dest = 'effectOutDuration', default = 0.8) + self.OptionParser.add_option('--effectOut', action = 'store', type = 'string', dest = 'effectOut', default = 'none') + + inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink" + + def effect(self): + # Check version. + scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS) + + if len(scriptNodes) != 1: + sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n") + + if len(self.selected) == 0: + sys.stderr.write("No object selected. Please select the object you want to assign an effect to and then press apply.\n") + + for id, node in self.selected.items(): + if (self.options.effectIn == "appear") or (self.options.effectIn == "fade") or (self.options.effectIn == "pop"): + node.set("{" + inkex.NSS["jessyink"] + "}effectIn","name:" + self.options.effectIn + ";order:" + self.options.effectInOrder + ";length:" + str(int(self.options.effectInDuration * 1000))) + # Remove possible view argument. + if node.attrib.has_key("{" + inkex.NSS["jessyink"] + "}view"): + del node.attrib["{" + inkex.NSS["jessyink"] + "}view"] + else: + if node.attrib.has_key("{" + inkex.NSS["jessyink"] + "}effectIn"): + del node.attrib["{" + inkex.NSS["jessyink"] + "}effectIn"] + + if (self.options.effectOut == "appear") or (self.options.effectOut == "fade") or (self.options.effectOut == "pop"): + node.set("{" + inkex.NSS["jessyink"] + "}effectOut","name:" + self.options.effectOut + ";order:" + self.options.effectOutOrder + ";length:" + str(int(self.options.effectOutDuration * 1000))) + # Remove possible view argument. + if node.attrib.has_key("{" + inkex.NSS["jessyink"] + "}view"): + del node.attrib["{" + inkex.NSS["jessyink"] + "}view"] + else: + if node.attrib.has_key("{" + inkex.NSS["jessyink"] + "}effectOut"): + del node.attrib["{" + inkex.NSS["jessyink"] + "}effectOut"] + +# Create effect instance +effect = JessyInk_Effects() +effect.affect() + diff --git a/share/extensions/jessyInk_export.inx b/share/extensions/jessyInk_export.inx new file mode 100644 index 000000000..42ab811b9 --- /dev/null +++ b/share/extensions/jessyInk_export.inx @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> + <_name>JessyInk zipped pdf or png output</_name> + <id>jessyink.export</id> + <dependency type="extension">org.inkscape.output.svg.inkscape</dependency> + <dependency type="executable" location="extensions">jessyInk_export.py</dependency> + <dependency type="executable" location="extensions">inkex.py</dependency> + <param name="tab" type="notebook"> + <page name="settings" _gui-text="Settings"> + <param name="type" type="optiongroup" _gui-text="Type:"> + <_option value="PDF">PDF</_option> + <_option value="PNG">PNG</_option> + </param> + <param name="resolution" type="int" min="50" max="1000" _gui-text="Resolution:">92</param> + </page> + <page name="help" _gui-text="Help"> + <_param name="help_text" type="description">This extension allows you to export a JessyInk presentation once you created an export layer in your browser. Please see code.google.com/p/jessyink for more details.</_param> + </page> + </param> + <output> + <extension>.zip</extension> + <mimetype>application/x-zip</mimetype> + <_filetypename>JessyInk zipped pdf or png output (*.zip)</_filetypename> + <_filetypetooltip>Creates a zip file containing pdfs or pngs of all slides of a JessyInk presentation.</_filetypetooltip> + <dataloss>TRUE</dataloss> + </output> + <script> + <command reldir="extensions" interpreter="python">jessyInk_export.py</command> + <helper_extension>org.inkscape.output.svg.inkscape</helper_extension> + </script> +</inkscape-extension> diff --git a/share/extensions/jessyInk_export.py b/share/extensions/jessyInk_export.py new file mode 100755 index 000000000..6f9bf9ecc --- /dev/null +++ b/share/extensions/jessyInk_export.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python +# Copyright 2008, 2009 Hannes Hochreiner +# 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 3 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, see http://www.gnu.org/licenses/. + +# These lines are only needed if you don't put the script directly into +# the installation directory +import sys +# Unix +sys.path.append('/usr/share/inkscape/extensions') +# OS X +sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions') +# Windows +sys.path.append('C:\Program Files\Inkscape\share\extensions') + +import inkex, os.path +import subprocess +import tempfile +import os +import zipfile +import glob +import re + +def propStrToDict(inStr): + dictio = {} + + for prop in inStr.split(";"): + values = prop.split(":") + + if (len(values) == 2): + dictio[values[0].strip()] = values[1].strip() + + return dictio + +def dictToPropStr(dictio): + str = "" + + for key in dictio.keys(): + str += " " + key + ":" + dictio[key] + ";" + + return str[1:] + +def setStyle(node, propKey, propValue): + props = {} + + if node.attrib.has_key("style"): + props = propStrToDict(node.get("style")) + + props[propKey] = propValue + node.set("style", dictToPropStr(props)) + +class MyEffect(inkex.Effect): + inkscapeCommand = None + zipFile = None + + def __init__(self): + inkex.Effect.__init__(self) + + self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what') + self.OptionParser.add_option('--type', action = 'store', type = 'string', dest = 'type', default = '') + self.OptionParser.add_option('--resolution', action = 'store', type = 'string', dest = 'resolution', default = '') + + # Register jessyink namespace. + inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink" + + # Set inkscape command. + self.inkscapeCommand = self.findInkscapeCommand() + + if (self.inkscapeCommand == None): + sys.stderr.write("Could not find Inkscape command.\n") + sys.exit(1) + + def output(self): + pass + + def effect(self): + # Remove any temporary files that might be left from last time. + self.removeJessyInkFilesInTempDir() + + # Check whether the JessyInk-script is present (indicating that the presentation has not been properly exported). + scriptNodes = self.document.xpath("//svg:script[@jessyink:version]", namespaces=inkex.NSS) + + if len(scriptNodes) != 0: + sys.stderr.write("The JessyInk-script is present in this SVG file. This indicates that the presentation has not been properly exported. Please visit code.goolge.com/p/jessyink to find information on how to export a JessyInk presentation.\n\n") + + zipFileDesc, zpFile = tempfile.mkstemp(suffix=".zip", prefix="jessyInk__") + + output = zipfile.ZipFile(zpFile, "w", compression=zipfile.ZIP_STORED) + + # Find layers. + exportNodes = self.document.xpath("//svg:g[@inkscape:groupmode='layer']", namespaces=inkex.NSS) + + if len(exportNodes) < 1: + sys.stderr.write("No layers found.") + + for node in exportNodes: + setStyle(node, "display", "none") + + for node in exportNodes: + setStyle(node, "display", "inherit") + setStyle(node, "opacity", "1") + self.takeSnapshot(output, node.attrib["{" + inkex.NSS["inkscape"] + "}label"]) + setStyle(node, "display", "none") + + # Write temporary zip file to stdout. + output.close() + out = open(zpFile,'rb') + + # Switch stdout to binary on Windows. + if sys.platform == "win32": + import os, msvcrt + msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) + + # Output the file. + sys.stdout.write(out.read()) + sys.stdout.close() + out.close() + + # Delete temporary files. + self.removeJessyInkFilesInTempDir() + + # Function to export the current state of the file using Inkscape. + def takeSnapshot(self, output, fileName): + # Write the svg file. + svgFileDesc, svgFile = tempfile.mkstemp(suffix=".svg", prefix="jessyInk__") + self.document.write(os.fdopen(svgFileDesc, "wb")) + + ext = str(self.options.type).lower() + + # Prepare output file. + outFileDesc, outFile = tempfile.mkstemp(suffix="." + ext, prefix="jessyInk__") + + proc = subprocess.Popen([self.inkscapeCommand + " --file=" + svgFile + " --without-gui --export-dpi=" + str(self.options.resolution) + " --export-" + ext + "=" + outFile], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout_value, stderr_value = proc.communicate() + + output.write(outFile, fileName + "." + ext) + + # Function to remove any temporary files created during the export. + def removeJessyInkFilesInTempDir(self): + for infile in glob.glob(os.path.join(tempfile.gettempdir(), 'jessyInk__*')): + try: + os.remove(infile) + except: + pass + + # Function to try and find the correct command to invoke Inkscape. + def findInkscapeCommand(self): + commands = [] + commands.append("inkscape") + commands.append("C:\Program Files\Inkscape\inkscape.exe") + commands.append("/Applications/Inkscape.app/Contents/Resources/bin/inkscape") + + for command in commands: + proc = subprocess.Popen([command + " --without-gui --version"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout_value, stderr_value = proc.communicate() + + if proc.returncode == 0: + return command + + return None + +e = MyEffect() +e.affect() diff --git a/share/extensions/jessyInk_install.inx b/share/extensions/jessyInk_install.inx new file mode 100644 index 000000000..0e89b60b4 --- /dev/null +++ b/share/extensions/jessyInk_install.inx @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> + <_name>Install/update</_name> + <id>jessyink.install</id> + <dependency type="executable" location="extensions">inkex.py</dependency> + <dependency type="executable" location="extensions">jessyInk_install.py</dependency> + <dependency type="executable" location="extensions">jessyInk.js</dependency> + <param name="tab" type="notebook"> + <page name="help" _gui-text="Help"> + <_param name="help_text" type="description">This extension allows you to install or update the JessyInk script in order to turn your SVG file into a presentation. Please see code.google.com/p/jessyink for more details.</_param> + </page> + </param> + <effect> + <object-type>all</object-type> + <effects-menu> + <submenu _name="JessyInk"/> + </effects-menu> + </effect> + <script> + <command reldir="extensions" interpreter="python">jessyInk_install.py</command> + </script> +</inkscape-extension> + diff --git a/share/extensions/jessyInk_install.py b/share/extensions/jessyInk_install.py new file mode 100755 index 000000000..f96615289 --- /dev/null +++ b/share/extensions/jessyInk_install.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +# Copyright 2008, 2009 Hannes Hochreiner +# 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 3 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, see http://www.gnu.org/licenses/. + +import os + +# These lines are only needed if you don't put the script directly into +# the installation directory +import sys +# Unix +sys.path.append('/usr/share/inkscape/extensions') +# OS X +sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions') +# Windows +sys.path.append('C:\Program Files\Inkscape\share\extensions') + +# We will use the inkex module with the predefined Effect base class. +import inkex + +def propStrToList(str): + list = [] + propList = str.split(";") + for prop in propList: + if not (len(prop) == 0): + list.append(prop.strip()) + return list + +def listToPropStr(list): + str = "" + for prop in list: + str += " " + prop + ";" + return str[1:] + +class JessyInk_Install(inkex.Effect): + def __init__(self): + # Call the base class constructor. + inkex.Effect.__init__(self) + + self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what') + + inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink" + + def effect(self): + # Find and delete old script node + for node in self.document.xpath("//svg:script[@id='JessyInk']", namespaces=inkex.NSS): + node.getparent().remove(node) + + # Create new script node + scriptElm = inkex.etree.Element(inkex.addNS("script", "svg")) + scriptElm.text = open(os.path.join(os.path.dirname(__file__), "jessyInk.js")).read() + scriptElm.set("id","JessyInk") + scriptElm.set("{" + inkex.NSS["jessyink"] + "}version", "1.5.1") + self.document.getroot().append(scriptElm) + + # Remove "jessyInkInit()" in the "onload" attribute, if present. + if self.document.getroot().get("onload"): + propList = propStrToList(self.document.getroot().get("onload")) + else: + propList = [] + + for prop in propList: + if prop == "jessyInkInit()": + propList.remove("jessyInkInit()") + + if len(propList) > 0: + self.document.getroot().set("onload", listToPropStr(propList)) + else: + if self.document.getroot().get("onload"): + del self.document.getroot().attrib["onload"] + + # Update effect attributes. + for node in self.document.xpath("//*[@jessyInk_effectIn]", namespaces=inkex.NSS): + node.attrib["{" + inkex.NSS["jessyink"] + "}effectIn"] = node.attrib["jessyInk_effectIn"] + del node.attrib["jessyInk_effectIn"] + + for node in self.document.xpath("//*[@jessyink:effectIn]", namespaces=inkex.NSS): + node.attrib["{" + inkex.NSS["jessyink"] + "}effectIn"] = node.attrib["{" + inkex.NSS["jessyink"] + "}effectIn"].replace("=", ":") + + for node in self.document.xpath("//*[@jessyInk_effectOut]", namespaces=inkex.NSS): + node.attrib["{" + inkex.NSS["jessyink"] + "}effectOut"] = node.attrib["jessyInk_effectOut"] + del node.attrib["jessyInk_effectOut"] + + for node in self.document.xpath("//*[@jessyink:effectOut]", namespaces=inkex.NSS): + node.attrib["{" + inkex.NSS["jessyink"] + "}effectOut"] = node.attrib["{" + inkex.NSS["jessyink"] + "}effectOut"].replace("=", ":") + + # Update master slide assignment. + for node in self.document.xpath("//*[@jessyInk_masterSlide]", namespaces=inkex.NSS): + node.attrib["{" + inkex.NSS["jessyink"] + "}masterSlide"] = node.attrib["jessyInk_masterSlide"] + del node.attrib["jessyInk_masterSlide"] + + for node in self.document.xpath("//*[@jessyink:masterSlide]", namespaces=inkex.NSS): + node.attrib["{" + inkex.NSS["jessyink"] + "}masterSlide"] = node.attrib["{" + inkex.NSS["jessyink"] + "}masterSlide"].replace("=", ":") + + # Udpate transitions. + for node in self.document.xpath("//*[@jessyInk_transitionIn]", namespaces=inkex.NSS): + node.attrib["{" + inkex.NSS["jessyink"] + "}transitionIn"] = node.attrib["jessyInk_transitionIn"] + del node.attrib["jessyInk_transitionIn"] + + for node in self.document.xpath("//*[@jessyink:transitionIn]", namespaces=inkex.NSS): + node.attrib["{" + inkex.NSS["jessyink"] + "}transitionIn"] = node.attrib["{" + inkex.NSS["jessyink"] + "}transitionIn"].replace("=", ":") + + for node in self.document.xpath("//*[@jessyInk_transitionOut]", namespaces=inkex.NSS): + node.attrib["{" + inkex.NSS["jessyink"] + "}transitionOut"] = node.attrib["jessyInk_transitionOut"] + del node.attrib["jessyInk_transitionOut"] + + for node in self.document.xpath("//*[@jessyink:transitionOut]", namespaces=inkex.NSS): + node.attrib["{" + inkex.NSS["jessyink"] + "}transitionOut"] = node.attrib["{" + inkex.NSS["jessyink"] + "}transitionOut"].replace("=", ":") + + # Update auto texts. + for node in self.document.xpath("//*[@jessyInk_autoText]", namespaces=inkex.NSS): + node.attrib["{" + inkex.NSS["jessyink"] + "}autoText"] = node.attrib["jessyInk_autoText"] + del node.attrib["jessyInk_autoText"] + + for node in self.document.xpath("//*[@jessyink:autoText]", namespaces=inkex.NSS): + node.attrib["{" + inkex.NSS["jessyink"] + "}autoText"] = node.attrib["{" + inkex.NSS["jessyink"] + "}autoText"].replace("=", ":") + +# Create effect instance +effect = JessyInk_Install() +effect.affect() + diff --git a/share/extensions/jessyInk_keyBindings.inx b/share/extensions/jessyInk_keyBindings.inx new file mode 100644 index 000000000..965079127 --- /dev/null +++ b/share/extensions/jessyInk_keyBindings.inx @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> + <_name>Key bindings</_name> + <id>jessyink.keyBindings</id> + <dependency type="executable" location="extensions">inkex.py</dependency> + <dependency type="executable" location="extensions">jessyInk_keyBindings.py</dependency> + <param name="tab" type="notebook"> + <page name="settings" _gui-text="Slide mode"> + <param name="slide_backWithEffects" type="string" _gui-text="Back (with effects):">LEFT, PAGE_UP</param> + <param name="slide_nextWithEffects" type="string" _gui-text="Next (with effects):">RIGHT, PAGE_DOWN, SPACE</param> + <param name="slide_backWithoutEffects" type="string" _gui-text="Back (without effects):">UP</param> + <param name="slide_nextWithoutEffects" type="string" _gui-text="Next (without effects):">DOWN</param> + <param name="slide_firstSlide" type="string" _gui-text="First slide:">HOME</param> + <param name="slide_lastSlide" type="string" _gui-text="Last slide:">END</param> + <param name="slide_switchToIndexMode" type="string" _gui-text="Switch to index mode:">i</param> + <param name="slide_switchToDrawingMode" type="string" _gui-text="Switch to drawing mode:">d</param> + <param name="slide_setDuration" type="string" _gui-text="Set duration:">D</param> + <param name="slide_addSlide" type="string" _gui-text="Add slide:">n</param> + <param name="slide_toggleProgressBar" type="string" _gui-text="Toggle progress bar:">p</param> + <param name="slide_resetTimer" type="string" _gui-text="Reset timer:">t</param> + </page> + <page name="settings" _gui-text="Drawing mode"> + <param name="drawing_switchToSlideMode" type="string" _gui-text="Switch to slide mode:">d</param> + <param name="drawing_pathWidthDefault" type="string" _gui-text="Set path width to default:">0</param> + <param name="drawing_pathWidth1" type="string" _gui-text="Set path width to 1:">1</param> + <param name="drawing_pathWidth3" type="string" _gui-text="Set path width to 3:">3</param> + <param name="drawing_pathWidth5" type="string" _gui-text="Set path width to 5:">5</param> + <param name="drawing_pathWidth7" type="string" _gui-text="Set path width to 7:">7</param> + <param name="drawing_pathWidth9" type="string" _gui-text="Set path width to 9:">9</param> + <param name="drawing_pathColourBlue" type="string" _gui-text="Set path color to blue:">b</param> + <param name="drawing_pathColourCyan" type="string" _gui-text="Set path color to cyan:">c</param> + <param name="drawing_pathColourGreen" type="string" _gui-text="Set path color to green:">g</param> + <param name="drawing_pathColourBlack" type="string" _gui-text="Set path color to black:">k</param> + <param name="drawing_pathColourMagenta" type="string" _gui-text="Set path color to magenta:">m</param> + <param name="drawing_pathColourOrange" type="string" _gui-text="Set path color to orange:">o</param> + <param name="drawing_pathColourRed" type="string" _gui-text="Set path color to red:">r</param> + <param name="drawing_pathColourWhite" type="string" _gui-text="Set path color to white:">w</param> + <param name="drawing_pathColourYellow" type="string" _gui-text="Set path color to yellow:">y</param> + <param name="drawing_undo" type="string" _gui-text="Undo last path segment:">z</param> + </page> + <page name="settings" _gui-text="Index mode"> + <param name="index_selectSlideToLeft" type="string" _gui-text="Select the slide to the left:">LEFT</param> + <param name="index_selectSlideToRight" type="string" _gui-text="Select the slide to the right:">RIGHT</param> + <param name="index_selectSlideAbove" type="string" _gui-text="Select the slide above:">UP</param> + <param name="index_selectSlideBelow" type="string" _gui-text="Select the slide below:">DOWN</param> + <param name="index_previousPage" type="string" _gui-text="Previous page:">PAGE_UP</param> + <param name="index_nextPage" type="string" _gui-text="Next page:">PAGE_DOWN</param> + <param name="index_firstSlide" type="string" _gui-text="First slide:">HOME</param> + <param name="index_lastSlide" type="string" _gui-text="Last slide:">END</param> + <param name="index_switchToSlideMode" type="string" _gui-text="Switch to slide mode:">ENTER, i</param> + <param name="index_decreaseNumberOfColumns" type="string" _gui-text="Decrease number of columns:">-</param> + <param name="index_increaseNumberOfColumns" type="string" _gui-text="Increase number of columns:">+, =</param> + <param name="index_setNumberOfColumnsToDefault" type="string" _gui-text="Set number of columns to default:">0</param> + </page> + <page name="help" _gui-text="Help"> + <_param name="help_text" type="description">This extension allows you customise the key bindings JessyInk uses. Please see code.google.com/p/jessyink for more details.</_param> + </page> + </param> + <effect> + <object-type>g</object-type> + <effects-menu> + <submenu _name="JessyInk"/> + </effects-menu> + </effect> + <script> + <command reldir="extensions" interpreter="python">jessyInk_keyBindings.py</command> + </script> +</inkscape-extension> + diff --git a/share/extensions/jessyInk_keyBindings.py b/share/extensions/jessyInk_keyBindings.py new file mode 100755 index 000000000..45c0fe2ef --- /dev/null +++ b/share/extensions/jessyInk_keyBindings.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python +# Copyright 2008, 2009 Hannes Hochreiner +# 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 3 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, see http://www.gnu.org/licenses/. + +# These lines are only needed if you don't put the script directly into +# the installation directory +import sys +# Unix +sys.path.append('/usr/share/inkscape/extensions') +# OS X +sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions') +# Windows +sys.path.append('C:\Program Files\Inkscape\share\extensions') + +# We will use the inkex module with the predefined Effect base class. +import inkex + +class JessyInk_CustomKeyBindings(inkex.Effect): + modes = ('slide', 'index', 'drawing') + keyCodes = ('LEFT', 'RIGHT', 'DOWN', 'UP', 'HOME', 'END', 'ENTER', 'SPACE', 'PAGE_UP', 'PAGE_DOWN') + slideActions = {} + slideCharCodes = {} + slideKeyCodes = {} + drawingActions = {} + drawingCharCodes = {} + drawingKeyCodes = {} + indexActions = {} + indexCharCodes = {} + indexKeyCodes = {} + + + def __init__(self): + # Call the base class constructor. + inkex.Effect.__init__(self) + + self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what') + self.OptionParser.add_option('--slide_backWithEffects', action = 'callback', type = 'string', callback = self.slideOptions, default = '') + self.OptionParser.add_option('--slide_nextWithEffects', action = 'callback', type = 'string', callback = self.slideOptions, default = '') + self.OptionParser.add_option('--slide_backWithoutEffects', action = 'callback', type = 'string', callback = self.slideOptions, default = '') + self.OptionParser.add_option('--slide_nextWithoutEffects', action = 'callback', type = 'string', callback = self.slideOptions, default = '') + self.OptionParser.add_option('--slide_firstSlide', action = 'callback', type = 'string', callback = self.slideOptions, default = '') + self.OptionParser.add_option('--slide_lastSlide', action = 'callback', type = 'string', callback = self.slideOptions, default = '') + self.OptionParser.add_option('--slide_switchToIndexMode', action = 'callback', type = 'string', callback = self.slideOptions, default = '') + self.OptionParser.add_option('--slide_switchToDrawingMode', action = 'callback', type = 'string', callback = self.slideOptions, default = '') + self.OptionParser.add_option('--slide_setDuration', action = 'callback', type = 'string', callback = self.slideOptions, default = '') + self.OptionParser.add_option('--slide_addSlide', action = 'callback', type = 'string', callback = self.slideOptions, default = '') + self.OptionParser.add_option('--slide_toggleProgressBar', action = 'callback', type = 'string', callback = self.slideOptions, default = '') + self.OptionParser.add_option('--slide_resetTimer', action = 'callback', type = 'string', callback = self.slideOptions, default = '') + self.OptionParser.add_option('--drawing_switchToSlideMode', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathWidthDefault', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathWidth1', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathWidth2', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathWidth3', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathWidth4', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathWidth5', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathWidth6', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathWidth7', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathWidth8', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathWidth9', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathColourBlue', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathColourCyan', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathColourGreen', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathColourBlack', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathColourMagenta', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathColourOrange', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathColourRed', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathColourWhite', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_pathColourYellow', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--drawing_undo', action = 'callback', type = 'string', callback = self.drawingOptions, default = '') + self.OptionParser.add_option('--index_selectSlideToLeft', action = 'callback', type = 'string', callback = self.indexOptions, default = '') + self.OptionParser.add_option('--index_selectSlideToRight', action = 'callback', type = 'string', callback = self.indexOptions, default = '') + self.OptionParser.add_option('--index_selectSlideAbove', action = 'callback', type = 'string', callback = self.indexOptions, default = '') + self.OptionParser.add_option('--index_selectSlideBelow', action = 'callback', type = 'string', callback = self.indexOptions, default = '') + self.OptionParser.add_option('--index_previousPage', action = 'callback', type = 'string', callback = self.indexOptions, default = '') + self.OptionParser.add_option('--index_nextPage', action = 'callback', type = 'string', callback = self.indexOptions, default = '') + self.OptionParser.add_option('--index_firstSlide', action = 'callback', type = 'string', callback = self.indexOptions, default = '') + self.OptionParser.add_option('--index_lastSlide', action = 'callback', type = 'string', callback = self.indexOptions, default = '') + self.OptionParser.add_option('--index_switchToSlideMode', action = 'callback', type = 'string', callback = self.indexOptions, default = '') + self.OptionParser.add_option('--index_decreaseNumberOfColumns', action = 'callback', type = 'string', callback = self.indexOptions, default = '') + self.OptionParser.add_option('--index_increaseNumberOfColumns', action = 'callback', type = 'string', callback = self.indexOptions, default = '') + self.OptionParser.add_option('--index_setNumberOfColumnsToDefault', action = 'callback', type = 'string', callback = self.indexOptions, default = '') + + inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink" + + self.slideActions["backWithEffects"] = "dispatchEffects(-1);" + self.slideActions["nextWithEffects"] = "dispatchEffects(1);" + self.slideActions["backWithoutEffects"] = "skipEffects(-1);" + self.slideActions["nextWithoutEffects"] = "skipEffects(1);" + self.slideActions["firstSlide"] = "slideSetActiveSlide(0);" + self.slideActions["lastSlide"] = "slideSetActiveSlide(slides.length - 1);" + self.slideActions["switchToIndexMode"] = "toggleSlideIndex();" + self.slideActions["switchToDrawingMode"] = "slideSwitchToDrawingMode();" + self.slideActions["setDuration"] = "slideQueryDuration();" + self.slideActions["addSlide"] = "slideAddSlide(activeSlide);" + self.slideActions["toggleProgressBar"] = "slideToggleProgressBarVisibility();" + self.slideActions["resetTimer"] = "slideResetTimer();" + + self.drawingActions["switchToSlideMode"] = "drawingSwitchToSlideMode();" + self.drawingActions["pathWidthDefault"] = "drawingResetPathWidth();" + self.drawingActions["pathWidth1"] = "drawingSetPathWidth(1.0);" + self.drawingActions["pathWidth3"] = "drawingSetPathWidth(3.0);" + self.drawingActions["pathWidth5"] = "drawingSetPathWidth(5.0);" + self.drawingActions["pathWidth7"] = "drawingSetPathWidth(7.0);" + self.drawingActions["pathWidth9"] = "drawingSetPathWidth(9.0);" + self.drawingActions["pathColourBlue"] = "drawingSetPathColour(\"blue\");" + self.drawingActions["pathColourCyan"] = "drawingSetPathColour(\"cyan\");" + self.drawingActions["pathColourGreen"] = "drawingSetPathColour(\"green\");" + self.drawingActions["pathColourBlack"] = "drawingSetPathColour(\"black\");" + self.drawingActions["pathColourMagenta"] = "drawingSetPathColour(\"magenta\");" + self.drawingActions["pathColourOrange"] = "drawingSetPathColour(\"orange\");" + self.drawingActions["pathColourRed"] = "drawingSetPathColour(\"red\");" + self.drawingActions["pathColourWhite"] = "drawingSetPathColour(\"white\");" + self.drawingActions["pathColourYellow"] = "drawingSetPathColour(\"yellow\");" + self.drawingActions["undo"] = "drawingUndo();" + + self.indexActions["selectSlideToLeft"] = "indexSetPageSlide(activeSlide - 1);" + self.indexActions["selectSlideToRight"] = "indexSetPageSlide(activeSlide + 1);" + self.indexActions["selectSlideAbove"] = "indexSetPageSlide(activeSlide - INDEX_COLUMNS);" + self.indexActions["selectSlideBelow"] = "indexSetPageSlide(activeSlide + INDEX_COLUMNS);" + self.indexActions["previousPage"] = "indexSetPageSlide(activeSlide - INDEX_COLUMNS * INDEX_COLUMNS);" + self.indexActions["nextPage"] = "indexSetPageSlide(activeSlide + INDEX_COLUMNS * INDEX_COLUMNS);" + self.indexActions["firstSlide"] = "indexSetPageSlide(0);" + self.indexActions["lastSlide"] = "indexSetPageSlide(slides.length - 1);" + self.indexActions["switchToSlideMode"] = "toggleSlideIndex();" + self.indexActions["decreaseNumberOfColumns"] = "indexDecreaseNumberOfColumns();" + self.indexActions["increaseNumberOfColumns"] = "indexIncreaseNumberOfColumns();" + self.indexActions["setNumberOfColumnsToDefault"] = "indexResetNumberOfColumns();" + + def slideOptions(self, option, opt_str, value, parser): + action = self.getAction(opt_str) + + valueArray = value.split(",") + + for val in valueArray: + val = val.strip() + + if val in self.keyCodes: + self.slideKeyCodes[val + "_KEY"] = self.slideActions[action] + elif len(val) == 1: + self.slideCharCodes[val] = self.slideActions[action] + + def drawingOptions(self, option, opt_str, value, parser): + action = self.getAction(opt_str) + + valueArray = value.split(",") + + for val in valueArray: + val = val.strip() + + if val in self.keyCodes: + self.drawingKeyCodes[val + "_KEY"] = self.drawingActions[action] + elif len(val) == 1: + self.drawingCharCodes[val] = self.drawingActions[action] + + def indexOptions(self, option, opt_str, value, parser): + action = self.getAction(opt_str) + + valueArray = value.split(",") + + for val in valueArray: + val = val.strip() + + if val in self.keyCodes: + self.indexKeyCodes[val + "_KEY"] = self.indexActions[action] + elif len(val) == 1: + self.indexCharCodes[val] = self.indexActions[action] + + def effect(self): + # Check version. + scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS) + + if len(scriptNodes) != 1: + sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n") + + # Remove old master slide property + for node in self.document.xpath("//svg:g[@jessyink:customKeyBindings='customKeyBindings']", namespaces=inkex.NSS): + node.getparent().remove(node) + + # Set custom key bindings. + nodeText = "function getCustomKeyBindingsSub()" + "\n" + nodeText += "{" + "\n" + nodeText += " var keyDict = new Object();" + "\n" + nodeText += " keyDict[SLIDE_MODE] = new Object();" + "\n" + nodeText += " keyDict[INDEX_MODE] = new Object();" + "\n" + nodeText += " keyDict[DRAWING_MODE] = new Object();" + "\n" + + for key, value in self.slideKeyCodes.items(): + nodeText += " keyDict[SLIDE_MODE][" + key + "] = function() { " + value + " };" + "\n" + + for key, value in self.drawingKeyCodes.items(): + nodeText += " keyDict[DRAWING_MODE][" + key + "] = function() { " + value + " };" + "\n" + + for key, value in self.indexKeyCodes.items(): + nodeText += " keyDict[INDEX_MODE][" + key + "] = function() { " + value + " };" + "\n" + + nodeText += " return keyDict;" + "\n" + nodeText += "}" + "\n\n" + + # Set custom char bindings. + nodeText += "function getCustomCharBindingsSub()" + "\n" + nodeText += "{" + "\n" + nodeText += " var charDict = new Object();" + "\n" + nodeText += " charDict[SLIDE_MODE] = new Object();" + "\n" + nodeText += " charDict[INDEX_MODE] = new Object();" + "\n" + nodeText += " charDict[DRAWING_MODE] = new Object();" + "\n" + + for key, value in self.slideCharCodes.items(): + nodeText += " charDict[SLIDE_MODE][\"" + key + "\"] = function() { " + value + " };" + "\n" + + for key, value in self.drawingCharCodes.items(): + nodeText += " charDict[DRAWING_MODE][\"" + key + "\"] = function() { " + value + " };" + "\n" + + for key, value in self.indexCharCodes.items(): + nodeText += " charDict[INDEX_MODE][\"" + key + "\"] = function() { " + value + " };" + "\n" + + nodeText += " return charDict;" + "\n" + nodeText += "}" + "\n" + + # Create new script node + scriptElm = inkex.etree.Element(inkex.addNS("script", "svg")) + scriptElm.text = nodeText + groupElm = inkex.etree.Element(inkex.addNS("g", "svg")) + groupElm.set("{" + inkex.NSS["jessyink"] + "}customKeyBindings", "customKeyBindings") + groupElm.set("onload", "this.getCustomCharBindings = function() { return getCustomCharBindingsSub(); }; this.getCustomKeyBindings = function() { return getCustomKeyBindingsSub(); };") + groupElm.append(scriptElm) + self.document.getroot().append(groupElm) + + def getAction(self, varName): + parts = varName.split('_') + + if (len(parts) != 2): + raise StandardException("Error parsing variable name.") + + return parts[1] + +# Create effect instance +effect = JessyInk_CustomKeyBindings() +effect.affect() + diff --git a/share/extensions/jessyInk_masterSlide.inx b/share/extensions/jessyInk_masterSlide.inx new file mode 100644 index 000000000..e9518ba11 --- /dev/null +++ b/share/extensions/jessyInk_masterSlide.inx @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> + <_name>Master slide</_name> + <id>jessyink.masterSlide</id> + <dependency type="executable" location="extensions">inkex.py</dependency> + <dependency type="executable" location="extensions">jessyInk_masterSlide.py</dependency> + <param name="tab" type="notebook"> + <page name="settings" _gui-text="Settings"> + <param name="layerName" type="string" _gui-text="Name of layer:"></param> + <_param name="info_text" type="description">If no layer name is supplied, the master slide is unset.</_param> + </page> + <page name="help" _gui-text="Help"> + <_param name="help_text" type="description">This extension allows you to change the master slide JessyInk uses. Please see code.google.com/p/jessyink for more details.</_param> + </page> + </param> + <effect> + <object-type>g</object-type> + <effects-menu> + <submenu _name="JessyInk"/> + </effects-menu> + </effect> + <script> + <command reldir="extensions" interpreter="python">jessyInk_masterSlide.py</command> + </script> +</inkscape-extension> + diff --git a/share/extensions/jessyInk_masterSlide.py b/share/extensions/jessyInk_masterSlide.py new file mode 100755 index 000000000..9089215d9 --- /dev/null +++ b/share/extensions/jessyInk_masterSlide.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# Copyright 2008, 2009 Hannes Hochreiner +# 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 3 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, see http://www.gnu.org/licenses/. + +# These lines are only needed if you don't put the script directly into +# the installation directory +import sys +# Unix +sys.path.append('/usr/share/inkscape/extensions') +# OS X +sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions') +# Windows +sys.path.append('C:\Program Files\Inkscape\share\extensions') + +# We will use the inkex module with the predefined Effect base class. +import inkex + +class JessyInk_MasterSlide(inkex.Effect): + def __init__(self): + # Call the base class constructor. + inkex.Effect.__init__(self) + + self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what') + self.OptionParser.add_option('--layerName', action = 'store', type = 'string', dest = 'layerName', default = '') + + inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink" + + def effect(self): + # Check version. + scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS) + + if len(scriptNodes) != 1: + sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n") + + # Remove old master slide property + for node in self.document.xpath("//*[@jessyink:masterSlide='masterSlide']", namespaces=inkex.NSS): + del node.attrib["{" + inkex.NSS["jessyink"] + "}masterSlide"] + + # Set new master slide. + if self.options.layerName != "": + nodes = self.document.xpath("//*[@inkscape:groupmode='layer' and @inkscape:label='" + self.options.layerName + "']", namespaces=inkex.NSS) + if len(nodes) == 0: + sys.stderr.write("Layer not found. Removed current master slide selection.\n") + elif len(nodes) > 1: + sys.stderr.write("More than one layer with this name found. Removed current master slide selection.\n") + else: + nodes[0].set("{" + inkex.NSS["jessyink"] + "}masterSlide","masterSlide") + +# Create effect instance +effect = JessyInk_MasterSlide() +effect.affect() + diff --git a/share/extensions/jessyInk_mouseHandler.inx b/share/extensions/jessyInk_mouseHandler.inx new file mode 100644 index 000000000..528360781 --- /dev/null +++ b/share/extensions/jessyInk_mouseHandler.inx @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> + <_name>Mouse handler</_name> + <id>jessyink.mouseHandler</id> + <dependency type="executable" location="extensions">inkex.py</dependency> + <dependency type="executable" location="extensions">jessyInk_mouseHandler.py</dependency> + <param name="tab" type="notebook"> + <page name="settings" _gui-text="Mouse handler"> + <param name="mouseSetting" type="optiongroup" _gui-text="Mouse settings:"> + <_option value="default">Default</_option> + <_option value="noclick">No-click</_option> + <_option value="draggingZoom">Dragging/zoom</_option> + </param> + </page> + <page name="help" _gui-text="Help"> + <_param name="help_text" type="description">This extension allows you customise the mouse handler JessyInk uses. Please see code.google.com/p/jessyink for more details.</_param> + </page> + </param> + <effect> + <object-type>g</object-type> + <effects-menu> + <submenu _name="JessyInk"/> + </effects-menu> + </effect> + <script> + <command reldir="extensions" interpreter="python">jessyInk_mouseHandler.py</command> + </script> +</inkscape-extension> + diff --git a/share/extensions/jessyInk_mouseHandler.py b/share/extensions/jessyInk_mouseHandler.py new file mode 100755 index 000000000..a0c06c196 --- /dev/null +++ b/share/extensions/jessyInk_mouseHandler.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# Copyright 2008, 2009 Hannes Hochreiner +# 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 3 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, see http://www.gnu.org/licenses/. + +import os + +# These lines are only needed if you don't put the script directly into +# the installation directory +import sys +# Unix +sys.path.append('/usr/share/inkscape/extensions') +# OS X +sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions') +# Windows +sys.path.append('C:\Program Files\Inkscape\share\extensions') + +# We will use the inkex module with the predefined Effect base class. +import inkex + +class JessyInk_CustomMouseHandler(inkex.Effect): + def __init__(self): + # Call the base class constructor. + inkex.Effect.__init__(self) + + self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what') + self.OptionParser.add_option('--mouseSettings', action = 'store', type = 'string', dest = 'mouseSettings', default = 'default') + + inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink" + + def effect(self): + # Check version. + scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS) + + if len(scriptNodes) != 1: + sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n") + + # Remove old mouse handler + for node in self.document.xpath("//jessyink:mousehandler", namespaces=inkex.NSS): + node.getparent().remove(node) + + if self.options.mouseSettings == "noclick": + # Create new script node. + scriptElm = inkex.etree.Element(inkex.addNS("script", "svg")) + scriptElm.text = open(os.path.join(os.path.dirname(__file__), "jessyInk_core_mouseHandler_noclick.js")).read() + groupElm = inkex.etree.Element(inkex.addNS("mousehandler", "jessyink")) + groupElm.set("{" + inkex.NSS["jessyink"] + "}subtype", "jessyInk_core_mouseHandler_noclick") + groupElm.append(scriptElm) + self.document.getroot().append(groupElm) + elif self.options.mouseSettings == "draggingZoom": + # Create new script node. + scriptElm = inkex.etree.Element(inkex.addNS("script", "svg")) + scriptElm.text = open(os.path.join(os.path.dirname(__file__), "jessyInk_core_mouseHandler_zoomControl.js")).read() + groupElm = inkex.etree.Element(inkex.addNS("mousehandler", "jessyink")) + groupElm.set("{" + inkex.NSS["jessyink"] + "}subtype", "jessyInk_core_mouseHandler_zoomControl") + groupElm.append(scriptElm) + self.document.getroot().append(groupElm) + +# Create effect instance +effect = JessyInk_CustomMouseHandler() +effect.affect() + diff --git a/share/extensions/jessyInk_summary.inx b/share/extensions/jessyInk_summary.inx new file mode 100644 index 000000000..37bc206fc --- /dev/null +++ b/share/extensions/jessyInk_summary.inx @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> + <_name>Summary</_name> + <id>jessyink.summary</id> + <dependency type="executable" location="extensions">inkex.py</dependency> + <dependency type="executable" location="extensions">jessyInk_summary.py</dependency> + <param name="tab" type="notebook"> + <page name="help" _gui-text="Help"> + <_param name="help_text" type="description">This extension allows you to obtain information about the JessyInk script, effects and transitions contained in this SVG file. Please see code.google.com/p/jessyink for more details.</_param> + </page> + </param> + <effect> + <object-type>all</object-type> + <effects-menu> + <submenu _name="JessyInk"/> + </effects-menu> + </effect> + <script> + <command reldir="extensions" interpreter="python">jessyInk_summary.py</command> + </script> +</inkscape-extension> + diff --git a/share/extensions/jessyInk_summary.py b/share/extensions/jessyInk_summary.py new file mode 100755 index 000000000..56cec0308 --- /dev/null +++ b/share/extensions/jessyInk_summary.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python +# Copyright 2008, 2009 Hannes Hochreiner +# 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 3 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, see http://www.gnu.org/licenses/. + +# These lines are only needed if you don't put the script directly into +# the installation directory +import sys +# Unix +sys.path.append('/usr/share/inkscape/extensions') +# OS X +sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions') +# Windows +sys.path.append('C:\Program Files\Inkscape\share\extensions') + +# We will use the inkex module with the predefined Effect base class. +import inkex + +def propStrToList(str): + list = [] + propList = str.split(";") + for prop in propList: + if not (len(prop) == 0): + list.append(prop.strip()) + return list + +def propListToDict(list): + dictio = {} + + for prop in list: + keyValue = prop.split(":") + + if len(keyValue) == 2: + dictio[keyValue[0].strip()] = keyValue[1].strip() + + return dictio + +class JessyInk_Summary(inkex.Effect): + def __init__(self): + # Call the base class constructor. + inkex.Effect.__init__(self) + + self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what') + + inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink" + + def effect(self): + # Check version. + scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS) + + if len(scriptNodes) != 1: + sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n") + + # Find the script node, if present + for node in self.document.xpath("//svg:script[@id='JessyInk']", namespaces=inkex.NSS): + sys.stderr.write("JessyInk script ") + + if node.get("{" + inkex.NSS["jessyink"] + "}version"): + sys.stderr.write("version " + node.get("{" + inkex.NSS["jessyink"] + "}version") + " ") + + sys.stderr.write("installed.\n") + + slides = [] + masterSlide = None + + for node in self.document.xpath("//svg:g[@inkscape:groupmode='layer']", namespaces=inkex.NSS): + if node.get("{" + inkex.NSS["jessyink"] + "}masterSlide"): + masterSlide = node + else: + slides.append(node) + + if masterSlide is not None: + sys.stderr.write("\nMaster slide:\n") + self.describeNode(masterSlide, "\t", "<the number of the slide>", str(len(slides)), "<the title of the slide>") + + slideCounter = 1 + + for slide in slides: + sys.stderr.write("\nSlide " + str(slideCounter) + ":\n") + self.describeNode(slide, "\t", str(slideCounter), str(len(slides)), slide.get("{" + inkex.NSS["inkscape"] + "}label")) + slideCounter += 1 + + def describeNode(self, node, prefix, slideNumber, numberOfSlides, slideTitle): + sys.stderr.write(prefix + "Layer name: " + node.get("{" + inkex.NSS["inkscape"] + "}label") + "\n") + + # Display information about transitions. + transitionInAttribute = node.get("{" + inkex.NSS["jessyink"] + "}transitionIn") + if transitionInAttribute: + transInDict = propListToDict(propStrToList(transitionInAttribute)) + sys.stderr.write(prefix + "Transition in: " + transInDict["name"]) + + if (transInDict["name"] != "appear") and transInDict.has_key("length"): + sys.stderr.write(" (" + str(int(transInDict["length"]) / 1000.0) + " s)") + + sys.stderr.write("\n") + + transitionOutAttribute = node.get("{" + inkex.NSS["jessyink"] + "}transitionOut") + if transitionOutAttribute: + transOutDict = propListToDict(propStrToList(transitionOutAttribute)) + sys.stderr.write(prefix + "Transition out: " + transOutDict["name"]) + + if (transOutDict["name"] != "appear") and transOutDict.has_key("length"): + sys.stderr.write(" (" + str(int(transOutDict["length"]) / 1000.0) + " s)") + + sys.stderr.write("\n") + + # Display information about auto-texts. + autoTexts = {"slideNumber" : slideNumber, "numberOfSlides" : numberOfSlides, "slideTitle" : slideTitle} + autoTextNodes = node.xpath(".//*[@jessyink:autoText]", namespaces=inkex.NSS) + + if (len(autoTextNodes) > 0): + sys.stderr.write("\n" + prefix + "Auto-texts:\n") + + for atNode in autoTextNodes: + sys.stderr.write(prefix + "\t\"" + atNode.text + "\" (object id \"" + atNode.getparent().get("id") + "\") will be replaced by") + sys.stderr.write(" \"" + autoTexts[atNode.get("{" + inkex.NSS["jessyink"] + "}autoText")] + "\".\n") + + # Collect information about effects. + effects = {} + + for effectNode in node.xpath(".//*[@jessyink:effectIn]", namespaces=inkex.NSS): + dictio = propListToDict(propStrToList(effectNode.get("{" + inkex.NSS["jessyink"] + "}effectIn"))) + dictio["direction"] = "in" + dictio["id"] = effectNode.get("id") + dictio["type"] = "effect" + + if not effects.has_key(dictio["order"]): + effects[dictio["order"]] = [] + + effects[dictio["order"]].append(dictio) + + for effectNode in node.xpath(".//*[@jessyink:effectOut]", namespaces=inkex.NSS): + dictio = propListToDict(propStrToList(effectNode.get("{" + inkex.NSS["jessyink"] + "}effectOut"))) + dictio["direction"] = "out" + dictio["id"] = effectNode.get("id") + dictio["type"] = "effect" + + if not effects.has_key(dictio["order"]): + effects[dictio["order"]] = [] + + effects[dictio["order"]].append(dictio) + + for viewNode in node.xpath(".//*[@jessyink:view]", namespaces=inkex.NSS): + dictio = propListToDict(propStrToList(viewNode.get("{" + inkex.NSS["jessyink"] + "}view"))) + dictio["id"] = viewNode.get("id") + dictio["type"] = "view" + + if not effects.has_key(dictio["order"]): + effects[dictio["order"]] = [] + + effects[dictio["order"]].append(dictio) + + order = sorted(effects.keys()) + orderNumber = 0 + + # Display information about effects. + for orderItem in order: + if orderNumber == 0: + sys.stderr.write("\n" + prefix + "Initial effect (order number " + effects[orderItem][0]["order"] + "):\n") + else: + sys.stderr.write("\n" + prefix + "Effect " + str(orderNumber) + " (order number " + effects[orderItem][0]["order"] + "):\n") + + for item in effects[orderItem]: + if item["type"] == "view": + sys.stderr.write(prefix + "\tView will be set according to object \"" + item["id"] + "\"") + else: + sys.stderr.write(prefix + "\tObject \"" + item["id"] + "\"") + + if item["direction"] == "in": + sys.stderr.write(" will appear") + elif item["direction"] == "out": + sys.stderr.write(" will disappear") + + if item["name"] != "appear": + sys.stderr.write(" using effect \"" + item["name"] + "\"") + + if item.has_key("length"): + sys.stderr.write(" in " + str(int(item["length"]) / 1000.0) + " s") + + sys.stderr.write(".\n") + + orderNumber += 1 + +# Create effect instance +effect = JessyInk_Summary() +effect.affect() + diff --git a/share/extensions/jessyInk_transitions.inx b/share/extensions/jessyInk_transitions.inx new file mode 100644 index 000000000..1599d2c20 --- /dev/null +++ b/share/extensions/jessyInk_transitions.inx @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> + <_name>Transitions</_name> + <id>jessyink.transitions</id> + <dependency type="executable" location="extensions">inkex.py</dependency> + <dependency type="executable" location="extensions">jessyInk_transitions.py</dependency> + <param name="tab" type="notebook"> + <page name="settings" _gui-text="Settings"> + <param name="layerName" type="string" _gui-text="Name of layer:"></param> + <_param name="effectInLabel" type="description">Transition in effect</_param> + <param name="effectInDuration" precision="1" type="float" min="0.1" max="10.0" _gui-text="Duration in seconds:">0.8</param> + <param name="effectIn" type="optiongroup" _gui-text="Type:"> + <_option value="default">Default</_option> + <_option value="appear">Appear</_option> + <_option value="fade">Fade</_option> + <_option value="pop">Pop</_option> + </param> + <_param name="effectOutLabel" type="description">Transition out effect</_param> + <param name="effectOutDuration" precision="1" type="float" min="0.1" max="10.0" _gui-text="Duration in seconds:">0.8</param> + <param name="effectOut" type="optiongroup" _gui-text="Type:"> + <_option value="default">Default</_option> + <_option value="appear">Appear</_option> + <_option value="fade">Fade</_option> + <_option value="pop">Pop</_option> + </param> + </page> + <page name="help" _gui-text="Help"> + <_param name="help_text" type="description">This extension allows you to change the transition JessyInk uses for the selected layer. Please see code.google.com/p/jessyink for more details.</_param> + </page> + </param> + <effect> + <object-type>g</object-type> + <effects-menu> + <submenu _name="JessyInk"/> + </effects-menu> + </effect> + <script> + <command reldir="extensions" interpreter="python">jessyInk_transitions.py</command> + </script> +</inkscape-extension> + diff --git a/share/extensions/jessyInk_transitions.py b/share/extensions/jessyInk_transitions.py new file mode 100755 index 000000000..d35477d20 --- /dev/null +++ b/share/extensions/jessyInk_transitions.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# Copyright 2008, 2009 Hannes Hochreiner +# 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 3 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, see http://www.gnu.org/licenses/. + +# These lines are only needed if you don't put the script directly into +# the installation directory +import sys +# Unix +sys.path.append('/usr/share/inkscape/extensions') +# OS X +sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions') +# Windows +sys.path.append('C:\Program Files\Inkscape\share\extensions') + +# We will use the inkex module with the predefined Effect base class. +import inkex + +class JessyInk_Transitions(inkex.Effect): + def __init__(self): + # Call the base class constructor. + inkex.Effect.__init__(self) + + self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what') + self.OptionParser.add_option('--layerName', action = 'store', type = 'string', dest = 'layerName', default = '') + self.OptionParser.add_option('--effectIn', action = 'store', type = 'string', dest = 'effectIn', default = 'default') + self.OptionParser.add_option('--effectInDuration', action = 'store', type = 'float', dest = 'effectInDuration', default = 0.8) + self.OptionParser.add_option('--effectOut', action = 'store', type = 'string', dest = 'effectOut', default = 'default') + self.OptionParser.add_option('--effectOutDuration', action = 'store', type = 'float', dest = 'effectOutDuration', default = 0.8) + + inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink" + + def effect(self): + # Check version. + scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS) + + if len(scriptNodes) != 1: + sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n") + + if self.options.layerName != "": + nodes = self.document.xpath("//*[@inkscape:groupmode='layer' and @inkscape:label='" + self.options.layerName + "']", namespaces=inkex.NSS) + if len(nodes) == 0: + sys.stderr.write("Layer not found.\n") + elif len(nodes) > 1: + sys.stderr.write("More than one layer with this name found.\n") + else: + if self.options.effectIn == "default": + if nodes[0].get("{" + inkex.NSS["jessyink"] + "}transitionIn"): + del nodes[0].attrib["{" + inkex.NSS["jessyink"] + "}transitionIn"] + else: + nodes[0].set("{" + inkex.NSS["jessyink"] + "}transitionIn","name:" + self.options.effectIn + ";length:" + str(int(self.options.effectInDuration * 1000))) + if self.options.effectOut == "default": + if nodes[0].get("{" + inkex.NSS["jessyink"] + "}transitionOut"): + del nodes[0].attrib["{" + inkex.NSS["jessyink"] + "}transitionOut"] + else: + nodes[0].set("{" + inkex.NSS["jessyink"] + "}transitionOut","name:" + self.options.effectOut + ";length:" + str(int(self.options.effectOutDuration * 1000))) + else: + sys.stderr.write("Please enter a layer name.\n") + +# Create effect instance +effect = JessyInk_Transitions() +effect.affect() + diff --git a/share/extensions/jessyInk_uninstall.inx b/share/extensions/jessyInk_uninstall.inx new file mode 100644 index 000000000..802b3c394 --- /dev/null +++ b/share/extensions/jessyInk_uninstall.inx @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> + <_name>Uninstall/remove</_name> + <id>jessyink.uninstall</id> + <dependency type="executable" location="extensions">inkex.py</dependency> + <dependency type="executable" location="extensions">jessyInk_uninstall.py</dependency> + <param name="tab" type="notebook"> + <page name="options" _gui-text="Options"> + <_param name="label" type="description">Please select the parts of JessyInk you want to uninstall/remove.</_param> + <param name="remove_script" type="boolean" _gui-text="Remove script">true</param> + <param name="remove_effects" type="boolean" _gui-text="Remove effects">true</param> + <param name="remove_masterSlide" type="boolean" _gui-text="Remove master slide assignment">true</param> + <param name="remove_transitions" type="boolean" _gui-text="Remove transitions">true</param> + <param name="remove_autoTexts" type="boolean" _gui-text="Remove auto-texts">true</param> + <param name="remove_views" type="boolean" _gui-text="Remove views">true</param> + </page> + <page name="help" _gui-text="Help"> + <_param name="help_text" type="description">This extension allows you to uninstall the JessyInk script. Please see code.google.com/p/jessyink for more details.</_param> + </page> + </param> + <effect> + <object-type>all</object-type> + <effects-menu> + <submenu _name="JessyInk"/> + </effects-menu> + </effect> + <script> + <command reldir="extensions" interpreter="python">jessyInk_uninstall.py</command> + </script> +</inkscape-extension> + diff --git a/share/extensions/jessyInk_uninstall.py b/share/extensions/jessyInk_uninstall.py new file mode 100755 index 000000000..c1ade2693 --- /dev/null +++ b/share/extensions/jessyInk_uninstall.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python +# Copyright 2008, 2009 Hannes Hochreiner +# 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 3 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, see http://www.gnu.org/licenses/. + +# These lines are only needed if you don't put the script directly into +# the installation directory +import sys +# Unix +sys.path.append('/usr/share/inkscape/extensions') +# OS X +sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions') +# Windows +sys.path.append('C:\Program Files\Inkscape\share\extensions') + +# We will use the inkex module with the predefined Effect base class. +import inkex + +def propStrToList(str): + list = [] + propList = str.split(";") + for prop in propList: + if not (len(prop) == 0): + list.append(prop.strip()) + return list + +def listToPropStr(list): + str = "" + for prop in list: + str += " " + prop + ";" + return str[1:] + +class JessyInk_Uninstall(inkex.Effect): + def __init__(self): + # Call the base class constructor. + inkex.Effect.__init__(self) + + self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what') + self.OptionParser.add_option('--remove_script', action = 'store', type = 'inkbool', dest = 'remove_script', default = True) + self.OptionParser.add_option('--remove_effects', action = 'store', type = 'inkbool', dest = 'remove_effects', default = True) + self.OptionParser.add_option('--remove_masterSlide', action = 'store', type = 'inkbool', dest = 'remove_masterSlide', default = True) + self.OptionParser.add_option('--remove_transitions', action = 'store', type = 'inkbool', dest = 'remove_transitions', default = True) + self.OptionParser.add_option('--remove_autoTexts', action = 'store', type = 'inkbool', dest = 'remove_autoTexts', default = True) + self.OptionParser.add_option('--remove_views', action = 'store', type = 'inkbool', dest = 'remove_views', default = True) + + inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink" + + def effect(self): + # Remove script, if so desired. + if self.options.remove_script: + # Find and delete script node. + for node in self.document.xpath("//svg:script[@id='JessyInk']", namespaces=inkex.NSS): + node.getparent().remove(node) + + # Remove "jessyInkInit()" in the "onload" attribute, if present. + if self.document.getroot().get("onload"): + propList = propStrToList(self.document.getroot().get("onload")) + else: + propList = [] + + for prop in propList: + if prop == "jessyInkInit()": + propList.remove("jessyInkInit()") + + if len(propList) > 0: + self.document.getroot().set("onload", listToPropStr(propList)) + else: + if self.document.getroot().get("onload"): + del self.document.getroot().attrib["onload"] + + # Remove effect attributes, if so desired. + if self.options.remove_effects: + for node in self.document.xpath("//*[@jessyink:effectIn]", namespaces=inkex.NSS): + del node.attrib["{" + inkex.NSS["jessyink"] + "}effectIn"] + + for node in self.document.xpath("//*[@jessyink:effectOut]", namespaces=inkex.NSS): + del node.attrib["{" + inkex.NSS["jessyink"] + "}effectOut"] + + # Remove old style attributes as well. + for node in self.document.xpath("//*[@jessyInk_effectIn]", namespaces=inkex.NSS): + del node.attrib["jessyInk_effectIn"] + + for node in self.document.xpath("//*[@jessyInk_effectOut]", namespaces=inkex.NSS): + del node.attrib["jessyInk_effectOut"] + + # Remove master slide assignment, if so desired. + if self.options.remove_masterSlide: + for node in self.document.xpath("//*[@jessyink:masterSlide]", namespaces=inkex.NSS): + del node.attrib["{" + inkex.NSS["jessyink"] + "}masterSlide"] + + # Remove old style attributes as well. + for node in self.document.xpath("//*[@jessyInk_masterSlide]", namespaces=inkex.NSS): + del node.attrib["jessyInk_masterSlide"] + + # Remove transitions, if so desired. + if self.options.remove_transitions: + for node in self.document.xpath("//*[@jessyink:transitionIn]", namespaces=inkex.NSS): + del node.attrib["{" + inkex.NSS["jessyink"] + "}transitionIn"] + + for node in self.document.xpath("//*[@jessyink:transitionOut]", namespaces=inkex.NSS): + del node.attrib["{" + inkex.NSS["jessyink"] + "}transitionOut"] + + # Remove old style attributes as well. + for node in self.document.xpath("//*[@jessyInk_transitionIn]", namespaces=inkex.NSS): + del node.attrib["jessyInk_transitionIn"] + + for node in self.document.xpath("//*[@jessyInk_transitionOut]", namespaces=inkex.NSS): + del node.attrib["jessyInk_transitionOut"] + + # Remove auto texts, if so desired. + if self.options.remove_autoTexts: + for node in self.document.xpath("//*[@jessyink:autoText]", namespaces=inkex.NSS): + del node.attrib["{" + inkex.NSS["jessyink"] + "}autoText"] + + # Remove old style attributes as well. + for node in self.document.xpath("//*[@jessyInk_autoText]", namespaces=inkex.NSS): + del node.attrib["jessyInk_autoText"] + + # Remove views, if so desired. + if self.options.remove_views: + for node in self.document.xpath("//*[@jessyink:view]", namespaces=inkex.NSS): + del node.attrib["{" + inkex.NSS["jessyink"] + "}view"] + +# Create effect instance. +effect = JessyInk_Uninstall() +effect.affect() + diff --git a/share/extensions/jessyInk_video.inx b/share/extensions/jessyInk_video.inx new file mode 100644 index 000000000..59c68114d --- /dev/null +++ b/share/extensions/jessyInk_video.inx @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> + <_name>Video</_name> + <id>jessyink.core.video</id> + <dependency type="executable" location="extensions">inkex.py</dependency> + <dependency type="executable" location="extensions">jessyInk_video.py</dependency> + <param name="tab" type="notebook"> + <page name="help" _gui-text="Help"> + <_param name="help_text" type="description">This extension puts a JessyInk video element on the current slide (layer). This element allows you to integrate a video into your JessyInk presentation. Please see code.google.com/p/jessyink for more details.</_param> + </page> + </param> + <effect> + <object-type>all</object-type> + <effects-menu> + <submenu _name="JessyInk"/> + </effects-menu> + </effect> + <script> + <command reldir="extensions" interpreter="python">jessyInk_video.py</command> + </script> +</inkscape-extension> + diff --git a/share/extensions/jessyInk_video.py b/share/extensions/jessyInk_video.py new file mode 100755 index 000000000..1d4ae0ee3 --- /dev/null +++ b/share/extensions/jessyInk_video.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# Copyright 2008, 2009 Hannes Hochreiner +# 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 3 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, see http://www.gnu.org/licenses/. + +# These lines are only needed if you don't put the script directly into +# the installation directory +import sys +# Unix +sys.path.append('/usr/share/inkscape/extensions') +# OS X +sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions') +# Windows +sys.path.append('C:\Program Files\Inkscape\share\extensions') + +# We will use the inkex module with the predefined Effect base class. +import inkex +import os +import re +from lxml import etree +from copy import deepcopy + +class JessyInk_Effects(inkex.Effect): + def __init__(self): + # Call the base class constructor. + inkex.Effect.__init__(self) + + self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what') + + inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink" + + def effect(self): + # Check version. + scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS) + + if len(scriptNodes) != 1: + sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n") + + baseView = self.document.xpath("//sodipodi:namedview[@id='base']", namespaces=inkex.NSS) + + if len(baseView) != 1: + sys.stderr.write("Could not obtain the selected layer for inclusion of the video element.\n\n") + + layer = self.document.xpath("//svg:g[@id='" + baseView[0].attrib["{" + inkex.NSS["inkscape"] + "}current-layer"] + "']", namespaces=inkex.NSS) + + if (len(layer) != 1): + sys.stderr.write("Could not obtain the selected layer for inclusion of the video element.\n\n") + + # Parse template file. + tmplFile = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'jessyInk_video.svg'), 'r') + tmplRoot = etree.fromstring(tmplFile.read()) + tmplFile.close() + + elem = deepcopy(tmplRoot.xpath("//svg:g[@jessyink:element='core.video']", namespaces=inkex.NSS)[0]) + nodeDict = findInternalLinks(elem, tmplRoot) + + deleteIds(elem) + + idSubst = {} + + for key in nodeDict: + idSubst[key] = getNewId("jessyink.core.video", self.document) + deleteIds(nodeDict[key]) + nodeDict[key].attrib['id'] = idSubst[key] + elem.insert(0, nodeDict[key]) + + for ndIter in elem.iter(): + for attrIter in ndIter.attrib: + for entryIter in idSubst: + ndIter.attrib[attrIter] = ndIter.attrib[attrIter].replace("#" + entryIter, "#" + idSubst[entryIter]) + + # Append element. + layer[0].append(elem) + +def findInternalLinks(node, docRoot, nodeDict = {}): + for entry in re.findall("url\(#.*\)", etree.tostring(node)): + linkId = entry[5:len(entry) - 1] + + if not nodeDict.has_key(linkId): + nodeDict[linkId] = deepcopy(docRoot.xpath("//*[@id='" + linkId + "']", namespaces=inkex.NSS)[0]) + nodeDict = findInternalLinks(nodeDict[linkId], docRoot, nodeDict) + + for entry in node.iter(): + if entry.attrib.has_key('{' + inkex.NSS['xlink'] + '}href'): + linkId = entry.attrib['{' + inkex.NSS['xlink'] + '}href'][1:len(entry.attrib['{' + inkex.NSS['xlink'] + '}href'])] + + if not nodeDict.has_key(linkId): + nodeDict[linkId] = deepcopy(docRoot.xpath("//*[@id='" + linkId + "']", namespaces=inkex.NSS)[0]) + nodeDict = findInternalLinks(nodeDict[linkId], docRoot, nodeDict) + + return nodeDict + +def getNewId(prefix, docRoot): + import datetime + + number = datetime.datetime.now().microsecond + + while len(docRoot.xpath("//*[@id='" + prefix + str(number) + "']", namespaces=inkex.NSS)) > 0: + number += 1 + + return prefix + str(number) + +def deleteIds(node): + for entry in node.iter(): + if entry.attrib.has_key('id'): + del entry.attrib['id'] + +# Create effect instance +effect = JessyInk_Effects() +effect.affect() + diff --git a/share/extensions/jessyInk_video.svg b/share/extensions/jessyInk_video.svg new file mode 100644 index 000000000..1d9e75506 --- /dev/null +++ b/share/extensions/jessyInk_video.svg @@ -0,0 +1,596 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:jessyink="https://launchpad.net/jessyink" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="478.3808" + height="320.73016" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.47 r22583" + version="1.0" + sodipodi:docname="jessyInk_video.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs4"> + <linearGradient + id="linearGradient3654"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop3656" /> + <stop + style="stop-color:#bebebe;stop-opacity:1;" + offset="1" + id="stop3658" /> + </linearGradient> + <linearGradient + id="linearGradient3398"> + <stop + style="stop-color:#d6d6d6;stop-opacity:1;" + offset="0" + id="stop3400" /> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="1" + id="stop3402" /> + </linearGradient> + <linearGradient + id="linearGradient3388"> + <stop + style="stop-color:#dfdfdf;stop-opacity:1;" + offset="0" + id="stop3390" /> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="1" + id="stop3392" /> + </linearGradient> + <linearGradient + id="linearGradient3327"> + <stop + style="stop-color:#859df1;stop-opacity:1;" + offset="0" + id="stop3329" /> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="1" + id="stop3331" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2953" + id="linearGradient2985" + x1="154.24722" + y1="110.70377" + x2="134.85132" + y2="87.365448" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9962198,-0.08686864,0.08686864,0.9962198,-7.0296853,12.597208)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2953" + id="linearGradient2977" + x1="129.11227" + y1="115.96979" + x2="120.07692" + y2="96.278084" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(0.441989,-0.2209945)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2953" + id="linearGradient2968" + x1="110.75861" + y1="116.03537" + x2="106.21297" + y2="98.169502" + gradientUnits="userSpaceOnUse" /> + <linearGradient + id="linearGradient2953"> + <stop + style="stop-color:#2502ff;stop-opacity:1;" + offset="0" + id="stop2955" /> + <stop + style="stop-color:#a8a5ff;stop-opacity:1;" + offset="1" + id="stop2958" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2953" + id="linearGradient2960" + x1="108.78014" + y1="92.046555" + x2="104.73283" + y2="88.124817" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2913" + id="linearGradient2919" + x1="35.085781" + y1="55.376659" + x2="25.629274" + y2="92.503731" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.8103484,0,0,0.8087248,2.2289552,16.936481)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2913" + id="linearGradient2927" + x1="118.00729" + y1="86.390717" + x2="156.74582" + y2="86.390717" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.8103484,0,0,0.8087248,14.383651,12.295597)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2913" + id="linearGradient2943" + x1="81.277565" + y1="68.547112" + x2="91.105675" + y2="91.024132" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.7897017,-0.1817575,0.1813933,0.7881194,-4.8653748,24.856814)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2913" + id="linearGradient2935" + x1="117.19492" + y1="96.566895" + x2="108.4378" + y2="77.16523" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.8103484,0,0,0.8087248,11.876496,9.4552995)" /> + <linearGradient + id="linearGradient2913"> + <stop + style="stop-color:#f1ff1d;stop-opacity:1;" + offset="0" + id="stop2915" /> + <stop + style="stop-color:#fba100;stop-opacity:1" + offset="1" + id="stop2917" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2913" + id="linearGradient2951" + x1="34.869572" + y1="85.162331" + x2="69.447136" + y2="85.162331" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.8103484,0,0,0.8087248,11.67459,15.220437)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2955" + id="linearGradient2970" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.0574713,0,0,1.0581729,53.099639,13.989279)" + x1="22.693371" + y1="28.950274" + x2="108.57735" + y2="84.930939" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2955" + id="linearGradient2966" + gradientUnits="userSpaceOnUse" + x1="22.693371" + y1="28.950274" + x2="108.57735" + y2="84.930939" + gradientTransform="matrix(1.0511003,0,0,1.0517241,27.50652,1.2611923)" /> + <linearGradient + id="linearGradient2955"> + <stop + style="stop-color:#fbfcff;stop-opacity:1;" + offset="0" + id="stop2957" /> + <stop + style="stop-color:#5258ef;stop-opacity:1;" + offset="1" + id="stop2959" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2955" + id="linearGradient2972" + gradientUnits="userSpaceOnUse" + x1="22.693371" + y1="28.950274" + x2="108.57735" + y2="84.930939" + gradientTransform="translate(1.5469614,-10.38674)" /> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 526.18109 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="744.09448 : 526.18109 : 1" + inkscape:persp3d-origin="372.04724 : 350.78739 : 1" + id="perspective10" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2955" + id="linearGradient3333" + x1="347.24408" + y1="126.14174" + x2="400.7677" + y2="267.53937" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(8.5039369,12.755905)" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3388" + id="radialGradient3433" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.4883686,0.6863398,-0.5578719,-0.3969566,490.07776,437.40628)" + cx="176.60922" + cy="402.68719" + fx="176.60922" + fy="402.68719" + r="30.22246" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3398" + id="linearGradient3435" + gradientUnits="userSpaceOnUse" + x1="183.88486" + y1="434.88504" + x2="177.69821" + y2="413.2713" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3398" + id="linearGradient3437" + gradientUnits="userSpaceOnUse" + x1="211.84167" + y1="415.36514" + x2="198.24878" + y2="405.86191" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3398" + id="linearGradient3447" + x1="323.48425" + y1="440.07874" + x2="308.64169" + y2="331.98819" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9709646,0,0,0.7586207,2.3456911,80.73853)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3398" + id="linearGradient3521" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1652108,0,0,1.3041467,-12.256157,-100.64265)" + x1="323.48425" + y1="440.07874" + x2="308.64169" + y2="331.98819" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3388" + id="radialGradient3523" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.4883686,0.6863398,-0.5578719,-0.3969566,490.07776,437.40628)" + cx="176.60922" + cy="402.68719" + fx="176.60922" + fy="402.68719" + r="30.22246" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3398" + id="linearGradient3525" + gradientUnits="userSpaceOnUse" + x1="183.88486" + y1="434.88504" + x2="177.69821" + y2="413.2713" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3398" + id="linearGradient3527" + gradientUnits="userSpaceOnUse" + x1="211.84167" + y1="415.36514" + x2="198.24878" + y2="405.86191" /> + <inkscape:perspective + id="perspective2914" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_z="1 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /> + <inkscape:perspective + id="perspective2885" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_z="1 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /> + <inkscape:perspective + id="perspective4492" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_z="1 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /> + <linearGradient + id="linearGradient4061"> + <stop + style="stop-color:#cccccc;stop-opacity:1;" + offset="0" + id="stop4063" /> + <stop + style="stop-color:#686868;stop-opacity:1;" + offset="1" + id="stop4065" /> + </linearGradient> + <linearGradient + id="linearGradient4501"> + <stop + style="stop-color:#cccccc;stop-opacity:1;" + offset="0" + id="stop4503" /> + <stop + style="stop-color:#686868;stop-opacity:1;" + offset="1" + id="stop4505" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4061" + id="radialGradient4482" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.0793653,1.1633659,-0.39752037,0.710516,-120.33298,7.3257338)" + cx="267.89597" + cy="424.69434" + fx="267.89597" + fy="424.69434" + r="50.740894" /> + <linearGradient + id="linearGradient4508"> + <stop + style="stop-color:#cccccc;stop-opacity:1;" + offset="0" + id="stop4510" /> + <stop + style="stop-color:#686868;stop-opacity:1;" + offset="1" + id="stop4512" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4061" + id="radialGradient3811" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.2261425,0.84939356,-0.2902365,0.76066958,-210.78387,-103.79557)" + cx="267.89597" + cy="424.69434" + fx="267.89597" + fy="424.69434" + r="50.740894" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4061" + id="radialGradient3813" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.2261425,0.84939356,-0.2902365,0.76066958,-196.85663,-8.8111487)" + cx="267.89597" + cy="424.69434" + fx="267.89597" + fy="424.69434" + r="50.740894" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4061" + id="radialGradient3815" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.2261425,0.84939356,-0.2902365,0.76066958,-182.92939,86.173254)" + cx="267.89597" + cy="424.69434" + fx="267.89597" + fy="424.69434" + r="50.740894" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4061" + id="radialGradient3843" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.2261425,0.84939356,-0.2902365,0.76066958,-210.78387,-103.79557)" + cx="267.89597" + cy="424.69434" + fx="267.89597" + fy="424.69434" + r="50.740894" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4061" + id="radialGradient3845" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.2261425,0.84939356,-0.2902365,0.76066958,-196.85663,-8.8111487)" + cx="267.89597" + cy="424.69434" + fx="267.89597" + fy="424.69434" + r="50.740894" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4061" + id="radialGradient3847" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.2261425,0.84939356,-0.2902365,0.76066958,-182.92939,86.173254)" + cx="267.89597" + cy="424.69434" + fx="267.89597" + fy="424.69434" + r="50.740894" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3654" + id="linearGradient3660" + x1="255.21498" + y1="420.35464" + x2="322.05756" + y2="475.00314" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3654" + id="linearGradient3668" + x1="252.90028" + y1="515.85352" + x2="319.14331" + y2="564.18585" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3654" + id="linearGradient3676" + x1="254.72079" + y1="615.9538" + x2="317.50601" + y2="661.42786" + gradientUnits="userSpaceOnUse" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + gridtolerance="10000" + guidetolerance="10" + objecttolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:zoom="1.5433534" + inkscape:cx="239.1904" + inkscape:cy="160.36508" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="1268" + inkscape:window-height="746" + inkscape:window-x="0" + inkscape:window-y="25" + showborder="true" + inkscape:showpageshadow="false" + borderlayer="true" + inkscape:window-maximized="0" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-255.05484,-196.27865)"> + <g + id="g2879" + transform="translate(-434.78725,69.112709)" + jessyink:element="core.video"> + <jessyink:video /> + <rect + style="fill:#b3b3b3;fill-opacity:1;stroke:none" + id="rect2961" + width="400" + height="250" + x="767.70032" + y="198.79166" + rx="0" + ry="0" + jessyink:video="rect" + transform="matrix(0.99999932,-0.00116615,0.00116615,0.99999932,0,0)" /> + <rect + style="font-size:20px;fill:#dfdfdf;fill-opacity:1;stroke:#000000;stroke-width:3.48164916;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect2927" + width="388.22516" + height="75.056519" + x="737.79498" + y="175.88173" + rx="12" + ry="11.999999" + sodipodi:insensitive="true" /> + <text + xml:space="preserve" + style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="788.77039" + y="234.86638" + id="text2886" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2888" + x="788.77039" + y="234.86638" + jessyink:video="url"><replace this text with the url of the movie></tspan></text> + <g + transform="matrix(0.35587188,0.26994219,-0.26994219,0.35587188,800.78552,-70.706091)" + id="g4309" + sodipodi:insensitive="true"> + <path + style="fill:#000000;fill-opacity:1;stroke:none" + d="m 209.78125,112.53125 0,290.65625 5.25,0 0,-4.21875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 l 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,4.21875 113,0 0,-4.21875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 l 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,4.21875 5.03125,0 0,-290.65625 -5.03125,0 0,3.34375 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-3.34375 -113,0 0,3.34375 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-3.34375 -5.25,0 z m 8.59375,13.09375 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m -128,19.3125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m -128,19.3125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z" + id="path4311" + transform="translate(0,284.36221)" /> + <rect + style="fill:url(#linearGradient3660);fill-opacity:1;stroke:none" + id="rect4313" + width="101.48179" + height="82.622345" + x="235.6795" + y="405.14673" /> + <rect + style="fill:url(#linearGradient3668);fill-opacity:1;stroke:none" + id="rect4315" + width="101.48179" + height="82.622345" + x="235.6795" + y="501.14676" /> + <rect + y="597.14679" + x="235.6795" + height="82.622345" + width="101.48179" + id="rect4317" + style="fill:url(#linearGradient3676);fill-opacity:1;stroke:none" /> + </g> + <text + xml:space="preserve" + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold" + x="811.12927" + y="205.86632" + id="text3701" + sodipodi:linespacing="125%" + sodipodi:insensitive="true"><tspan + sodipodi:role="line" + id="tspan3703" + x="811.12927" + y="205.86632">JessyInk video element</tspan></text> + </g> + </g> +</svg> diff --git a/share/extensions/jessyInk_view.inx b/share/extensions/jessyInk_view.inx new file mode 100644 index 000000000..06fc08bc5 --- /dev/null +++ b/share/extensions/jessyInk_view.inx @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> + <_name>View</_name> + <id>jessyink.view</id> + <dependency type="executable" location="extensions">inkex.py</dependency> + <dependency type="executable" location="extensions">jessyInk_view.py</dependency> + <param name="tab" type="notebook"> + <page name="settings" _gui-text="Settings"> + <param name="viewOrder" type="int" min="0" max="100" _gui-text="Order:">1</param> + <param name="viewDuration" precision="1" type="float" min="0.1" max="10.0" _gui-text="Duration in seconds:">0.8</param> + <param name="removeView" type="boolean" _gui-text="Remove view">false</param> + <_param name="help_text" type="description">Choose order number 0 to set the initial view of a slide.</_param> + </page> + <page name="help" _gui-text="Help"> + <_param name="help_text" type="description">This extension allows you to set, update and remove views for a JessyInk presentation. Please see code.google.com/p/jessyink for more details.</_param> + </page> + </param> + <effect> + <object-type>rect</object-type> + <effects-menu> + <submenu _name="JessyInk"/> + </effects-menu> + </effect> + <script> + <command reldir="extensions" interpreter="python">jessyInk_view.py</command> + </script> +</inkscape-extension> + diff --git a/share/extensions/jessyInk_view.py b/share/extensions/jessyInk_view.py new file mode 100755 index 000000000..639cb6b47 --- /dev/null +++ b/share/extensions/jessyInk_view.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python +# Copyright 2008, 2009 Hannes Hochreiner +# 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 3 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, see http://www.gnu.org/licenses/. + +# These lines are only needed if you don't put the script directly into +# the installation directory +import sys +# Unix +sys.path.append('/usr/share/inkscape/extensions') +# OS X +sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions') +# Windows +sys.path.append('C:\Program Files\Inkscape\share\extensions') + +# We will use the inkex module with the predefined Effect base class. +import inkex + +def propStrToList(str): + list = [] + propList = str.split(";") + for prop in propList: + if not (len(prop) == 0): + list.append(prop.strip()) + return list + +def propListToDict(list): + dictio = {} + + for prop in list: + keyValue = prop.split(":") + + if len(keyValue) == 2: + dictio[keyValue[0].strip()] = keyValue[1].strip() + + return dictio + +class JessyInk_Effects(inkex.Effect): + def __init__(self): + # Call the base class constructor. + inkex.Effect.__init__(self) + + self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what') + self.OptionParser.add_option('--viewOrder', action = 'store', type = 'string', dest = 'viewOrder', default = 1) + self.OptionParser.add_option('--viewDuration', action = 'store', type = 'float', dest = 'viewDuration', default = 0.8) + self.OptionParser.add_option('--removeView', action = 'store', type = 'inkbool', dest = 'removeView', default = False) + + inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink" + + def effect(self): + # Check version. + scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS) + + if len(scriptNodes) != 1: + sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n") + + rect = None + + for id, node in self.selected.items(): + if rect == None: + rect = node + else: + sys.stderr.write("More than one object selected. Please select only one object.\n") + exit() + + if rect == None: + sys.stderr.write("No object selected. Please select the object you want to assign a view to and then press apply.\n") + exit() + + if not self.options.removeView: + # Remove the view that currently has the requested order number. + for node in rect.xpath("ancestor::svg:g[@inkscape:groupmode='layer']/descendant::*[@jessyink:view]", namespaces=inkex.NSS): + propDict = propListToDict(propStrToList(node.attrib["{" + inkex.NSS["jessyink"] + "}view"])) + + if propDict["order"] == self.options.viewOrder: + del node.attrib["{" + inkex.NSS["jessyink"] + "}view"] + + # Set the new view. + rect.set("{" + inkex.NSS["jessyink"] + "}view","name:view;order:" + self.options.viewOrder + ";length:" + str(int(self.options.viewDuration * 1000))) + + # Remove possible effect arguments. + if rect.attrib.has_key("{" + inkex.NSS["jessyink"] + "}effectIn"): + del rect.attrib["{" + inkex.NSS["jessyink"] + "}effectIn"] + + if rect.attrib.has_key("{" + inkex.NSS["jessyink"] + "}effectOut"): + del rect.attrib["{" + inkex.NSS["jessyink"] + "}effectOut"] + else: + if node.attrib.has_key("{" + inkex.NSS["jessyink"] + "}view"): + del node.attrib["{" + inkex.NSS["jessyink"] + "}view"] + +# Create effect instance +effect = JessyInk_Effects() +effect.affect() + diff --git a/share/extensions/scour.inx b/share/extensions/scour.inx index 472643dce..e5b6e90f5 100644 --- a/share/extensions/scour.inx +++ b/share/extensions/scour.inx @@ -26,7 +26,7 @@ <_param name="instructions" type="description" xml:space="preserve">This extension optimizes the SVG file according to the following options: * Simplify colors: convert all colors to #RRGGBB format. * Style to xml: convert styles into XML attributes. - * Group collapsing: collapse <g> elements. + * Group collapsing: collapse group elements. * Enable id stripping: remove all un-referenced ID attributes. * Embed rasters: embed rasters as base64-encoded data. * Keep editor data: don't remove Inkscape, Sodipodi or Adobe Illustrator elements and attributes. diff --git a/share/extensions/scour.py b/share/extensions/scour.py index b851e48c3..3a5392ad2 100755 --- a/share/extensions/scour.py +++ b/share/extensions/scour.py @@ -34,7 +34,6 @@ # at rounded corners) # Next Up: -# - Bug 511186: option to keep XML comments between prolog and root element # - only remove unreferenced elements if they are not children of a referenced element # - add an option to remove ids if they match the Inkscape-style of IDs # - investigate point-reducing algorithms @@ -72,7 +71,7 @@ except ImportError: pass APP = 'scour' -VER = '0.24' +VER = '0.25r171' COPYRIGHT = 'Copyright Jeff Schiller, 2010' NS = { 'SVG': 'http://www.w3.org/2000/svg', @@ -116,6 +115,7 @@ svgAttributes = [ 'stop-color', 'stop-opacity', 'stroke', + 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', @@ -1355,7 +1355,7 @@ def cleanPath(element) : else: # we have a move and then 1 or more coords for lines N = len(path[0][1]) - if path[0] == 'M': + if path[0][0] == 'M': # take the last pair of coordinates for the starting point x = path[0][1][N-2] y = path[0][1][N-1] @@ -1624,13 +1624,14 @@ def cleanPath(element) : for (cmd,data) in path[1:]: # flush the previous command if it is not the same type as the current command if prevCmd != '': - if cmd != prevCmd: + if cmd != prevCmd or cmd == 'm': newPath.append( (prevCmd, prevData) ) prevCmd = '' prevData = [] # if the previous and current commands are the same type, collapse - if cmd == prevCmd: + # but only if they are not move commands (since move can contain implicit lineto commands) + if cmd == prevCmd and cmd != 'm': for coord in data: prevData.append(coord) @@ -1757,13 +1758,13 @@ def cleanPath(element) : for (cmd,data) in path[1:]: # flush the previous command if it is not the same type as the current command if prevCmd != '': - if cmd != prevCmd: + if cmd != prevCmd or cmd == 'm': newPath.append( (prevCmd, prevData) ) prevCmd = '' prevData = [] # if the previous and current commands are the same type, collapse - if cmd == prevCmd: + if cmd == prevCmd and cmd != 'm': for coord in data: prevData.append(coord) @@ -1786,12 +1787,43 @@ def parseListOfPoints(s): Returns a list of containing an even number of coordinate strings """ + i = 0 + points = [] + # (wsp)? comma-or-wsp-separated coordinate pairs (wsp)? # coordinate-pair = coordinate comma-or-wsp coordinate # coordinate = sign? integer - nums = re.split("\\s*\\,?\\s*", s.strip()) + # comma-wsp: (wsp+ comma? wsp*) | (comma wsp*) + ws_nums = re.split("\\s*\\,?\\s*", s.strip()) + nums = [] + + # also, if 100-100 is found, split it into two also + # <polygon points="100,-100,100-100,100-100-100,-100-100" /> + for i in range(len(ws_nums)): + negcoords = re.split("\\-", ws_nums[i]); + + # this string didn't have any negative coordinates + if len(negcoords) == 1: + nums.append(negcoords[0]) + # we got negative coords + else: + for j in range(len(negcoords)): + # first number could be positive + if j == 0: + if negcoords[0] != '': + nums.append(negcoords[0]) + # otherwise all other strings will be negative + else: + # unless we accidentally split a number that was in scientific notation + # and had a negative exponent (500.00e-1) + prev = nums[len(nums)-1] + if prev[len(prev)-1] == 'e' or prev[len(prev)-1] == 'E': + nums[len(nums)-1] = prev + '-' + negcoords[j] + else: + nums.append( '-'+negcoords[j] ) + + # now resolve into SVGLength values i = 0 - points = [] while i < len(nums): x = SVGLength(nums[i]) # if we had an odd number of points, return empty @@ -1803,7 +1835,7 @@ def parseListOfPoints(s): points.append( str(x.value) ) points.append( str(y.value) ) i += 2 - + return points def cleanPolygon(elem): @@ -1820,14 +1852,14 @@ def cleanPolygon(elem): if startx == endx and starty == endy: pts = pts[:-2] numPointsRemovedFromPolygon += 1 - elem.setAttribute('points', scourCoordinates(pts)) + elem.setAttribute('points', scourCoordinates(pts,True)) def cleanPolyline(elem): """ Scour the polyline points attribute """ pts = parseListOfPoints(elem.getAttribute('points')) - elem.setAttribute('points', scourCoordinates(pts)) + elem.setAttribute('points', scourCoordinates(pts,True)) def serializePath(pathObj): """ @@ -2449,5 +2481,3 @@ if __name__ == '__main__': sizediff = (newsize / oldsize) * 100 print >>sys.stderr, ' Original file size:', oldsize, 'bytes;', \ 'new file size:', newsize, 'bytes (' + str(sizediff)[:5] + '%)' - - diff --git a/share/extensions/svg2xaml.xsl b/share/extensions/svg2xaml.xsl index ef01e2023..cda6c7a97 100755 --- a/share/extensions/svg2xaml.xsl +++ b/share/extensions/svg2xaml.xsl @@ -1,7 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?>
<!--
-Copyright (c) 2005-2007 Toine de Greef (a.degreef@chello.nl)
+Copyright (c) 2005-2007 authors:
+Original version: Toine de Greef (a.degreef@chello.nl)
+Modified (2010) by Nicolas Dufour (nicoduf@yahoo.fr) (blur support, units
+convertion, comments, and some other fixes)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -38,10 +41,352 @@ exclude-result-prefixes="rdf xlink xs exsl libxslt"> <xsl:param name="silverlight_compatible" select="1" />
+<!-- Root template.
+Everything starts here! -->
+<xsl:template match="/">
+ <xsl:choose>
+ <xsl:when test="$silverlight_compatible = 1">
+ <xsl:apply-templates mode="forward" />
+ </xsl:when>
+ <xsl:otherwise>
+ <Viewbox Stretch="Uniform">
+ <xsl:apply-templates mode="forward" />
+ </Viewbox>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- SVG and groups
+(including layers) -->
+<xsl:template mode="forward" match="*[name(.) = 'svg' or name(.) = 'g']">
+ <xsl:choose>
+ <xsl:when test="name(.) = 'svg' or @transform or @viewBox or @id or @clip-path or (@style and contains(@style, 'clip-path:url(#')) or (@width and not(contains(@width, '%'))) or @x or @y or (@height and not(contains(@height, '%'))) or *[name(.) = 'linearGradient' or name(.) = 'radialGradient' or name(.) = 'defs' or name(.) = 'clipPath']">
+ <Canvas>
+ <xsl:apply-templates mode="id" select="." />
+ <!--
+ <xsl:apply-templates mode="clip" select="." />
+ -->
+ <xsl:if test="@style and contains(@style, 'display:none')"><xsl:attribute name="Visibility">Collapsed</xsl:attribute></xsl:if>
+ <xsl:if test="@style and contains(@style, 'opacity:')">
+ <xsl:attribute name="Opacity">
+ <xsl:choose>
+ <xsl:when test="contains(substring-after(@style, 'opacity:'), ';')"><xsl:value-of select="substring-before(substring-after(@style, 'opacity:'), ';')" /></xsl:when>
+ <xsl:otherwise><xsl:value-of select="substring-after(@style, 'opacity:')" /></xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:if test="@width and not(contains(@width, '%'))">
+ <xsl:attribute name="Width">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@width" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
+ <xsl:if test="@height and not(contains(@height, '%'))">
+ <xsl:attribute name="Height">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@height" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
+ <xsl:if test="@x">
+ <xsl:attribute name="Canvas.Left">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@x" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
+ <xsl:if test="@y">
+ <xsl:attribute name="Canvas.Top">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@y" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
+ <xsl:if test="@viewBox">
+ <xsl:variable name="viewBox"><xsl:value-of select="normalize-space(translate(@viewBox, ',', ' '))" /></xsl:variable>
+ <xsl:attribute name="Width"><xsl:value-of select="substring-before(substring-after(substring-after($viewBox, ' '), ' '), ' ')" /></xsl:attribute>
+ <xsl:attribute name="Height"><xsl:value-of select="substring-after(substring-after(substring-after($viewBox, ' '), ' '), ' ')" /></xsl:attribute>
+ <Canvas.RenderTransform>
+ <TranslateTransform>
+ <xsl:attribute name="X"><xsl:value-of select="-number(substring-before($viewBox, ' '))" /></xsl:attribute>
+ <xsl:attribute name="Y"><xsl:value-of select="-number(substring-before(substring-after($viewBox, ' '), ' '))" /></xsl:attribute>
+ </TranslateTransform>
+ </Canvas.RenderTransform>
+ </xsl:if>
+ <xsl:if test="@transform">
+ <Canvas>
+ <Canvas.RenderTransform>
+ <TransformGroup><xsl:apply-templates mode="transform" select="." /></TransformGroup>
+ </Canvas.RenderTransform>
+ <xsl:apply-templates mode="forward" select="*" />
+ </Canvas>
+ </xsl:if>
+
+ <xsl:if test="*[name(.) = 'linearGradient' or name(.) = 'radialGradient' or name(.) = 'defs' or name(.) = 'clipPath']">
+ <Canvas.Resources>
+ <xsl:apply-templates mode="forward" select="*[name(.) = 'linearGradient' or name(.) = 'radialGradient' or name(.) = 'defs' or name(.) = 'clipPath']" />
+ </Canvas.Resources>
+ </xsl:if>
+ <xsl:if test="not(@transform)">
+ <xsl:apply-templates mode="forward" select="*[name(.) != 'linearGradient' and name(.) != 'radialGradient' and name(.) != 'defs' and name(.) != 'clipPath']" />
+ </xsl:if>
+ </Canvas>
+ </xsl:when>
+ <xsl:when test="not(@transform)">
+ <xsl:apply-templates mode="forward" select="*" />
+ </xsl:when>
+ </xsl:choose>
+</xsl:template>
+
<!--
-Values with units (except %) are converted to pixels.
-Unknown units are kept.
+// Resources (defs) //
+
+* Resources ids
+* Generic defs template
+* Generic filters template
+* Filter effects
+* Linked filter effects
+* Linear gradients
+* Radial gradients
+* Generic gradient stops
+* Clipping
-->
+
+<!-- Resources ids -->
+<xsl:template mode="resources" match="*">
+ <!-- should be in-depth -->
+ <xsl:if test="ancestor::*[name(.) = 'defs']"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
+</xsl:template>
+
+<!-- Generic defs template -->
+<xsl:template mode="forward" match="defs">
+ <xsl:apply-templates mode="forward" />
+</xsl:template>
+
+<!-- Generic filters template
+Limited to one filter (can be improved) -->
+<xsl:template mode="forward" match="*[name(.) = 'filter']">
+ <xsl:if test="count(*) = 1">
+ <xsl:apply-templates mode="forward" />
+ </xsl:if>
+</xsl:template>
+
+<!-- Filter effects -->
+<xsl:template mode="forward" match="*[name(.) = 'feGaussianBlur']">
+ <BlurEffect>
+ <xsl:if test="../@id"><xsl:attribute name="x:Key"><xsl:value-of select="../@id" /></xsl:attribute></xsl:if>
+ <xsl:if test="@stdDeviation"><xsl:attribute name="Radius"><xsl:value-of select="round(@stdDeviation * 3)" /></xsl:attribute></xsl:if>
+ </BlurEffect>
+</xsl:template>
+
+<!-- Linked filter effect -->
+<xsl:template mode="filter_effect" match="*">
+<xsl:choose>
+ <xsl:when test="@filter and starts-with(@filter, 'url(#')">
+ <xsl:attribute name="Effect">
+ <xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@filter, 'url(#'), ')'), '}')" />
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:when test="@style and contains(@style, 'filter:url(#')">
+ <xsl:attribute name="Effect">
+ <xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@style, 'filter:url(#'), ')'), '}')" />
+ </xsl:attribute>
+ </xsl:when>
+</xsl:choose>
+</xsl:template>
+
+<!-- Linear gradient -->
+<xsl:template mode="forward" match="*[name(.) = 'linearGradient']">
+ <LinearGradientBrush>
+ <xsl:if test="@id"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
+ <xsl:attribute name="MappingMode">
+ <xsl:choose>
+ <xsl:when test="@gradientUnits = 'userSpaceOnUse' ">Absolute</xsl:when>
+ <xsl:otherwise>RelativeToBoundingBox</xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ <xsl:if test="@spreadMethod">
+ <xsl:attribute name="SpreadMethod">
+ <xsl:choose>
+ <xsl:when test="@spreadMethod = 'pad'">Pad</xsl:when>
+ <xsl:when test="@spreadMethod = 'reflect'">Reflect</xsl:when>
+ <xsl:when test="@spreadMethod = 'repeat'">Repeat</xsl:when>
+ </xsl:choose>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:choose>
+ <xsl:when test="@x1 and @y1 and @x2 and @y2">
+ <xsl:choose>
+ <xsl:when test="contains(@x1, '%') and contains(@y1, '%')">
+ <xsl:attribute name="StartPoint"><xsl:value-of select="concat(substring-before(@x1, '%') div 100, ',', substring-before(@y1,'%') div 100)" /></xsl:attribute>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="StartPoint"><xsl:value-of select="concat(@x1, ',', @y1)" /></xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:choose>
+ <xsl:when test="contains(@x2, '%') and contains(@y2, '%')">
+ <xsl:attribute name="EndPoint"><xsl:value-of select="concat(substring-before(@x2, '%') div 100, ',', substring-before(@y2,'%') div 100)" /></xsl:attribute>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="EndPoint"><xsl:value-of select="concat(@x2, ',', @y2)" /></xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="StartPoint"><xsl:value-of select="'0,0'" /></xsl:attribute>
+ <xsl:attribute name="EndPoint"><xsl:value-of select="'1,1'" /></xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+ <LinearGradientBrush.GradientStops>
+ <GradientStopCollection>
+ <xsl:choose>
+ <xsl:when test="@xlink:href">
+ <xsl:variable name="reference_id" select="@xlink:href" />
+ <xsl:apply-templates mode="forward" select="//*[name(.) = 'linearGradient' and $reference_id = concat('#', @id)]/*" />
+ </xsl:when>
+ <xsl:otherwise><xsl:apply-templates mode="forward" /></xsl:otherwise>
+ </xsl:choose>
+ </GradientStopCollection>
+ </LinearGradientBrush.GradientStops>
+ <xsl:if test="@gradientTransform">
+ <LinearGradientBrush.Transform>
+ <xsl:apply-templates mode="transform" select="." />
+ </LinearGradientBrush.Transform>
+ </xsl:if>
+ </LinearGradientBrush>
+</xsl:template>
+
+<!-- Radial gradient -->
+<xsl:template mode="forward" match="*[name(.) = 'radialGradient']">
+ <RadialGradientBrush>
+ <xsl:if test="@id"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
+ <xsl:attribute name="MappingMode">
+ <xsl:choose>
+ <xsl:when test="@gradientUnits = 'userSpaceOnUse' ">Absolute</xsl:when>
+ <xsl:otherwise>RelativeToBoundingBox</xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ <xsl:if test="@spreadMethod">
+ <xsl:attribute name="SpreadMethod">
+ <xsl:choose>
+ <xsl:when test="@spreadMethod = 'pad'">Pad</xsl:when>
+ <xsl:when test="@spreadMethod = 'reflect'">Reflect</xsl:when>
+ <xsl:when test="@spreadMethod = 'repeat'">Repeat</xsl:when>
+ </xsl:choose>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:if test="@cx and @cy">
+ <xsl:attribute name="Center">
+ <xsl:choose>
+ <xsl:when test="contains(@cx, '%') and contains(@cy, '%')">
+ <xsl:value-of select="concat(number(substring-before(@cx, '%')) div 100, ',', number(substring-before(@cy, '%')) div 100)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat(@cx, ',', @cy)" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:if test="@fx and @fy">
+ <xsl:attribute name="GradientOrigin">
+ <xsl:choose>
+ <xsl:when test="contains(@fx, '%') and contains(@fy, '%')">
+ <xsl:value-of select="concat(number(substring-before(@fx, '%')) div 100, ',', number(substring-before(@fy, '%')) div 100)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat(@fx, ',', @fy)" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:if test="@r">
+ <xsl:choose>
+ <xsl:when test="contains(@r, '%')">
+ <xsl:attribute name="RadiusX"><xsl:value-of select="number(substring-before(@r, '%')) div 100" /></xsl:attribute>
+ <xsl:attribute name="RadiusY"><xsl:value-of select="number(substring-before(@r, '%')) div 100" /></xsl:attribute>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="RadiusX"><xsl:value-of select="@r" /></xsl:attribute>
+ <xsl:attribute name="RadiusY"><xsl:value-of select="@r" /></xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ <RadialGradientBrush.GradientStops>
+ <GradientStopCollection>
+ <xsl:choose>
+ <xsl:when test="@xlink:href">
+ <xsl:variable name="reference_id" select="@xlink:href" />
+ <xsl:apply-templates mode="forward" select="//*[name(.) = 'linearGradient' and $reference_id = concat('#', @id)]/*" />
+ </xsl:when>
+ <xsl:otherwise><xsl:apply-templates mode="forward" /></xsl:otherwise>
+ </xsl:choose>
+ </GradientStopCollection>
+ </RadialGradientBrush.GradientStops>
+ <xsl:if test="@gradientTransform">
+ <RadialGradientBrush.Transform>
+ <xsl:apply-templates mode="transform" select="." />
+ </RadialGradientBrush.Transform>
+ </xsl:if>
+ </RadialGradientBrush>
+</xsl:template>
+
+<!-- Generic gradient stops -->
+<xsl:template mode="forward" match="*[name(.) = 'stop']">
+ <GradientStop>
+ <!--xsl:apply-templates mode="stop_opacity" select="." /-->
+ <xsl:apply-templates mode="stop_color" select="." />
+ <xsl:apply-templates mode="offset" select="." />
+ <xsl:apply-templates mode="forward" />
+ </GradientStop>
+</xsl:template>
+
+<!-- Clipping -->
+<xsl:template mode="clip" match="*">
+ <xsl:choose>
+ <xsl:when test="@clip-path and defs/clipPath/path/@d"><xsl:attribute name="Clip"><xsl:value-of select="defs/clipPath/path/@d" /></xsl:attribute></xsl:when>
+ <xsl:when test="@clip-path and starts-with(@clip-path, 'url(#')"><xsl:attribute name="Clip"><xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@clip-path, 'url(#'), ')'), '}')" /></xsl:attribute></xsl:when>
+ <xsl:when test="@style and contains(@style, 'clip-path:url(#')"><xsl:attribute name="Clip"><xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@style, 'url(#'), ')'), '}')" /></xsl:attribute></xsl:when>
+ <xsl:when test="clipPath"><xsl:apply-templates mode="forward" /></xsl:when>
+ </xsl:choose>
+</xsl:template>
+
+<!--
+// Misc templates //
+
+* Object description
+* Id converter
+* Decimal to hexadecimal converter
+* Unit to pixel converter
+* Title and description
+* Misc ignored stuff (markers, patterns, styles)
+* Symbols
+* Use
+* RDF and foreign objects
+* Unknows tags
+-->
+
+<!-- Object description -->
+<xsl:template mode="desc" match="*">
+ <xsl:if test="*[name(.) = 'desc']/text()"><xsl:attribute name="Tag"><xsl:value-of select="*[name(.) = 'desc']/text()" /></xsl:attribute></xsl:if>
+</xsl:template>
+
+<!-- Id converter. Removes "-" from the original id. -->
+<xsl:template mode="id" match="*">
+<xsl:if test="@id">
+ <xsl:attribute name="Name"><xsl:value-of select="translate(@id, '- ', '')" /></xsl:attribute>
+ <!--
+ <xsl:attribute name="x:Key"><xsl:value-of select="translate(@id, '- ', '')" /></xsl:attribute>
+ -->
+</xsl:if>
+</xsl:template>
+
+<!-- Decimal to hexadecimal converter -->
+<xsl:template name="to_hex">
+ <xsl:param name="convert" />
+ <xsl:value-of select="concat(substring('0123456789ABCDEF', 1 + floor(round($convert) div 16), 1), substring('0123456789ABCDEF', 1 + round($convert) mod 16, 1))" />
+</xsl:template>
+
+<!-- Unit to pixel converter
+Values with units (except %) are converted to pixels and rounded.
+Unknown units are kept. -->
<xsl:template name="convert_unit">
<xsl:param name="convert_value" />
<xsl:choose>
@@ -66,12 +411,82 @@ Unknown units are kept. <xsl:when test="contains($convert_value, 'ft')">
<xsl:value-of select="round(translate($convert_value, 'ft', '') * 1080)" />
</xsl:when>
+ <xsl:when test="not(string(number($convert_value))='NaN')">
+ <xsl:value-of select="round($convert_value)" />
+ </xsl:when>
<xsl:otherwise>
<xsl:value-of select="$convert_value" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
+<!-- Title and description
+Blank template. Title is ignored and desc is converted to Tag in the mode="desc" template
+-->
+<xsl:template mode="forward" match="*[name(.) = 'title' or name(.) = 'desc']">
+ <!-- -->
+</xsl:template>
+
+<!-- Misc ignored stuff (markers, patterns, styles) -->
+<xsl:template mode="forward" match="*[name(.) = 'marker' or name(.) = 'pattern' or name(.) = 'style']">
+ <!-- -->
+</xsl:template>
+
+<!-- Symbols -->
+<xsl:template mode="forward" match="*[name(.) = 'symbol']">
+ <Style>
+ <xsl:if test="@id"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
+ <Canvas>
+ <xsl:apply-templates mode="forward" />
+ </Canvas>
+ </Style>
+</xsl:template>
+
+<!-- Use -->
+<xsl:template mode="forward" match="*[name(.) = 'use']">
+ <Canvas>
+ <xsl:if test="@xlink:href"><xsl:attribute name="Style"><xsl:value-of select="@xlink:href" /></xsl:attribute></xsl:if>
+ <!--xsl:apply-templates mode="transform" select="." /-->
+ <xsl:apply-templates mode="forward" />
+ </Canvas>
+</xsl:template>
+
+<!-- RDF and foreign objects -->
+<xsl:template mode="forward" match="rdf:RDF | *[name(.) = 'foreignObject']">
+ <!-- -->
+</xsl:template>
+
+<!-- Unknown tags -->
+<xsl:template match="*">
+<xsl:comment><xsl:value-of select="concat('Unknown tag: ', name(.))" /></xsl:comment>
+</xsl:template>
+
+
+<!--
+// Colors and patterns //
+
+* Generic color template
+* Fill
+* Fill opacity
+* Fill rule
+* Generic fill template
+* Stroke
+* Stroke opacity
+* Generic stroke template
+* Stroke width
+* Stroke mitterlimit
+* Stroke dasharray
+* Stroke dashoffset
+* Linejoin SVG to XAML converter
+* Stroke linejoin
+* Linecap SVG to XAML converter
+* Stroke linecap
+* Gradient stop
+* Gradient stop opacity
+* Gradient stop offset
+-->
+
+<!-- Generic color template -->
<xsl:template name="template_color">
<xsl:param name="colorspec" />
<xsl:param name="opacityspec" />
@@ -117,6 +532,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!-- Fill -->
<xsl:template mode="fill" match="*">
<xsl:choose>
<xsl:when test="@fill and starts-with(@fill, 'url(#')"><xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@fill, 'url(#'), ')'), '}')" /></xsl:when>
@@ -135,6 +551,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!-- Fill opacity -->
<xsl:template mode="fill_opacity" match="*">
<xsl:choose>
<xsl:when test="@fill-opacity"><xsl:value-of select="@fill-opacity" /></xsl:when>
@@ -149,6 +566,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!-- Fill rule -->
<xsl:template mode="fill_rule" match="*">
<xsl:choose>
<xsl:when test="@fill-rule and (@fill-rule = 'nonzero' or @fill-rule = 'evenodd')"><xsl:attribute name="FillRule"><xsl:value-of select="@fill-rule" /></xsl:attribute></xsl:when>
@@ -166,6 +584,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!-- Generic fill template -->
<xsl:template mode="template_fill" match="*">
<xsl:variable name="fill"><xsl:apply-templates mode="fill" select="." /></xsl:variable>
<xsl:variable name="fill_opacity"><xsl:apply-templates mode="fill_opacity" select="." /></xsl:variable>
@@ -186,6 +605,7 @@ Unknown units are kept. </xsl:if>
</xsl:template>
+<!-- Stroke -->
<xsl:template mode="stroke" match="*">
<xsl:choose>
<xsl:when test="@stroke and starts-with(@stroke, 'url(#')"><xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@stroke, 'url(#'), ')'), '}')" /></xsl:when>
@@ -204,6 +624,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!-- Stroke opacity -->
<xsl:template mode="stroke_opacity" match="*">
<xsl:choose>
<xsl:when test="@stroke-opacity"><xsl:value-of select="@stroke-opacity" /></xsl:when>
@@ -218,6 +639,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!-- Generic stroke template -->
<xsl:template mode="template_stroke" match="*">
<xsl:variable name="stroke"><xsl:apply-templates mode="stroke" select="." /></xsl:variable>
<xsl:variable name="stroke_opacity"><xsl:apply-templates mode="stroke_opacity" select="." /></xsl:variable>
@@ -231,6 +653,7 @@ Unknown units are kept. </xsl:if>
</xsl:template>
+<!-- Stroke width -->
<xsl:template mode="stroke_width" match="*">
<xsl:choose>
<xsl:when test="@stroke-width">
@@ -252,6 +675,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!-- Stroke miterlimit -->
<xsl:template mode="stroke_miterlimit" match="*">
<xsl:choose>
<xsl:when test="@stroke-miterlimit"><xsl:attribute name="StrokeMiterLimit"><xsl:value-of select="@stroke-miterlimit" /></xsl:attribute></xsl:when>
@@ -268,6 +692,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!-- Stroke dasharray -->
<xsl:template mode="stroke_dasharray" match="*">
<!-- stroke-dasharray="10,30,20,30" becomes StrokeDashArray="1 3 2 3" ?? -->
<xsl:choose>
@@ -285,6 +710,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!-- Stroke dashoffset -->
<xsl:template mode="stroke_dashoffset" match="*">
<xsl:choose>
<xsl:when test="@stroke-dashoffset"><xsl:attribute name="StrokeDashOffset"><xsl:value-of select="@stroke-dashoffset" /></xsl:attribute></xsl:when>
@@ -301,6 +727,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!-- Linejoin SVG to XAML converter -->
<xsl:template name="linejoin_svg_to_xaml">
<xsl:param name="linejoin" />
<xsl:choose>
@@ -310,6 +737,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!-- Stroke linejoin -->
<xsl:template mode="stroke_linejoin" match="*">
<xsl:choose>
<xsl:when test="@stroke-miterlimit">
@@ -333,6 +761,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!-- Linecap SVG to XAML converter -->
<xsl:template name="linecap_svg_to_xaml">
<xsl:param name="linecap" />
<xsl:choose>
@@ -342,6 +771,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!-- Stroke linecap -->
<xsl:template mode="stroke_linecap" match="*">
<xsl:choose>
<xsl:when test="@stroke-linecap">
@@ -379,20 +809,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
-<xsl:template mode="resources" match="*">
- <!-- should be in-depth -->
- <xsl:if test="ancestor::*[name(.) = 'defs']"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
-</xsl:template>
-
-<xsl:template mode="desc" match="*">
- <xsl:if test="*[name(.) = 'desc']/text()"><xsl:attribute name="Tag"><xsl:value-of select="*[name(.) = 'desc']/text()" /></xsl:attribute></xsl:if>
-</xsl:template>
-
-<xsl:template name="to_hex">
- <xsl:param name="convert" />
- <xsl:value-of select="concat(substring('0123456789ABCDEF', 1 + floor(round($convert) div 16), 1), substring('0123456789ABCDEF', 1 + round($convert) mod 16, 1))" />
-</xsl:template>
-
+<!-- Gradient stops -->
<xsl:template mode="stop_color" match="*">
<xsl:variable name="Opacity">
<xsl:choose>
@@ -441,6 +858,7 @@ Unknown units are kept. </xsl:attribute>
</xsl:template>
+<!-- Gradient stop opacity -->
<xsl:template mode="stop_opacity" match="*">
<xsl:choose>
<xsl:when test="@stop-opacity"><xsl:attribute name="Opacity"><xsl:value-of select="@stop-opacity" /></xsl:attribute></xsl:when>
@@ -457,6 +875,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!-- Gradient stop offset -->
<xsl:template mode="offset" match="*">
<xsl:choose>
<xsl:when test="@offset">
@@ -481,6 +900,17 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!--
+// Transforms //
+All the matrix, translate, rotate... stuff.
+Fixme: XAML transforms don't show the same result as SVG ones with the same values.
+
+* Parse transform
+* Apply transform
+* Apply transform v2
+-->
+
+<!-- Parse transform -->
<xsl:template name="parse_transform">
<xsl:param name="input" />
<xsl:choose>
@@ -544,6 +974,7 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!-- Apply transform -->
<xsl:template mode="transform" match="*">
<xsl:param name="mapped_type" />
<xsl:if test="@transform or @gradientTransform">
@@ -583,108 +1014,8 @@ Unknown units are kept. </xsl:if>
</xsl:template>
-<xsl:template mode="clip" match="*">
- <xsl:choose>
- <xsl:when test="@clip-path and defs/clipPath/path/@d"><xsl:attribute name="Clip"><xsl:value-of select="defs/clipPath/path/@d" /></xsl:attribute></xsl:when>
- <xsl:when test="@clip-path and starts-with(@clip-path, 'url(#')"><xsl:attribute name="Clip"><xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@clip-path, 'url(#'), ')'), '}')" /></xsl:attribute></xsl:when>
- <xsl:when test="@style and contains(@style, 'clip-path:url(#')"><xsl:attribute name="Clip"><xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@style, 'url(#'), ')'), '}')" /></xsl:attribute></xsl:when>
- <xsl:when test="clipPath"><xsl:apply-templates mode="forward" /></xsl:when>
- </xsl:choose>
-</xsl:template>
-
-<xsl:template mode="id" match="*">
-<xsl:if test="@id">
- <xsl:attribute name="Name"><xsl:value-of select="translate(@id, '- ', '')" /></xsl:attribute>
- <!--
- <xsl:attribute name="x:Key"><xsl:value-of select="translate(@id, '- ', '')" /></xsl:attribute>
- -->
-</xsl:if>
-</xsl:template>
-
-<xsl:template match="/">
- <xsl:choose>
- <xsl:when test="$silverlight_compatible = 1">
- <xsl:apply-templates mode="forward" />
- </xsl:when>
- <xsl:otherwise>
- <Viewbox Stretch="Uniform">
- <xsl:apply-templates mode="forward" />
- </Viewbox>
- </xsl:otherwise>
- </xsl:choose>
-</xsl:template>
-
-<xsl:template mode="forward" match="defs">
- <xsl:apply-templates mode="forward" />
-</xsl:template>
-
-<xsl:template mode="forward" match="*[name(.) = 'svg' or name(.) = 'g']">
- <xsl:choose>
- <xsl:when test="name(.) = 'svg' or @transform or @viewBox or @id or @clip-path or (@style and contains(@style, 'clip-path:url(#')) or (@width and not(contains(@width, '%'))) or @x or @y or (@height and not(contains(@height, '%'))) or *[name(.) = 'linearGradient' or name(.) = 'radialGradient' or name(.) = 'defs' or name(.) = 'clipPath']">
- <Canvas>
- <xsl:apply-templates mode="id" select="." />
- <!--
- <xsl:apply-templates mode="clip" select="." />
- -->
- <xsl:if test="@style and contains(@style, 'display:none')"><xsl:attribute name="Visibility">Collapsed</xsl:attribute></xsl:if>
- <xsl:if test="@style and contains(@style, 'opacity:')">
- <xsl:attribute name="Opacity">
- <xsl:choose>
- <xsl:when test="contains(substring-after(@style, 'opacity:'), ';')"><xsl:value-of select="substring-before(substring-after(@style, 'opacity:'), ';')" /></xsl:when>
- <xsl:otherwise><xsl:value-of select="substring-after(@style, 'opacity:')" /></xsl:otherwise>
- </xsl:choose>
- </xsl:attribute>
- </xsl:if>
- <xsl:if test="@width and not(contains(@width, '%'))">
- <xsl:attribute name="Width">
- <xsl:call-template name="convert_unit">
- <xsl:with-param name="convert_value" select="@width" />
- </xsl:call-template>
- </xsl:attribute></xsl:if>
- <xsl:if test="@height and not(contains(@height, '%'))">
- <xsl:attribute name="Height">
- <xsl:call-template name="convert_unit">
- <xsl:with-param name="convert_value" select="@height" />
- </xsl:call-template>
- </xsl:attribute></xsl:if>
- <xsl:if test="@x"><xsl:attribute name="Canvas.Left"><xsl:value-of select="@x" /></xsl:attribute></xsl:if>
- <xsl:if test="@y"><xsl:attribute name="Canvas.Top"><xsl:value-of select="@y" /></xsl:attribute></xsl:if>
- <xsl:if test="@viewBox">
- <xsl:variable name="viewBox"><xsl:value-of select="normalize-space(translate(@viewBox, ',', ' '))" /></xsl:variable>
- <xsl:attribute name="Width"><xsl:value-of select="substring-before(substring-after(substring-after($viewBox, ' '), ' '), ' ')" /></xsl:attribute>
- <xsl:attribute name="Height"><xsl:value-of select="substring-after(substring-after(substring-after($viewBox, ' '), ' '), ' ')" /></xsl:attribute>
- <Canvas.RenderTransform>
- <TranslateTransform>
- <xsl:attribute name="X"><xsl:value-of select="-number(substring-before($viewBox, ' '))" /></xsl:attribute>
- <xsl:attribute name="Y"><xsl:value-of select="-number(substring-before(substring-after($viewBox, ' '), ' '))" /></xsl:attribute>
- </TranslateTransform>
- </Canvas.RenderTransform>
- </xsl:if>
- <xsl:if test="@transform">
- <Canvas>
- <Canvas.RenderTransform>
- <TransformGroup><xsl:apply-templates mode="transform" select="." /></TransformGroup>
- </Canvas.RenderTransform>
- <xsl:apply-templates mode="forward" select="*" />
- </Canvas>
- </xsl:if>
-
- <xsl:if test="*[name(.) = 'linearGradient' or name(.) = 'radialGradient' or name(.) = 'defs' or name(.) = 'clipPath']">
- <Canvas.Resources>
- <xsl:apply-templates mode="forward" select="*[name(.) = 'linearGradient' or name(.) = 'radialGradient' or name(.) = 'defs' or name(.) = 'clipPath']" />
- </Canvas.Resources>
- </xsl:if>
- <xsl:if test="not(@transform)">
- <xsl:apply-templates mode="forward" select="*[name(.) != 'linearGradient' and name(.) != 'radialGradient' and name(.) != 'defs' and name(.) != 'clipPath']" />
- </xsl:if>
- </Canvas>
- </xsl:when>
- <xsl:when test="not(@transform)">
- <xsl:apply-templates mode="forward" select="*" />
- </xsl:when>
- </xsl:choose>
-</xsl:template>
-
+<!-- Apply transform v2
+Fixme: is this template still in use? -->
<xsl:template mode="transform2" match="*">
<xsl:choose>
<xsl:when test="@transform">
@@ -701,13 +1032,35 @@ Unknown units are kept. </xsl:choose>
</xsl:template>
+<!--
+// Objects //
+
+* Image
+* Text
+* Lines
+* Rectangle
+* Polygon
+* Polyline
+* Path
+* Ellipse
+* Circle
+-->
+
+<!-- Image -->
<xsl:template mode="forward" match="*[name(.) = 'image']">
<Image>
<xsl:apply-templates mode="id" select="." />
- <xsl:if test="@x"><xsl:attribute name="Canvas.Left"><xsl:value-of select="@x" /></xsl:attribute></xsl:if>
- <xsl:if test="@y"><xsl:attribute name="Canvas.Top"><xsl:value-of select="@y" /></xsl:attribute></xsl:if>
+ <xsl:if test="@x"><xsl:attribute name="Canvas.Left">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@x" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
+ <xsl:if test="@y"><xsl:attribute name="Canvas.Top">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@y" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
<xsl:apply-templates mode="desc" select="." />
-
<xsl:apply-templates mode="clip" select="." />
<xsl:if test="@xlink:href"><xsl:attribute name="Source"><xsl:value-of select="@xlink:href" /></xsl:attribute></xsl:if>
<xsl:if test="@width"><xsl:attribute name="Width">
@@ -728,6 +1081,7 @@ Unknown units are kept. </Image>
</xsl:template>
+<!-- Text -->
<xsl:template mode="forward" match="*[name(.) = 'text']">
<TextBlock>
<xsl:if test="@font-size"><xsl:attribute name="FontSize"><xsl:value-of select="@font-size" /></xsl:attribute></xsl:if>
@@ -809,12 +1163,19 @@ Unknown units are kept. <xsl:with-param name="convert_value" select="@height" />
</xsl:call-template>
</xsl:attribute></xsl:if>
- <xsl:if test="@x"><xsl:attribute name="Canvas.Left"><xsl:value-of select="@x" /></xsl:attribute></xsl:if>
- <xsl:if test="@y"><xsl:attribute name="Canvas.Top"><xsl:value-of select="@y" /></xsl:attribute></xsl:if>
+ <xsl:if test="@x"><xsl:attribute name="Canvas.Left">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@x" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
+ <xsl:if test="@y"><xsl:attribute name="Canvas.Top">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@y" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
<xsl:apply-templates mode="id" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="desc" select="." />
-
<xsl:apply-templates mode="clip" select="." />
<!--xsl:apply-templates mode="transform" select="." /-->
<!--xsl:apply-templates mode="forward" /-->
@@ -822,187 +1183,8 @@ Unknown units are kept. <xsl:if test="*[name(.) = 'tspan']/text()"><xsl:value-of select="*[name(.) = 'tspan']/text()" /></xsl:if>
</TextBlock>
</xsl:template>
-
-<xsl:template mode="forward" match="*[name(.) = 'title' or name(.) = 'desc']">
- <!-- -->
-</xsl:template>
-
-<xsl:template mode="forward" match="*[name(.) = 'marker' or name(.) = 'pattern' or name(.) = 'style']">
- <!-- -->
-</xsl:template>
-
-<xsl:template mode="forward" match="*[name(.) = 'symbol']">
- <Style>
- <xsl:if test="@id"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
- <Canvas>
- <xsl:apply-templates mode="forward" />
- </Canvas>
- </Style>
-</xsl:template>
-
-<xsl:template mode="forward" match="*[name(.) = 'use']">
- <Canvas>
- <xsl:if test="@xlink:href"><xsl:attribute name="Style"><xsl:value-of select="@xlink:href" /></xsl:attribute></xsl:if>
- <!--xsl:apply-templates mode="transform" select="." /-->
- <xsl:apply-templates mode="forward" />
- </Canvas>
-</xsl:template>
-
-<xsl:template mode="forward" match="rdf:RDF | *[name(.) = 'foreignObject']">
- <!-- -->
-</xsl:template>
-
-<xsl:template match="*">
-<xsl:comment><xsl:value-of select="concat('Unknown tag: ', name(.))" /></xsl:comment>
-</xsl:template>
-
-<!-- BRUSHES -->
-
-<xsl:template mode="forward" match="*[name(.) = 'linearGradient']">
- <LinearGradientBrush>
- <xsl:if test="@id"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
- <xsl:attribute name="MappingMode">
- <xsl:choose>
- <xsl:when test="@gradientUnits = 'userSpaceOnUse' ">Absolute</xsl:when>
- <xsl:otherwise>RelativeToBoundingBox</xsl:otherwise>
- </xsl:choose>
- </xsl:attribute>
- <xsl:if test="@spreadMethod">
- <xsl:attribute name="SpreadMethod">
- <xsl:choose>
- <xsl:when test="@spreadMethod = 'pad'">Pad</xsl:when>
- <xsl:when test="@spreadMethod = 'reflect'">Reflect</xsl:when>
- <xsl:when test="@spreadMethod = 'repeat'">Repeat</xsl:when>
- </xsl:choose>
- </xsl:attribute>
- </xsl:if>
- <xsl:choose>
- <xsl:when test="@x1 and @y1 and @x2 and @y2">
- <xsl:choose>
- <xsl:when test="contains(@x1, '%') and contains(@y1, '%')">
- <xsl:attribute name="StartPoint"><xsl:value-of select="concat(substring-before(@x1, '%') div 100, ',', substring-before(@y1,'%') div 100)" /></xsl:attribute>
- </xsl:when>
- <xsl:otherwise>
- <xsl:attribute name="StartPoint"><xsl:value-of select="concat(@x1, ',', @y1)" /></xsl:attribute>
- </xsl:otherwise>
- </xsl:choose>
- <xsl:choose>
- <xsl:when test="contains(@x2, '%') and contains(@y2, '%')">
- <xsl:attribute name="EndPoint"><xsl:value-of select="concat(substring-before(@x2, '%') div 100, ',', substring-before(@y2,'%') div 100)" /></xsl:attribute>
- </xsl:when>
- <xsl:otherwise>
- <xsl:attribute name="EndPoint"><xsl:value-of select="concat(@x2, ',', @y2)" /></xsl:attribute>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:when>
- <xsl:otherwise>
- <xsl:attribute name="StartPoint"><xsl:value-of select="'0,0'" /></xsl:attribute>
- <xsl:attribute name="EndPoint"><xsl:value-of select="'1,1'" /></xsl:attribute>
- </xsl:otherwise>
- </xsl:choose>
- <LinearGradientBrush.GradientStops>
- <GradientStopCollection>
- <xsl:choose>
- <xsl:when test="@xlink:href">
- <xsl:variable name="reference_id" select="@xlink:href" />
- <xsl:apply-templates mode="forward" select="//*[name(.) = 'linearGradient' and $reference_id = concat('#', @id)]/*" />
- </xsl:when>
- <xsl:otherwise><xsl:apply-templates mode="forward" /></xsl:otherwise>
- </xsl:choose>
- </GradientStopCollection>
- </LinearGradientBrush.GradientStops>
- <xsl:if test="@gradientTransform">
- <LinearGradientBrush.Transform>
- <xsl:apply-templates mode="transform" select="." />
- </LinearGradientBrush.Transform>
- </xsl:if>
- </LinearGradientBrush>
-</xsl:template>
-
-<xsl:template mode="forward" match="*[name(.) = 'radialGradient']">
- <RadialGradientBrush>
- <xsl:if test="@id"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
- <xsl:attribute name="MappingMode">
- <xsl:choose>
- <xsl:when test="@gradientUnits = 'userSpaceOnUse' ">Absolute</xsl:when>
- <xsl:otherwise>RelativeToBoundingBox</xsl:otherwise>
- </xsl:choose>
- </xsl:attribute>
- <xsl:if test="@spreadMethod">
- <xsl:attribute name="SpreadMethod">
- <xsl:choose>
- <xsl:when test="@spreadMethod = 'pad'">Pad</xsl:when>
- <xsl:when test="@spreadMethod = 'reflect'">Reflect</xsl:when>
- <xsl:when test="@spreadMethod = 'repeat'">Repeat</xsl:when>
- </xsl:choose>
- </xsl:attribute>
- </xsl:if>
- <xsl:if test="@cx and @cy">
- <xsl:attribute name="Center">
- <xsl:choose>
- <xsl:when test="contains(@cx, '%') and contains(@cy, '%')">
- <xsl:value-of select="concat(number(substring-before(@cx, '%')) div 100, ',', number(substring-before(@cy, '%')) div 100)" />
- </xsl:when>
- <xsl:otherwise>
- <xsl:value-of select="concat(@cx, ',', @cy)" />
- </xsl:otherwise>
- </xsl:choose>
- </xsl:attribute>
- </xsl:if>
- <xsl:if test="@fx and @fy">
- <xsl:attribute name="GradientOrigin">
- <xsl:choose>
- <xsl:when test="contains(@fx, '%') and contains(@fy, '%')">
- <xsl:value-of select="concat(number(substring-before(@fx, '%')) div 100, ',', number(substring-before(@fy, '%')) div 100)" />
- </xsl:when>
- <xsl:otherwise>
- <xsl:value-of select="concat(@fx, ',', @fy)" />
- </xsl:otherwise>
- </xsl:choose>
- </xsl:attribute>
- </xsl:if>
- <xsl:if test="@r">
- <xsl:choose>
- <xsl:when test="contains(@r, '%')">
- <xsl:attribute name="RadiusX"><xsl:value-of select="number(substring-before(@r, '%')) div 100" /></xsl:attribute>
- <xsl:attribute name="RadiusY"><xsl:value-of select="number(substring-before(@r, '%')) div 100" /></xsl:attribute>
- </xsl:when>
- <xsl:otherwise>
- <xsl:attribute name="RadiusX"><xsl:value-of select="@r" /></xsl:attribute>
- <xsl:attribute name="RadiusY"><xsl:value-of select="@r" /></xsl:attribute>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:if>
- <RadialGradientBrush.GradientStops>
- <GradientStopCollection>
- <xsl:choose>
- <xsl:when test="@xlink:href">
- <xsl:variable name="reference_id" select="@xlink:href" />
- <xsl:apply-templates mode="forward" select="//*[name(.) = 'linearGradient' and $reference_id = concat('#', @id)]/*" />
- </xsl:when>
- <xsl:otherwise><xsl:apply-templates mode="forward" /></xsl:otherwise>
- </xsl:choose>
- </GradientStopCollection>
- </RadialGradientBrush.GradientStops>
- <xsl:if test="@gradientTransform">
- <RadialGradientBrush.Transform>
- <xsl:apply-templates mode="transform" select="." />
- </RadialGradientBrush.Transform>
- </xsl:if>
- </RadialGradientBrush>
-</xsl:template>
-
-<xsl:template mode="forward" match="*[name(.) = 'stop']">
- <GradientStop>
- <!--xsl:apply-templates mode="stop_opacity" select="." /-->
- <xsl:apply-templates mode="stop_color" select="." />
- <xsl:apply-templates mode="offset" select="." />
- <xsl:apply-templates mode="forward" />
- </GradientStop>
-</xsl:template>
-
-<!-- SHAPES -->
-
+
+<!-- Lines -->
<xsl:template mode="forward" match="*[name(.) = 'line']">
<Line>
<xsl:if test="@x1"><xsl:attribute name="X1"><xsl:value-of select="@x1" /></xsl:attribute></xsl:if>
@@ -1018,7 +1200,7 @@ Unknown units are kept. <xsl:apply-templates mode="stroke_dashoffset" select="." />
<xsl:apply-templates mode="stroke_linejoin" select="." />
<xsl:apply-templates mode="stroke_linecap" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="desc" select="." />
<xsl:apply-templates mode="transform" select=".">
@@ -1029,10 +1211,19 @@ Unknown units are kept. </Line>
</xsl:template>
+<!-- Rectangle -->
<xsl:template mode="forward" match="*[name(.) = 'rect']">
<Rectangle>
- <xsl:if test="@x"><xsl:attribute name="Canvas.Left"><xsl:value-of select="@x" /></xsl:attribute></xsl:if>
- <xsl:if test="@y"><xsl:attribute name="Canvas.Top"><xsl:value-of select="@y" /></xsl:attribute></xsl:if>
+ <xsl:if test="@x"><xsl:attribute name="Canvas.Left">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@x" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
+ <xsl:if test="@y"><xsl:attribute name="Canvas.Top">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@y" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
<xsl:if test="@width"><xsl:attribute name="Width">
<xsl:call-template name="convert_unit">
<xsl:with-param name="convert_value" select="@width" />
@@ -1057,11 +1248,9 @@ Unknown units are kept. <xsl:apply-templates mode="stroke_dashoffset" select="." />
<xsl:apply-templates mode="stroke_linejoin" select="." />
<xsl:apply-templates mode="stroke_linecap" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="resources" select="." />
-
<xsl:apply-templates mode="desc" select="." />
-
<xsl:apply-templates mode="clip" select="." />
<xsl:apply-templates mode="transform" select=".">
@@ -1072,6 +1261,7 @@ Unknown units are kept. </Rectangle>
</xsl:template>
+<!-- Polygon -->
<xsl:template mode="forward" match="*[name(.) = 'polygon']">
<Polygon>
<xsl:if test="@points"><xsl:attribute name="Points"><xsl:value-of select="@points" /></xsl:attribute></xsl:if>
@@ -1085,7 +1275,7 @@ Unknown units are kept. <xsl:apply-templates mode="stroke_dashoffset" select="." />
<xsl:apply-templates mode="stroke_linejoin" select="." />
<xsl:apply-templates mode="stroke_linecap" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="desc" select="." />
<xsl:apply-templates mode="transform" select=".">
@@ -1096,6 +1286,7 @@ Unknown units are kept. </Polygon>
</xsl:template>
+<!-- Polyline -->
<xsl:template mode="forward" match="*[name(.) = 'polyline']">
<Polyline>
<xsl:if test="@points"><xsl:attribute name="Points"><xsl:value-of select="@points" /></xsl:attribute></xsl:if>
@@ -1109,7 +1300,7 @@ Unknown units are kept. <xsl:apply-templates mode="stroke_dashoffset" select="." />
<xsl:apply-templates mode="stroke_linejoin" select="." />
<xsl:apply-templates mode="stroke_linecap" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="desc" select="." />
<xsl:apply-templates mode="transform" select=".">
@@ -1120,6 +1311,7 @@ Unknown units are kept. </Polyline>
</xsl:template>
+<!-- Path -->
<xsl:template mode="forward" match="*[name(.) = 'path']">
<Path>
<xsl:apply-templates mode="id" select="." />
@@ -1131,7 +1323,7 @@ Unknown units are kept. <xsl:apply-templates mode="stroke_dashoffset" select="." />
<xsl:apply-templates mode="stroke_linejoin" select="." />
<xsl:apply-templates mode="stroke_linecap" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="desc" select="." />
<xsl:if test="@d">
@@ -1162,6 +1354,7 @@ Unknown units are kept. </Path>
</xsl:template>
+<!-- Ellipse -->
<xsl:template mode="forward" match="*[name(.) = 'ellipse']">
<Ellipse>
<xsl:variable name="cx">
@@ -1193,9 +1386,8 @@ Unknown units are kept. <xsl:apply-templates mode="stroke_dashoffset" select="." />
<xsl:apply-templates mode="stroke_linejoin" select="." />
<xsl:apply-templates mode="stroke_linecap" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="desc" select="." />
-
<xsl:apply-templates mode="clip" select="." />
<xsl:apply-templates mode="transform" select=".">
@@ -1206,6 +1398,7 @@ Unknown units are kept. </Ellipse>
</xsl:template>
+<!-- Circle -->
<xsl:template mode="forward" match="*[name(.) = 'circle']">
<Ellipse>
<xsl:variable name="cx">
@@ -1221,9 +1414,9 @@ Unknown units are kept. </xsl:choose>
</xsl:variable>
<xsl:if test="@r">
- <xsl:attribute name="Canvas.Left"><xsl:value-of select='format-number($cx - @r, "#.#")' /></xsl:attribute> - <xsl:attribute name="Canvas.Top"><xsl:value-of select='format-number($cy - @r, "#.#")' /></xsl:attribute> - <xsl:attribute name="Width"><xsl:value-of select='format-number(2 * @r, "#.#")' /></xsl:attribute> + <xsl:attribute name="Canvas.Left"><xsl:value-of select='format-number($cx - @r, "#.#")' /></xsl:attribute>
+ <xsl:attribute name="Canvas.Top"><xsl:value-of select='format-number($cy - @r, "#.#")' /></xsl:attribute>
+ <xsl:attribute name="Width"><xsl:value-of select='format-number(2 * @r, "#.#")' /></xsl:attribute>
<xsl:attribute name="Height"><xsl:value-of select='format-number(2 * @r, "#.#")' /></xsl:attribute>
</xsl:if>
<xsl:apply-templates mode="id" select="." />
@@ -1235,9 +1428,8 @@ Unknown units are kept. <xsl:apply-templates mode="stroke_dashoffset" select="." />
<xsl:apply-templates mode="stroke_linejoin" select="." />
<xsl:apply-templates mode="stroke_linecap" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="desc" select="." />
-
<xsl:apply-templates mode="clip" select="." />
<xsl:apply-templates mode="transform" select=".">
@@ -1248,10 +1440,19 @@ Unknown units are kept. </Ellipse>
</xsl:template>
+<!--
+// Geometry //
+* Generic clip path template
+* Geometry for circle
+* Geometry for rectangle
+-->
+
+<!-- Generic clip path template -->
<xsl:template mode="forward" match="*[name(.) = 'clipPath']">
<xsl:apply-templates mode="geometry" />
</xsl:template>
+<!-- Geometry for circle -->
<xsl:template mode="geometry" match="*[name(.) = 'circle']">
<EllipseGeometry>
<xsl:if test="../@id"><xsl:attribute name="x:Key"><xsl:value-of select="../@id" /></xsl:attribute></xsl:if>
@@ -1263,6 +1464,7 @@ Unknown units are kept. </EllipseGeometry>
</xsl:template>
+<!-- Geometry for rectangle -->
<xsl:template mode="geometry" match="*[name(.) = 'rect']">
<RectangleGeometry>
<xsl:if test="../@id"><xsl:attribute name="x:Key"><xsl:value-of select="../@id" /></xsl:attribute></xsl:if>
diff --git a/share/extensions/test/inkweb-debug.js b/share/extensions/test/inkweb-debug.js index 3dd3a4541..a4035503a 100644 --- a/share/extensions/test/inkweb-debug.js +++ b/share/extensions/test/inkweb-debug.js @@ -253,7 +253,7 @@ InkWeb.__logSequence = function ( msg ) { InkWeb.logToString = function (conf) { /* Show the log in a formated string. - ** conf atributes: + ** conf attributes: ** format: a string to format the log itens. ** formatError: to format the error log itens. ** sep: the log itens separator string. diff --git a/share/extensions/uniconv-ext.py b/share/extensions/uniconv-ext.py index 3b248d8b8..d3d69546c 100644 --- a/share/extensions/uniconv-ext.py +++ b/share/extensions/uniconv-ext.py @@ -23,6 +23,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import sys from run_command import run +import gettext +_ = gettext.gettext cmd = None @@ -49,11 +51,11 @@ if cmd == None: import imp imp.find_module("uniconvertor") except ImportError: - sys.stderr.write('You need to install the UniConvertor software.\n'+\ + sys.stderr.write(_('You need to install the UniConvertor software.\n'+\ 'For GNU/Linux: install the package python-uniconvertor.\n'+\ 'For Windows: download it from\n'+\ 'http://sk1project.org/modules.php?name=Products&product=uniconvertor\n'+\ - 'and install into your Inkscape\'s Python location\n') + 'and install into your Inkscape\'s Python location\n')) sys.exit(1) cmd = 'python -c "import uniconvertor"' diff --git a/share/extensions/uniconv_output.py b/share/extensions/uniconv_output.py index 9cdac7fc2..f7746c2f4 100644 --- a/share/extensions/uniconv_output.py +++ b/share/extensions/uniconv_output.py @@ -31,6 +31,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import os import sys import tempfile +import gettext +_ = gettext.gettext def run(command_format, prog_name, uniconv_format): outfile = tempfile.mktemp(uniconv_format) diff --git a/share/extensions/webslicer_create_group.inx b/share/extensions/webslicer_create_group.inx index 6352c2066..2238a0901 100644 --- a/share/extensions/webslicer_create_group.inx +++ b/share/extensions/webslicer_create_group.inx @@ -6,8 +6,8 @@ <dependency type="executable" location="extensions">webslicer_create_group.py</dependency> <dependency type="executable" location="extensions">inkex.py</dependency> <_param name="about" type="description">Layout Group is only about to help a better code generation (if you need it). To use this, you must to select some "Slicer rectangles" first.</_param> - <param name="html-id" type="string" _gui-text="HTML id atribute:"></param> - <param name="html-class" type="string" _gui-text="HTML class atribute:"></param> + <param name="html-id" type="string" _gui-text="HTML id attribute:"></param> + <param name="html-class" type="string" _gui-text="HTML class attribute:"></param> <param name="width-unity" type="enum" _gui-text="Width unit:"> <_item value="px">Pixel (fixed)</_item> <_item value="percent">Percent (relative to parent size)</_item> diff --git a/share/extensions/webslicer_create_rect.inx b/share/extensions/webslicer_create_rect.inx index 651dda668..acf440cca 100644 --- a/share/extensions/webslicer_create_rect.inx +++ b/share/extensions/webslicer_create_rect.inx @@ -13,7 +13,8 @@ </param> <param name="dpi" type="float" min="1" max="9999" _gui-text="DPI:">90</param> <param name="dimension" type="string" _gui-text="Force Dimension:"></param> - <_param name="help-dimension1" type="description">Force Dimension must be set as "<width>x<height>"</_param> +<!-- i18n. Description duplicated in a fake value attribute in order to make it translatable --> + <param name="help-dimension1" type="description" _value="Force Dimension must be set as <width>x<height>">Force Dimension must be set as <width>x<height></param> <_param name="help-dimension2" type="description">If set, this will replace DPI.</_param> <param name="bg-color" type="string" _gui-text="Background color:"></param> <param name="tab" type="notebook"> @@ -31,8 +32,8 @@ <param name="palette-size" type="int" min="2" max="256" _gui-text="Palette size:">256</param> </page> <page name="tabHTML" gui-text="HTML"> - <param name="html-id" type="string" _gui-text="HTML id atribute:"></param> - <param name="html-class" type="string" _gui-text="HTML class atribute:"></param> + <param name="html-id" type="string" _gui-text="HTML id attribute:"></param> + <param name="html-class" type="string" _gui-text="HTML class attribute:"></param> <_param name="help-gif" type="description">Options for HTML export</_param> <param name="layout-disposition" type="enum" _gui-text="Layout disposition:"> <_item value="bg-el-norepeat">Positioned html block element with the image as Background</_item> diff --git a/share/extensions/webslicer_export.inx b/share/extensions/webslicer_export.inx index eeb753add..3a7324370 100644 --- a/share/extensions/webslicer_export.inx +++ b/share/extensions/webslicer_export.inx @@ -5,8 +5,8 @@ <dependency type="executable" location="extensions">webslicer_effect.py</dependency> <dependency type="executable" location="extensions">webslicer_export.py</dependency> <dependency type="executable" location="extensions">inkex.py</dependency> - <_param name="about" type="description">All sliced images, and optionally code, will be generated as you had configured and saved to one directory.</_param> - <param name="dir" type="string" _gui-text="Directory path to export:"></param> + <_param name="about" type="description">All sliced images, and optionaly code, will be generated as you had configured and saved to one directory.</_param> + <param name="dir" type="string" _gui-text="Directory path to export"></param> <param name="create-dir" type="boolean" _gui-text="Create directory, if it does not exists">false</param> <param name="with-code" type="boolean" _gui-text="With HTML and CSS">true</param> <effect needs-live-preview="false"> diff --git a/share/extensions/webslicer_export.py b/share/extensions/webslicer_export.py index 6b4ef75d8..78de70a37 100755 --- a/share/extensions/webslicer_export.py +++ b/share/extensions/webslicer_export.py @@ -21,6 +21,7 @@ from webslicer_effect import * import inkex import gettext import os.path +import subprocess import tempfile import commands @@ -45,7 +46,11 @@ class WebSlicer_Export(WebSlicer_Effect): dest="with_code", help="") - def effect(self): + + svgNS = '{http://www.w3.org/2000/svg}' + + + def validate_inputs(self): # The user must supply a directory to export: if is_empty( self.options.dir ): inkex.errormsg(_('You must to give a directory to export the slices.')) @@ -66,32 +71,78 @@ class WebSlicer_Export(WebSlicer_Effect): else: inkex.errormsg(_('The directory "%s" does not exists.') % self.options.dir) return - # Create HTML and CSS files, if the user wants: - if self.options.with_code: - try: - self.html = open(os.path.join(self.options.dir,'layout.html'), 'w') - self.css = open(os.path.join(self.options.dir,'style.css'), 'w') - self.html.write('Only a test yet\n\n') - self.css.write('/* Only a test yet */\n\n') - except Exception as e: - inkex.errormsg( _('Can\'t create code files.') ) - inkex.errormsg( _('Error: %s') % e ) - return {'error':'Can\'t create code files.'} + self.unique_html_id( self.get_slicer_layer() ) + return None + + + _html_ids = [] + def unique_html_id(self, el): + for child in el.getchildren(): + if child.tag in [ self.svgNS+'rect', self.svgNS+'path', + self.svgNS+'circle', self.svgNS+'g' ]: + conf = self.get_el_conf(child) + if conf['html-id'] in self._html_ids: + inkex.errormsg( + _('You have more than one element with "%s" html-id.') % + conf['html-id'] ) + n = 2 + while (conf['html-id']+"-"+str(n)) in self._html_ids: n += 1 + conf['html-id'] += "-"+str(n) + self._html_ids.append( conf['html-id'] ) + self.save_conf( conf, child ) + self.unique_html_id( child ) + + + def test_if_has_imagemagick(self): + (status, output) = commands.getstatusoutput('convert --version') + self.has_magick = ( status == 0 and 'ImageMagick' in output ) + + + def effect(self): + self.test_if_has_imagemagick() + error = self.validate_inputs() + if error: return error + # Register the basic CSS code: + self.reg_css( 'body', 'text-align', 'center' ) # Create the temporary SVG with invisible Slicer layer to export image pieces self.create_the_temporary_svg() # Start what we really want! self.export_chids_of( self.get_slicer_layer() ) - # Close the HTML and CSS files: + # Write the HTML and CSS files if asked for: if self.options.with_code: - self.html.close() - self.css.write( self.css_code() ) - self.css.close() + self.make_html_file() + self.make_css_file() # Delete the temporary SVG with invisible Slicer layer self.delete_the_temporary_svg() #TODO: prevent inkex to return svg code to update Inkscape - svgNS = '{http://www.w3.org/2000/svg}' + def make_html_file(self): + f = open(os.path.join(self.options.dir,'layout.html'), 'w') + f.write( + '<html>\n<head>\n' +\ + ' <title>Web Layout Testing</title>\n' +\ + ' <style type="text/css" media="screen">\n' +\ + ' @import url("style.css")\n' +\ + ' </style>\n' +\ + '</head>\n<body>\n' +\ + self.html_code() +\ + ' <p style="position:absolute; bottom:10px">\n' +\ + ' This HTML code is not done to the web. <br/>\n' +\ + ' The automatic HTML and CSS code are only a helper.' +\ + '</p>\n</body>\n</html>' ) + f.close() + + + def make_css_file(self): + f = open(os.path.join(self.options.dir,'style.css'), 'w') + f.write( + '/*\n' +\ + '** This CSS code is not done to the web.\n' +\ + '** The automatic HTML and CSS code are only a helper.\n' +\ + '*/\n' +\ + self.css_code() ) + f.close() def create_the_temporary_svg(self): @@ -109,7 +160,7 @@ class WebSlicer_Export(WebSlicer_Effect): noid_element_count = 0 def get_el_conf(self, el): - desc = el.find('{http://www.w3.org/2000/svg}desc') + desc = el.find(self.svgNS+'desc') conf = {} if desc is None: desc = inkex.etree.SubElement(el, 'desc') @@ -129,6 +180,15 @@ class WebSlicer_Export(WebSlicer_Effect): return conf + def save_conf(self, conf, el): + desc = el.find('{http://www.w3.org/2000/svg}desc') + if desc is not None: + conf_a = [] + for k in conf: + conf_a.append( k+' : '+conf[k] ) + desc.text = "\n".join(conf_a) + + def export_chids_of(self, parent): parent_id = self.get_el_conf( parent )['html-id'] for el in parent.getchildren(): @@ -145,66 +205,217 @@ class WebSlicer_Export(WebSlicer_Effect): def register_group_code(self, group, conf): - #inkex.errormsg( 'group CSS and HTML' ) - self.html.write( '<div id="G">\n' ) - for att in conf: - self.html.write( ' <!-- {att} : {val} -->\n'.format(att=att, val=conf[att]) ) + self.reg_html('div', group) + selec = '#'+conf['html-id'] + self.reg_css( selec, 'position', 'absolute' ) + geometry = self.get_relative_el_geometry(group) + self.reg_css( selec, 'top', str(int(geometry['y']))+'px' ) + self.reg_css( selec, 'left', str(int(geometry['x']))+'px' ) + self.reg_css( selec, 'width', str(int(geometry['w']))+'px' ) + self.reg_css( selec, 'height', str(int(geometry['h']))+'px' ) self.export_chids_of( group ) - self.html.write( '</div><!-- end id="G" -->\n' ) - def register_unity_code(self, el, conf, parent_id): - #inkex.errormsg( 'unity CSS and HTML' ) - css_selector = '#'+conf['html-id'] + def __validate_slice_conf(self, conf): if not 'layout-disposition' in conf: conf['layout-disposition'] = 'bg-el-norepeat' - if conf['layout-disposition'][0:9] == 'bg-parent': - if parent_id == '#body#': - css_selector = 'body' - else: - css_selector = '#'+parent_id - for att in conf: - self.html.write( '<!-- bg {att} : {val} -->\n'.format(att=att, val=conf[att]) ) - else: - self.html.write( '<div id="image">\n' ) - for att in conf: - self.html.write( ' <!-- {att} : {val} -->\n'.format(att=att, val=conf[att]) ) - self.html.write( '</div><!-- end id="image" -->\n' ) - self.reg_css( css_selector, 'background', - 'url("%s")' % self.img_name(el, conf) ) + if not 'layout-position-anchor' in conf: + conf['layout-position-anchor'] = 'mc' + return conf + + + def register_unity_code(self, el, conf, parent_id): + conf = self.__validate_slice_conf(conf) + css_selector = '#'+conf['html-id'] + bg_repeat = 'no-repeat' + img_name = self.img_name(el, conf) + if conf['layout-disposition'][0:2] == 'bg': + if conf['layout-disposition'][0:9] == 'bg-parent': + if parent_id == '#body#': + css_selector = 'body' + else: + css_selector = '#'+parent_id + if conf['layout-disposition'] == 'bg-parent-repeat': + bg_repeat = 'repeat' + if conf['layout-disposition'] == 'bg-parent-repeat-x': + bg_repeat = 'repeat-x' + if conf['layout-disposition'] == 'bg-parent-repeat-y': + bg_repeat = 'repeat-y' + lay_anchor = conf['layout-position-anchor'] + if lay_anchor == 'tl': lay_anchor = 'top left' + if lay_anchor == 'tc': lay_anchor = 'top center' + if lay_anchor == 'tr': lay_anchor = 'top right' + if lay_anchor == 'ml': lay_anchor = 'middle left' + if lay_anchor == 'mc': lay_anchor = 'middle center' + if lay_anchor == 'mr': lay_anchor = 'middle right' + if lay_anchor == 'bl': lay_anchor = 'bottom left' + if lay_anchor == 'bc': lay_anchor = 'bottom center' + if lay_anchor == 'br': lay_anchor = 'bottom right' + self.reg_css( css_selector, 'background', + 'url("%s") %s %s' % (img_name, bg_repeat, lay_anchor) ) + else: # conf['layout-disposition'][0:9] == 'bg-el...' + self.reg_html('div', el) + self.reg_css( css_selector, 'background', + 'url("%s") %s' % (img_name, 'no-repeat') ) + self.reg_css( css_selector, 'position', 'absolute' ) + geo = self.get_relative_el_geometry(el,True) + self.reg_css( css_selector, 'top', geo['y'] ) + self.reg_css( css_selector, 'left', geo['x'] ) + self.reg_css( css_selector, 'width', geo['w'] ) + self.reg_css( css_selector, 'height', geo['h'] ) + else: # conf['layout-disposition'] == 'img...' + self.reg_html('img', el) + if conf['layout-disposition'] == 'img-pos': + self.reg_css( css_selector, 'position', 'absolute' ) + geo = self.get_relative_el_geometry(el) + self.reg_css( css_selector, 'left', str(geo['x'])+'px' ) + self.reg_css( css_selector, 'top', str(geo['y'])+'px' ) + if conf['layout-disposition'] == 'img-float-left': + self.reg_css( css_selector, 'float', 'right' ) + if conf['layout-disposition'] == 'img-float-right': + self.reg_css( css_selector, 'float', 'right' ) + + + el_geo = { } + def register_all_els_geometry(self): + ink_cmm = 'inkscape --query-all '+self.tmp_svg + (status, output) = commands.getstatusoutput( ink_cmm ) + self.el_geo = { } + if status == 0: + for el in output.split('\n'): + el = el.split(',') + if len(el) == 5: + self.el_geo[el[0]] = { 'x':float(el[1]), 'y':float(el[2]), + 'w':float(el[3]), 'h':float(el[4]) } + doc_w = inkex.unittouu( self.document.getroot().get('width') ) + doc_h = inkex.unittouu( self.document.getroot().get('height') ) + self.el_geo['webslicer-layer'] = { 'x':0, 'y':0, 'w':doc_w, 'h':doc_h } + + + def get_relative_el_geometry(self, el, value_to_css=False): + # This method return a dictionary with x, y, w and h keys. + # All values are float, if value_to_css is False, otherwise + # that is a string ended with "px". The x and y values are + # relative to parent position. + if not self.el_geo: self.register_all_els_geometry() + parent = self.getParentNode(el) + geometry = self.el_geo[el.attrib['id']] + geometry['x'] -= self.el_geo[parent.attrib['id']]['x'] + geometry['y'] -= self.el_geo[parent.attrib['id']]['y'] + if value_to_css: + for k in geometry: geometry[k] = str(int(geometry[k]))+'px' + return geometry def img_name(self, el, conf): - return el.attrib['id']+'.png' + return el.attrib['id']+'.'+conf['format'] + def export_img(self, el, conf): + if not self.has_magick: + inkex.errormsg(_('You must install the ImageMagick to get JPG and GIF.')) + conf['format'] = 'png' + img_name = os.path.join( self.options.dir, self.img_name(el, conf) ) + img_name_png = img_name + if conf['format'] != 'png': + img_name_png = img_name+'.png' + opts = '' + if 'bg-color' in conf: opts += ' -b "'+conf['bg-color']+'" -y 1' + if 'dpi' in conf: opts += ' -d '+conf['dpi'] + if 'dimension' in conf: + dim = conf['dimension'].split('x') + opts += ' -w '+dim[0]+' -h '+dim[1] (status, output) = commands.getstatusoutput( - "inkscape -i '%s' -e '%s' '%s'" % ( - el.attrib['id'], - os.path.join( self.options.dir, self.img_name(el, conf) ), - self.tmp_svg + 'inkscape %s -i "%s" -e "%s" "%s"' % ( + opts, el.attrib['id'], img_name_png, self.tmp_svg ) ) - #inkex.errormsg( status ) - #inkex.errormsg( output ) + if conf['format'] != 'png': + opts = '' + if conf['format'] == 'jpg': + opts += ' -quality '+str(conf['quality']) + if conf['format'] == 'gif': + if conf['gif-type'] == 'grayscale': + opts += ' -type Grayscale' + else: + opts += ' -type Palette' + if conf['palette-size'] < 256: + opts += ' -colors '+str(conf['palette-size']) + (status, output) = commands.getstatusoutput( + 'convert "%s" %s "%s"' % ( img_name_png, opts, img_name ) + ) + if status != 0: + inkex.errormsg('Upss... ImageMagick error: '+output) + os.remove(img_name_png) - _css = {} + _html = {} + def reg_html(self, el_tag, el): + parent = self.getParentNode( el ) + parent_id = self.get_el_conf( parent )['html-id'] + if parent == self.get_slicer_layer(): parent_id = 'body' + conf = self.get_el_conf( el ) + el_id = conf['html-id'] + if 'html-class' in conf: + el_class = conf['html-class'] + else: + el_class = '' + if not parent_id in self._html: self._html[parent_id] = [] + self._html[parent_id].append({ 'tag':el_tag, 'id':el_id, 'class':el_class }) + + + def html_code(self, parent='body', ident=' '): + #inkex.errormsg( self._html ) + if not parent in self._html: return '' + code = '' + for el in self._html[parent]: + child_code = self.html_code(el['id'], ident+' ') + tag_class = '' + if el['class'] != '': tag_class = ' class="'+el['class']+'"' + if el['tag'] == 'img': + code += ident+'<img id="'+el['id']+'"'+tag_class+\ + ' src="'+self.img_name(el, self.get_el_conf(el))+'"/>\n' + else: + code += ident+'<'+el['tag']+' id="'+el['id']+'"'+tag_class+'>\n' + if child_code: + code += child_code + else: + code += ident+' Element '+el['id']+'\n' + code += ident+'</'+el['tag']+'><!-- id="'+el['id']+'" -->\n' + return code + + + _css = [] def reg_css(self, selector, att, val): - if not selector in self._css: self._css[selector] = {} - if not att in self._css[selector]: self._css[selector][att] = [] - self._css[selector][att].append( val ) + pos = i = -1 + for s in self._css: + i += 1 + if s['selector'] == selector: pos = i + if pos == -1: + pos = i + 1 + self._css.append({ 'selector':selector, 'atts':{} }) + if not att in self._css[pos]['atts']: self._css[pos]['atts'][att] = [] + self._css[pos]['atts'][att].append( val ) + def css_code(self): code = '' - for selector in self._css: - code += '\n'+selector+' {\n' - for att in self._css[selector]: - code += ' '+ att +': '+ (', '.join(self._css[selector][att])) +';\n' + for s in self._css: + code += '\n'+s['selector']+' {\n' + for att in s['atts']: + val = s['atts'][att] + if att == 'background' and len(val) > 1: + code += ' /* the next attribute needs a CSS3 enabled browser */\n' + code += ' '+ att +': '+ (', '.join(val)) +';\n' code += '}\n' return code + def output(self): + # Cancel document serialization to stdout + pass + + if __name__ == '__main__': e = WebSlicer_Export() e.affect() |
