1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
6
* Moonlight List (moonlight-list@lists.ximian.com)
8
* Copyright 2007 Novell, Inc. (http://www.novell.com)
10
* See the LICENSE file included with the distribution for details.
19
#include <sys/types.h>
24
#include "file-downloader.h"
33
static SolidColorBrush *default_foreground_brush = NULL;
36
default_foreground (void)
38
if (!default_foreground_brush)
39
default_foreground_brush = new SolidColorBrush ("black");
41
return (Brush *) default_foreground_brush;
47
if (default_foreground_brush) {
48
default_foreground_brush->unref ();
49
default_foreground_brush = NULL;
65
/* initialize the font description */
66
font = new TextFontDescription ();
74
static DependencyProperty *
75
textblock_property (DependencyProperty *prop)
77
if (prop == Inline::FontFamilyProperty)
78
return TextBlock::FontFamilyProperty;
80
if (prop == Inline::FontStretchProperty)
81
return TextBlock::FontStretchProperty;
83
if (prop == Inline::FontWeightProperty)
84
return TextBlock::FontWeightProperty;
86
if (prop == Inline::FontStyleProperty)
87
return TextBlock::FontStyleProperty;
89
if (prop == Inline::FontSizeProperty)
90
return TextBlock::FontSizeProperty;
92
if (prop == Inline::ForegroundProperty)
93
return TextBlock::ForegroundProperty;
95
if (prop == Inline::TextDecorationsProperty)
96
return TextBlock::TextDecorationsProperty;
102
Inline::GetDefaultValue (DependencyProperty *prop)
104
DependencyObject *parent = GetLogicalParent ();
106
if (parent && parent->Is (Type::TEXTBLOCK)) {
107
DependencyProperty *text_prop = textblock_property (prop);
110
return parent->GetValue (text_prop);
112
return prop->GetDefaultValue();
115
// not yet attached to a textblock
117
if (prop == Inline::ForegroundProperty) {
118
SolidColorBrush *brush = new SolidColorBrush ("black");
120
SetValue (prop, Value (brush));
123
return GetValue (prop);
126
// all other properties have a default value
127
return prop->GetDefaultValue();
131
Inline::OnPropertyChanged (PropertyChangedEventArgs *args)
133
if (args->property->GetOwnerType() != Type::INLINE) {
134
DependencyObject::OnPropertyChanged (args);
138
if (args->property == Inline::FontFamilyProperty) {
139
if (args->new_value) {
140
char *family = args->new_value->AsString ();
141
font->SetFamily (family);
143
font->UnsetFields (FontMaskFamily);
145
} else if (args->property == Inline::FontSizeProperty) {
146
if (args->new_value) {
147
double size = args->new_value->AsDouble ();
148
font->SetSize (size);
150
font->UnsetFields (FontMaskSize);
152
} else if (args->property == Inline::FontStretchProperty) {
153
if (args->new_value) {
154
FontStretches stretch = (FontStretches) args->new_value->AsInt32 ();
155
font->SetStretch (stretch);
157
font->UnsetFields (FontMaskStretch);
159
} else if (args->property == Inline::FontStyleProperty) {
160
if (args->new_value) {
161
FontStyles style = (FontStyles) args->new_value->AsInt32 ();
162
font->SetStyle (style);
164
font->UnsetFields (FontMaskStyle);
166
} else if (args->property == Inline::FontWeightProperty) {
167
if (args->new_value) {
168
FontWeights weight = (FontWeights) args->new_value->AsInt32 ();
169
font->SetWeight (weight);
171
font->UnsetFields (FontMaskWeight);
173
} else if (args->property == Inline::ForegroundProperty) {
174
foreground = args->new_value ? args->new_value->AsBrush () : NULL;
178
NotifyListenersOfPropertyChange (args);
182
Inline::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
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);
196
TextBlock::TextBlock ()
205
/* initialize the font description and layout */
206
hints = new TextLayoutHints (TextAlignmentLeft, LineStackingStrategyMaxHeight, 0.0);
207
layout = new TextLayout ();
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);
216
Brush *brush = new SolidColorBrush ("black");
219
SetValue (TextBlock::ForegroundProperty, Value (brush));
220
SetValue (TextBlock::InlinesProperty, Value::CreateUnref (new InlineCollection ()));
225
TextBlock::~TextBlock ()
231
if (downloader != NULL) {
232
downloader_abort (downloader);
233
downloader->unref ();
238
TextBlock::SetFontSource (Downloader *downloader)
240
if (this->downloader == downloader)
243
if (this->downloader) {
244
this->downloader->Abort ();
245
this->downloader->unref ();
246
this->downloader = NULL;
250
this->downloader = downloader;
253
downloader->AddHandler (downloader->CompletedEvent, downloader_complete, this);
254
if (downloader->Started () || downloader->Completed ()) {
255
if (downloader->Completed ())
256
DownloaderComplete ();
258
downloader->SetWriteFunc (data_write, size_notify, this);
260
// This is what actually triggers the download
264
font->SetFilename (NULL);
273
TextBlock::Render (cairo_t *cr, int x, int y, int width, int height)
279
cairo_set_matrix (cr, &absolute_xform);
285
TextBlock::ComputeBounds ()
287
extents = Rect (0, 0, GetBoundingWidth (), GetBoundingHeight ());
288
bounds = IntersectBoundsWithClipPath (extents, false).Transform (&absolute_xform);
292
TextBlock::InsideObject (cairo_t *cr, double x, double y)
301
TransformPoint (&nx, &ny);
303
if (nx >= 0.0 && ny >= 0.0 && nx < GetActualWidth () && ny < GetActualHeight ())
311
TextBlock::GetTransformOrigin ()
313
Point *user_xform_origin = GetRenderTransformOrigin ();
315
return Point (user_xform_origin->x * GetBoundingWidth (), user_xform_origin->y * GetBoundingHeight ());
319
TextBlock::GetSizeForBrush (cairo_t *cr, double *width, double *height)
321
*height = GetActualHeight ();
322
*width = GetActualWidth ();
326
TextBlock::CalcActualWidthHeight (cairo_t *cr)
328
bool destroy = false;
331
cr = measuring_context_create ();
337
cairo_identity_matrix (cr);
342
measuring_context_destroy (cr);
350
TextBlock::Layout (cairo_t *cr)
352
Value *value = GetValueNoDefault (TextBlock::TextProperty);
353
InlineCollection *inlines = GetInlines ();
354
TextDecorations decorations;
360
// If no text has ever been set on this TextBlock,
361
// then skip calculating actual width/height.
370
layout->SetWrapping (GetTextWrapping ());
372
if ((value = GetValueNoDefault (FrameworkElement::WidthProperty))) {
374
Thickness *padding = GetPadding ();
375
double pad = padding->left + padding->right;
379
double width = value->AsDouble ();
382
layout->SetTextRuns (runs);
388
layout->SetMaxWidth (width - pad);
390
layout->SetMaxWidth (-1.0);
393
decorations = GetTextDecorations ();
394
font_mask = font->GetFields ();
396
if (inlines != NULL) {
397
guint8 run_mask, inherited_mask;
398
TextFontDescription *ifont;
399
TextDecorations deco;
404
for (int i = 0; i < inlines->GetCount (); i++) {
405
item = inlines->GetValueAt (i)->AsInline ();
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);
414
inherited_mask = (FontMask) (font_mask & ~run_mask);
416
// Inherit the TextDecorations from the parent TextBlock if unset
417
if ((value = item->GetValue (Inline::TextDecorationsProperty)))
418
deco = (TextDecorations) value->AsInt32 ();
422
switch (item->GetObjectType ()) {
426
text = run->GetText ();
428
if (text && text[0]) {
429
const char *inptr, *inend;
435
while (*inend && *inend != '\n')
439
runs->Append (new TextRun (inptr, inend - inptr, deco,
440
ifont, &item->foreground));
445
runs->Append (new TextRun (ifont));
451
case Type::LINEBREAK:
452
runs->Append (new TextRun (ifont));
458
if (inherited_mask != 0)
459
ifont->UnsetFields (inherited_mask);
463
layout->SetTextRuns (runs);
464
layout->Layout (hints);
466
layout->GetActualExtents (&actual_width, &actual_height);
467
//layout->GetLayoutExtents (&bbox_width, &bbox_height);
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 ();
481
SetActualHeight (actual_height);
482
SetActualWidth (actual_width);
488
TextBlock::Paint (cairo_t *cr)
494
if (!(fg = GetForeground ()))
495
fg = default_foreground ();
497
Point offset = Point (0.0, 0.0);
499
Thickness *padding = GetPadding ();
500
offset.x = padding->left;
501
offset.y = padding->top;
504
layout->Render (cr, GetOriginPoint (), offset, hints, fg);
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);
515
TextBlock::GetTextInternal ()
517
InlineCollection *inlines = GetInlines ();
521
block = g_string_new ("");
523
if (inlines != NULL) {
527
for (int i = 0; i < inlines->GetCount (); i++) {
528
item = inlines->GetValueAt (i)->AsInline ();
530
switch (item->GetObjectType ()) {
532
text = ((Run *) item)->GetText ();
535
g_string_append (block, text);
537
case Type::LINEBREAK:
538
g_string_append_c (block, '\n');
547
g_string_free (block, false);
553
inlines_simple_text_equal (InlineCollection *curInlines, InlineCollection *newInlines)
555
const char *text1, *text2;
558
if (curInlines->GetCount () != newInlines->GetCount ())
561
for (int i = 0; i < curInlines->GetCount () && i < newInlines->GetCount (); i++) {
562
run1 = curInlines->GetValueAt (i)->AsInline ();
563
run2 = newInlines->GetValueAt (i)->AsInline ();
565
if (run1->GetObjectType () != run2->GetObjectType ())
568
if (run1->GetObjectType () == Type::RUN) {
569
text1 = ((Run *) run1)->GetText ();
570
text2 = ((Run *) run2)->GetText ();
572
if (text1 && text2 && strcmp (text1, text2) != 0)
574
else if ((text1 && !text2) || (!text1 && text2))
578
// newInlines uses TextBlock font/brush properties, so
579
// if curInlines uses any non-default props then they
582
if (run1->font->GetFields () != 0)
585
if (run1->GetValueNoDefault (Inline::TextDecorationsProperty) != NULL)
588
if (run1->foreground != NULL)
596
TextBlock::SetTextInternal (const char *text)
598
InlineCollection *curInlines = GetInlines ();
599
InlineCollection *inlines = NULL;
600
char *inptr, *buf, *d;
604
if (text && text[0]) {
605
inlines = new InlineCollection ();
607
d = buf = (char *) g_malloc (strlen (text) + 1);
620
while (*inptr != '\n')
627
run->SetValue (Run::TextProperty, Value (txt));
633
run = new LineBreak ();
643
if (curInlines && inlines_simple_text_equal (curInlines, inlines)) {
644
// old/new inlines are equal, don't set the new value
650
SetValue (TextBlock::InlinesProperty, Value (inlines));
654
} else if (curInlines) {
655
curInlines->Clear ();
662
TextBlock::OnPropertyChanged (PropertyChangedEventArgs *args)
664
bool invalidate = true;
666
if (args->property->GetOwnerType () != Type::TEXTBLOCK) {
667
FrameworkElement::OnPropertyChanged (args);
668
if (args->property == FrameworkElement::WidthProperty) {
669
if (GetTextWrapping () != TextWrappingNoWrap)
678
if (args->property == TextBlock::FontFamilyProperty) {
679
char *family = args->new_value ? args->new_value->AsString () : NULL;
680
font->SetFamily (family);
683
} else if (args->property == TextBlock::FontSizeProperty) {
684
double size = args->new_value->AsDouble ();
685
font->SetSize (size);
688
} else if (args->property == TextBlock::FontStretchProperty) {
689
FontStretches stretch = (FontStretches) args->new_value->AsInt32 ();
690
font->SetStretch (stretch);
693
} else if (args->property == TextBlock::FontStyleProperty) {
694
FontStyles style = (FontStyles) args->new_value->AsInt32 ();
695
font->SetStyle (style);
698
} else if (args->property == TextBlock::FontWeightProperty) {
699
FontWeights weight = (FontWeights) args->new_value->AsInt32 ();
700
font->SetWeight (weight);
703
} else if (args->property == TextBlock::TextProperty) {
705
// result of a change to the TextBlock.Text property
706
char *text = args->new_value ? args->new_value->AsString () : NULL;
708
if (!SetTextInternal (text)) {
709
// no change so nothing to invalidate
715
// result of a change to the TextBlock.Inlines property
718
} else if (args->property == TextBlock::TextDecorationsProperty) {
720
} else if (args->property == TextBlock::TextWrappingProperty) {
722
} else if (args->property == TextBlock::InlinesProperty) {
724
// result of a change to the TextBlock.Inlines property
725
char *text = GetTextInternal ();
728
SetValue (TextBlock::TextProperty, Value (text));
733
// result of a change to the TextBlock.Text property
737
} else if (args->property == TextBlock::LineStackingStrategyProperty) {
738
hints->SetLineStackingStrategy ((LineStackingStrategy) args->new_value->AsInt32 ());
740
} else if (args->property == TextBlock::LineHeightProperty) {
741
hints->SetLineHeight (args->new_value->AsDouble ());
743
} else if (args->property == TextBlock::TextAlignmentProperty) {
744
hints->SetTextAlignment ((TextAlignment) args->new_value->AsInt32 ());
745
} else if (args->property == TextBlock::PaddingProperty) {
748
} else if (args->property == TextBlock::ActualHeightProperty) {
750
} else if (args->property == TextBlock::ActualWidthProperty) {
761
NotifyListenersOfPropertyChange (args);
765
TextBlock::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
767
if (prop->GetOwnerType () != Type::TEXTBLOCK) {
768
FrameworkElement::OnSubPropertyChanged (prop, obj, subobj_args);
772
if (prop == TextBlock::ForegroundProperty)
777
TextBlock::OnCollectionChanged (Collection *col, CollectionChangedEventArgs *args)
779
bool update_bounds = false;
780
bool update_text = false;
782
if (col != GetInlines ()) {
783
FrameworkElement::OnCollectionChanged (col, args);
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;
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;
807
char *text = GetTextInternal ();
810
SetValue (TextBlock::TextProperty, Value (text));
822
TextBlock::OnCollectionItemChanged (Collection *col, DependencyObject *obj, PropertyChangedEventArgs *args)
827
if (col != GetInlines ()) {
828
FrameworkElement::OnCollectionItemChanged (col, obj, args);
832
// only update bounds if a property other than the Foreground changed
833
update_bounds = args->property != Inline::ForegroundProperty;
835
// only update our TextProperty if change was in a Run's Text property
836
update_text = args->property == Run::TextProperty;
841
char *text = GetTextInternal ();
844
SetValue (TextBlock::TextProperty, Value (text));
856
TextBlock::GetValue (DependencyProperty *property)
858
if (dirty && ((property == TextBlock::ActualHeightProperty) || (property == TextBlock::ActualWidthProperty)))
859
CalcActualWidthHeight (NULL);
861
return DependencyObject::GetValue (property);
865
TextBlock::data_write (void *buf, gint32 offset, gint32 n, gpointer data)
871
TextBlock::size_notify (gint64 size, gpointer data)
877
TextBlock::downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
879
((TextBlock *) closure)->DownloaderComplete ();
883
deobfuscate_font (Downloader *downloader, const char *path)
885
char *filename, guid[16];
891
if (!(str = downloader->GetUri ()))
895
if (!uri->Parse (str) || !uri->path) {
900
if (!(str = strrchr (uri->path, '/')))
905
if (!DecodeObfuscatedFontGUID (str, guid)) {
910
name = g_string_new (str);
911
g_string_append (name, ".XXXXXX");
914
filename = g_build_filename (g_get_tmp_dir (), name->str, NULL);
915
g_string_free (name, true);
917
if ((fd = g_mkstemp (filename)) == -1) {
922
if (CopyFileTo (path, fd) == -1 || !DeobfuscateFontFileWithGUID (filename, guid, NULL)) {
928
downloader->getFileDownloader ()->SetDeobfuscatedFile (filename);
931
return downloader->getFileDownloader ()->GetDownloadedFile ();
935
TextBlock::DownloaderComplete ()
937
const char *filename, *path;
940
/* the download was aborted */
941
if (!(path = downloader->getFileDownloader ()->GetUnzippedPath ()))
944
if (stat (path, &st) == -1)
947
// check for obfuscated fonts
948
if (S_ISREG (st.st_mode) && !downloader->getFileDownloader ()->IsDeobfuscated ()) {
949
if ((filename = deobfuscate_font (downloader, path)))
952
downloader->getFileDownloader ()->SetDeobfuscated (true);
955
font->SetFilename (path);
963
TextBlock::SetActualHeight (double height)
965
SetValue (TextBlock::ActualHeightProperty, Value (height));
969
TextBlock::SetActualWidth (double width)
971
SetValue (TextBlock::ActualWidthProperty, Value (width));
986
class GlyphAttr : public List::Node {
999
GlyphAttr::GlyphAttr ()
1008
desc = new TextFontDescription ();
1009
desc->SetSize (0.0);
1015
attrs = new List ();
1019
origin_y_specified = false;
1028
simulation_none = true;
1029
uri_changed = false;
1037
downloader_abort (downloader);
1038
downloader->unref ();
1042
moon_path_destroy (path);
1044
attrs->Clear (true);
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;
1078
moon_path_destroy (path);
1082
// Silverlight only renders for None (other, invalid, values do not render anything)
1083
if (!simulation_none) {
1088
if (!desc->GetFilename () || desc->GetSize () == 0.0) {
1089
// required font fields have not been set
1093
if (((!text || !text[0]) && attrs->IsEmpty ())) {
1094
// no glyphs to render
1099
// no fill specified (unlike TextBlock, there is no default brush)
1103
font = desc->GetFont ();
1105
// scale Advance, uOffset and vOffset units to pixels
1106
scale = desc->GetSize () * 20.0 / 2048.0;
1112
// OriginY is the baseline if specified
1113
if (origin_y_specified) {
1114
top0 = origin_y - font->Ascender ();
1117
y0 = font->Ascender ();
1121
bottom0 = top0 + font->Height ();
1126
path = moon_path_new (16);
1128
attr = (GlyphAttr *) attrs->First ();
1130
if (text && text[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;
1143
if (glyph_count == 1 && code_units == 1)
1148
// render the glyph cluster
1151
if (attr && (attr->set & Index)) {
1152
if (!(glyph = font->GetGlyphInfoByIndex (attr->index)))
1154
} else if (cluster) {
1155
// indexes MUST be specified for each glyph in a cluster
1156
moon_path_destroy (path);
1161
glyph = font->GetGlyphInfo (*c);
1167
if (attr && (attr->set & vOffset)) {
1168
offset = -(attr->voffset * scale);
1169
bottom = MAX (bottom, bottom0 + offset);
1170
top = MIN (top, top0 + offset);
1174
if (attr && (attr->set & uOffset)) {
1175
offset = (attr->uoffset * scale);
1176
left = MIN (left, x0 + offset);
1178
} else if (first_char) {
1179
if (glyph->metrics.horiBearingX < 0)
1180
x0 -= glyph->metrics.horiBearingX;
1188
right = MAX (right, x1 + glyph->metrics.horiAdvance);
1190
font->AppendPath (path, glyph, x1, y1);
1193
if (attr && (attr->set & Advance))
1194
x0 += attr->advance * scale;
1196
x0 += glyph->metrics.horiAdvance;
1200
attr = attr ? (GlyphAttr *) attr->next : NULL;
1203
if (i == glyph_count)
1207
// there MUST be an attr for each glyph in a cluster
1208
moon_path_destroy (path);
1214
if ((attr->set & Cluster)) {
1215
// only the first glyph in a cluster may specify a cluster mapping
1216
moon_path_destroy (path);
1223
// consume the code units
1224
for (i = 0; i < code_units && *c != 0; i++)
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);
1240
if (!(attr->set & Index)) {
1241
LOG_TEXT (stderr, "No index specified for glyph %d\n", n + 1);
1242
moon_path_destroy (path);
1248
if (!(glyph = font->GetGlyphInfoByIndex (attr->index)))
1252
if ((attr->set & vOffset)) {
1253
offset = -(attr->voffset * scale);
1254
bottom = MAX (bottom, bottom0 + offset);
1255
top = MIN (top, top0 + offset);
1259
if ((attr->set & uOffset)) {
1260
offset = (attr->uoffset * scale);
1261
left = MIN (left, x0 + offset);
1263
} else if (first_char) {
1264
if (glyph->metrics.horiBearingX < 0)
1265
x0 -= glyph->metrics.horiBearingX;
1273
right = MAX (right, x1 + glyph->metrics.horiAdvance);
1275
font->AppendPath (path, glyph, x1, y1);
1278
if ((attr->set & Advance))
1279
x0 += attr->advance * scale;
1281
x0 += glyph->metrics.horiAdvance;
1285
attr = (GlyphAttr *) attr->next;
1290
height = bottom - top;
1291
width = right - left;
1293
moon_path_destroy (path);
1303
Glyphs::GetSizeForBrush (cairo_t *cr, double *width, double *height)
1308
*height = this->height;
1309
*width = this->width;
1313
Glyphs::GetOriginPoint ()
1315
if (origin_y_specified) {
1316
TextFont *font = desc->GetFont ();
1318
double d = font->Descender ();
1319
double h = font->Height ();
1323
return Point (origin_x, origin_y - d - h);
1325
return Point (origin_x, 0);
1330
Glyphs::Render (cairo_t *cr, int x, int y, int width, int height)
1332
if (this->width == 0.0 && this->height == 0.0)
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)
1341
if (path == NULL || path->cairo.num_data == 0) {
1342
// No glyphs to render
1347
cairo_set_matrix (cr, &absolute_xform);
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);
1354
cairo_append_path (cr, &path->cairo);
1361
Glyphs::ComputeBounds ()
1366
bounds = IntersectBoundsWithClipPath (Rect (left, top, width, height), false).Transform (&absolute_xform);
1370
Glyphs::InsideObject (cairo_t *cr, double x, double y)
1375
TransformPoint (&nx, &ny);
1377
return (nx >= left && ny >= top && nx < left + width && ny < top + height);
1381
Glyphs::GetTransformOrigin ()
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);
1388
Glyphs::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
1390
if (prop == Glyphs::FillProperty)
1393
FrameworkElement::OnSubPropertyChanged (prop, obj, subobj_args);
1397
Glyphs::data_write (void *buf, gint32 offset, gint32 n, gpointer data)
1403
Glyphs::size_notify (gint64 size, gpointer data)
1409
Glyphs::downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
1411
((Glyphs *) closure)->DownloaderComplete ();
1415
Glyphs::DownloaderComplete ()
1417
const char *filename, *path;
1420
/* the download was aborted */
1421
if (!(filename = downloader->getFileDownloader ()->GetDownloadedFile ()))
1424
if (stat (filename, &st) == -1 || !S_ISREG (st.st_mode))
1427
if (!downloader->getFileDownloader ()->IsDeobfuscated ()) {
1428
if ((path = deobfuscate_font (downloader, filename)))
1431
downloader->getFileDownloader ()->SetDeobfuscated (true);
1434
desc->SetFilename (filename);
1435
desc->SetIndex (index);
1438
UpdateBounds (true);
1443
print_parse_error (const char *in, const char *where, const char *reason)
1446
if (debug_flags & RUNTIME_DEBUG_TEXT) {
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");
1459
Glyphs::SetIndicesInternal (const char *in)
1461
register const char *inptr = in;
1468
attrs->Clear (true);
1473
while (g_ascii_isspace (*inptr))
1477
glyph = new GlyphAttr ();
1479
while (g_ascii_isspace (*inptr))
1482
// check for a cluster
1483
if (*inptr == '(') {
1485
while (g_ascii_isspace (*inptr))
1489
glyph->code_units = strtoul (inptr, &end, 10);
1490
if (glyph->code_units == 0 || (glyph->code_units == LONG_MAX && errno != 0)) {
1492
print_parse_error (in, inptr, errno ? strerror (errno) : "invalid cluster mapping; CodeUnitCount cannot be 0");
1498
while (g_ascii_isspace (*inptr))
1501
if (*inptr != ':') {
1503
print_parse_error (in, inptr, "expected ':'");
1509
while (g_ascii_isspace (*inptr))
1513
glyph->glyph_count = strtoul (inptr, &end, 10);
1514
if (glyph->glyph_count == 0 || (glyph->glyph_count == LONG_MAX && errno != 0)) {
1516
print_parse_error (in, inptr, errno ? strerror (errno) : "invalid cluster mapping; GlyphCount cannot be 0");
1522
while (g_ascii_isspace (*inptr))
1525
if (*inptr != ')') {
1527
print_parse_error (in, inptr, "expected ')'");
1532
glyph->set |= Cluster;
1535
while (g_ascii_isspace (*inptr))
1539
if (*inptr >= '0' && *inptr <= '9') {
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));
1549
glyph->set |= Index;
1552
while (g_ascii_isspace (*inptr))
1556
bit = (uint) Advance;
1559
while (*inptr == ',' && n < 3) {
1561
while (g_ascii_isspace (*inptr))
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));
1573
end = (char *) inptr;
1577
switch ((GlyphAttrMask) bit) {
1579
glyph->advance = value;
1580
glyph->set |= Advance;
1583
glyph->uoffset = value;
1584
glyph->set |= uOffset;
1587
glyph->voffset = value;
1588
glyph->set |= vOffset;
1596
while (g_ascii_isspace (*inptr))
1603
attrs->Append (glyph);
1605
while (g_ascii_isspace (*inptr))
1608
if (*inptr && *inptr != ';') {
1609
print_parse_error (in, inptr, "expected ';'");
1621
Glyphs::DownloadFont (Surface *surface, const char *url)
1623
Uri *uri = new Uri ();
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)
1633
str = uri->ToString (UriHideFragment);
1634
downloader->Open ("GET", str, XamlPolicy);
1637
downloader->AddHandler (downloader->CompletedEvent, downloader_complete, this);
1638
if (downloader->Started () || downloader->Completed ()) {
1639
if (downloader->Completed ())
1640
DownloaderComplete ();
1642
downloader->SetWriteFunc (data_write, size_notify, this);
1644
// This is what actually triggers the download
1645
downloader->Send ();
1648
// we're shutting down
1656
Glyphs::SetSurface (Surface *surface)
1658
if (GetSurface() == surface)
1663
FrameworkElement::SetSurface (surface);
1665
if (!uri_changed || !surface)
1668
if ((uri = GetFontUri ()) && *uri)
1669
DownloadFont (surface, uri);
1671
uri_changed = false;
1675
Glyphs::OnPropertyChanged (PropertyChangedEventArgs *args)
1677
bool invalidate = true;
1679
if (args->property->GetOwnerType() != Type::GLYPHS) {
1680
FrameworkElement::OnPropertyChanged (args);
1684
if (args->property == Glyphs::FontUriProperty) {
1685
const char *str = args->new_value ? args->new_value->AsString () : NULL;
1686
Surface *surface = GetSurface ();
1689
downloader->Abort ();
1690
downloader->unref ();
1697
DownloadFont (surface, str);
1698
uri_changed = 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;
1711
text = g_utf8_to_ucs4_fast (str, -1, NULL);
1716
} else if (args->property == Glyphs::IndicesProperty) {
1717
const char *str = args->new_value ? args->new_value->AsString () : NULL;
1718
SetIndicesInternal (str);
1720
} else if (args->property == Glyphs::FontRenderingEmSizeProperty) {
1721
double size = args->new_value->AsDouble();
1722
desc->SetSize (size);
1724
} else if (args->property == Glyphs::OriginXProperty) {
1725
origin_x = args->new_value->AsDouble ();
1727
} else if (args->property == Glyphs::OriginYProperty) {
1728
origin_y = args->new_value->AsDouble ();
1729
origin_y_specified = 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;
1743
UpdateBounds (true);
1745
NotifyListenersOfPropertyChange (args);
1750
Glyphs::SetFill (Brush *fill)
1752
SetValue (Glyphs::FillProperty, Value (fill));
1758
Value *value = GetValue (Glyphs::FillProperty);
1760
return value ? value->AsBrush () : NULL;