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"
37 #define ORIGIN_IS_SET(x) ((x) > -HUGE)
51 class GlyphAttr
: public List::Node
{
64 GlyphAttr::GlyphAttr ()
73 SetObjectType (Type::GLYPHS
);
99 moon_path_destroy (path
);
110 Glyphs::CleanupDownloader ()
113 downloader
->RemoveHandler (Downloader::CompletedEvent
, downloader_complete
, this);
114 downloader
->Abort ();
115 downloader
->unref ();
123 double size
= GetFontRenderingEmSize ();
124 guint32 code_units
, glyph_count
, i
;
125 bool first_char
= true;
126 double x0
, x1
, y0
, y1
;
127 double bottom
, right
;
128 double bottom0
, top0
;
146 moon_path_destroy (path
);
151 // required font fields have not been set
155 if (((!text
|| !text
[0]) && attrs
->IsEmpty ())) {
156 // no glyphs to render
161 // no fill specified (unlike TextBlock, there is no default brush)
165 // scale Advance, uOffset and vOffset units to pixels
166 scale
= round (size
) / 100.0;
169 if (!ORIGIN_IS_SET (x0
))
175 // OriginY is the baseline if specified
177 if (ORIGIN_IS_SET (y0
)) {
178 top0
= y0
- font
->Ascender ();
180 y0
= font
->Ascender ();
184 bottom0
= top0
+ font
->Height ();
189 path
= moon_path_new (16);
191 attr
= (GlyphAttr
*) attrs
->First ();
193 if (text
&& text
[0]) {
197 if (attr
&& (attr
->set
& Cluster
)) {
198 // get the cluster's GlyphCount and CodeUnitCount
199 glyph_count
= attr
->glyph_count
;
200 code_units
= attr
->code_units
;
206 if (glyph_count
== 1 && code_units
== 1)
211 // render the glyph cluster
214 if (attr
&& (attr
->set
& Index
)) {
215 if (!(glyph
= font
->GetGlyphInfoByIndex (attr
->index
)))
217 } else if (cluster
) {
218 // indexes MUST be specified for each glyph in a cluster
219 moon_path_destroy (path
);
224 if (!(glyph
= font
->GetGlyphInfo (*c
)))
229 if (attr
&& (attr
->set
& vOffset
)) {
230 offset
= -(attr
->voffset
* scale
);
231 bottom
= MAX (bottom
, bottom0
+ offset
);
232 top
= MIN (top
, top0
+ offset
);
236 if (attr
&& (attr
->set
& uOffset
)) {
237 offset
= (attr
->uoffset
* scale
);
238 left
= MIN (left
, x0
+ offset
);
240 } else if (first_char
) {
241 if (glyph
->metrics
.horiBearingX
< 0)
242 x0
-= glyph
->metrics
.horiBearingX
;
250 right
= MAX (right
, x1
+ glyph
->metrics
.horiAdvance
);
252 font
->AppendPath (path
, glyph
, x1
, y1
);
255 if (attr
&& (attr
->set
& Advance
))
256 x0
+= attr
->advance
* scale
;
258 x0
+= glyph
->metrics
.horiAdvance
;
262 attr
= attr
? (GlyphAttr
*) attr
->next
: NULL
;
265 if (i
== glyph_count
)
269 // there MUST be an attr for each glyph in a cluster
270 moon_path_destroy (path
);
276 if ((attr
->set
& Cluster
)) {
277 // only the first glyph in a cluster may specify a cluster mapping
278 moon_path_destroy (path
);
285 // consume the code units
286 for (i
= 0; i
< code_units
&& *c
!= 0; i
++)
294 if (attr
->set
& Cluster
) {
295 LOG_TEXT (stderr
, "Can't use clusters past the end of the UnicodeString\n");
296 moon_path_destroy (path
);
302 if (!(attr
->set
& Index
)) {
303 LOG_TEXT (stderr
, "No index specified for glyph %d\n", n
+ 1);
304 moon_path_destroy (path
);
310 if (!(glyph
= font
->GetGlyphInfoByIndex (attr
->index
)))
314 if ((attr
->set
& vOffset
)) {
315 offset
= -(attr
->voffset
* scale
);
316 bottom
= MAX (bottom
, bottom0
+ offset
);
317 top
= MIN (top
, top0
+ offset
);
321 if ((attr
->set
& uOffset
)) {
322 offset
= (attr
->uoffset
* scale
);
323 left
= MIN (left
, x0
+ offset
);
325 } else if (first_char
) {
326 if (glyph
->metrics
.horiBearingX
< 0)
327 x0
-= glyph
->metrics
.horiBearingX
;
335 right
= MAX (right
, x1
+ glyph
->metrics
.horiAdvance
);
337 font
->AppendPath (path
, glyph
, x1
, y1
);
340 if ((attr
->set
& Advance
))
341 x0
+= attr
->advance
* scale
;
343 x0
+= glyph
->metrics
.horiAdvance
;
347 attr
= (GlyphAttr
*) attr
->next
;
352 height
= bottom
- top
;
353 width
= right
- left
;
355 moon_path_destroy (path
);
361 Glyphs::GetSizeForBrush (cairo_t
*cr
, double *width
, double *height
)
366 *height
= this->height
;
367 *width
= this->width
;
371 Glyphs::GetOriginPoint ()
373 double x0
= GetOriginX ();
374 double y0
= GetOriginY ();
376 if (!ORIGIN_IS_SET (x0
))
379 if (ORIGIN_IS_SET (y0
)) {
380 double ascend
= font
? font
->Ascender () : 0.0;
382 return Point (x0
, y0
- ascend
);
384 return Point (x0
, 0);
389 Glyphs::Render (cairo_t
*cr
, Region
*region
, bool path_only
)
391 if (width
== 0.0 && height
== 0.0)
395 // do not render anything if our state is invalid to keep with Silverlight's behavior.
396 // (Note: rendering code also assumes everything is kosher)
400 if (path
== NULL
|| path
->cairo
.num_data
== 0) {
401 // No glyphs to render
406 cairo_set_matrix (cr
, &absolute_xform
);
409 RenderLayoutClip (cr
);
411 Rect area
= Rect (left
, top
, width
, height
);
412 fill
->SetupBrush (cr
, area
);
414 cairo_append_path (cr
, &path
->cairo
);
421 Glyphs::ComputeActualSize ()
426 return Size (left
+ width
, top
+ height
);
430 Glyphs::MeasureOverride (Size availableSize
)
435 return Size (left
+ width
, top
+ height
).Min (availableSize
);
439 Glyphs::ArrangeOverride (Size finalSize
)
444 finalSize
= ApplySizeConstraints (finalSize
);
445 return Size (left
+ width
, top
+ height
).Max (finalSize
);
449 Glyphs::ComputeBounds ()
454 bounds
= IntersectBoundsWithClipPath (Rect (left
, top
, width
, height
), false).Transform (&absolute_xform
);
458 Glyphs::InsideObject (cairo_t
*cr
, double x
, double y
)
463 TransformPoint (&nx
, &ny
);
465 return (nx
>= left
&& ny
>= top
&& nx
< left
+ width
&& ny
< top
+ height
);
469 Glyphs::GetTransformOrigin ()
471 // Glyphs seems to always use 0,0 no matter what is specified in the RenderTransformOrigin nor the OriginX/Y points
476 Glyphs::OnSubPropertyChanged (DependencyProperty
*prop
, DependencyObject
*obj
, PropertyChangedEventArgs
*subobj_args
)
478 if (prop
&& prop
->GetId () == Glyphs::FillProperty
) {
481 FrameworkElement::OnSubPropertyChanged (prop
, obj
, subobj_args
);
486 Glyphs::LoadFont (const Uri
*uri
, const char *path
)
488 FontManager
*manager
= Deployment::GetCurrent ()->GetFontManager ();
489 StyleSimulations simulate
= GetStyleSimulations ();
490 double size
= GetFontRenderingEmSize ();
494 if (uri
->GetFragment ()) {
495 if ((index
= strtol (uri
->GetFragment (), NULL
, 10)) < 0 || index
== G_MAXINT
)
501 resource
= uri
->ToString ((UriToStringFlags
) (UriHidePasswd
| UriHideFragment
| UriHideQuery
));
502 manager
->AddResource (resource
, path
);
503 font
= TextFont::Load (resource
, index
, size
, simulate
);
508 Glyphs::downloader_complete (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
510 ((Glyphs
*) closure
)->DownloaderComplete ();
514 Glyphs::DownloaderComplete ()
516 Uri
*uri
= GetFontUri ();
522 // get the downloaded file path
523 if (!(filename
= downloader
->GetDownloadedFilename (NULL
))) {
530 LoadFont (uri
, filename
);
540 print_parse_error (const char *in
, const char *where
, const char *reason
)
542 if (debug_flags
& RUNTIME_DEBUG_TEXT
) {
543 fprintf (stderr
, "Glyph Indices parse error: \"%s\": %s\n", in
, reason
);
544 fprintf (stderr
, " ");
545 for (int i
= 0; i
< (where
- in
); i
++)
547 fprintf (stderr
, "^\n");
553 Glyphs::SetIndicesInternal (const char *in
)
555 register const char *inptr
= in
;
567 while (g_ascii_isspace (*inptr
))
571 glyph
= new GlyphAttr ();
573 while (g_ascii_isspace (*inptr
))
576 // check for a cluster
579 while (g_ascii_isspace (*inptr
))
583 glyph
->code_units
= strtoul (inptr
, &end
, 10);
584 if (glyph
->code_units
== 0 || (glyph
->code_units
== LONG_MAX
&& errno
!= 0)) {
586 d(print_parse_error (in
, inptr
, errno
? strerror (errno
) : "invalid cluster mapping; CodeUnitCount cannot be 0"));
592 while (g_ascii_isspace (*inptr
))
597 print_parse_error (in
, inptr
, "expected ':'");
603 while (g_ascii_isspace (*inptr
))
607 glyph
->glyph_count
= strtoul (inptr
, &end
, 10);
608 if (glyph
->glyph_count
== 0 || (glyph
->glyph_count
== LONG_MAX
&& errno
!= 0)) {
610 d(print_parse_error (in
, inptr
, errno
? strerror (errno
) : "invalid cluster mapping; GlyphCount cannot be 0"));
616 while (g_ascii_isspace (*inptr
))
621 d(print_parse_error (in
, inptr
, "expected ')'"));
626 glyph
->set
|= Cluster
;
629 while (g_ascii_isspace (*inptr
))
633 if (*inptr
>= '0' && *inptr
<= '9') {
635 glyph
->index
= strtoul (inptr
, &end
, 10);
636 if ((glyph
->index
== 0 || glyph
->index
== LONG_MAX
) && errno
!= 0) {
637 // invalid glyph index
638 d(print_parse_error (in
, inptr
, strerror (errno
)));
646 while (g_ascii_isspace (*inptr
))
650 bit
= (uint
) Advance
;
653 while (*inptr
== ',' && n
< 3) {
655 while (g_ascii_isspace (*inptr
))
659 value
= g_ascii_strtod (inptr
, &end
);
660 if ((value
== 0.0 || value
== HUGE_VAL
|| value
== -HUGE_VAL
) && errno
!= 0) {
661 // invalid advance or offset
662 d(print_parse_error (in
, inptr
, strerror (errno
)));
667 end
= (char *) inptr
;
671 switch ((GlyphAttrMask
) bit
) {
673 glyph
->advance
= value
;
674 glyph
->set
|= Advance
;
677 glyph
->uoffset
= value
;
678 glyph
->set
|= uOffset
;
681 glyph
->voffset
= value
;
682 glyph
->set
|= vOffset
;
690 while (g_ascii_isspace (*inptr
))
697 attrs
->Append (glyph
);
699 while (g_ascii_isspace (*inptr
))
702 if (*inptr
&& *inptr
!= ';') {
703 d(print_parse_error (in
, inptr
, "expected ';'"));
715 Glyphs::DownloadFont (Surface
*surface
, Uri
*uri
)
717 if ((downloader
= surface
->CreateDownloader ())) {
718 char *str
= uri
->ToString (UriHideFragment
);
719 downloader
->Open ("GET", str
, FontPolicy
);
722 downloader
->AddHandler (downloader
->CompletedEvent
, downloader_complete
, this);
723 if (downloader
->Started () || downloader
->Completed ()) {
724 if (downloader
->Completed ())
725 DownloaderComplete ();
727 // This is what actually triggers the download
731 // we're shutting down
736 Glyphs::SetFontResource (const Uri
*uri
)
738 Application
*application
= Application::GetCurrent ();
741 if (!application
|| !(path
= application
->GetResourceAsPath (GetResourceBase(), uri
)))
744 LoadFont (uri
, path
);
751 Glyphs::SetSurface (Surface
*surface
)
755 if (GetSurface () == surface
)
758 FrameworkElement::SetSurface (surface
);
760 if (!uri_changed
|| !surface
)
763 if ((uri
= GetFontUri ()))
764 DownloadFont (surface
, uri
);
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
;
781 Surface
*surface
= GetSurface ();
783 CleanupDownloader ();
788 if (!Uri::IsNullOrEmpty (uri
)) {
789 if (!SetFontResource (uri
)) {
790 // need to create a downloader for this font...
792 DownloadFont (surface
, uri
);
795 // queue a font download
804 } else if (args
->GetId () == Glyphs::FillProperty
) {
805 fill
= args
->GetNewValue() ? args
->GetNewValue()->AsBrush() : NULL
;
806 } else if (args
->GetId () == Glyphs::UnicodeStringProperty
) {
807 const char *str
= args
->GetNewValue() ? args
->GetNewValue()->AsString () : NULL
;
811 text
= g_utf8_to_ucs4_fast (str
, -1, NULL
);
816 } else if (args
->GetId () == Glyphs::IndicesProperty
) {
817 const char *str
= args
->GetNewValue() ? args
->GetNewValue()->AsString () : NULL
;
818 SetIndicesInternal (str
);
820 } else if (args
->GetId () == Glyphs::FontRenderingEmSizeProperty
) {
822 dirty
= font
->SetSize (args
->GetNewValue ()->AsDouble ());
825 } else if (args
->GetId () == Glyphs::OriginXProperty
) {
827 } else if (args
->GetId () == Glyphs::OriginYProperty
) {
829 } else if (args
->GetId () == Glyphs::StyleSimulationsProperty
) {
830 StyleSimulations simulate
= (StyleSimulations
) args
->GetNewValue ()->AsInt32 ();
832 // clear any unsupported flags
833 simulate
= (StyleSimulations
) (simulate
& StyleSimulationsBoldItalic
);
836 dirty
= font
->SetStyleSimulations (simulate
);
847 NotifyListenersOfPropertyChange (args
, error
);