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.
17 #include <sys/types.h>
23 #include "file-downloader.h"
30 #include "deployment.h"
38 #define ORIGIN_IS_SET(x) ((x) > -HUGE)
52 class GlyphAttr
: public List::Node
{
65 GlyphAttr::GlyphAttr ()
74 SetObjectType (Type::GLYPHS
);
100 moon_path_destroy (path
);
111 Glyphs::CleanupDownloader ()
114 downloader
->RemoveHandler (Downloader::CompletedEvent
, downloader_complete
, this);
115 downloader
->Abort ();
116 downloader
->unref ();
124 double size
= GetFontRenderingEmSize ();
125 guint32 code_units
, glyph_count
, i
;
126 bool first_char
= true;
127 double x0
, x1
, y0
, y1
;
128 double bottom
, right
;
129 double bottom0
, top0
;
147 moon_path_destroy (path
);
152 // required font fields have not been set
156 if (((!text
|| !text
[0]) && attrs
->IsEmpty ())) {
157 // no glyphs to render
162 // no fill specified (unlike TextBlock, there is no default brush)
166 // scale Advance, uOffset and vOffset units to pixels
167 scale
= round (size
) / 100.0;
170 if (!ORIGIN_IS_SET (x0
))
176 // OriginY is the baseline if specified
178 if (ORIGIN_IS_SET (y0
)) {
179 top0
= y0
- font
->Ascender ();
181 y0
= font
->Ascender ();
185 bottom0
= top0
+ font
->Height ();
190 path
= moon_path_new (16);
192 attr
= (GlyphAttr
*) attrs
->First ();
194 if (text
&& text
[0]) {
198 if (attr
&& (attr
->set
& Cluster
)) {
199 // get the cluster's GlyphCount and CodeUnitCount
200 glyph_count
= attr
->glyph_count
;
201 code_units
= attr
->code_units
;
207 if (glyph_count
== 1 && code_units
== 1)
212 // render the glyph cluster
215 if (attr
&& (attr
->set
& Index
)) {
216 if (!(glyph
= font
->GetGlyphInfoByIndex (attr
->index
)))
218 } else if (cluster
) {
219 // indexes MUST be specified for each glyph in a cluster
220 moon_path_destroy (path
);
225 if (!(glyph
= font
->GetGlyphInfo (*c
)))
230 if (attr
&& (attr
->set
& vOffset
)) {
231 offset
= -(attr
->voffset
* scale
);
232 bottom
= MAX (bottom
, bottom0
+ offset
);
233 top
= MIN (top
, top0
+ offset
);
237 if (attr
&& (attr
->set
& uOffset
)) {
238 offset
= (attr
->uoffset
* scale
);
239 left
= MIN (left
, x0
+ offset
);
241 } else if (first_char
) {
242 if (glyph
->metrics
.horiBearingX
< 0)
243 x0
-= glyph
->metrics
.horiBearingX
;
251 right
= MAX (right
, x1
+ glyph
->metrics
.horiAdvance
);
253 font
->AppendPath (path
, glyph
, x1
, y1
);
256 if (attr
&& (attr
->set
& Advance
))
257 x0
+= attr
->advance
* scale
;
259 x0
+= glyph
->metrics
.horiAdvance
;
263 attr
= attr
? (GlyphAttr
*) attr
->next
: NULL
;
266 if (i
== glyph_count
)
270 // there MUST be an attr for each glyph in a cluster
271 moon_path_destroy (path
);
277 if ((attr
->set
& Cluster
)) {
278 // only the first glyph in a cluster may specify a cluster mapping
279 moon_path_destroy (path
);
286 // consume the code units
287 for (i
= 0; i
< code_units
&& *c
!= 0; i
++)
295 if (attr
->set
& Cluster
) {
296 LOG_TEXT (stderr
, "Can't use clusters past the end of the UnicodeString\n");
297 moon_path_destroy (path
);
303 if (!(attr
->set
& Index
)) {
304 LOG_TEXT (stderr
, "No index specified for glyph %d\n", n
+ 1);
305 moon_path_destroy (path
);
311 if (!(glyph
= font
->GetGlyphInfoByIndex (attr
->index
)))
315 if ((attr
->set
& vOffset
)) {
316 offset
= -(attr
->voffset
* scale
);
317 bottom
= MAX (bottom
, bottom0
+ offset
);
318 top
= MIN (top
, top0
+ offset
);
322 if ((attr
->set
& uOffset
)) {
323 offset
= (attr
->uoffset
* scale
);
324 left
= MIN (left
, x0
+ offset
);
326 } else if (first_char
) {
327 if (glyph
->metrics
.horiBearingX
< 0)
328 x0
-= glyph
->metrics
.horiBearingX
;
336 right
= MAX (right
, x1
+ glyph
->metrics
.horiAdvance
);
338 font
->AppendPath (path
, glyph
, x1
, y1
);
341 if ((attr
->set
& Advance
))
342 x0
+= attr
->advance
* scale
;
344 x0
+= glyph
->metrics
.horiAdvance
;
348 attr
= (GlyphAttr
*) attr
->next
;
353 height
= bottom
- top
;
354 width
= right
- left
;
356 moon_path_destroy (path
);
362 Glyphs::GetSizeForBrush (cairo_t
*cr
, double *width
, double *height
)
367 *height
= this->height
;
368 *width
= this->width
;
372 Glyphs::GetOriginPoint ()
374 double x0
= GetOriginX ();
375 double y0
= GetOriginY ();
377 if (!ORIGIN_IS_SET (x0
))
380 if (ORIGIN_IS_SET (y0
)) {
381 double ascend
= font
? font
->Ascender () : 0.0;
383 return Point (x0
, y0
- ascend
);
385 return Point (x0
, 0);
390 Glyphs::Render (cairo_t
*cr
, Region
*region
, bool path_only
)
392 if (width
== 0.0 && height
== 0.0)
396 // do not render anything if our state is invalid to keep with Silverlight's behavior.
397 // (Note: rendering code also assumes everything is kosher)
401 if (path
== NULL
|| path
->cairo
.num_data
== 0) {
402 // No glyphs to render
407 cairo_set_matrix (cr
, &absolute_xform
);
410 RenderLayoutClip (cr
);
412 Rect area
= Rect (left
, top
, width
, height
);
413 fill
->SetupBrush (cr
, area
);
415 cairo_append_path (cr
, &path
->cairo
);
422 Glyphs::ComputeActualSize ()
427 return Size (left
+ width
, top
+ height
);
431 Glyphs::MeasureOverride (Size availableSize
)
436 return Size (left
+ width
, top
+ height
).Min (availableSize
);
440 Glyphs::ArrangeOverride (Size finalSize
)
445 finalSize
= ApplySizeConstraints (finalSize
);
446 return Size (left
+ width
, top
+ height
).Max (finalSize
);
450 Glyphs::ComputeBounds ()
455 bounds
= IntersectBoundsWithClipPath (Rect (left
, top
, width
, height
), false).Transform (&absolute_xform
);
459 Glyphs::GetTransformOrigin ()
461 // Glyphs seems to always use 0,0 no matter what is specified in the RenderTransformOrigin nor the OriginX/Y points
466 Glyphs::OnSubPropertyChanged (DependencyProperty
*prop
, DependencyObject
*obj
, PropertyChangedEventArgs
*subobj_args
)
468 if (prop
&& prop
->GetId () == Glyphs::FillProperty
) {
471 FrameworkElement::OnSubPropertyChanged (prop
, obj
, subobj_args
);
476 Glyphs::LoadFont (const Uri
*uri
, const char *path
)
478 FontManager
*manager
= Deployment::GetCurrent ()->GetFontManager ();
479 StyleSimulations simulate
= GetStyleSimulations ();
480 double size
= GetFontRenderingEmSize ();
484 if (uri
->GetFragment ()) {
485 if ((index
= strtol (uri
->GetFragment (), NULL
, 10)) < 0 || index
== G_MAXINT
)
491 resource
= uri
->ToString ((UriToStringFlags
) (UriHidePasswd
| UriHideFragment
| UriHideQuery
));
492 manager
->AddResource (resource
, path
);
493 font
= TextFont::Load (resource
, index
, size
, simulate
);
498 Glyphs::downloader_complete (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
500 ((Glyphs
*) closure
)->DownloaderComplete ();
504 Glyphs::DownloaderComplete ()
506 Uri
*uri
= GetFontUri ();
512 // get the downloaded file path
513 if (!(filename
= downloader
->GetDownloadedFilename (NULL
))) {
520 LoadFont (uri
, filename
);
530 print_parse_error (const char *in
, const char *where
, const char *reason
)
532 if (debug_flags
& RUNTIME_DEBUG_TEXT
) {
533 fprintf (stderr
, "Glyph Indices parse error: \"%s\": %s\n", in
, reason
);
534 fprintf (stderr
, " ");
535 for (int i
= 0; i
< (where
- in
); i
++)
537 fprintf (stderr
, "^\n");
543 Glyphs::SetIndicesInternal (const char *in
)
545 register const char *inptr
= in
;
557 while (g_ascii_isspace (*inptr
))
561 glyph
= new GlyphAttr ();
563 while (g_ascii_isspace (*inptr
))
566 // check for a cluster
569 while (g_ascii_isspace (*inptr
))
573 glyph
->code_units
= strtoul (inptr
, &end
, 10);
574 if (glyph
->code_units
== 0 || (glyph
->code_units
== LONG_MAX
&& errno
!= 0)) {
576 d(print_parse_error (in
, inptr
, errno
? strerror (errno
) : "invalid cluster mapping; CodeUnitCount cannot be 0"));
582 while (g_ascii_isspace (*inptr
))
587 d(print_parse_error (in
, inptr
, "expected ':'"));
593 while (g_ascii_isspace (*inptr
))
597 glyph
->glyph_count
= strtoul (inptr
, &end
, 10);
598 if (glyph
->glyph_count
== 0 || (glyph
->glyph_count
== LONG_MAX
&& errno
!= 0)) {
600 d(print_parse_error (in
, inptr
, errno
? strerror (errno
) : "invalid cluster mapping; GlyphCount cannot be 0"));
606 while (g_ascii_isspace (*inptr
))
611 d(print_parse_error (in
, inptr
, "expected ')'"));
616 glyph
->set
|= Cluster
;
619 while (g_ascii_isspace (*inptr
))
623 if (*inptr
>= '0' && *inptr
<= '9') {
625 glyph
->index
= strtoul (inptr
, &end
, 10);
626 if ((glyph
->index
== 0 || glyph
->index
== LONG_MAX
) && errno
!= 0) {
627 // invalid glyph index
628 d(print_parse_error (in
, inptr
, strerror (errno
)));
636 while (g_ascii_isspace (*inptr
))
640 bit
= (uint
) Advance
;
643 while (*inptr
== ',' && n
< 3) {
645 while (g_ascii_isspace (*inptr
))
649 value
= g_ascii_strtod (inptr
, &end
);
650 if ((value
== 0.0 || value
== HUGE_VAL
|| value
== -HUGE_VAL
) && errno
!= 0) {
651 // invalid advance or offset
652 d(print_parse_error (in
, inptr
, strerror (errno
)));
657 end
= (char *) inptr
;
661 switch ((GlyphAttrMask
) bit
) {
663 glyph
->advance
= value
;
664 glyph
->set
|= Advance
;
667 glyph
->uoffset
= value
;
668 glyph
->set
|= uOffset
;
671 glyph
->voffset
= value
;
672 glyph
->set
|= vOffset
;
680 while (g_ascii_isspace (*inptr
))
687 attrs
->Append (glyph
);
689 while (g_ascii_isspace (*inptr
))
692 if (*inptr
&& *inptr
!= ';') {
693 d(print_parse_error (in
, inptr
, "expected ';'"));
705 Glyphs::DownloadFont (Uri
*uri
, MoonError
*error
)
707 Surface
*surface
= GetDeployment ()->GetSurface ();
709 if ((downloader
= surface
->CreateDownloader ())) {
710 char *str
= uri
->ToString (UriHideFragment
);
711 downloader
->Open ("GET", str
, FontPolicy
);
714 if (downloader
->GetFailedMessage () != NULL
) {
715 MoonError::FillIn (error
, MoonError::ARGUMENT_OUT_OF_RANGE
, 1000, downloader
->GetFailedMessage ());
716 downloader
->unref ();
721 downloader
->AddHandler (downloader
->CompletedEvent
, downloader_complete
, this);
722 if (downloader
->Started () || downloader
->Completed ()) {
723 if (downloader
->Completed ())
724 DownloaderComplete ();
726 // This is what actually triggers the download
730 // we're shutting down
735 Glyphs::SetFontResource (const Uri
*uri
)
737 Application
*application
= Application::GetCurrent ();
740 if (!application
|| !(path
= application
->GetResourceAsPath (GetResourceBase(), uri
)))
743 LoadFont (uri
, path
);
750 Glyphs::SetParent (DependencyObject
*parent
, MoonError
*error
)
752 if (parent
&& IsAttached () && uri_changed
) {
753 // we've been added to the tree, kick off any pending
754 // download we may have
757 if ((uri
= GetFontUri ()))
758 DownloadFont (uri
, error
);
762 if (error
&& error
->number
)
766 FrameworkElement::SetParent (parent
, error
);
770 Glyphs::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
772 bool invalidate
= true;
774 if (args
->GetProperty ()->GetOwnerType() != Type::GLYPHS
) {
775 FrameworkElement::OnPropertyChanged (args
, error
);
779 if (args
->GetId () == Glyphs::FontUriProperty
) {
780 Uri
*uri
= args
->GetNewValue() ? args
->GetNewValue()->AsUri () : NULL
;
782 CleanupDownloader ();
787 if (!Uri::IsNullOrEmpty (uri
)) {
788 if (!SetFontResource (uri
)) {
789 if (uri
->IsInvalidPath ()) {
790 if (IsBeingParsed ()) {
791 MoonError::FillIn (error
, MoonError::XAML_PARSE_EXCEPTION
, 0, "invalid path found in uri");
793 // FIXME: I'm guessing, based on moon-unit tests, that this event should only be emitted
794 // when being parsed from javascript as opposed to managed land...
795 if (IsAttached () && uri
->IsUncPath ())
796 GetDeployment ()->GetSurface ()->EmitError (new ParserErrorEventArgs ("invalid uri", NULL
, 0, 0, 0, NULL
, NULL
));
799 // need to create a downloader for this font...
801 DownloadFont (uri
, IsBeingParsed () ? error
: NULL
);
804 // queue a font download
814 } else if (args
->GetId () == Glyphs::FillProperty
) {
815 fill
= args
->GetNewValue() ? args
->GetNewValue()->AsBrush() : NULL
;
816 } else if (args
->GetId () == Glyphs::UnicodeStringProperty
) {
817 const char *str
= args
->GetNewValue() ? args
->GetNewValue()->AsString () : NULL
;
821 text
= g_utf8_to_ucs4_fast (str
, -1, NULL
);
826 } else if (args
->GetId () == Glyphs::IndicesProperty
) {
827 const char *str
= args
->GetNewValue() ? args
->GetNewValue()->AsString () : NULL
;
828 SetIndicesInternal (str
);
830 } else if (args
->GetId () == Glyphs::FontRenderingEmSizeProperty
) {
832 dirty
= font
->SetSize (args
->GetNewValue ()->AsDouble ());
835 } else if (args
->GetId () == Glyphs::OriginXProperty
) {
837 } else if (args
->GetId () == Glyphs::OriginYProperty
) {
839 } else if (args
->GetId () == Glyphs::StyleSimulationsProperty
) {
840 StyleSimulations simulate
= (StyleSimulations
) args
->GetNewValue ()->AsInt32 ();
842 // clear any unsupported flags
843 simulate
= (StyleSimulations
) (simulate
& StyleSimulationsBoldItalic
);
846 dirty
= font
->SetStyleSimulations (simulate
);
857 NotifyListenersOfPropertyChange (args
, error
);