File: devguide-23.html

package info (click to toggle)
sketch 0.6.13-1
  • links: PTS
  • area: main
  • in suites: woody
  • size: 5,284 kB
  • ctags: 8,453
  • sloc: python: 34,711; ansic: 16,543; makefile: 83; sh: 26
file content (235 lines) | stat: -rw-r--r-- 8,303 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
<html>
<head>
<title>Developer's Guide: Export Filters
</title>
</head>
<body bgcolor=white text=black link=blue vlink=navy alink=red>
<TABLE WIDTH="100%">
<TR>
<TH ALIGN="left" WIDTH="33%"><img SRC="Images/arrow-left.png" WIDTH="16" HEIGHT="16" ALIGN="top" ALT="Prev"></TH>
<TH ALIGN="center" WIDTH="33%"><img SRC="Images/arrow-up.png" WIDTH="16" HEIGHT="16" ALIGN="top" ALT="Up"></TH>
<TH ALIGN="right" WIDTH="33%"><img SRC="Images/arrow-right.png" WIDTH="16" HEIGHT="16" ALIGN="top" ALT="Next"></TH>
</TR>
<TR>
<TD ALIGN="left"><A HREF="devguide-22.html">Import Filters
</A></TD>
<TD ALIGN="center"><A HREF="devguide-20.html">Plugins
</A></TD>
<TD ALIGN="right"><A HREF="devguide-24.html">Plugin Objects
</A></TD>
</TR>
</TABLE>
<HR NOSHADE>
<H2><FONT face="Helvetica,Arial"><A NAME="N1"></A>Export Filters
</font></H2>

<P>Sketch uses export filters to write documents to files.</P>

<H3><FONT face="Helvetica,Arial"><A NAME="N2"></A>Configuration Variables</font></H3>

<P>For export filter configuration, Sketch uses the following variables:
<DL>
<DT><B><CODE>type</CODE></B><DD>
<P>The type must be <CODE>Export</CODE>. This identifier is defined in the
globals() dictionary.</P>

<DT><B><CODE>tk_file_type</CODE></B><DD>

<DT><B><CODE>unload</CODE> (optional)</B><DD>

<DT><B><CODE>format_name</CODE></B><DD>
<P>These variables have the same meaning as <A HREF="devguide-22.html#N2">in an import filter</A>.</P>

<DT><B><CODE>extensions</CODE></B><DD>
<P>A string with the standard file extension of the export format.
Alternatively this can be a tuple of file extension strings.</P>
<P>Sketch uses the file extension to find the export filter.</P>

<DT><B><CODE>standard_messages</CODE> (optional)</B><DD>
<P>A boolean indicating whether the messages in the plugin are
standard messages that are defined in Sketch's message catalogs.
For third-party plugins this should be omitted.</P>
</DL>
</P>

<H3><FONT face="Helvetica,Arial"><A NAME="N3"></A>External Interface</font></H3>

<P>Given a document object and a filename for the output file, Sketch
does the following to find an export filter and to export the drawing:</P>
<P>
<OL>
<LI>Take the extension of the output filename and find the
export filter that uses that extension.
</LI>
<LI>Load the module of the plugin
</LI>
<LI>Call the module's <CODE>save</CODE> function.</LI>
</OL>
</P>

<H3><FONT face="Helvetica,Arial"><A NAME="N4"></A>The <CODE>save</CODE> function</font></H3>

<P>The function <CODE>save</CODE> is the main entry point for an export plugin. It
is called with four positional parameters <i>document</i>, <i>file</i>,
<i>filename</i> and <i>options</i>.</P>
<P><i>document</i> is the document that is to be saved. <i>file</i> is the
output file object open for writing and <i>filename</i> is the
corresponding filename. Finally, <i>options</i> is a dictionary with
(user specified) options. Currently <i>options</i> is always an empty
dictinary as there is no way yet to specify such options.</P>


<H3><FONT face="Helvetica,Arial"><A NAME="N5"></A>Implementation Strategy</font></H3>

<P>Apart from the <CODE>save</CODE> function entry point Sketch requires very
little from the plugin. The only other thing it expects is that the
plugin traverses the object tree by itself and uses the interface
provided by the <A HREF="devguide-14.html#N2"><tt>Protocols</tt></A> class to distinguish the
different object types.</P>
<P>In the following, the svg export filter serves as the basis for a a
brief outline of this technique.</P>
<P>Both the svg and the ai filter use the same basic approach. They
implement the filter as a class and use an instance of that class to do
the work:</P>
<P>
<table width="100%" cellpadding="10"><tr><td bgcolor="#FFFFD0">
<PRE>
def save(document, file, filename, options = {}):
    saver = SVGSaver(file, filename, document, options)
    saver.Save()
    saver.close()
</PRE>
</td></tr></table>
</P>
<P>The interesting parts happen in the class. The constructor doesn't do
much, it just saves the parameters in instance variables:
<table width="100%" cellpadding="10"><tr><td bgcolor="#FFFFD0">
<PRE>
class SVGSaver:

    def __init__(self, file, filename, document, options):
        self.file = file
        self.filename = filename
        self.document = document
        self.options = options
