~ubuntu-branches/ubuntu/karmic/moon/karmic

« back to all changes in this revision

Viewing changes to src/text.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2009-02-14 12:01:08 UTC
  • Revision ID: james.westby@ubuntu.com-20090214120108-06539vb25vhbd8bn
Tags: upstream-1.0
ImportĀ upstreamĀ versionĀ 1.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
2
/*
 
3
 * text.cpp: 
 
4
 *
 
5
 * Contact:
 
6
 *   Moonlight List (moonlight-list@lists.ximian.com)
 
7
 *
 
8
 * Copyright 2007 Novell, Inc. (http://www.novell.com)
 
9
 *
 
10
 * See the LICENSE file included with the distribution for details.
 
11
 */
 
12
 
 
13
#ifdef HAVE_CONFIG_H
 
14
#include <config.h>
 
15
#endif
 
16
 
 
17
#include <cairo.h>
 
18
 
 
19
#include <sys/types.h>
 
20
#include <sys/stat.h>
 
21
#include <unistd.h>
 
22
#include <errno.h>
 
23
 
 
24
#include "file-downloader.h"
 
25
#include "runtime.h"
 
26
#include "color.h"
 
27
#include "text.h"
 
28
#include "uri.h"
 
29
#include "utils.h"
 
30
#include "debug.h"
 
31
 
 
32
 
 
33
static SolidColorBrush *default_foreground_brush = NULL;
 
34
 
 
35
static Brush *
 
36
default_foreground (void)
 
37
{
 
38
        if (!default_foreground_brush)
 
39
                default_foreground_brush = new SolidColorBrush ("black");
 
40
        
 
41
        return (Brush *) default_foreground_brush;
 
42
}
 
43
 
 
44
void
 
45
text_shutdown (void)
 
46
{
 
47
        if (default_foreground_brush) {
 
48
                default_foreground_brush->unref ();
 
49
                default_foreground_brush = NULL;
 
50
        }
 
51
}
 
52
 
 
53
 
 
54
 
 
55
 
 
56
//
 
57
// Inline
 
58
//
 
59
 
 
60
Inline::Inline ()
 
61
{
 
62
        foreground = NULL;
 
63
        autogen = false;
 
64
        
 
65
        /* initialize the font description */
 
66
        font = new TextFontDescription ();
 
67
}
 
68
 
 
69
Inline::~Inline ()
 
70
{
 
71
        delete font;
 
72
}
 
73
 
 
74
static DependencyProperty *
 
75
textblock_property (DependencyProperty *prop)
 
76
{
 
77
        if (prop == Inline::FontFamilyProperty)
 
78
                return TextBlock::FontFamilyProperty;
 
79
        
 
80
        if (prop == Inline::FontStretchProperty)
 
81
                return TextBlock::FontStretchProperty;
 
82
        
 
83
        if (prop == Inline::FontWeightProperty)
 
84
                return TextBlock::FontWeightProperty;
 
85
        
 
86
        if (prop == Inline::FontStyleProperty)
 
87
                return TextBlock::FontStyleProperty;
 
88
        
 
89
        if (prop == Inline::FontSizeProperty)
 
90
                return TextBlock::FontSizeProperty;
 
91
        
 
92
        if (prop == Inline::ForegroundProperty)
 
93
                return TextBlock::ForegroundProperty;
 
94
        
 
95
        if (prop == Inline::TextDecorationsProperty)
 
96
                return TextBlock::TextDecorationsProperty;
 
97
        
 
98
        return NULL;
 
99
}
 
100
 
 
101
Value *
 
102
Inline::GetDefaultValue (DependencyProperty *prop)
 
103
{
 
104
        DependencyObject *parent = GetLogicalParent ();
 
105
        
 
106
        if (parent && parent->Is (Type::TEXTBLOCK)) {
 
107
                DependencyProperty *text_prop = textblock_property (prop);
 
108
                
 
109
                if (text_prop)
 
110
                        return parent->GetValue (text_prop);
 
111
                
 
112
                return prop->GetDefaultValue();
 
113
        }
 
114
        
 
115
        // not yet attached to a textblock
 
116
        
 
117
        if (prop == Inline::ForegroundProperty) {
 
118
                SolidColorBrush *brush = new SolidColorBrush ("black");
 
119
                
 
120
                SetValue (prop, Value (brush));
 
121
                brush->unref ();
 
122
                
 
123
                return GetValue (prop);
 
124
        }
 
125
        
 
126
        // all other properties have a default value
 
127
        return prop->GetDefaultValue();
 
128
}
 
129
 
 
130
void
 
131
Inline::OnPropertyChanged (PropertyChangedEventArgs *args)
 
132
{
 
133
        if (args->property->GetOwnerType() != Type::INLINE) {
 
134
                DependencyObject::OnPropertyChanged (args);
 
135
                return;
 
136
        }
 
137
        
 
138
        if (args->property == Inline::FontFamilyProperty) {
 
139
                if (args->new_value) {
 
140
                        char *family = args->new_value->AsString ();
 
141
                        font->SetFamily (family);
 
142
                } else {
 
143
                        font->UnsetFields (FontMaskFamily);
 
144
                }
 
145
        } else if (args->property == Inline::FontSizeProperty) {
 
146
                if (args->new_value) {
 
147
                        double size = args->new_value->AsDouble ();
 
148
                        font->SetSize (size);
 
149
                } else {
 
150
                        font->UnsetFields (FontMaskSize);
 
151
                }
 
152
        } else if (args->property == Inline::FontStretchProperty) {
 
153
                if (args->new_value) {
 
154
                        FontStretches stretch = (FontStretches) args->new_value->AsInt32 ();
 
155
                        font->SetStretch (stretch);
 
156
                } else {
 
157
                        font->UnsetFields (FontMaskStretch);
 
158
                }
 
159
        } else if (args->property == Inline::FontStyleProperty) {
 
160
                if (args->new_value) {
 
161
                        FontStyles style = (FontStyles) args->new_value->AsInt32 ();
 
162
                        font->SetStyle (style);
 
163
                } else {
 
164
                        font->UnsetFields (FontMaskStyle);
 
165
                }
 
166
        } else if (args->property == Inline::FontWeightProperty) {
 
167
                if (args->new_value) {
 
168
                        FontWeights weight = (FontWeights) args->new_value->AsInt32 ();
 
169
                        font->SetWeight (weight);
 
170
                } else {
 
171
                        font->UnsetFields (FontMaskWeight);
 
172
                }
 
173
        } else if (args->property == Inline::ForegroundProperty) {
 
174
                foreground = args->new_value ? args->new_value->AsBrush () : NULL;
 
175
        }
 
176
        
 
177
        
 
178
        NotifyListenersOfPropertyChange (args);
 
179
}
 
180
 
 
181
void
 
182
Inline::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
 
183
{
 
184
        if (prop == Inline::ForegroundProperty) {
 
185
                // this isn't exactly what we want, I don't
 
186
                // think... but it'll have to do.
 
187
                NotifyListenersOfPropertyChange (prop);
 
188
        }
 
189
}
 
190
 
 
191
 
 
192
//
 
193
// TextBlock
 
194
//
 
195
 
 
196
TextBlock::TextBlock ()
 
197
{
 
198
        downloader = NULL;
 
199
        
 
200
        dirty = true;
 
201
        
 
202
        actual_height = 0.0;
 
203
        actual_width = 0.0;
 
204
        
 
205
        /* initialize the font description and layout */
 
206
        hints = new TextLayoutHints (TextAlignmentLeft, LineStackingStrategyMaxHeight, 0.0);
 
207
        layout = new TextLayout ();
 
208
        
 
209
        font = new TextFontDescription ();
 
210
        font->SetFamily (TEXTBLOCK_FONT_FAMILY);
 
211
        font->SetStretch (TEXTBLOCK_FONT_STRETCH);
 
212
        font->SetWeight (TEXTBLOCK_FONT_WEIGHT);
 
213
        font->SetStyle (TEXTBLOCK_FONT_STYLE);
 
214
        font->SetSize (TEXTBLOCK_FONT_SIZE);
 
215
        
 
216
        Brush *brush = new SolidColorBrush ("black");
 
217
        
 
218
        setvalue = false;
 
219
        SetValue (TextBlock::ForegroundProperty, Value (brush));
 
220
        SetValue (TextBlock::InlinesProperty, Value::CreateUnref (new InlineCollection ()));
 
221
        brush->unref ();
 
222
        setvalue = true;
 
223
}
 
