source: Dev/trunk/src/client/dojox/gfx/VectorText.js @ 529

Last change on this file since 529 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 24.9 KB
Line 
1define(["dojo/_base/lang","dojo/_base/declare","dojo/_base/array", "dojo/_base/loader" /* dojo._getText */,
2            "dojo/_base/xhr","./_base", "dojox/xml/DomParser", "dojox/html/metrics","./matrix"],
3function (lang,declare,arr,loader,xhr,gfx,xmlDomParser,HtmlMetrics,Matrix){
4        var _getText = function(url){
5                var result;
6                xhr.get({url:url, sync:true, load:function(text){ // Note synchronous!
7                        result = text;
8                }});
9                return result;
10        };
11
12         lang.getObject("dojox.gfx.VectorText", true);
13
14         lang.mixin(gfx, {
15                  vectorFontFitting: {
16                          NONE: 0,      //              render text according to passed size.
17                          FLOW: 1,              //              render text based on the passed width and size
18                          FIT: 2                        //              render text based on a passed viewbox.
19                  },
20                  defaultVectorText: {
21                          type:"vectortext", x:0, y:0, width: null, height: null,
22                          text: "", align: "start", decoration: "none", fitting: 0,     //              vectorFontFitting.NONE
23                          leading: 1.5  //              in ems.
24                  },
25                  defaultVectorFont: {
26                          type:"vectorfont", size: "10pt", family: null
27                  },
28                  _vectorFontCache: {},
29                  _svgFontCache: {},
30                  getVectorFont: function(/* String */url){
31                          if(gfx._vectorFontCache[url]){
32                                  return gfx._vectorFontCache[url];
33                          }
34                          return new gfx.VectorFont(url);
35                  }
36         });
37
38        // TODO: Make up your mind.   Module is called VectorText but it's creating and returning a global called VectorFont
39
40        return declare("dojox.gfx.VectorFont", null, {
41                // summary:
42                //              An implementation of the SVG Font 1.1 spec, using dojox/gfx.
43                //
44                //              Basic interface:
45                //      |       var f = new gfx.Font(url|string);
46                //      |       surface||group.createVectorText(text)
47                //      |       .setFill(fill)
48                //      |       .setStroke(stroke)
49                //      |       .setFont(fontStyleObject);
50                //
51                //              The arguments passed to createVectorText are the same as you would
52                //              pass to surface||group.createText; the difference is that this
53                //              is entirely renderer-agnostic, and the return value is a subclass
54                //              of dojox/gfx.Group.
55                //
56                //              Note also that the "defaultText" object is slightly different:
57                //              { type:"vectortext", x:0, y:0, width:null, height: null,
58                //              text: "", align: "start", decoration: "none" }
59                //
60                //              ...as well as the "defaultVectorFont" object:
61                //              { type:"vectorfont", size:"10pt" }
62                //
63                //              The reason for this should be obvious: most of the style for the font is defined
64                //              by the font object itself.
65                //
66                //              Note that this will only render IF and WHEN you set the font.
67
68                _entityRe: /&(quot|apos|lt|gt|amp|#x[^;]+|#\d+);/g,
69                _decodeEntitySequence: function(str){
70                        //              unescape the unicode sequences
71
72                        //              nothing to decode
73                        if(!str.match(this._entityRe)){ return; }  // undefined
74                        var xmlEntityMap = {
75                                amp:"&", apos:"'", quot:'"', lt:"<", gt:">"
76                        };
77
78                        //              we have at least one encoded entity.
79                        var r, tmp="";
80                        while((r=this._entityRe.exec(str))!==null){
81                                if(r[1].charAt(1)=="x"){
82                                        tmp += String.fromCharCode(parseInt(r[1].slice(2), 16));
83                                }
84                                else if(!isNaN(parseInt(r[1].slice(1),10))){
85                                        tmp += String.fromCharCode(parseInt(r[1].slice(1), 10));
86                                }
87                                else {
88                                        tmp += xmlEntityMap[r[1]] || "";
89                                }
90                        }
91                        return tmp;     //              String
92                },
93                _parse: function(/* String */svg, /* String */url){
94                        // summary:
95                        //              Take the loaded SVG Font definition file and convert the info
96                        //              into things we can use. The SVG Font definition must follow
97                        //              the SVG 1.1 Font specification.
98                        var doc = gfx._svgFontCache[url]||xmlDomParser.parse(svg);
99
100                        //              font information
101                        var f = doc.documentElement.byName("font")[0], face = doc.documentElement.byName("font-face")[0];
102                        var unitsPerEm = parseFloat(face.getAttribute("units-per-em")||1000, 10);
103                        var advance = {
104                                x: parseFloat(f.getAttribute("horiz-adv-x"), 10),
105                                y: parseFloat(f.getAttribute("vert-adv-y")||0, 10)
106                        };
107                        if(!advance.y){
108                                advance.y = unitsPerEm;
109                        }
110
111                        var origin = {
112                                horiz: {
113                                        x: parseFloat(f.getAttribute("horiz-origin-x")||0, 10),
114                                        y: parseFloat(f.getAttribute("horiz-origin-y")||0, 10)
115                                },
116                                vert: {
117                                        x: parseFloat(f.getAttribute("vert-origin-x")||0, 10),
118                                        y: parseFloat(f.getAttribute("vert-origin-y")||0, 10)
119                                }
120                        };
121
122                        //              face information
123                        var family = face.getAttribute("font-family"),
124                                style = face.getAttribute("font-style")||"all",
125                                variant = face.getAttribute("font-variant")||"normal",
126                                weight = face.getAttribute("font-weight")||"all",
127                                stretch = face.getAttribute("font-stretch")||"normal",
128
129                                //              additional info, may not be needed
130                                range = face.getAttribute("unicode-range")||"U+0-10FFFF",
131                                panose = face.getAttribute("panose-1") || "0 0 0 0 0 0 0 0 0 0",
132                                capHeight = face.getAttribute("cap-height"),
133                                ascent = parseFloat(face.getAttribute("ascent")||(unitsPerEm-origin.vert.y), 10),
134                                descent = parseFloat(face.getAttribute("descent")||origin.vert.y, 10),
135                                baseline = {};
136
137                        //              check for font-face-src/font-face-name
138                        var name = family;
139                        if(face.byName("font-face-name")[0]){
140                                name = face.byName("font-face-name")[0].getAttribute("name");
141                        }
142
143                        //              see if this is cached already, and if so, forget the rest of the parsing.
144                        if(gfx._vectorFontCache[name]){ return; }
145
146                        //              get any provided baseline alignment offsets.
147                        arr.forEach(["alphabetic", "ideographic", "mathematical", "hanging" ], function(attr){
148                                var a = face.getAttribute(attr);
149                                if(a !== null /* be explicit, might be 0 */){
150                                        baseline[attr] = parseFloat(a, 10);
151                                }
152                        });
153
154                /*
155                        //              TODO: decoration hinting.
156                        var decoration = { };
157                        arr.forEach(["underline", "strikethrough", "overline"], function(type){
158                                if(face.getAttribute(type+"-position")!=null){
159                                        decoration[type]={ };
160                                }
161                        });
162                */
163
164                        //              missing glyph info
165                        var missing = parseFloat(doc.documentElement.byName("missing-glyph")[0].getAttribute("horiz-adv-x")||advance.x, 10);
166
167                        //              glyph information
168                        var glyphs = {}, glyphsByName={}, g=doc.documentElement.byName("glyph");
169                        arr.forEach(g, function(node){
170                                //              we are going to assume the following:
171                                //              1) we have the unicode attribute
172                                //              2) we have the name attribute
173                                //              3) we have the horiz-adv-x and d attributes.
174                                var code = node.getAttribute("unicode"),
175                                        name = node.getAttribute("glyph-name"),
176                                        xAdv = parseFloat(node.getAttribute("horiz-adv-x")||advance.x, 10),
177                                        path = node.getAttribute("d");
178
179                                //              unescape the unicode sequences
180                                if(code.match(this._entityRe)){
181                                        code = this._decodeEntitySequence(code);
182                                }
183
184                                // build our glyph objects
185                                var o = { code: code, name: name, xAdvance: xAdv, path: path };
186                                glyphs[code]=o;
187                                glyphsByName[name]=o;
188                        }, this);
189
190                        //              now the fun part: look for kerning pairs.
191                        var hkern=doc.documentElement.byName("hkern");
192                        arr.forEach(hkern, function(node, i){
193                                var k = -parseInt(node.getAttribute("k"),10);
194                                //              look for either a code or a name
195                                var u1=node.getAttribute("u1"),
196                                        g1=node.getAttribute("g1"),
197                                        u2=node.getAttribute("u2"),
198                                        g2=node.getAttribute("g2"),
199                                        gl;
200
201                                if(u1){
202                                        //              the first of the pair is a sequence of unicode characters.
203                                        //              TODO: deal with unicode ranges and mulitple characters.
204                                        u1 = this._decodeEntitySequence(u1);
205                                        if(glyphs[u1]){
206                                                gl = glyphs[u1];
207                                        }
208                                } else {
209                                        //              we are referring to a name.
210                                        //              TODO: deal with multiple names
211                                        if(glyphsByName[g1]){
212                                                gl = glyphsByName[g1];
213                                        }
214                                }
215
216                                if(gl){
217                                        if(!gl.kern){ gl.kern = {}; }
218                                        if(u2){
219                                                //              see the notes above.
220                                                u2 = this._decodeEntitySequence(u2);
221                                                gl.kern[u2] = { x: k };
222                                        } else {
223                                                if(glyphsByName[g2]){
224                                                        gl.kern[glyphsByName[g2].code] = { x: k };
225                                                }
226                                        }
227                                }
228                        }, this);
229
230                        //              pop the final definition in the font cache.
231                        lang.mixin(this, {
232                                family: family,
233                                name: name,
234                                style: style,
235                                variant: variant,
236                                weight: weight,
237                                stretch: stretch,
238                                range: range,
239                                viewbox: { width: unitsPerEm, height: unitsPerEm },
240                                origin: origin,
241                                advance: lang.mixin(advance, {
242                                        missing:{ x: missing, y: missing }
243                                }),
244                                ascent: ascent,
245                                descent: descent,
246                                baseline: baseline,
247                                glyphs: glyphs
248                        });
249
250                        //              cache the parsed font
251                        gfx._vectorFontCache[name] = this;
252                        gfx._vectorFontCache[url] = this;
253                        if(name!=family && !gfx._vectorFontCache[family]){
254                                gfx._vectorFontCache[family] = this;
255                        }
256
257                        //              cache the doc
258                        if(!gfx._svgFontCache[url]){
259                                gfx._svgFontCache[url]=doc;
260                        }
261                },
262                _clean: function(){
263                        // summary:
264                        //              Clean off all of the given mixin parameters.
265                        var name = this.name, family = this.family;
266                        arr.forEach(["family","name","style","variant",
267                                "weight","stretch","range","viewbox",
268                                "origin","advance","ascent","descent",
269                                "baseline","glyphs"], function(prop){
270                                        try{ delete this[prop]; } catch(e) { }
271                        }, this);
272
273                        //              try to pull out of the font cache.
274                        if(gfx._vectorFontCache[name]){
275                                delete gfx._vectorFontCache[name];
276                        }
277                        if(gfx._vectorFontCache[family]){
278                                delete gfx._vectorFontCache[family];
279                        }
280                        return this;
281                },
282
283                constructor: function(/* String|dojo._Url */url){
284                        // summary:
285                        //              Create this font object based on the SVG Font definition at url.
286                        // url:
287                        //              An url pointing to the SVG Font definition.
288                        this._defaultLeading = 1.5;
289                        if(url!==undefined){
290                                this.load(url);
291                        }
292                },
293                load: function(/* String|dojo._Url */url){
294                        // summary:
295                        //              Load the passed SVG and send it to the parser for parsing.
296                        // url:
297                        //              The svg to parse.
298                        this.onLoadBegin(url.toString());
299                        this._parse(
300                                gfx._svgFontCache[url.toString()]||_getText(url.toString()),
301                                url.toString()
302                        );
303                        this.onLoad(this);
304                        return this;    //              dojox/gfx.VectorFont
305                },
306                initialized: function(){
307                        // summary:
308                        //              Return if we've loaded a font def, and the parsing was successful.
309                        return (this.glyphs!==null);    //              Boolean
310                },
311
312                //              preset round to 3 places.
313                _round: function(n){ return Math.round(1000*n)/1000; },
314                _leading: function(unit){ return this.viewbox.height * (unit||this._defaultLeading); },
315                _normalize: function(str){
316                        return str.replace(/\s+/g, String.fromCharCode(0x20));
317                },
318
319                _getWidth: function(glyphs){
320                        var w=0, last=0, lastGlyph=null;
321                        arr.forEach(glyphs, function(glyph, i){
322                                last=glyph.xAdvance;
323                                if(glyphs[i] && glyph.kern && glyph.kern[glyphs[i].code]){
324                                        last += glyph.kern[glyphs[i].code].x;
325                                }
326                                w += last;
327                                lastGlyph = glyph;
328                        });
329
330                        //              if the last glyph was a space, pull it off.
331                        if(lastGlyph && lastGlyph.code == " "){
332                                w -= lastGlyph.xAdvance;
333                        }
334
335                        return this._round(w/*-last*/);
336                },
337
338                _getLongestLine: function(lines){
339                        var maxw=0, idx=0;
340                        arr.forEach(lines, function(line, i){
341                                var max = Math.max(maxw, this._getWidth(line));
342                                if(max > maxw){
343                                        maxw = max;
344                                        idx=i;
345                                }
346                        }, this);
347                        return { width: maxw, index: idx, line: lines[idx] };
348                },
349
350                _trim: function(lines){
351                        var fn = function(arr){
352                                //              check if the first or last character is a space and if so, remove it.
353                                if(!arr.length){ return; }
354                                if(arr[arr.length-1].code == " "){ arr.splice(arr.length-1, 1); }
355                                if(!arr.length){ return; }
356                                if(arr[0].code == " "){ arr.splice(0, 1); }
357                        };
358
359                        if(lang.isArray(lines[0])){
360                                //              more than one line.
361                                arr.forEach(lines, fn);
362                        } else {
363                                fn(lines);
364                        }
365                        return lines;
366                },
367
368                _split: function(chars, nLines){
369                        // summary:
370                        //              split passed chars into nLines by finding the closest whitespace.
371                        var w = this._getWidth(chars),
372                                limit = Math.floor(w/nLines),
373                                lines = [],
374                                cw = 0,
375                                c = [],
376                                found = false;
377
378                        for(var i=0, l=chars.length; i<l; i++){
379                                if(chars[i].code == " "){ found = true; }
380                                cw += chars[i].xAdvance;
381                                if(i+1<l && chars[i].kern && chars[i].kern[chars[i+1].code]){
382                                        cw += chars[i].kern[chars[i+1].code].x;
383                                }
384
385                                if(cw>=limit){
386                                        var chr=chars[i];
387                                        while(found && chr.code != " " && i>=0){
388                                                chr = c.pop(); i--;
389                                        }
390                                        lines.push(c);
391                                        c=[];
392                                        cw=0;
393                                        found=false;
394                                }
395                                c.push(chars[i]);
396                        }
397                        if(c.length){ lines.push(c); }
398                        //              "trim" it
399                        return this._trim(lines);
400                },
401
402                _getSizeFactor: function(size){
403                        //              given the size, return a scaling factor based on the height of the
404                        //              font as defined in the font definition file.
405                        size += "";     //              force the string cast.
406                        var metrics = HtmlMetrics.getCachedFontMeasurements(),
407                                height=this.viewbox.height,
408                                f=metrics["1em"],
409                                unit=parseFloat(size, 10);      //              the default.
410                        if(size.indexOf("em")>-1){
411                                return this._round((metrics["1em"]*unit)/height);
412                        }
413                        else if(size.indexOf("ex")>-1){
414                                return this._round((metrics["1ex"]*unit)/height);
415                        }
416                        else if(size.indexOf("pt")>-1){
417                                return this._round(((metrics["12pt"] / 12)*unit) / height);
418                        }
419                        else if(size.indexOf("px")>-1){
420                                return this._round(((metrics["16px"] / 16)*unit) / height);
421                        }
422                        else if(size.indexOf("%")>-1){
423                                return this._round((metrics["1em"]*(unit / 100)) / height);
424                        }
425                        else {
426                                f=metrics[size]||metrics.medium;
427                                return this._round(f/height);
428                        }
429                },
430
431                _getFitFactor: function(lines, w, h, l){
432                        // summary:
433                        //              Find the scaling factor for the given phrase set.
434                        if(!h){
435                                //              if no height was passed, we assume an array of glyphs instead of lines.
436                                return this._round(w/this._getWidth(lines));
437                        } else {
438                                var maxw = this._getLongestLine(lines).width,
439                                        maxh = (lines.length*(this.viewbox.height*l))-((this.viewbox.height*l)-this.viewbox.height);
440                                return this._round(Math.min(w/maxw, h/maxh));
441                        }
442                },
443                _getBestFit: function(chars, w, h, ldng){
444                        // summary:
445                        //              Get the best number of lines to return given w and h.
446                        var limit=32,
447                                factor=0,
448                                lines=limit;
449                        while(limit>0){
450                                var f=this._getFitFactor(this._split(chars, limit), w, h, ldng);
451                                if(f>factor){
452                                        factor = f;
453                                        lines=limit;
454                                }
455                                limit--;
456                        }
457                        return { scale: factor, lines: this._split(chars, lines) };
458                },
459
460                _getBestFlow: function(chars, w, scale){
461                        // summary:
462                        //              Based on the given scale, do the best line splitting possible.
463                        var lines = [],
464                                cw = 0,
465                                c = [],
466                                found = false;
467                        for(var i=0, l=chars.length; i<l; i++){
468                                if(chars[i].code == " "){ found = true; }
469                                var tw = chars[i].xAdvance;
470                                if(i+1<l && chars[i].kern && chars[i].kern[chars[i+1].code]){
471                                        tw += chars[i].kern[chars[i+1].code].x;
472                                }
473                                cw += scale*tw;
474
475                                if(cw>=w){
476                                        var chr=chars[i];
477                                        while(found && chr.code != " " && i>=0){
478                                                chr = c.pop(); i--;
479                                        }
480                                        lines.push(c);
481                                        c=[];
482                                        cw=0;
483                                        found=false;
484                                }
485                                c.push(chars[i]);
486                        }
487                        if(c.length){ lines.push(c); }
488                        return this._trim(lines);
489                },
490
491                //              public functions
492                getWidth: function(/* String */text, /* Float? */scale){
493                        // summary:
494                        //              Get the width of the rendered text without actually rendering it.
495                        // text:
496                        //              The string to measure.
497                        // scale:
498                        //              an optional scaling factor.
499                        return this._getWidth(arr.map(this._normalize(text).split(""), function(chr){
500                                return this.glyphs[chr] || { xAdvance: this.advance.missing.x };
501                        }, this)) * (scale || 1);       //              Float
502                },
503                getLineHeight: function(/* Float? */scale){
504                        // summary:
505                        //              return the height of a single line, sans leading, based on scale.
506                        // scale:
507                        //              an optional scaling factor.
508                        return this.viewbox.height * (scale || 1);      //              Float
509                },
510
511                //              A note:
512                //              Many SVG exports do not include information such as x-height, caps-height
513                //              and other coords that may help alignment.  We can calc the baseline and
514                //              we can get a mean line (i.e. center alignment) but that's about all, reliably.
515                getCenterline: function(/* Float? */scale){
516                        // summary:
517                        //              return the y coordinate that is the center of the viewbox.
518                        // scale:
519                        //              an optional scaling factor.
520                        return (scale||1) * (this.viewbox.height/2);
521                },
522                getBaseline: function(/* Float? */scale){
523                        // summary:
524                        //              Find the baseline coord for alignment; adjust for scale if passed.
525                        // scale:
526                        //              an optional scaling factor.
527                        return (scale||1) * (this.viewbox.height+this.descent); //              Float
528                },
529
530                draw: function(
531                                /* dojox/gfx.Container */group,
532                                /* dojox/gfx.Text */ textArgs,
533                                /* dojox/gfx.Font */fontArgs,
534                                /* dojox/gfx.Fill */fillArgs,
535                                /* dojox/gfx.Stroke */strokeArgs){
536                        // summary:
537                        //              based on the passed parameters, draw the given text using paths
538                        //              defined by this font.
539                        // description:
540                        //              The main method of a VectorFont, draw() will take a text fragment
541                        //              and render it in a set of groups and paths based on the parameters
542                        //              passed.
543                        //
544                        //              The basics of drawing text are simple enough: pass it your text as
545                        //              part of the textArgs object, pass size and family info as part of
546                        //              the fontArgs object, pass at least a color as the fillArgs object,
547                        //              and if you are looking to create an outline, pass the strokeArgs
548                        //              object as well. fillArgs and strokeArgs are the same as any other
549                        //              gfx fill and stroke arguments; they are simply applied to any path
550                        //              object generated by this method.
551                        //
552                        //              Resulting GFX structure
553                        //              -----------------------
554                        //
555                        //              The result of this function is a set of gfx objects in the following structure:
556                        //
557                        //      |       gfx.Group                       //              the parent group generated by this function
558                        //      |       +       gfx.Group[]             //              a group generated for each line of text
559                        //      |               +       gfx.Path[]      //              each glyph/character in the text
560                        //
561                        //              Scaling transformations (i.e. making the generated text the correct size)
562                        //              are always applied to the parent Group that is generated (i.e. the top
563                        //              node in the above example).  In theory, if you are looking to do any kind
564                        //              of other transformations (such as a translation), you should apply it to
565                        //              the group reference you pass to this method.  If you find that you need
566                        //              to apply transformations to the group that is returned by this method,
567                        //              you will need to reapply the scaling transformation as the *last* transform,
568                        //              like so:
569                        //
570                        //      |       textGroup.setTransform(new matrix.Matrix2D([
571                        //      |               matrix.translate({ dx: dx, dy: dy }),
572                        //      |               textGroup.getTransform()
573                        //      |       ]));
574                        //
575                        //              In general, this should never be necessary unless you are doing advanced
576                        //              placement of your text.
577                        //
578                        //              Advanced Layout Functionality
579                        //              -----------------------------
580                        //
581                        //              In addition to straight text fragments, draw() supports a few advanced
582                        //              operations not normally available with vector graphics:
583                        //
584                        //              - Flow operations (i.e. wrap to a given width)
585                        //              - Fitting operations (i.e. find a best fit to a given rectangle)
586                        //
587                        //              To enable either, pass a `fitting` property along with the textArgs object.
588                        //              The possible values are contained in the dojox/gfx.vectorFontFitting enum
589                        //              (NONE, FLOW, FIT).
590                        //
591                        //              `Flow fitting`
592                        //              Flow fitting requires both a passed size (in the fontArgs object) and a
593                        //              width (passed with the textArgs object).  draw() will attempt to split the
594                        //              passed text up into lines, at the closest whitespace according to the
595                        //              passed width.  If a width is missing, it will revert to NONE.
596                        //
597                        //              `Best fit fitting`
598                        //              Doing a "best fit" means taking the passed text, and finding the largest
599                        //              size and line breaks so that it is the closest fit possible.  With best
600                        //              fit, any size arguments are ignored; if a height is missing, it will revert
601                        //              to NONE.
602                        //
603                        //              Other notes
604                        //              -----------
605                        //
606                        //              `a11y`
607                        //              Since the results of this method are rendering using pure paths (think
608                        //              "convert to outlines" in Adobe Illustrator), any text rendered by this
609                        //              code is NOT considered a11y-friendly.  If a11y is a requirement, we
610                        //              suggest using other, more a11y-friendly methods.
611                        //
612                        //              `Font sources`
613                        //              Always make sure that you are legally allowed to use any fonts that you
614                        //              convert to SVG format; we claim no responsibility for any licensing
615                        //              infractions that may be caused by the use of this code.
616                        // returns: dojox/gfx.Group
617                        if(!this.initialized()){
618                                throw new Error("dojox.gfx.VectorFont.draw(): we have not been initialized yet.");
619                        }
620                        //              TODO: BIDI handling.  Deal with layout/alignments based on font parameters.
621
622                        //              start by creating the overall group.  This is the INNER group (the caller
623                        //              should be the outer).
624                        var g = group.createGroup();
625
626                        //              do the x/y translation on the parent group
627                        //              FIXME: this is probably not the best way of doing this.
628                        if(textArgs.x || textArgs.y){
629                                group.applyTransform({ dx: textArgs.x||0, dy: textArgs.y||0 });
630                        }
631
632                        //              go get the glyph array.
633                        var text = arr.map(this._normalize(textArgs.text).split(""), function(chr){
634                                return this.glyphs[chr] || { path:null, xAdvance: this.advance.missing.x };
635                        }, this);
636
637                        //              determine the font style info, ignore decoration.
638                        var size = fontArgs.size,
639                                fitting = textArgs.fitting,
640                                width = textArgs.width,
641                                height = textArgs.height,
642                                align = textArgs.align,
643                                leading = textArgs.leading||this._defaultLeading;
644
645                        //              figure out if we have to do fitting at all.
646                        if(fitting){
647                                //              more than zero.
648                                if((fitting==gfx.vectorFontFitting.FLOW && !width) || (fitting==gfx.vectorFontFitting.FIT && (!width || !height))){
649                                        //              reset the fitting if we don't have everything we need.
650                                        fitting = gfx.vectorFontFitting.NONE;
651                                }
652                        }
653
654                        //              set up the lines array and the scaling factor.
655                        var lines, scale;
656                        switch(fitting){
657                                case gfx.vectorFontFitting.FIT:
658                                        var o=this._getBestFit(text, width, height, leading);
659                                        scale = o.scale;
660                                        lines = o.lines;
661                                        break;
662
663                                case gfx.vectorFontFitting.FLOW:
664                                        scale = this._getSizeFactor(size);
665                                        lines = this._getBestFlow(text, width, scale);
666                                        break;
667
668                                default:
669                                        scale = this._getSizeFactor(size);
670                                        lines = [ text ];
671
672                        }
673
674                        //              make sure lines doesn't have any empty lines.
675                        lines = arr.filter(lines, function(item){
676                                return item.length>0;
677                        });
678
679                        //              let's start drawing.
680                        var cy = 0,
681                                maxw = this._getLongestLine(lines).width;
682
683                        for(var i=0, l=lines.length; i<l; i++){
684                                var cx = 0,
685                                        line=lines[i],
686                                        linew = this._getWidth(line),
687                                        lg=g.createGroup();
688
689                                //              loop through the glyphs and add them to the line group (lg)
690                                for (var j=0; j<line.length; j++){
691                                        var glyph=line[j];
692                                        if(glyph.path!==null){
693                                                var p = lg.createPath(glyph.path).setFill(fillArgs);
694                                                if(strokeArgs){ p.setStroke(strokeArgs); }
695                                                p.setTransform([
696                                                        Matrix.flipY,
697                                                        Matrix.translate(cx, -this.viewbox.height-this.descent)
698                                                ]);
699                                        }
700                                        cx += glyph.xAdvance;
701                                        if(j+1<line.length && glyph.kern && glyph.kern[line[j+1].code]){
702                                                cx += glyph.kern[line[j+1].code].x;
703                                        }
704                                }
705
706                                //              transform the line group.
707                                var dx = 0;
708                                if(align=="middle"){ dx = maxw/2 - linew/2; }
709                                else if(align=="end"){ dx = maxw - linew; }
710                                lg.setTransform({ dx: dx, dy: cy });
711                                cy += this.viewbox.height * leading;
712                        }
713
714                        //              scale the group
715                        g.setTransform(Matrix.scale(scale));
716
717                        //              return the overall group
718                        return g;       //              dojox/gfx.Group
719                },
720
721                //              events
722                onLoadBegin: function(/* String */url){ },
723                onLoad: function(/* dojox/gfx/VectorText */font){ }
724        });
725
726        //              TODO: dojox/gfx integration
727/*
728
729        //              Inherit from Group but attach Text properties to it.
730        dojo.declare("dojox.gfx.VectorText", dojox.gfx.Group, {
731                constructor: function(rawNode){
732                        dojox.gfx.Group._init.call(this);
733                        this.fontStyle = null;
734                },
735
736                //              private methods.
737                _setFont: function(){
738                        //              render this using the font code.
739                        var f = this.fontStyle;
740                        var font = dojox.gfx._vectorFontCache[f.family];
741                        if(!font){
742                                throw new Error("dojox.gfx.VectorText._setFont: the passed font family '" + f.family + "' was not found.");
743                        }
744
745                        //              the actual rendering belongs to the font itself.
746                        font.draw(this, this.shape, this.fontStyle, this.fillStyle, this.strokeStyle);
747                },
748
749                getFont: function(){ return this.fontStyle; },
750
751                //              overridden public methods.
752                setShape: function(newShape){
753                        dojox.gfx.Group.setShape.call(this);
754                        this.shape = dojox.gfx.makeParameters(this.shape, newShape);
755                        this.bbox = null;
756                        this._setFont();
757                        return this;
758                },
759
760                //              if we've been drawing, we should have exactly one child, and that
761                //              child will contain the real children.
762                setFill: function(fill){
763                        this.fillStyle = fill;
764                        if(this.children[0]){
765                                dojo.forEach(this.children[0].children, function(group){
766                                        dojo.forEach(group.children, function(path){
767                                                path.setFill(fill);
768                                        });
769                                }, this);
770                        }
771                        return this;
772                },
773                setStroke: function(stroke){
774                        this.strokeStyle = stroke;
775                        if(this.children[0]){
776                                dojo.forEach(this.children[0].children, function(group){
777                                        dojo.forEach(group.children, function(path){
778                                                path.setStroke(stroke);
779                                        });
780                                }, this);
781                        }
782                        return this;
783                },
784
785                setFont: function(newFont){
786                        //              this will do the real rendering.
787                        this.fontStyle = typeof newFont == "string" ? dojox.gfx.splitFontString(newFont)
788                                : dojox.gfx.makeParameters(dojox.gfx.defaultFont, newFont);
789                        this._setFont();
790                        return this;
791                },
792
793                getBoundingBox: function(){
794                        return this.bbox;
795                }
796        });
797
798        //              TODO: figure out how to add this to container objects!
799        dojox.gfx.shape.Creator.createVectorText = function(text){
800                return this.createObject(dojox.gfx.VectorText, text);
801        }
802*/
803});
Note: See TracBrowser for help on using the repository browser.