Sync with SVG-edit

This commit is contained in:
Jacques Distler 2010-02-05 23:17:34 -06:00
parent de3008d3e4
commit bad5beec29
8 changed files with 386 additions and 60 deletions

View file

@ -127,7 +127,7 @@
{"id": "tool_zoom", "title": "ज़ूम उपकरण"},
{"id": "zoom", "title": "बदलें स्तर ज़ूम"},
{"id": "zoomLabel", "textContent": "जूम:"},
{"id": "sidepanel_handle", "textContent": "परतें", "title": "दायें/बाएं घसीट कर आकार बदलें"},
{"id": "sidepanel_handle","textContent":"प तें","title":"दायें/बाएं घसीट कर आकार बदलें"},
{
"js_strings": {
"QerrorsRevertToSource": "आपके एस.वी.जी. स्रोत में त्रुटियों थी.\nक्या आप मूल एस.वी.जी स्रोत पर वापिस जाना चाहते हैं?",

View file

@ -87,12 +87,12 @@
{"id":"tool_aligntop","title":"頂端對齊"},
{"id":"tool_bold","title":"粗體"},
{"id":"tool_circle","title":"圓"},
{"id":"tool_clear","title":"清空圖像"},
{"id":"tool_clear","textContent":"清空圖像"},
{"id":"tool_clone","title":"複製"},
{"id":"tool_clone_multi","title":"複製所選元素"},
{"id":"tool_delete","title":"刪除"},
{"id":"tool_delete_multi","title":"刪除所選元素"},
{"id":"tool_docprops","title":"文件屬性"},
{"id":"tool_docprops","textContent":"文件屬性"},
{"id":"tool_docprops_cancel","textContent":"取消"},
{"id":"tool_docprops_save","textContent":"保存"},
{"id":"tool_ellipse","title":"橢圓"},
@ -108,12 +108,12 @@
{"id":"tool_node_clone","title":"增加節點"},
{"id":"tool_node_delete","title":"刪除節點"},
{"id":"tool_node_link","title":"將控制點連起來"},
{"id":"tool_open","title":"打開圖像"},
{"id":"tool_open","textContent":"打開圖像"},
{"id":"tool_path","title":"路徑工具"},
{"id":"tool_rect","title":"矩形"},
{"id":"tool_redo","title":"復原"},
{"id":"tool_reorient","title":"調整路徑"},
{"id":"tool_save","title":"保存圖像"},
{"id":"tool_save","textContent":"保存圖像"},
{"id":"tool_select","title":"選擇工具"},
{"id":"tool_source","title":"編輯SVG原始碼"},
{"id":"tool_source_cancel","textContent":"取消"},
@ -125,8 +125,6 @@
{"id":"tool_ungroup","title":"取消群組"},
{"id":"tool_wireframe","title":"框線模式(只瀏覽線條)"},
{"id":"tool_zoom","title":"縮放工具"},
{"id":"tools_ellipse_show","title":"橢圓/圓工具"},
{"id":"tools_rect_show","title":"矩形/方形工具"},
{"id":"zoom","title":"更改縮放級別"},
{"id":"zoomLabel","textContent":"調整頁面大小:"},
{"id":"sidepanel_handle","textContent":"圖層","title":"拖拉以改變側邊面板的大小"},

View file

@ -179,7 +179,6 @@ function svg_edit_setup() {
// https://bugzilla.mozilla.org/show_bug.cgi?id=308590
var saveHandler = function(window,svg) {
window.opener.postMessage(svg, window.location.protocol + '//' + window.location.host);
// window.open("data:image/svg+xml;base64," + Utils.encode64(svg));
};
// called when we've selected a different element
@ -383,12 +382,23 @@ function svg_edit_setup() {
}
var extAdded = function(window, ext) {
if("buttons" in ext) {
var cb_called = false;
var runCallback = function() {
if(ext.callback && !cb_called) {
cb_called = true;
ext.callback();
}
}
if(ext.buttons) {
var fallback_obj = {},
placement_obj = {},
svgicons = ext.svgicons;
var holders = {};
// Add buttons given by extension
$.each(ext.buttons, function(i, btn) {
@ -515,10 +525,70 @@ function svg_edit_setup() {
setIconSize('m');
setIconSize(old);
}
runCallback();
}
});
}
if(ext.context_tools) {
$.each(ext.context_tools, function(i, tool) {
// Add select tool
var cont_id = tool.container_id?(' id="' + tool.container_id + '"'):"";
var panel = $('#' + tool.panel);
if(!panel.length) {
panel = $('<div>', {id: tool.panel}).appendTo("#tools_top");
}
// TODO: Allow support for other types, or adding to existing tool
switch (tool.type) {
case 'select':
var html = '<label' + cont_id + '>'
+ '<select id="' + tool.id + '">';
$.each(tool.options, function(val, text) {
var sel = (val == tool.defval) ? " selected":"";
html += '<option value="'+val+'"' + sel + '>' + text + '</option>';
});
html += "</select></label>";
// Creates the tool, hides & adds it, returns the select element
var sel = $(html).appendTo(panel).find('select');
$.each(tool.events, function(evt, func) {
$(sel).bind(evt, func);
});
break;
case 'input':
var html = '<label' + cont_id + '">'
+ '<span id="' + tool.id + '_label">'
+ tool.label + ':</span>'
+ '<input id="' + tool.id + '" title="' + tool.title
+ '" size="' + (tool.size || "4") + '" value="' + (tool.defval || "") + '" type="text"/></label>'
// Creates the tool, hides & adds it, returns the select element
// Add to given tool.panel
var inp = $(html).appendTo(panel).find('input');
if(tool.spindata) {
inp.SpinButton(tool.spindata);
}
if(tool.events) {
$.each(tool.events, function(evt, func) {
$(sel).bind(evt, func);
});
}
break;
default:
break;
}
});
}
runCallback();
};
var getPaint = function(color, opac) {
@ -1865,7 +1935,7 @@ function svg_edit_setup() {
var bNoFill = (svgCanvas.getFillColor() == 'none');
var bNoStroke = (svgCanvas.getStrokeColor() == 'none');
var buttonsNeedingStroke = [ '#tool_fhpath', '#tool_line' ];
var buttonsNeedingFillAndStroke = [ '#tools_rect .tool_button', '#tools_ellipse .tool_button', '#tool_text' ];
var buttonsNeedingFillAndStroke = [ '#tools_rect .tool_button', '#tools_ellipse .tool_button', '#tool_text', '#tool_path'];
if (bNoStroke) {
for (index in buttonsNeedingStroke) {
var button = buttonsNeedingStroke[index];
@ -2634,7 +2704,7 @@ function svg_edit_setup() {
updateCanvas(true);
});
// var revnums = "svg-editor.js ($Rev: 1342 $) ";
// var revnums = "svg-editor.js ($Rev: 1347 $) ";
// revnums += svgCanvas.getVersion();
// $('#copyright')[0].setAttribute("title", revnums);
return svgCanvas;

View file

@ -1,4 +1,4 @@
/*
/*
* svgcanvas.js
*
* Licensed under the Apache License, Version 2
@ -984,27 +984,6 @@ function BatchCommand(text) {
return result;
}
this.addExtension = function(name, ext_func) {
if(!(name in extensions)) {
// Provide constants here (or should these be accessed through getSomething()?
var ext = ext_func({
content: svgcontent,
root: svgroot,
call: call,
getNextId: getNextId,
getElem: getElem,
addSvgElementFromJson: addSvgElementFromJson,
selectorManager: selectorManager,
findDefs: findDefs,
recalculateDimensions: recalculateDimensions
});
extensions[name] = ext;
call("extension_added", ext);
} else {
console.log('Cannot add extension "' + name + '", an extension by that name already exists"');
}
};
// This method rounds the incoming value to the nearest value based on the current_zoom
var round = function(val){
return parseInt(val*current_zoom)/current_zoom;
@ -2322,6 +2301,51 @@ function BatchCommand(text) {
return (m.a == 1 && m.b == 0 && m.c == 0 && m.d == 1 && m.e == 0 && m.f == 0);
}
// expects three points to be sent, each point must have an x,y field
// returns an array of two points that are the smoothed
this.smoothControlPoints = function(ct1, ct2, pt) {
// each point must not be the origin
var x1 = ct1.x - pt.x,
y1 = ct1.y - pt.y,
x2 = ct2.x - pt.x,
y2 = ct2.y - pt.y;
if ( (x1 != 0 || y1 != 0) && (x2 != 0 || y2 != 0) ) {
var anglea = Math.atan2(y1,x1),
angleb = Math.atan2(y2,x2),
r1 = Math.sqrt(x1*x1+y1*y1),
r2 = Math.sqrt(x2*x2+y2*y2),
nct1 = svgroot.createSVGPoint(),
nct2 = svgroot.createSVGPoint();
if (anglea < 0) { anglea += 2*Math.PI; }
if (angleb < 0) { angleb += 2*Math.PI; }
var angleBetween = Math.abs(anglea - angleb),
angleDiff = Math.abs(Math.PI - angleBetween)/2;
var new_anglea, new_angleb;
if (anglea - angleb > 0) {
new_anglea = angleBetween < Math.PI ? (anglea + angleDiff) : (anglea - angleDiff);
new_angleb = angleBetween < Math.PI ? (angleb - angleDiff) : (angleb + angleDiff);
}
else {
new_anglea = angleBetween < Math.PI ? (anglea - angleDiff) : (anglea + angleDiff);
new_angleb = angleBetween < Math.PI ? (angleb + angleDiff) : (angleb - angleDiff);
}
// rotate the points
nct1.x = r1 * Math.cos(new_anglea) + pt.x;
nct1.y = r1 * Math.sin(new_anglea) + pt.y;
nct2.x = r2 * Math.cos(new_angleb) + pt.x;
nct2.y = r2 * Math.sin(new_angleb) + pt.y;
return [nct1, nct2];
}
return undefined;
};
var smoothControlPoints = this.smoothControlPoints;
// matrixMultiply() is provided because WebKit didn't implement multiply() correctly
// on the SVGMatrix interface. See https://bugs.webkit.org/show_bug.cgi?id=16062
// This function tries to return a SVGMatrix that is the multiplication m1*m2.
@ -3521,6 +3545,18 @@ function BatchCommand(text) {
var N = points.numberOfItems;
if (N >= 4) {
// loop through every 3 points and convert to a cubic bezier curve segment
//
// NOTE: this is cheating, it means that every 3 points has the potential to
// be a corner instead of treating each point in an equal manner. In general,
// this technique does not look that good.
//
// I am open to better ideas!
//
// Reading:
// - http://www.efg2.com/Lab/Graphics/Jean-YvesQueinecBezierCurves.htm
// - http://www.codeproject.com/KB/graphics/BezierSpline.aspx?msg=2956963
// - http://www.ian-ko.com/ET_GeoWizards/UserGuide/smooth.htm
// - http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-der.html
var curpos = points.getItem(0), prevCtlPt = null;
var d = [];
d.push(["M",curpos.x,",",curpos.y," C"].join(""));
@ -3532,7 +3568,14 @@ function BatchCommand(text) {
// if the previous segment had a control point, we want to smooth out
// the control points on both sides
if (prevCtlPt) {
// TODO: fancy processing here :)
var newpts = smoothControlPoints( prevCtlPt, ct1, curpos );
if (newpts && newpts.length == 2) {
var prevArr = d[d.length-1].split(',');
prevArr[2] = newpts[0].x;
prevArr[3] = newpts[0].y;
d[d.length-1] = prevArr.join(',');
ct1 = newpts[1];
}
}
d.push([ct1.x,ct1.y,ct2.x,ct2.y,end.x,end.y].join(','));
@ -6549,6 +6592,9 @@ function BatchCommand(text) {
// selector if the element is in that array
if ($.inArray(elem, selectedElements) != -1) {
setTimeout(function() {
// Due to element replacement, this element may no longer
// be part of the DOM
if(!elem.parentNode) return;
selectorManager.requestSelector(elem).resize();
},0);
}
@ -7391,7 +7437,7 @@ function BatchCommand(text) {
// Function: getVersion
// Returns a string which describes the revision number of SvgCanvas.
this.getVersion = function() {
return "svgcanvas.js ($Rev: 1341 $)";
return "svgcanvas.js ($Rev: 1349 $)";
};
this.setUiStrings = function(strs) {
@ -7426,6 +7472,78 @@ function BatchCommand(text) {
// return svgdoc.getElementById(id);
}
// Being able to access private methods publicly seems wrong somehow,
// but currently appears to be the best way to allow testing and provide
// access to them to plugins.
this.getPrivateMethods = function() {
return {
addCommandToHistory: addCommandToHistory,
addGradient: addGradient,
addSvgElementFromJson: addSvgElementFromJson,
assignAttributes: assignAttributes,
BatchCommand: BatchCommand,
call: call,
ChangeElementCommand: ChangeElementCommand,
cleanupElement: cleanupElement,
copyElem: copyElem,
ffClone: ffClone,
findDefs: findDefs,
findDuplicateGradient: findDuplicateGradient,
fromXml: fromXml,
getElem: getElem,
getId: getId,
getIntersectionList: getIntersectionList,
getNextId: getNextId,
getPathBBox: getPathBBox,
getUrlFromAttr: getUrlFromAttr,
hasMatrixTransform: hasMatrixTransform,
identifyLayers: identifyLayers,
InsertElementCommand: InsertElementCommand,
isIdentity: isIdentity,
logMatrix: logMatrix,
matrixMultiply: matrixMultiply,
MoveElementCommand: MoveElementCommand,
preventClickDefault: preventClickDefault,
recalculateAllSelectedDimensions: recalculateAllSelectedDimensions,
recalculateDimensions: recalculateDimensions,
remapElement: remapElement,
RemoveElementCommand: RemoveElementCommand,
removeUnusedGrads: removeUnusedGrads,
resetUndoStack: resetUndoStack,
round: round,
runExtensions: runExtensions,
sanitizeSvg: sanitizeSvg,
Selector: Selector,
SelectorManager: SelectorManager,
shortFloat: shortFloat,
svgCanvasToString: svgCanvasToString,
SVGEditTransformList: SVGEditTransformList,
svgToString: svgToString,
toString: toString,
toXml: toXml,
transformBox: transformBox,
transformListToTransform: transformListToTransform,
transformPoint: transformPoint,
transformToObj: transformToObj,
walkTree: walkTree
}
}
this.addExtension = function(name, ext_func) {
if(!(name in extensions)) {
// Provide private vars/funcs here. Is there a better way to do this?
var ext = ext_func($.extend(canvas.getPrivateMethods(), {
svgroot: svgroot,
svgcontent: svgcontent,
selectorManager: selectorManager
}));
extensions[name] = ext;
call("extension_added", ext);
} else {
console.log('Cannot add extension "' + name + '", an extension by that name already exists"');
}
};
// Test support for features/bugs
(function() {
// segList functions (for FF1.5 and 2.0)

View file

@ -9,8 +9,7 @@
$(function() {
svgCanvas.addExtension("Arrows", function(S) {
var svgcontent = S.content,
getElem = S.getElem,
var svgcontent = S.svgcontent,
addElem = S.addSvgElementFromJson,
selElems;
@ -64,7 +63,7 @@ $(function() {
function addMarker(id, type) {
// TODO: Make marker (or use?) per arrow type, since refX can be different
var marker = getElem(id);
var marker = S.getElem(id);
var pathdata = {
se_arrow_fw: {d:"m0,0l10,5l-10,5l5,-5l-5,-5z", refx:8},
@ -104,7 +103,8 @@ $(function() {
marker.setAttribute('refX', data.refx);
}
function setArrow(type) {
function setArrow() {
var type = this.value;
resetMarker();
if(type == "none") {
@ -114,7 +114,6 @@ $(function() {
var fw_id = "se_arrow_fw";
var bk_id = "se_arrow_bk";
// Set marker on element
var id = fw_id;
if(type == "mid_bk") {
@ -134,24 +133,29 @@ $(function() {
S.call("changed", selElems);
}
// Init code
(function() {
var conn_tools = $('<div id="arrow_panel">\
<label><select id="arrow_list">\
<option value="none">No arrow</option>\
<option value="end">----&gt;</option>\
<option value="start">&lt;----</option>\
<option value="both">&lt;---&gt;</option>\
<option value="mid">--&gt;--</option>\
<option value="mid_bk">--&lt;--</option>\
</select></label></div>"').hide().appendTo("#tools_top");
$('#arrow_list').change(function() {
setArrow(this.value);
});
}());
return {
name: "Arrows",
context_tools: [{
type: "select",
panel: "arrow_panel",
title: "Select arrow type",
id: "arrow_list",
options: {
none: "No arrow",
end: "----&gt;",
start: "&lt;----",
both: "&lt;---&gt;",
mid: "--&gt;--",
mid_bk: "--&lt;--"
},
defval: "none",
events: {
change: setArrow
}
}],
callback: function() {
$('#arrow_panel').hide();
},
addLangData: function(lang) {
return {
data: lang_list[lang]
@ -180,4 +184,4 @@ $(function() {
}
};
});
});
});

View file

@ -9,8 +9,8 @@
$(function() {
svgCanvas.addExtension("Connector", function(S) {
var svgcontent = S.content,
svgroot = S.root,
var svgcontent = S.svgcontent,
svgroot = S.svgroot,
getNextId = S.getNextId,
getElem = S.getElem,
addElem = S.addSvgElementFromJson,
@ -104,14 +104,29 @@ $(function() {
svgCanvas.removeFromSelection($(conn_sel).toArray());
}
// Loop through selected elements
while(i--) {
var elem = all_els[i];
if(!elem) continue;
// Element was deleted, so delete connector
var was_deleted = !elem.parentNode;
// Skip connector
if($(elem).data('c_start')) continue;
// Loop through connectors to see if one is connected to the element
connectors.each(function() {
var start = $(this).data("c_start");
var end = $(this).data("c_end");
// Connector found for this element
if(start == elem.id || end == elem.id) {
if(was_deleted) {
$(this).remove();
return;
}
var bb = svgCanvas.getStrokedBBox([elem]);
connections.push({
elem: elem,
@ -138,8 +153,8 @@ $(function() {
var line = conn.connector;
var elem = conn.elem;
var pre = conn.is_start?'start':'end';
var sw = line.getAttribute('stroke-width');
var pre = conn.is_start?'start':'end';
// Update bbox for this element
var bb = svgCanvas.getStrokedBBox([elem]);
@ -458,6 +473,7 @@ $(function() {
se_ns = svgCanvas.getEditorNS(true);
cur_line.setAttributeNS(se_ns, "se:connector", conn_str);
cur_line.setAttribute('class', conn_sel.substr(1));
cur_line.setAttribute('opacity', 1);
svgCanvas.addToSelection([cur_line]);
svgCanvas.moveToBottomSelectedElement();
selManager.requestSelector(cur_line).showGrips(false);
@ -507,11 +523,40 @@ $(function() {
elem.getAttribute("marker-end")
)) {
var start = elem.getAttribute("marker-start");
var mid = elem.getAttribute("marker-mid");
var end = elem.getAttribute("marker-end");
cur_line = elem;
$(elem)
.data("start_off", !!start)
.data("end_off", !!end);
if(elem.tagName == "line" && mid) {
// Convert to polyline to accept mid-arrow
var x1 = elem.getAttribute('x1')-0;
var x2 = elem.getAttribute('x2')-0;
var y1 = elem.getAttribute('y1')-0;
var y2 = elem.getAttribute('y2')-0;
var id = elem.id;
var mid_pt = (' '+((x1+x2)/2)+','+((y1+y2)/2) + ' ');
var pline = addElem({
"element": "polyline",
"attr": {
"points": (x1+','+y1+ mid_pt +x2+','+y2),
"stroke": elem.getAttribute('stroke'),
"stroke-width": elem.getAttribute('stroke-width'),
"marker-mid": mid,
"fill": "none",
"opacity": elem.getAttribute('opacity') || 1
}
});
$(elem).after(pline).remove();
svgCanvas.clearSelection();
pline.id = id;
svgCanvas.addToSelection([pline]);
}
}
updateConnectors();
},

View file

@ -0,0 +1,52 @@
import sys, json, codecs
infile = codecs.open(sys.argv[1], "r", "utf-8")
outfile = codecs.open(sys.argv[1][:-3], "w", "utf-8")
indata = infile.readlines()
look = False
out = "[\n"
js = []
jss = ""
def readfrompos(pos):
global out
global js
if (indata[pos].startswith("#, -x-svg-edit-title")) or (indata[pos].startswith("#, -x-svg-edit-textContent")):
out += '{'
out += '"id": '
out += " ".join(indata[pos+1].split()[1:]) + ", "
out += '"' + line[15:].strip() + '": '
out += " ".join(indata[pos+2].split()[1:])
out += '}'
elif (indata[pos].startswith("#, -x-svg-edit-both")):
out += '{'
out += '"id": '
out += " ".join(indata[pos+1].split()[1:]) + ", "
out += '"textContent": '
out += '"' + " ".join(indata[pos+2].split()[1:]).split('|')[1] + ', '
out += '"title": '
out += " ".join(indata[pos+2].split()[1:]).split('|')[0] + '"'
out += '}'
elif (indata[pos].startswith("#, -x-svg-edit-js_strings")):
js.append((" ".join(indata[pos+1].split()[1:]), " ".join(indata[pos+2].split()[1:])))
for pos, line in enumerate(indata):
if (not look) and (line.startswith('# ---')):
look = True
marker = pos
elif (look) and (line.startswith('#, -x-svg-edit')):
readfrompos(pos)
js.sort()
for j in js:
jss += " %s: %s,\n" % (j[0], j[1])
out += '{\n "js_strings": {\n'
out += str(jss)
out += ' "": ""\n }'
out += "\n}"
out += "\n]"
out = out.replace('}{', '},\n{')
outfile.write(out)

View file

@ -0,0 +1,39 @@
import sys, json, codecs
infile = json.load(codecs.open(sys.argv[1], "r", "utf-8"))
outfile = codecs.open(sys.argv[1] + ".po", "w", "utf-8")
out = []
out.append("""# LANGUAGE FILE FOR SVG-EDIT, AUTOGENERATED BY TOPO.PY
msgid ""
msgstr ""
"Content-Type: text/plain; charset=utf-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
# ---
""")
def printstr(flag, i, s):
out.append('\n')
if flag == '-x-svg-edit-both':
out.append("# Enter the title first, then the contents, seperated by a pipe char (|)\n")
out.append("#, " + flag + '\n')
out.append("msgid \"" + i + "\"" + '\n')
out.append("msgstr \"" + s.replace('\n', '\\n') + "\"" + '\n')
for line in infile:
if line.has_key('title') and line.has_key('textContent'):
printstr('-x-svg-edit-both', line['id'], "|".join(((line['title'], line['textContent']))))
elif line.has_key('title'):
printstr('-x-svg-edit-title', line['id'], line['title'])
elif line.has_key('textContent'):
printstr('-x-svg-edit-textContent', line['id'], line['textContent'])
elif line.has_key('js_strings'):
for i, s in line['js_strings'].items():
printstr('-x-svg-edit-js_strings', i, s)
else:
pass # The line wasn't really a string
outfile.writelines(out)
outfile.close()