224
 
 
225
TextBlock::~TextBlock ()
 
226
{
 
227
        delete layout;
 
228
        delete hints;
 
229
        delete font;
 
230
        
 
231
        if (downloader != NULL) {
 
232
                downloader_abort (downloader);
 
233
                downloader->unref ();
 
234
        }
 
235
}
 
236
 
 
237
void
 
238
TextBlock::SetFontSource (Downloader *downloader)
 
239
{
 
240
        if (this->downloader == downloader)
 
241
                return;
 
242
        
 
243
        if (this->downloader) {
 
244
                this->downloader->Abort ();
 
245
                this->downloader->unref ();
 
246
                this->downloader = NULL;
 
247
        }
 
248
        
 
249
        if (downloader) {
 
250
                this->downloader = downloader;
 
251
                downloader->ref ();
 
252
                
 
253
                downloader->AddHandler (downloader->CompletedEvent, downloader_complete, this);
 
254
                if (downloader->Started () || downloader->Completed ()) {
 
255
                        if (downloader->Completed ())
 
256
                                DownloaderComplete ();
 
257
                } else {
 
258
                        downloader->SetWriteFunc (data_write, size_notify, this);
 
259
                        
 
260
                        // This is what actually triggers the download
 
261
                        downloader->Send ();
 
262
                }
 
263
        } else {
 
264
                font->SetFilename (NULL);
 
265
                dirty = true;
 
266
                
 
267
                UpdateBounds (true);
 
268
                Invalidate ();
 
269
        }
 
270
}
 
271
 
 
272
void
 
273
TextBlock::Render (cairo_t *cr, int x, int y, int width, int height)
 
274
{
 
275
        if (dirty)
 
276
                Layout (cr);
 
277
        
 
278
        cairo_save (cr);
 
279
        cairo_set_matrix (cr, &absolute_xform);
 
280
        Paint (cr);
 
281
        cairo_restore (cr);
 
282
}
 
283
 
 
284
void 
 
285
TextBlock::ComputeBounds ()
 
286
{
 
287
        extents = Rect (0, 0, GetBoundingWidth (), GetBoundingHeight ());
 
288
        bounds = IntersectBoundsWithClipPath (extents, false).Transform (&absolute_xform);
 
289
}
 
290
 
 
291
bool
 
292
TextBlock::InsideObject (cairo_t *cr, double x, double y)
 
293
{
 
294
        bool ret = false;
 
295
        
 
296
        cairo_save (cr);
 
297
        
 
298
        double nx = x;
 
299
        double ny = y;
 
300
                
 
301
        TransformPoint (&nx, &ny);
 
302
        
 
303
        if (nx >= 0.0 && ny >= 0.0 && nx < GetActualWidth () && ny < GetActualHeight ())
 
304
                ret = true;
 
305
        
 
306
        cairo_restore (cr);
 
307
        return ret;
 
308
}
 
309
 
 
310
Point
 
311
TextBlock::GetTransformOrigin ()
 
312
{
 
313
        Point *user_xform_origin = GetRenderTransformOrigin ();
 
314
        
 
315
        return Point (user_xform_origin->x * GetBoundingWidth (), user_xform_origin->y * GetBoundingHeight ());
 
316
}
 
317
 
 
318
void
 
319
TextBlock::GetSizeForBrush (cairo_t *cr, double *width, double *height)
 
320
{
 
321
        *height = GetActualHeight ();
 
322
        *width = GetActualWidth ();
 
323
}
 
324
 
 
325
void
 
326
TextBlock::CalcActualWidthHeight (cairo_t *cr)
 
327
{
 
328
        bool destroy = false;
 
329
        
 
330
        if (cr == NULL) {
 
331
                cr = measuring_context_create ();
 
332
                destroy = true;
 
333
        } else {
 
334
                cairo_save (cr);
 
335
        }
 
336
        
 
337
        cairo_identity_matrix (cr);
 
338
        
 
339
        Layout (cr);
 
340
        
 
341
        if (destroy) {
 
342
                measuring_context_destroy (cr);
 
343
        } else {
 
344
                cairo_new_path (cr);
 
345
                cairo_restore (cr);
 
346
        }
 
347
}
 
348
 
 
349
void
 
350
TextBlock::Layout (cairo_t *cr)
 
351
{
 
352
        Value *value = GetValueNoDefault (TextBlock::TextProperty);
 
353
        InlineCollection *inlines = GetInlines ();
 
354
        TextDecorations decorations;
 
355
        List *runs;
 
356
        guint8 font_mask;
 
357
        const char *text;
 
358
        
 
359
        if (!value) {
 
360
                // If no text has ever been set on this TextBlock,
 
361
                // then skip calculating actual width/height.
 
362
                // Fixes bug #435798
 
363
                actual_height = 0.0;
 
364
                actual_width = 0.0;
 
365
                goto done;
 
366
        }
 
367
 
 
368
        runs = new List ();
 
369
        
 
370
        layout->SetWrapping (GetTextWrapping ());
 
371
        
 
372
        if ((value = GetValueNoDefault (FrameworkElement::WidthProperty))) {
 
373
#if SL_2_0
 
374
                Thickness *padding = GetPadding ();
 
375
                double pad = padding->left + padding->right;
 
376
#else
 
377
                double pad = 0.0;
 
378
#endif
 
379
                double width = value->AsDouble ();
 
380
                
 
381
                if (pad >= width) {
 
382
                        layout->SetTextRuns (runs);
 
383
                        actual_height = 0.0;
 
384
                        actual_width = 0.0;
 
385
                        goto done;
 
386
                }
 
387
                
 
388
                layout->SetMaxWidth (width - pad);
 
389
        } else {
 
390
                layout->SetMaxWidth (-1.0);
 
391
        }
 
392
        
 
393
        decorations = GetTextDecorations ();
 
394
        font_mask = font->GetFields ();
 
395
        
 
396
        if (inlines != NULL) {
 
397
                guint8 run_mask, inherited_mask;
 
398
                TextFontDescription *ifont;
 
399
                TextDecorations deco;
 
400
                Value *value;
 
401
                Inline *item;
 
402
                Run *run;
 
403
                
 
404
                for (int i = 0; i < inlines->GetCount (); i++) {
 
405
                        item = inlines->GetValueAt (i)->AsInline ();
 
406
                        
 
407
                        ifont = item->font;
 
408
                        
 
409
                        // Inlines inherit their parent TextBlock's font properties if
 
410
                        // they don't specify their own.
 
411
                        run_mask = ifont->GetFields ();
 
412
                        ifont->Merge (font, false);
 
413
                        
 
414
                        inherited_mask = (FontMask) (font_mask & ~run_mask);
 
415
                        
 
416
                        // Inherit the TextDecorations from the parent TextBlock if unset
 
417
                        if ((value = item->GetValue (Inline::TextDecorationsProperty)))
 
418
                                deco = (TextDecorations) value->AsInt32 ();
 
419
                        else
 
420
                                deco = decorations;
 
421
                        
 
422
                        switch (item->GetObjectType ()) {
 
423
                        case Type::RUN:
 
424
                                run = (Run *) item;
 
425
                                
 
426
                                text = run->GetText ();
 
427
                                
 
428
                                if (text && text[0]) {
 
429
                                        const char *inptr, *inend;
 
430
                                        
 
431
                                        inptr = text;
 
432
                                        
 
433
                                        do {
 
434
                                                inend = inptr;
 
435
                                                while (*inend && *inend != '\n')
 
436
                                                        inend++;
 
437
                                                
 
438
                                                if (inend > inptr)
 
439
                                                        runs->Append (new TextRun (inptr, inend - inptr, deco,
 
440
                                                                                   ifont, &item->foreground));
 
441
                                                
 
442
                                                if (*inend == '\0')
 
443
                                                        break;
 
444
                                                
 
445
                                                runs->Append (new TextRun (ifont));
 
446
                                                inptr = inend + 1;
 
447
                                        } while (*inptr);
 
448
                                }
 
449
                                
 
450
                                break;
 
451
                        case Type::LINEBREAK:
 
452
                                runs->Append (new TextRun (ifont));
 
453
                                break;
 
454
                        default:
 
455
                                break;
 
456
                        }
 
457
                        
 
458
                        if (inherited_mask != 0)
 
459
                                ifont->UnsetFields (inherited_mask);
 
460
                }
 
461
        }
 
462
        
 
463
        layout->SetTextRuns (runs);
 
464
        layout->Layout (hints);
 
465
        
 
466
        layout->GetActualExtents (&actual_width, &actual_height);
 
467
        //layout->GetLayoutExtents (&bbox_width, &bbox_height);
 
468
        
 
469
        if (runs->IsEmpty ()) {
 
470
                // If the Text property had been set once upon a time,
 
471
                // but is currently empty, Silverlight seems to set
 
472
                // the ActualHeight property to the font height. See
 
473
                // bug #405514 for details.
 
474
                TextFont *font = this->font->GetFont ();
 
475
                actual_height = font->Height ();
 
476
                font->unref ();
 
477
        }
 
478
        
 
479
 done:
 
480
        
 
481
        SetActualHeight (actual_height);
 
482
        SetActualWidth (actual_width);
 
483
        
 
484
        dirty = false;
 
485
}
 