</PRE>
</td></tr></table>
</P>
<P>The Save method is where the real work begins.
First it writes a kind of file header:
<table width="100%" cellpadding="10"><tr><td bgcolor="#FFFFD0">
<PRE>
    def Save(self):
        self.file.write('&lt;?xml version="1.0" standalone="yes"?&gt;\n')
        left, bottom, right, top = self.document.BoundingRect()
        width = right - left
        height = top - bottom
        self.trafo = Trafo(1, 0, 0, -1, -left, top)
        self.file.write('&lt;svg width="%g" height="%g"' % (width, height))
        self.file.write('>\n')
</PRE>
</td></tr></table>

The document method <CODE>BoundingRect</CODE> returns the bounding rectangle of the
entire document</P>

<P>After the header it loops over all the layers in the document...
<table width="100%" cellpadding="10"><tr><td bgcolor="#FFFFD0">
<PRE>
        for layer in self.document.Layers():
</PRE>
</td></tr></table>

...and if the layer is not a special layer, such as grid or guide layers,
and if the layer is printable...
<table width="100%" cellpadding="10"><tr><td bgcolor="#FFFFD0">
<PRE>
            if not layer.is_SpecialLayer and layer.Printable():
</PRE>
</td></tr></table>

... save the layer as a group:
<table width="100%" cellpadding="10"><tr><td bgcolor="#FFFFD0">
<PRE>
                self.BeginGroup()
                self.save_objects(layer.GetObjects())
                self.EndGroup()
</PRE>
</td></tr></table>

<CODE>GetObjects()</CODE> returns a list of all objects in the layer. All
compend objects have this method. In the SVG filter, the <CODE>BeginGroup</CODE>
and <CODE>EndGroup</CODE> methods write the <CODE>&lt;g&gt;</CODE> and <CODE>&lt;/g&gt;</CODE>
tags that mark groups in SVG.</P>
<P>Finally, the trailer to close the <CODE>&lt;svg&gt;</CODE> tag.
<table width="100%" cellpadding="10"><tr><td bgcolor="#FFFFD0">
<PRE>
        self.file.write('&lt;/svg&gt;')
</PRE>
</td></tr></table>
</P>

<P>The next interesting method is save_objects. It loops over the list of
objects...
<table width="100%" cellpadding="10"><tr><td bgcolor="#FFFFD0">
<PRE>
    def save_objects(self, objects):
        for object in objects:
</PRE>
</td></tr></table>

...and if the object is a compound object...
<table width="100%" cellpadding="10"><tr><td bgcolor="#FFFFD0">
<PRE>
            if object.is_Compound:
</PRE>
</td></tr></table>

...save it as a group
<table width="100%" cellpadding="10"><tr><td bgcolor="#FFFFD0">
<PRE>
                self.BeginGroup()
                self.save_objects(object.GetObjects())
                self.EndGroup()
</PRE>
</td></tr></table>
</P>
<P>otherwise, if the object is a bezier object, a rectangle or an ellipse...
<table width="100%" cellpadding="10"><tr><td bgcolor="#FFFFD0">
<PRE>
            elif object.is_Bezier or object.is_Rectangle or object.is_Ellipse:
</PRE>
</td></tr></table>

...save the object as bezier curves.
<table width="100%" cellpadding="10"><tr><td bgcolor="#FFFFD0">
<PRE>
                self.PolyBezier(object.Paths(), object.Properties(),
                                object.bounding_rect)
</PRE>
</td></tr></table>
</P>
<P>The <CODE>Paths</CODE> method returns a tuple of <A HREF="devguide-8.html">path
objects</A>. <CODE>Properties</CODE> returns an object that has members like
<CODE>fill_pattern</CODE> and <CODE>line_width</CODE> that define the properties of the
object.</P>


<HR NOSHADE>
<TABLE WIDTH="100%">
<TR>
<TD ALIGN="left"><A HREF="devguide-22.html">Import Filters
</A></TD>
<TD ALIGN="center"><A HREF="devguide-20.html">Plugins
</A></TD>
<TD ALIGN="right"><A HREF="devguide-24.html">Plugin Objects
</A></TD>
</TR>
<TR>
<TH ALIGN="left" WIDTH="33%"><img SRC="Images/arrow-left.png" WIDTH="16" HEIGHT="16" ALIGN="top" ALT="Prev"></TH>
<TH ALIGN="center" WIDTH="33%"><img SRC="Images/arrow-up.png" WIDTH="16" HEIGHT="16" ALIGN="top" ALT="Up"></TH>
<TH ALIGN="right" WIDTH="33%"><img SRC="Images/arrow-right.png" WIDTH="16" HEIGHT="16" ALIGN="top" ALT="Next"></TH>
</TR>
</TABLE>
</body>
</html>