/// <summary>Called when a @font-face font is done loading.</summary> public void FontLoaded(DynamicFont font) { Css.TextRenderingProperty text = RenderData.Text; if (text != null) { text.FontLoaded(font); } }
/// <summary>Runs before reflow.</summary> public override void UpdateCss(Renderman renderer) { // Clear the blocks: FirstBox = null; LastBox = null; // Get the text renderer (or create it): Css.TextRenderingProperty text = RequireTextProperty(); // Get computed style: ComputedStyle cs = computedStyle; // Get the first box as it contains the fontface/ size: LayoutBox box = cs.FirstBox; // Colour too: Color fontColour = cs.Resolve(Css.Properties.ColorProperty.GlobalProperty).GetColour(this, Css.Properties.ColorProperty.GlobalProperty); // Colour: text.BaseColour = fontColour; // Font size update: float fontSize = box.FontSize; text.FontSize = fontSize; // Spacing: float wordSpacing = cs.ResolveDecimal(Css.Properties.WordSpacing.GlobalProperty); float letterSpacing = cs.ResolveDecimal(Css.Properties.LetterSpacing.GlobalProperty); // If word spacing is not 'normal', remove 1em from it (Note that letter spacing is always additive): if (wordSpacing == -1f) { wordSpacing = 0f; } else { wordSpacing -= fontSize; } text.WordSpacing = wordSpacing; text.LetterSpacing = letterSpacing; // Decoration: int decoration = cs.ResolveInt(Css.Properties.TextDecorationLine.GlobalProperty); if (decoration == 0) { // Remove a line if we have one: text.TextLine = null; } else { // Got a line! if (text.TextLine == null) { text.TextLine = new TextDecorationInfo(decoration); } // Get the colour: Css.Value lineColour = cs.Resolve(Css.Properties.TextDecorationColor.GlobalProperty); if (lineColour == null || lineColour.IsType(typeof(Css.Keywords.CurrentColor))) { // No override: text.TextLine.ColourOverride = false; } else { // Set the colour: text.TextLine.SetColour(lineColour.GetColour(this, Css.Properties.TextDecorationColor.GlobalProperty)); } } // Get the font face: text.FontToDraw = box.FontFace; // Overflow-wrap mode (only active for 'break-word' which is just '1'): text.OverflowWrapActive = (cs.ResolveInt(Css.Properties.OverflowWrap.GlobalProperty) == 1); // Check if the text is 'dirty'. // If it is, that means we'll need to rebuild the TextRenderingProperty's Glyph array. if (text.Dirty) { // Setup text now: // (Resets text.Characters based on all the text related CSS properties like variant etc). text.LoadCharacters((Node as RenderableTextNode).characterData_, this); } if (text.Characters == null || text.AllEmpty) { text.FontSize = 0f; return; } }
public override void Reflow(Renderman renderer) { // Get the text renderer (or create it): Css.TextRenderingProperty text = RequireTextProperty(); if (text.Characters == null || text.AllEmpty) { return; } LayoutBox box = null; // Get the baseline offset: float baseline = text.FontSize * text.FontToDraw.Descender; // Compute our line boxes based on text.Characters and the available space. // Safely ignore direction here because either way the selected characters are the same. // Note that things like first-letter are also considered. // Get the top of the stack: LineBoxMeta lbm = renderer.TopOfStack; float cssLineHeight = lbm.CssLineHeight; // Is it justify (if so, every word is its own box): bool breakAllWords = (lbm.HorizontalAlign == HorizontalAlignMode.Justify); // Offset the baseline: float lineHeightOffset = (cssLineHeight - text.FontSize) / 2f; text.LineHeightOffset = lineHeightOffset; baseline += lineHeightOffset; float wordWidth = 0f; float boxWidth = 0f; bool wrappable = ((lbm.WhiteSpace & WhiteSpaceMode.Wrappable) != 0); int i = 0; int latestBreakpoint = -1; // bidi text direction (0 = Not set yet, UnicodeBidiMode.LeftwardsNormal, UnicodeBidiMode.RightwardsNormal) int direction = 0; // Direction before the current block of weak chars. int beforeWeak = 0; while (i < text.Characters.Length) { // Get the glyph: InfiniText.Glyph glyph = text.Characters[i]; if (glyph == null) { // Skip! i++; continue; } // The glyph's width is.. float width = (glyph.AdvanceWidth * text.FontSize) + text.LetterSpacing; if (box == null) { // The box always has an inner height of 'font size': if (renderer.FirstLetter != null) { // Clear FL immediately (so it can't go recursive): SparkInformerNode firstLetter = renderer.FirstLetter; renderer.FirstLetter = null; // Update its internal text node: RenderableTextNode textNode = firstLetter.firstChild as RenderableTextNode; // Note that we have to do it this way as the node might // change the *font*. textNode.characterData_ = ((char)glyph.Charcode) + ""; Css.TextRenderingProperty textData = textNode.RenderData.RequireTextProperty(); textData.Dirty = true; // Ask it to reflow right now (must ask the node so it correctly takes the style into account): firstLetter.RenderData.UpdateCss(renderer); firstLetter.RenderData.Reflow(renderer); i++; continue; } // Create the box now: box = new LayoutBox(); box.PositionMode = PositionMode.Static; box.DisplayMode = DisplayMode.Inline; box.Baseline = baseline; box.TextStart = i; // Adopt current direction: box.UnicodeBidi = direction; if (FirstBox == null) { FirstBox = box; LastBox = box; } else { // add to this element: LastBox.NextInElement = box; LastBox = box; } // line-height is the inner height here: box.InnerHeight = cssLineHeight; boxWidth = 0f; wordWidth = 0f; } // Are we breaking this word? bool implicitNewline = (glyph.Charcode == (int)'\n'); int breakMode = implicitNewline ? 1 : 0; // direction: int dir = glyph.Directionality; if (dir == InfiniText.BidiBlock.Rightwards) { // Rightwards (includes weak ones): // Was the previous one 'weak'? if (dir == InfiniText.BidiBlock.WeakLeftToRight) { // Update before weak: beforeWeak = direction; } else { // Not weak: beforeWeak = 0; } if (direction != UnicodeBidiMode.RightwardsNormal) { if (direction == 0) { // Not set yet - Set the mode into the box: box.UnicodeBidi = UnicodeBidiMode.RightwardsNormal; } else { // Changed from leftwards to rightwards. Break the boxes here. breakMode = 3; } // Update dir: direction = UnicodeBidiMode.RightwardsNormal; } } else if (dir == InfiniText.BidiBlock.RightToLeft) { // Leftwards if (direction != UnicodeBidiMode.LeftwardsNormal) { if (direction == 0) { // Not set yet - Set the mode into the box: box.UnicodeBidi = UnicodeBidiMode.LeftwardsNormal; } else { // Changed from rightwards to leftwards. Break the boxes here. breakMode = 3; } // Update dir: direction = UnicodeBidiMode.LeftwardsNormal; } // Never weak: beforeWeak = 0; } else if (beforeWeak != 0) { // Neutral otherwise // (adopts whatever the current is, unless the previous one was *weak*, // in which case, it adopts whatever the direction was before that): if (direction != beforeWeak) { // Change direction! direction = beforeWeak; breakMode = 3; } } // Got a space? bool space = (glyph.Charcode == (int)' '); if (space) { // Advance width: width += text.WordSpacing; if (breakAllWords) { breakMode = 3; } else { latestBreakpoint = i; } // Lock in the previous text: boxWidth += wordWidth + width; wordWidth = 0f; } else { // Advance word width now: wordWidth += width; } // Word wrapping next: if (breakMode == 0 && wrappable && i != box.TextStart) { // Test if we can break here: breakMode = lbm.GetLineSpace(wordWidth, boxWidth); // Return to the previous space if we should. if (breakMode == 2 && text.OverflowWrapActive) { // The word doesn't fit at all (2) and we're supposed to break it. boxWidth += wordWidth - width; wordWidth = 0f; } else if (breakMode != 0) { if (latestBreakpoint == -1) { // Isn't a previous space! if (breakMode == 2) { // Instead, we'll try and break a parent. // This typically happens with inline elements // which are right on the end of the host line. lbm.TryBreakParent(); // Don't break the node: breakMode = 0; } else if (breakMode == 3) { // Break but no newline - just advance to the following char: i++; } else if (lbm.PenX != lbm.LineStart) { // Newline: lbm.CompleteLine(LineBreakMode.Normal); // Don't break the node: breakMode = 0; } else { // Don't break the node: breakMode = 0; } } else { i = latestBreakpoint + 1; } } } if (breakMode != 0) { // We're breaking! box.InnerWidth = boxWidth; box.TextEnd = i; latestBreakpoint = i; // If the previous glyph is a space, update EndSpaceSize: if (space) { // Update ending spaces: box.EndSpaceSize = width; } // Ensure dimensions are set: box.SetDimensions(false, false); // Add the box to the line: lbm.AddToLine(box); // Update dim's: lbm.AdvancePen(box); // Clear: box = null; boxWidth = 0f; if (breakMode != 3) { // Newline: if (implicitNewline) { // Also the last line: lbm.CompleteLine(LineBreakMode.Normal | LineBreakMode.Last); } else { // Normal: lbm.CompleteLine(LineBreakMode.Normal); // Process it again. continue; } } } // Next character: i++; } if (box != null) { // Always apply inner width: box.InnerWidth = boxWidth + wordWidth; box.TextEnd = text.Characters.Length; // Ensure dimensions are set: box.SetDimensions(false, false); // Add the box to the line: lbm.AddToLine(box); // Update dim's: lbm.AdvancePen(box); } }