486
 
 
487
void
 
488
TextBlock::Paint (cairo_t *cr)
 
489
{
 
490
#if SL_2_0
 
491
#endif
 
492
        Brush *fg;
 
493
        
 
494
        if (!(fg = GetForeground ()))
 
495
                fg = default_foreground ();
 
496
 
 
497
        Point offset = Point (0.0, 0.0);
 
498
#if SL_2_0
 
499
        Thickness *padding = GetPadding ();
 
500
        offset.x = padding->left;
 
501
        offset.y = padding->top;
 
502
#endif
 
503
        
 
504
        layout->Render (cr, GetOriginPoint (), offset, hints, fg);
 
505
        
 
506
        if (moonlight_flags & RUNTIME_INIT_SHOW_TEXTBOXES) {
 
507
                cairo_set_source_rgba (cr, 0.0, 1.0, 0.0, 1.0);
 
508
                cairo_set_line_width (cr, 1);
 
509
                cairo_rectangle (cr, 0, 0, actual_width, actual_height);
 
510
                cairo_stroke (cr);
 
511
        }
 
512
}
 
513
 
 
514
char *
 
515
TextBlock::GetTextInternal ()
 
516
{
 
517
        InlineCollection *inlines = GetInlines ();
 
518
        GString *block;
 
519
        char *str;
 
520
        
 
521
        block = g_string_new ("");
 
522
        
 
523
        if (inlines != NULL) {
 
524
                const char *text;
 
525
                Inline *item;
 
526
                
 
527
                for (int i = 0; i < inlines->GetCount (); i++) {
 
528
                        item = inlines->GetValueAt (i)->AsInline ();
 
529
                        
 
530
                        switch (item->GetObjectType ()) {
 
531
                        case Type::RUN:
 
532
                                text = ((Run *) item)->GetText ();
 
533
                                
 
534
                                if (text && text[0])
 
535
                                        g_string_append (block, text);
 
536
                                break;
 
537
                        case Type::LINEBREAK:
 
538
                                g_string_append_c (block, '\n');
 
539
                                break;
 
540
                        default:
 
541
                                break;
 
542
                        }
 
543
                }
 
544
        }
 
545
        
 
546
        str = block->str;
 
547
        g_string_free (block, false);
 
548
        
 
549
        return str;
 
550
}
 
551
 
 
552
static bool
 
553
inlines_simple_text_equal (InlineCollection *curInlines, InlineCollection *newInlines)
 
554
{
 
555
        const char *text1, *text2;
 
556
        Inline *run1, *run2;
 
557
        
 
558
        if (curInlines->GetCount () != newInlines->GetCount ())
 
559
                return false;
 
560
        
 
561
        for (int i = 0; i < curInlines->GetCount () && i < newInlines->GetCount (); i++) {
 
562
                run1 = curInlines->GetValueAt (i)->AsInline ();
 
563
                run2 = newInlines->GetValueAt (i)->AsInline ();
 
564
                
 
565
                if (run1->GetObjectType () != run2->GetObjectType ())
 
566
                        return false;
 
567
                
 
568
                if (run1->GetObjectType () == Type::RUN) {
 
569
                        text1 = ((Run *) run1)->GetText ();
 
570
                        text2 = ((Run *) run2)->GetText ();
 
571
                        
 
572
                        if (text1 && text2 && strcmp (text1, text2) != 0)
 
573
                                return false;
 
574
                        else if ((text1 && !text2) || (!text1 && text2))
 
575
                                return false;
 
576
                }
 
577
                
 
578
                // newInlines uses TextBlock font/brush properties, so
 
579
                // if curInlines uses any non-default props then they
 
580
                // are not equal.
 
581
                
 
582
                if (run1->font->GetFields () != 0)
 
583
                        return false;
 
584
                
 
585
                if (run1->GetValueNoDefault (Inline::TextDecorationsProperty) != NULL)
 
586
                        return false;
 
587
                
 
588
                if (run1->foreground != NULL)
 
589
                        return false;
 
590
        }
 
591
        
 
592
        return true;
 
593
}
 
594
 
 
595
bool
 
596
TextBlock::SetTextInternal (const char *text)
 
597
{
 
598
        InlineCollection *curInlines = GetInlines ();
 
599
        InlineCollection *inlines = NULL;
 
600
        char *inptr, *buf, *d;
 
601
        const char *txt;
 
602
        Inline *run;
 
603
        
 
604
        if (text && text[0]) {
 
605
                inlines = new InlineCollection ();
 
606
                
 
607
                d = buf = (char *) g_malloc (strlen (text) + 1);
 
608
                txt = text;
 
609
                
 
610
                while (*txt) {
 
611
                        if (*txt != '\r')
 
612
                                *d++ = *txt;
 
613
                        txt++;
 
614
                }
 
615
                *d = '\n';
 
616
                
 
617
                inptr = buf;
 
618
                while (inptr < d) {
 
619
                        txt = inptr;
 
620
                        while (*inptr != '\n')
 
621
                                inptr++;
 
622
                        
 
623
                        if (inptr > txt) {
 
624
                                *inptr = '\0';
 
625
                                run = new Run ();
 
626
                                run->autogen = true;
 
627
                                run->SetValue (Run::TextProperty, Value (txt));
 
628
                                inlines->Add (run);
 
629
                                run->unref ();
 
630
                        }
 
631
                        
 
632
                        if (inptr < d) {
 
633
                                run = new LineBreak ();
 
634
                                run->autogen = true;
 
635
                                inlines->Add (run);
 
636
                                run->unref ();
 
637
                                inptr++;
 
638
                        }
 
639
                }
 
640
                
 
641
                g_free (buf);
 
642
                
 
643
                if (curInlines && inlines_simple_text_equal (curInlines, inlines)) {
 
644
                        // old/new inlines are equal, don't set the new value
 
645
                        inlines->unref ();
 
646
                        return false;
 
647
                }
 
648
                
 
649
                setvalue = false;
 
650
                SetValue (TextBlock::InlinesProperty, Value (inlines));
 
651
                setvalue = true;
 
652
                
 
653
                inlines->unref ();
 
654
        } else if (curInlines) {
 
655
                curInlines->Clear ();
 
656
        }
 
657
        
 
658
        return true;
 
659
}
 
660
 
 
661
void
 
662
TextBlock::OnPropertyChanged (PropertyChangedEventArgs *args)
 
663
{
 
664
        bool invalidate = true;
 
665
        
 
666
        if (args->property->GetOwnerType () != Type::TEXTBLOCK) {
 
667
                FrameworkElement::OnPropertyChanged (args);
 
668
                if (args->property == FrameworkElement::WidthProperty) {
 
669
                        if (GetTextWrapping () != TextWrappingNoWrap)
 
670
                                dirty = true;
 
671
                        
 
672
                        UpdateBounds (true);
 
673
                }
 
674
                
 
675
                return;
 
676
        }
 
677
        
 
678
        if (args->property == TextBlock::FontFamilyProperty) {
 
679
                char *family = args->new_value ? args->new_value->AsString () : NULL;
 
680
                font->SetFamily (family);
 
681
                
 
682
                dirty = true;
 
683
        } else if (args->property == TextBlock::FontSizeProperty) {
 
684
                double size = args->new_value->AsDouble ();
 
685
                font->SetSize (size);
 
686
                
 
687
                dirty = true;
 
688
        } else if (args->property == TextBlock::FontStretchProperty) {
 
689
                FontStretches stretch = (FontStretches) args->new_value->AsInt32 ();
 
690
                font->SetStretch (stretch);
 
691
                
 
692
                dirty = true;
 
693
        } else if (args->property == TextBlock::FontStyleProperty) {
 
694
                FontStyles style = (FontStyles) args->new_value->AsInt32 ();
 
695
                font->SetStyle (style);
 
696
                
 
697
                dirty = true;
 
698
        } else if (args->property == TextBlock::FontWeightProperty) {
 
699
                FontWeights weight = (FontWeights) args->new_value->AsInt32 ();
 
700
                font->SetWeight (weight);
 
701
                
 
702
                dirty = true;
 
703
        } else if (args->property == TextBlock::TextProperty) {
 
704
                if (setvalue) {
 
705
                        // result of a change to the TextBlock.Text property
 
706
                        char *text = args->new_value ? args->new_value->AsString () : NULL;
 
707
                        
 
708
                        if (!SetTextInternal (text)) {
 
709
                                // no change so nothing to invalidate
 
710
                                invalidate = false;
 
711
                        } else {
 
712
                                dirty = true;
 
713
                        }
 
714
                } else {
 
715
                        // result of a change to the TextBlock.Inlines property
 
716
                        invalidate = false;
 
717
                }
 
718
        } else if (args->property == TextBlock::TextDecorationsProperty) {
 
719
                dirty = true;
 
720
        } else if (args->property == TextBlock::TextWrappingProperty) {
 
721
                dirty = true;
 
722
        } else if (args->property == TextBlock::InlinesProperty) {
 
723
                if (setvalue) {
 
724
                        // result of a change to the TextBlock.Inlines property
 
725
                        char *text = GetTextInternal ();
 
726
                        
 
727
                        setvalue = false;
 
728
                        SetValue (TextBlock::TextProperty, Value (text));
 
729
                        setvalue = true;
 
730
                        g_free (text);
 
731
                        dirty = true;
 
732
                } else {
 
733
                        // result of a change to the TextBlock.Text property
 
734
                        invalidate = false;
 
735
                }
 
736
#if SL_2_0
 
737
        } else if (args->property == TextBlock::LineStackingStrategyProperty) {
 
738
                hints->SetLineStackingStrategy ((LineStackingStrategy) args->new_value->AsInt32 ());
 
739
                dirty = true;
 
740
        } else if (args->property == TextBlock::LineHeightProperty) {
 
741
                hints->SetLineHeight (args->new_value->AsDouble ());
 
742
                dirty = true;
 
743
        } else if (args->property == TextBlock::TextAlignmentProperty) {
 
744
                hints->SetTextAlignment ((TextAlignment) args->new_value->AsInt32 ());
 
745
        } else if (args->property == TextBlock::PaddingProperty) {
 
746
                dirty = true;
 
747
#endif
 
748
        } else if (args->property == TextBlock::ActualHeightProperty) {
 
749
                invalidate = false;
 
750
        } else if (args->property == TextBlock::ActualWidthProperty) {
 
751
                invalidate = false;
 
752
        }
 
753
        
 
754
        if (invalidate) {
 
755
                if (dirty)
 
756
                        UpdateBounds (true);
 
757
                
 
758
                Invalidate ();
 
759
        }
 
760
        
 
761
        NotifyListenersOfPropertyChange (args);
 
762
}
 
763
 
 
764
void
 
765
TextBlock::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
 
766
{
 
767
        if (prop->GetOwnerType () != Type::TEXTBLOCK) {
 
768
                FrameworkElement::OnSubPropertyChanged (prop, obj, subobj_args);
 
769
                return;
 
770
        }
 
771
        
 
772
        if (prop == TextBlock::ForegroundProperty)
 
773
                Invalidate ();
 
774
}
 
775
 
 
776
void
 
777
TextBlock::OnCollectionChanged (Collection *col, CollectionChangedEventArgs *args)
 
778
{
 
779
        bool update_bounds = false;
 
780
        bool update_text = false;
 
781
        
 
782
        if (col != GetInlines ()) {
 
783
                FrameworkElement::OnCollectionChanged (col, args);
 
784
                return;
 
785
        }
 
786
        
 
787
        switch (args->action) {
 
788
        case CollectionChangedActionAdd:
 
789
        case CollectionChangedActionRemove:
 
790
        case CollectionChangedActionReplace:
 
791
                // an Inline element has been added or removed, update our TextProperty
 
792
                update_bounds = true;
 
793
                update_text = true;
 
794
                dirty = true;
 
795
                break;
 
796
        case CollectionChangedActionCleared:
 
797
                // the collection has changed, only update our TextProperty if it was the result of a SetValue
 
798
                update_bounds = setvalue;
 
799
                update_text = setvalue;
 
800
                dirty = true;
 
801
                break;
 
802
        default:
 
803
                break;
 
804
        }
 
805
        
 
806
        if (update_text) {
 
807
                char *text = GetTextInternal ();
 
808
                
 
809
                setvalue = false;
 
810
                SetValue (TextBlock::TextProperty, Value (text));
 
811
                setvalue = true;
 
812
                g_free (text);
 
813
        }
 
814
        
 
815
        if (update_bounds)
 
816
                UpdateBounds (true);
 
817
        
 
818
        Invalidate ();
 
819
}
 
820
 
 
821
void
 
822
TextBlock::OnCollectionItemChanged (Collection *col, DependencyObject *obj, PropertyChangedEventArgs *args)
 
823
{
 
824
        bool update_bounds;
 
825
        bool update_text;
 
826
        
 
827
        if (col != GetInlines ()) {
 
828
                FrameworkElement::OnCollectionItemChanged (col, obj, args);
 
829
                return;
 
830
        }
 
831
        
 
832
        // only update bounds if a property other than the Foreground changed
 
833
        update_bounds = args->property != Inline::ForegroundProperty;
 
834
        
 
835
        // only update our TextProperty if change was in a Run's Text property
 
836
        update_text = args->property == Run::TextProperty;
 
837
        
 
838
        dirty = true;
 
839
        
 
840
        if (update_text) {
 
841
                char *text = GetTextInternal ();
 
842
                
 
843
                setvalue = false;
 
844
                SetValue (TextBlock::TextProperty, Value (text));
 
845
                setvalue = true;
 
846
                g_free (text);
 
847
        }
 
848
        
 
849
        if (update_bounds)
 
850
                UpdateBounds (true);
 
851
        
 
852
        Invalidate ();
 
853
}
 
854
 
 
855
Value *
 
856
TextBlock::GetValue (DependencyProperty *property)
 
857
{
 
858
        if (dirty && ((property == TextBlock::ActualHeightProperty) || (property == TextBlock::ActualWidthProperty)))
 
859
                CalcActualWidthHeight (NULL);
 
860
        
 
861
        return DependencyObject::GetValue (property);
 
862
}
 
863
 
 
864
void
 
865
TextBlock::data_write (void *buf, gint32 offset, gint32 n, gpointer data)
 
866
{
 
867
        ;
 
868
}
 
869
 
 
870
void
 
871
TextBlock::size_notify (gint64 size, gpointer data)
 
872
{
 
873
        ;
 
874
}
 
875
 
 
876
void
 
877
TextBlock::downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
 
878
{
 
879
        ((TextBlock *) closure)->DownloaderComplete ();
 
880
}
 
881
 
 
882
static const char *
 
883
deobfuscate_font (Downloader *downloader, const char *path)
 
884
{
 
885
        char *filename, guid[16];
 
886
        const char *str;
 
887
        GString *name;
 
888
        Uri *uri;
 
889
        int fd;
 
890
        
 
891
        if (!(str = downloader->GetUri ()))
 
892
                return NULL;
 
893
        
 
894
        uri = new Uri ();
 
895
        if (!uri->Parse (str) || !uri->path) {
 
896
                delete uri;
 
897
                return NULL;
 
898
        }
 
899
        
 
900
        if (!(str = strrchr (uri->path, '/')))
 
901
                str = uri->path;
 
902
        else
 
903
                str++;
 
904
        
 
905
        if (!DecodeObfuscatedFontGUID (str, guid)) {
 
906
                delete uri;
 
907
                return NULL;
 
908
        }
 
909
        
 
910
        name = g_string_new (str);
 
911
        g_string_append (name, ".XXXXXX");
 
912
        delete uri;
 
913
        
 
914
        filename = g_build_filename (g_get_tmp_dir (), name->str, NULL);
 
915
        g_string_free (name, true);
 
916
        
 
917
        if ((fd = g_mkstemp (filename)) == -1) {
 
918
                g_free (filename);
 
919
                return NULL;
 
920
        }
 
921
        
 
922
        if (CopyFileTo (path, fd) == -1 || !DeobfuscateFontFileWithGUID (filename, guid, NULL)) {
 
923
                unlink (filename);
 
924
                g_free (filename);
 
925
                return NULL;
 
926
        }
 
927
        
 
928
        downloader->getFileDownloader ()->SetDeobfuscatedFile (filename);
 
929
        g_free (filename);
 
930
        
 
931
        return downloader->getFileDownloader ()->GetDownloadedFile ();
 
932
}
 
933
 
 
934
void
 
935
TextBlock::DownloaderComplete ()
 
936
{
 
937
        const char *filename, *path;
 
938
        struct stat st;
 
939
        
 
940
        /* the download was aborted */
 
941
        if (!(path = downloader->getFileDownloader ()->GetUnzippedPath ()))
 
942
                return;
 
943
        
 
944
        if (stat (path, &st) == -1)
 
945
                return;
 
946
        
 
947
        // check for obfuscated fonts
 
948
        if (S_ISREG (st.st_mode) && !downloader->getFileDownloader ()->IsDeobfuscated ()) {
 
949
                if ((filename = deobfuscate_font (downloader, path)))
 
950
                        path = filename;
 
951
                
 
952
                downloader->getFileDownloader ()->SetDeobfuscated (true);
 
953
        }
 
954
        
 
955
        font->SetFilename (path);
 
956
        dirty = true;
 
957
        
 
958
        UpdateBounds (true);
 
959
        Invalidate ();
 
960
}
 
961
 
 
962
void
 
963
TextBlock::SetActualHeight (double height)
 
964
{
 
965
        SetValue (TextBlock::ActualHeightProperty, Value (height));
 
966
}
 
967
 
 
968
void
 
969
TextBlock::SetActualWidth (double width)
 
970
{
 
971
        SetValue (TextBlock::ActualWidthProperty, Value (width));
 
972
}
 
973
 
 
974
//
 
975
// Glyphs
 
976
//
 
977
 
 
978
enum GlyphAttrMask {
 
979
        Cluster = 1 << 1,
 
980
        Index   = 1 << 2,
 
981
        Advance = 1 << 3,
 
982
        uOffset = 1 << 4,
 
983
        vOffset = 1 << 5,
 
984
};
 
985
 
 
986
class GlyphAttr : public List::Node {
 
987
public:
 
988
        guint32 glyph_count;
 
989
        guint32 code_units;
 
990
        guint32 index;
 
991
        double advance;
 
992
        double uoffset;
 
993
        double voffset;
 
994
        guint8 set;
 
995
        
 
996
        GlyphAttr ();
 
997
};
 
998
 
 
999
GlyphAttr::GlyphAttr ()
 
1000
{
 
1001
        glyph_count = 1;
 
1002
        code_units = 1;
 
1003
        set = 0;
 
1004
}
 
1005
 
 
1006
Glyphs::Glyphs ()
 
1007
{
 
1008
        desc = new TextFontDescription ();
 
1009
        desc->SetSize (0.0);
 
1010
        downloader = NULL;
 
1011
        
 
1012
        fill = NULL;
 
1013
        path = NULL;
 
1014
        
 
1015
        attrs = new List ();
 
1016
        text = NULL;
 
1017
        index = 0;
 
1018
        
 
1019
        origin_y_specified = false;
 
1020
        origin_x = 0.0;
 
1021
        origin_y = 0.0;
 
1022
        
 
1023
        height = 0.0;
 
1024
        width = 0.0;
 
1025
        left = 0.0;
 
1026
        top = 0.0;
 
1027
        
 
1028
        simulation_none = true;
 
1029
        uri_changed = false;
 
1030
        invalid = false;
 
1031
        dirty = false;
 
1032
}
 
1033
 
 
1034
Glyphs::~Glyphs ()
 
1035
{
 
1036
        if (downloader) {
 
1037
                downloader_abort (downloader);
 
1038
                downloader->unref ();
 
1039
        }
 
1040
        
 
1041
        if (path)
 
1042
                moon_path_destroy (path);
 
1043
        
 
1044
        attrs->Clear (true);
 
1045
        delete attrs;
 
1046
        
 
1047
        g_free (text);
 
1048
        
 
1049
        delete desc;
 
1050
}
 
1051
 
 
1052
void
 
1053
Glyphs::Layout ()
 
1054
{
 
1055
        guint32 code_units, glyph_count, i;
 
1056
        bool first_char = true;
 
1057
        double x0, x1, y0, y1;
 
1058
        double bottom, right;
 
1059
        double bottom0, top0;
 
1060
        GlyphInfo *glyph;
 
1061
        GlyphAttr *attr;
 
1062
        int nglyphs = 0;
 
1063
        TextFont *font;
 
1064
        double offset;
 
1065
        bool cluster;
 
1066
        double scale;
 
1067
        int n = 0;
 
1068
        
 
1069
        invalid = false;
 
1070
        dirty = false;
 
1071
        
 
1072
        height = 0.0;
 
1073
        width = 0.0;
 
1074
        left = 0.0;
 
1075
        top = 0.0;
 
1076
        
 
1077
        if (path) {
 
1078
                moon_path_destroy (path);
 
1079
                path = NULL;
 
1080
        }
 
1081
        
 
1082
        // Silverlight only renders for None (other, invalid, values do not render anything)
 
1083
        if (!simulation_none) {
 
1084
                invalid = true;
 
1085
                return;
 
1086
        }
 
1087
        
 
1088
        if (!desc->GetFilename () || desc->GetSize () == 0.0) {
 
1089
                // required font fields have not been set
 
1090
                return;
 
1091
        }
 
1092
        
 
1093
        if (((!text || !text[0]) && attrs->IsEmpty ())) {
 
1094
                // no glyphs to render
 
1095
                return;
 
1096
        }
 
1097
        
 
1098
        if (fill == NULL) {
 
1099
                // no fill specified (unlike TextBlock, there is no default brush)
 
1100
                return;
 
1101
        }
 
1102
        
 
1103
        font = desc->GetFont ();
 
1104
        
 
1105
        // scale Advance, uOffset and vOffset units to pixels
 
1106
        scale = desc->GetSize () * 20.0 / 2048.0;
 
1107
        
 
1108
        right = origin_x;
 
1109
        left = origin_x;
 
1110
        x0 = origin_x;
 
1111
        
 
1112
        // OriginY is the baseline if specified
 
1113
        if (origin_y_specified) {
 
1114
                top0 = origin_y - font->Ascender ();
 
1115
                y0 = origin_y;
 
1116
        } else {
 
1117
                y0 = font->Ascender ();
 
1118
                top0 = 0.0;
 
1119
        }
 
1120
        
 
1121
        bottom0 = top0 + font->Height ();
 
1122
        
 
1123
        bottom = bottom0;
 
1124
        top = top0;
 
1125
        
 
1126
        path = moon_path_new (16);
 
1127
        
 
1128
        attr = (GlyphAttr *) attrs->First ();
 
1129
        
 
1130
        if (text && text[0]) {
 
1131
                gunichar *c = text;
 
1132
                
 
1133
                while (*c != 0) {
 
1134
                        if (attr && (attr->set & Cluster)) {
 
1135
                                // get the cluster's GlyphCount and CodeUnitCount
 
1136
                                glyph_count = attr->glyph_count;
 
1137
                                code_units = attr->code_units;
 
1138
                        } else {
 
1139
                                glyph_count = 1;
 
1140
                                code_units = 1;
 
1141
                        }
 
1142
                        
 
1143
                        if (glyph_count == 1 && code_units == 1)
 
1144
                                cluster = false;
 
1145
                        else
 
1146
                                cluster = true;
 
1147
                        
 
1148
                        // render the glyph cluster
 
1149
                        i = 0;
 
1150
                        do {
 
1151
                                if (attr && (attr->set & Index)) {
 
1152
                                        if (!(glyph = font->GetGlyphInfoByIndex (attr->index)))
 
1153
                                                goto next1;
 
1154
                                } else if (cluster) {
 
1155
                                        // indexes MUST be specified for each glyph in a cluster
 
1156
                                        moon_path_destroy (path);
 
1157
                                        invalid = true;
 
1158
                                        path = NULL;
 
1159
                                        goto done;
 
1160
                                } else {
 
1161
                                        glyph = font->GetGlyphInfo (*c);
 
1162
                                        if (!glyph)
 
1163
                                                goto next1;
 
1164
                                }
 
1165
                                
 
1166
                                y1 = y0;
 
1167
                                if (attr && (attr->set & vOffset)) {
 
1168
                                        offset = -(attr->voffset * scale);
 
1169
                                        bottom = MAX (bottom, bottom0 + offset);
 
1170
                                        top = MIN (top, top0 + offset);
 
1171
                                        y1 += offset;
 
1172
                                } 
 
1173
                                
 
1174
                                if (attr && (attr->set & uOffset)) {
 
1175
                                        offset = (attr->uoffset * scale);
 
1176
                                        left = MIN (left, x0 + offset);
 
1177
                                        x1 = x0 + offset;
 
1178
                                } else if (first_char) {
 
1179
                                        if (glyph->metrics.horiBearingX < 0)
 
1180
                                                x0 -= glyph->metrics.horiBearingX;
 
1181
                                        
 
1182
                                        first_char = false;
 
1183
                                        x1 = x0;
 
1184
                                } else {
 
1185
                                        x1 = x0;
 
1186
                                }
 
1187
                                
 
1188
                                right = MAX (right, x1 + glyph->metrics.horiAdvance);
 
1189
                                
 
1190
                                font->AppendPath (path, glyph, x1, y1);
 
1191
                                nglyphs++;
 
1192
                                
 
1193
                                if (attr && (attr->set & Advance))
 
1194
                                        x0 += attr->advance * scale;
 
1195
                                else
 
1196
                                        x0 += glyph->metrics.horiAdvance;
 
1197
                                
 
1198
                        next1:
 
1199
                                
 
1200
                                attr = attr ? (GlyphAttr *) attr->next : NULL;
 
1201
                                i++;
 
1202
                                
 
1203
                                if (i == glyph_count)
 
1204
                                        break;
 
1205
                                
 
1206
                                if (!attr) {
 
1207
                                        // there MUST be an attr for each glyph in a cluster
 
1208
                                        moon_path_destroy (path);
 
1209
                                        invalid = true;
 
1210
                                        path = NULL;
 
1211
                                        goto done;
 
1212
                                }
 
1213
                                
 
1214
                                if ((attr->set & Cluster)) {
 
1215
                                        // only the first glyph in a cluster may specify a cluster mapping
 
1216
                                        moon_path_destroy (path);
 
1217
                                        invalid = true;
 
1218
                                        path = NULL;
 
1219
                                        goto done;
 
1220
                                }
 
1221
                        } while (true);
 
1222
                        
 
1223
                        // consume the code units
 
1224
                        for (i = 0; i < code_units && *c != 0; i++)
 
1225
                                c++;
 
1226
                        
 
1227
                        n++;
 
1228
                }
 
1229
        }
 
1230
        
 
1231
        while (attr) {
 
1232
                if (attr->set & Cluster) {
 
1233
                        LOG_TEXT (stderr, "Can't use clusters past the end of the UnicodeString\n");
 
1234
                        moon_path_destroy (path);
 
1235
                        invalid = true;
 
1236
                        path = NULL;
 
1237
                        goto done;
 
1238
                }
 
1239
                
 
1240
                if (!(attr->set & Index)) {
 
1241
                        LOG_TEXT (stderr, "No index specified for glyph %d\n", n + 1);
 
1242
                        moon_path_destroy (path);
 
1243
                        invalid = true;
 
1244
                        path = NULL;
 
1245
                        goto done;
 
1246
                }
 
1247
                
 
1248
                if (!(glyph = font->GetGlyphInfoByIndex (attr->index)))
 
1249
                        goto next;
 
1250
                
 
1251
                y1 = y0;
 
1252
                if ((attr->set & vOffset)) {
 
1253
                        offset = -(attr->voffset * scale);
 
1254
                        bottom = MAX (bottom, bottom0 + offset);
 
1255
                        top = MIN (top, top0 + offset);
 
1256
                        y1 += offset;
 
1257
                }
 
1258
                
 
1259
                if ((attr->set & uOffset)) {
 
1260
                        offset = (attr->uoffset * scale);
 
1261
                        left = MIN (left, x0 + offset);
 
1262
                        x1 = x0 + offset;
 
1263
                } else if (first_char) {
 
1264
                        if (glyph->metrics.horiBearingX < 0)
 
1265
                                x0 -= glyph->metrics.horiBearingX;
 
1266
                        
 
1267
                        first_char = false;
 
1268
                        x1 = x0;
 
1269
                } else {
 
1270
                        x1 = x0;
 
1271
                }
 
1272
                
 
1273
                right = MAX (right, x1 + glyph->metrics.horiAdvance);
 
1274
                
 
1275
                font->AppendPath (path, glyph, x1, y1);
 
1276
                nglyphs++;
 
1277
                
 
1278
                if ((attr->set & Advance))
 
1279
                        x0 += attr->advance * scale;
 
1280
                else
 
1281
                        x0 += glyph->metrics.horiAdvance;
 
1282
                
 
1283
        next:
 
1284
                
 
1285
                attr = (GlyphAttr *) attr->next;
 
1286
                n++;
 
1287
        }
 
1288
        
 
1289
        if (nglyphs > 0) {
 
1290
                height = bottom - top;
 
1291
                width = right - left;
 
1292
        } else {
 
1293
                moon_path_destroy (path);
 
1294
                path = NULL;
 
1295
        }
 
1296
        
 
1297
done:
 
1298
        
 
1299
        font->unref ();
 
1300
}
 
1301
 
 
1302
void
 
1303
Glyphs::GetSizeForBrush (cairo_t *cr, double *width, double *height)
 
1304
{
 
1305
        if (dirty)
 
1306
                Layout ();
 
1307
        
 
1308
        *height = this->height;
 
1309
        *width = this->width;
 
1310
}
 
1311
 
 
1312
Point
 
1313
Glyphs::GetOriginPoint () 
 
1314
{
 
1315
        if (origin_y_specified) {
 
1316
                TextFont *font = desc->GetFont ();
 
1317
                
 
1318
                double d = font->Descender ();
 
1319
                double h = font->Height ();
 
1320
                
 
1321
                font->unref ();
 
1322
                
 
1323
                return Point (origin_x, origin_y - d - h);
 
1324
        } else {
 
1325
                return Point (origin_x, 0);
 
1326
        }
 
1327
}
 
1328
 
 
1329
void
 
1330
Glyphs::Render (cairo_t *cr, int x, int y, int width, int height)
 
1331
{
 
1332
        if (this->width == 0.0 && this->height == 0.0)
 
1333
                return;
 
1334
        
 
1335
        if (invalid) {
 
1336
                // do not render anything if our state is invalid to keep with Silverlight's behavior.
 
1337
                // (Note: rendering code also assumes everything is kosher)
 
1338
                return;
 
1339
        }
 
1340
        
 
1341
        if (path == NULL || path->cairo.num_data == 0) {
 
1342
                // No glyphs to render
 
1343
                return;
 
1344
        }
 
1345
        
 
1346
        cairo_save (cr);
 
1347
        cairo_set_matrix (cr, &absolute_xform);
 
1348
        
 
1349
        Point p = GetOriginPoint ();
 
1350
        Rect area = Rect (p.x, p.y, 0, 0);
 
1351
        GetSizeForBrush (cr, &(area.width), &(area.height));
 
1352
        fill->SetupBrush (cr, area);
 
1353
        
 
1354
        cairo_append_path (cr, &path->cairo);
 
1355
        cairo_fill (cr);
 
1356
        
 
1357
        cairo_restore (cr);
 
1358
}
 
1359
 
 
1360
void 
 
1361
Glyphs::ComputeBounds ()
 
1362
{
 
1363
        if (dirty)
 
1364
                Layout ();
 
1365
        
 
1366
        bounds = IntersectBoundsWithClipPath (Rect (left, top, width, height), false).Transform (&absolute_xform);
 
1367
}
 
1368
 
 
1369
bool
 
1370
Glyphs::InsideObject (cairo_t *cr, double x, double y)
 
1371
{
 
1372
        double nx = x;
 
1373
        double ny = y;
 
1374
 
 
1375
        TransformPoint (&nx, &ny);
 
1376
        
 
1377
        return (nx >= left && ny >= top && nx < left + width && ny < top + height);
 
1378
}
 
1379
 
 
1380
Point
 
1381
Glyphs::GetTransformOrigin ()
 
1382
{
 
1383
        // Glyphs seems to always use 0,0 no matter what is specified in the RenderTransformOrigin nor the OriginX/Y points
 
1384
        return Point (0.0, 0.0);
 
1385
}
 
1386
 
 
1387
void
 
1388
Glyphs::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
 
1389
{
 
1390
        if (prop == Glyphs::FillProperty)
 
1391
                Invalidate ();
 
1392
        else
 
1393
                FrameworkElement::OnSubPropertyChanged (prop, obj, subobj_args);
 
1394
}
 
1395
 
 
1396
void
 
1397
Glyphs::data_write (void *buf, gint32 offset, gint32 n, gpointer data)
 
1398
{
 
1399
        ;
 
1400
}
 
1401
 
 
1402
void
 
1403
Glyphs::size_notify (gint64 size, gpointer data)
 
1404
{
 
1405
        ;
 
1406
}
 
1407
 
 
1408
void
 
1409
Glyphs::downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
 
1410
{
 
1411
        ((Glyphs *) closure)->DownloaderComplete ();
 
1412
}
 
1413
 
 
1414
void
 
1415
Glyphs::DownloaderComplete ()
 
1416
{
 
1417
        const char *filename, *path;
 
1418
        struct stat st;
 
1419
        
 
1420
        /* the download was aborted */
 
1421
        if (!(filename = downloader->getFileDownloader ()->GetDownloadedFile ()))
 
1422
                return;
 
1423
        
 
1424
        if (stat (filename, &st) == -1 || !S_ISREG (st.st_mode))
 
1425
                return;
 
1426
        
 
1427
        if (!downloader->getFileDownloader ()->IsDeobfuscated ()) {
 
1428
                if ((path = deobfuscate_font (downloader, filename)))
 
1429
                        filename = path;
 
1430
                
 
1431
                downloader->getFileDownloader ()->SetDeobfuscated (true);
 
1432
        }
 
1433
        
 
1434
        desc->SetFilename (filename);
 
1435
        desc->SetIndex (index);
 
1436
        dirty = true;
 
1437
        
 
1438
        UpdateBounds (true);
 
1439
        Invalidate ();
 
1440
}
 
1441
 
 
1442
static void
 
1443
print_parse_error (const char *in, const char *where, const char *reason)
 
1444
{
 
1445
#if DEBUG
 
1446
        if (debug_flags & RUNTIME_DEBUG_TEXT) {
 
1447
                int i;
 
1448
        
 
1449
                fprintf (stderr, "Glyph Indices parse error: \"%s\": %s\n", in, reason);
 
1450
                fprintf (stderr, "                            ");
 
1451
                for (i = 0; i < (where - in); i++)
 
1452
                        fputc (' ', stderr);
 
1453
                fprintf (stderr, "^\n");
 
1454
        }
 
1455
#endif
 
1456
}
 
1457
 
 
1458
void
 
1459
Glyphs::SetIndicesInternal (const char *in)
 
1460
{
 
1461
        register const char *inptr = in;
 
1462
        GlyphAttr *glyph;
 
1463
        double value;
 
1464
        char *end;
 
1465
        uint bit;
 
1466
        int n;
 
1467
        
 
1468
        attrs->Clear (true);
 
1469
        
 
1470
        if (in == NULL)
 
1471
                return;
 
1472
        
 
1473
        while (g_ascii_isspace (*inptr))
 
1474
                inptr++;
 
1475
        
 
1476
        while (*inptr) {
 
1477
                glyph = new GlyphAttr ();
 
1478
                
 
1479
                while (g_ascii_isspace (*inptr))
 
1480
                        inptr++;
 
1481
                
 
1482
                // check for a cluster
 
1483
                if (*inptr == '(') {
 
1484
                        inptr++;
 
1485
                        while (g_ascii_isspace (*inptr))
 
1486
                                inptr++;
 
1487
                        
 
1488
                        errno = 0;
 
1489
                        glyph->code_units = strtoul (inptr, &end, 10);
 
1490
                        if (glyph->code_units == 0 || (glyph->code_units == LONG_MAX && errno != 0)) {
 
1491
                                // invalid cluster
 
1492
                                print_parse_error (in, inptr, errno ? strerror (errno) : "invalid cluster mapping; CodeUnitCount cannot be 0");
 
1493
                                delete glyph;
 
1494
                                return;
 
1495
                        }
 
1496
                        
 
1497
                        inptr = end;
 
1498
                        while (g_ascii_isspace (*inptr))
 
1499
                                inptr++;
 
1500
                        
 
1501
                        if (*inptr != ':') {
 
1502
                                // invalid cluster
 
1503
                                print_parse_error (in, inptr, "expected ':'");
 
1504
                                delete glyph;
 
1505
                                return;
 
1506
                        }
 
1507
                        
 
1508
                        inptr++;
 
1509
                        while (g_ascii_isspace (*inptr))
 
1510
                                inptr++;
 
1511
                        
 
1512
                        errno = 0;
 
1513
                        glyph->glyph_count = strtoul (inptr, &end, 10);
 
1514
                        if (glyph->glyph_count == 0 || (glyph->glyph_count == LONG_MAX && errno != 0)) {
 
1515
                                // invalid cluster
 
1516
                                print_parse_error (in, inptr, errno ? strerror (errno) : "invalid cluster mapping; GlyphCount cannot be 0");
 
1517
                                delete glyph;
 
1518
                                return;
 
1519
                        }
 
1520
                        
 
1521
                        inptr = end;
 
1522
                        while (g_ascii_isspace (*inptr))
 
1523
                                inptr++;
 
1524
                        
 
1525
                        if (*inptr != ')') {
 
1526
                                // invalid cluster
 
1527
                                print_parse_error (in, inptr, "expected ')'");
 
1528
                                delete glyph;
 
1529
                                return;
 
1530
                        }
 
1531
                        
 
1532
                        glyph->set |= Cluster;
 
1533
                        inptr++;
 
1534
                        
 
1535
                        while (g_ascii_isspace (*inptr))
 
1536
                                inptr++;
 
1537
                }
 
1538
                
 
1539
                if (*inptr >= '0' && *inptr <= '9') {
 
1540
                        errno = 0;
 
1541
                        glyph->index = strtoul (inptr, &end, 10);
 
1542
                        if ((glyph->index == 0 || glyph->index == LONG_MAX) && errno != 0) {
 
1543
                                // invalid glyph index
 
1544
                                print_parse_error (in, inptr, strerror (errno));
 
1545
                                delete glyph;
 
1546
                                return;
 
1547
                        }
 
1548
                        
 
1549
                        glyph->set |= Index;
 
1550
                        
 
1551
                        inptr = end;
 
1552
                        while (g_ascii_isspace (*inptr))
 
1553
                                inptr++;
 
1554
                }
 
1555
                
 
1556
                bit = (uint) Advance;
 
1557
                n = 0;
 
1558
                
 
1559
                while (*inptr == ',' && n < 3) {
 
1560
                        inptr++;
 
1561
                        while (g_ascii_isspace (*inptr))
 
1562
                                inptr++;
 
1563
                        
 
1564
                        if (*inptr != ',') {
 
1565
                                value = g_ascii_strtod (inptr, &end);
 
1566
                                if ((value == 0.0 || value == HUGE_VAL || value == -HUGE_VAL) && errno != 0) {
 
1567
                                        // invalid advance or offset
 
1568
                                        print_parse_error (in, inptr, strerror (errno));
 
1569
                                        delete glyph;
 
1570
                                        return;
 
1571
                                }
 
1572
                        } else {
 
1573
                                end = (char *) inptr;
 
1574
                        }
 
1575
                        
 
1576
                        if (end > inptr) {
 
1577
                                switch ((GlyphAttrMask) bit) {
 
1578
                                case Advance:
 
1579
                                        glyph->advance = value;
 
1580
                                        glyph->set |= Advance;
 
1581
                                        break;
 
1582
                                case uOffset:
 
1583
                                        glyph->uoffset = value;
 
1584
                                        glyph->set |= uOffset;
 
1585
                                        break;
 
1586
                                case vOffset:
 
1587
                                        glyph->voffset = value;
 
1588
                                        glyph->set |= vOffset;
 
1589
                                        break;
 
1590
                                default:
 
1591
                                        break;
 
1592
                                }
 
1593
                        }
 
1594
                        
 
1595
                        inptr = end;
 
1596
                        while (g_ascii_isspace (*inptr))
 
1597
                                inptr++;
 
1598
                        
 
1599
                        bit <<= 1;
 
1600
                        n++;
 
1601
                }
 
1602
                
 
1603
                attrs->Append (glyph);
 
1604
                
 
1605
                while (g_ascii_isspace (*inptr))
 
1606
                        inptr++;
 
1607
                
 
1608
                if (*inptr && *inptr != ';') {
 
1609
                        print_parse_error (in, inptr, "expected ';'");
 
1610
                        return;
 
1611
                }
 
1612
                
 
1613
                if (*inptr == '\0')
 
1614
                        break;
 
1615
                
 
1616
                inptr++;
 
1617
        }
 
1618
}
 
1619
 
 
1620
void
 
1621
Glyphs::DownloadFont (Surface *surface, const char *url)
 
1622
{
 
1623
        Uri *uri = new Uri ();
 
1624
        char *str;
 
1625
        
 
1626
        if (uri->Parse (url)) {
 
1627
                if ((downloader = surface->CreateDownloader ())) {
 
1628
                        if (uri->fragment) {
 
1629
                                if ((index = strtol (uri->fragment, NULL, 10)) < 0 || index == LONG_MAX)
 
1630
                                        index = 0;
 
1631
                        }
 
1632
                        
 
1633
                        str = uri->ToString (UriHideFragment);
 
1634
                        downloader->Open ("GET", str, XamlPolicy);
 
1635
                        g_free (str);
 
1636
                        
 
1637
                        downloader->AddHandler (downloader->CompletedEvent, downloader_complete, this);
 
1638
                        if (downloader->Started () || downloader->Completed ()) {
 
1639
                                if (downloader->Completed ())
 
1640
                                        DownloaderComplete ();
 
1641
                        } else {
 
1642
                                downloader->SetWriteFunc (data_write, size_notify, this);
 
1643
                                
 
1644
                                // This is what actually triggers the download
 
1645
                                downloader->Send ();
 
1646
                        }
 
1647
                } else {
 
1648
                        // we're shutting down
 
1649
                }
 
1650
        }
 
1651
        
 
1652
        delete uri;
 
1653
}
 
1654
 
 
1655
void
 
1656
Glyphs::SetSurface (Surface *surface)
 
1657
{
 
1658
        if (GetSurface() == surface)
 
1659
                return;
 
1660
 
 
1661
        const char *uri;
 
1662
        
 
1663
        FrameworkElement::SetSurface (surface);
 
1664
        
 
1665
        if (!uri_changed || !surface)
 
1666
                return;
 
1667
        
 
1668
        if ((uri = GetFontUri ()) && *uri)
 
1669
                DownloadFont (surface, uri);
 
1670
        
 
1671
        uri_changed = false;
 
1672
}
 
1673
 
 
1674
void
 
1675
Glyphs::OnPropertyChanged (PropertyChangedEventArgs *args)
 
1676
{
 
1677
        bool invalidate = true;
 
1678
        
 
1679
        if (args->property->GetOwnerType() != Type::GLYPHS) {
 
1680
                FrameworkElement::OnPropertyChanged (args);
 
1681
                return;
 
1682
        }
 
1683
        
 
1684
        if (args->property == Glyphs::FontUriProperty) {
 
1685
                const char *str = args->new_value ? args->new_value->AsString () : NULL;
 
1686
                Surface *surface = GetSurface ();
 
1687
                
 
1688
                if (downloader) {
 
1689
                        downloader->Abort ();
 
1690
                        downloader->unref ();
 
1691
                        downloader = NULL;
 
1692
                        index = 0;
 
1693
                }
 
1694
                
 
1695
                if (surface) {
 
1696
                        if (str && *str)
 
1697
                                DownloadFont (surface, str);
 
1698
                        uri_changed = false;
 
1699
                } else {
 
1700
                        uri_changed = true;
 
1701
                }
 
1702
                
 
1703
                invalidate = false;
 
1704
        } else if (args->property == Glyphs::FillProperty) {
 
1705
                fill = args->new_value ? args->new_value->AsBrush() : NULL;
 
1706
        } else if (args->property == Glyphs::UnicodeStringProperty) {
 
1707
                const char *str = args->new_value ? args->new_value->AsString () : NULL;
 
1708
                g_free (text);
 
1709
                
 
1710
                if (str != NULL)
 
1711
                        text = g_utf8_to_ucs4_fast (str, -1, NULL);
 
1712
                else
 
1713
                        text = NULL;
 
1714
                
 
1715
                dirty = true;
 
1716
        } else if (args->property == Glyphs::IndicesProperty) {
 
1717
                const char *str = args->new_value ? args->new_value->AsString () : NULL;
 
1718
                SetIndicesInternal (str);
 
1719
                dirty = true;
 
1720
        } else if (args->property == Glyphs::FontRenderingEmSizeProperty) {
 
1721
                double size = args->new_value->AsDouble();
 
1722
                desc->SetSize (size);
 
1723
                dirty = true;
 
1724
        } else if (args->property == Glyphs::OriginXProperty) {
 
1725
                origin_x = args->new_value->AsDouble ();
 
1726
                dirty = true;
 
1727
        } else if (args->property == Glyphs::OriginYProperty) {
 
1728
                origin_y = args->new_value->AsDouble ();
 
1729
                origin_y_specified = true;
 
1730
                dirty = true;
 
1731
        } else if (args->property == Glyphs::StyleSimulationsProperty) {
 
1732
                // Silverlight 1.0 does not implement this property but, if present, 
 
1733
                // requires it to be 0 (or else nothing is displayed)
 
1734
                bool none = (args->new_value->AsInt32 () == StyleSimulationsNone);
 
1735
                dirty = (none != simulation_none);
 
1736
                simulation_none = none;
 
1737
        }
 
1738
        
 
1739
        if (invalidate)
 
1740
                Invalidate ();
 
1741
        
 
1742
        if (dirty)
 
1743
                UpdateBounds (true);
 
1744
        
 
1745
        NotifyListenersOfPropertyChange (args);
 
1746
}
 
1747
 
 
1748
 
 
1749
void
 
1750
Glyphs::SetFill (Brush *fill)
 
1751
{
 
1752
        SetValue (Glyphs::FillProperty, Value (fill));
 
1753
}
 
1754
 
 
1755
Brush *
 
1756
Glyphs::GetFill ()
 
1757
{
 
1758
        Value *value = GetValue (Glyphs::FillProperty);
 
1759
        
 
1760
        return value ? value->AsBrush () : NULL;
 
1761
}