public override void BuildFilter(RenderContext renderer) { switch (Overflow) { case Overflow.Auto: case Overflow.Visible: case Overflow.Scroll: base.BuildFilter(renderer); break; default: ScreenRegion prevClip = renderer.ClipRegion; try{ Css.ComputedStyle cs = Style.Computed; renderer.SetClip(new BoxRegion(cs.OffsetLeft, cs.OffsetTop, cs.PixelWidth, cs.PixelHeight), false); base.BuildFilter(renderer); }finally{ renderer.SetClip(prevClip, true); } break; } }
/// <summary>Only pops if the given element had a transform to add earlier.</summary> public void PopTransform(SVGElement by) { // Get CS: Css.ComputedStyle computed = by.Style.Computed; Transformation transform = computed.TransformX; if (transform != null) { // Pop it off again: Transformations.Pop(); } }
public void PushTransform(SVGElement by) { // Get CS: Css.ComputedStyle computed = by.Style.Computed; Transformation transform = computed.TransformX; // Push the transform to our stack, if we have one. if (transform != null) { // Add it to the stack: Transformations.Push(transform); // Update it: transform.RecalculateMatrix(computed, computed.FirstBox); } }
/// <summary>Removes this selector from all the computed styles it affects, except for the target. /// Use ComputedStyle.RemoveMatch(x) instead of calling this directly.</summary> internal void Remove() { for (int i = 0; i < MatchedRoots.Length; i++) { // Remove it (if it's the target and it was active, it'll also remove the style for us too): MatchingRoot root = MatchedRoots[i]; // Remove from matches: ComputedStyle cs = (root.Node as IRenderableNode).ComputedStyle; // Remove the node: if (root.NextInStyle == null) { cs.LastMatch = root.PreviousInStyle; } else { root.NextInStyle.PreviousInStyle = root.PreviousInStyle; } if (root.PreviousInStyle == null) { cs.FirstMatch = root.NextInStyle; } else { root.PreviousInStyle.NextInStyle = root.NextInStyle; } if (root.IsTarget && Active) { // Remove props: cs.MatchChanged(Style, false); } } }
public SparkInformerNode() { Computed = new ComputedStyle(this); }
/// <summary>Lets the renderer know that the given parent element has finished /// packing all of its kids. This allows alignment to occur next.</summary> /// <param name="renderable">The element that is done packing.</param> public void EndLines(LineBoxMeta lineZone, ComputedStyle computed, LayoutBox box) { // Pop the box: BoxStack.Pop(); // Complete the last line: lineZone.CompleteLine(LineBreakMode.NoBreak | LineBreakMode.Last); // If inline-block or float, clear: if (box.DisplayMode == DisplayMode.InlineBlock || box.FloatMode != FloatMode.None) { // Must clear: lineZone.ClearFloat(FloatMode.Both); lineZone.PenY += lineZone.ClearY_; lineZone.ClearY_ = 0f; } // block, inline-block if (lineZone is BlockBoxMeta) { bool heightChange = false; if (box.InnerHeight == -1f) { heightChange = true; box.InnerHeight = lineZone.PenY; // Clip height now: box.InnerHeight = computed.ClipHeight(box.DisplayMode, box.InnerHeight); } box.ContentHeight = lineZone.PenY; box.ContentWidth = lineZone.LargestLineWidth; // If it's inline then we set the line width. if (box.InnerWidth == -1f) { box.InnerWidth = lineZone.LargestLineWidth; // Apply valid width/height: box.SetDimensions(false, false); } else if (heightChange) { // Apply valid width/height: box.SetDimensions(false, false); } bool inFlow = (box.PositionMode & PositionMode.InFlow) != 0; // Update position of the top-of-stack pen: LineBoxMeta tos = TopOfStackSafe; if (tos == null) { LastBlockBox = null; } else { if (inFlow) { // Advance the pen: tos.AdvancePen(box); } // Restore previous block: LastBlockBox = tos as BlockBoxMeta; if (LastBlockBox == null) { // Rare - block inside inline. LastBlockBox = (tos as InlineBoxMeta).HostBlock; } } } }
/// <summary>Sets up this renderer so that it's ready to start packing child elements of /// a given element into lines.</summary> /// <param name="renderable">The parent render data whose children will be packed.</param> public LineBoxMeta BeginLines(RenderableData renderable, VirtualElements virts, LayoutBox box, bool autoWidth) { // Get CS: ComputedStyle cs = renderable.computedStyle; LineBoxMeta lineZone; InfiniText.FontFace face = box.FontFace; // Update line height: // Line height: Css.Value lineHeightValue = cs.LineHeightX; float cssLineHeight; if (lineHeightValue.IsType(typeof(Css.Keywords.Normal))) { // Get from the metrics font now: cssLineHeight = box.FontFace.BaselineToBaseline * box.FontSize; } else if ( lineHeightValue.Type != Css.ValueType.RelativeNumber && lineHeightValue.GetType() != typeof(Css.Units.DecimalUnit) ) { // E.g. line-height:14px, but not line-height:1. It's just as-is: cssLineHeight = lineHeightValue.GetRawDecimal(); } else { // Some multiple of the font size: cssLineHeight = lineHeightValue.GetRawDecimal() * box.FontSize; } // Check if it's a block context: if (box.DisplayMode == DisplayMode.Inline && LastBlockBox != null) { // Anything else uses the nearest parent block element as the max. lineZone = new InlineBoxMeta(LastBlockBox, TopOfStackSafe, box, renderable); // Put up the 'strut': lineZone.LineHeight = cssLineHeight; box.Baseline = box.FontSize * face.Descender; } else { lineZone = LastBlockBox = new BlockBoxMeta(TopOfStackSafe, box, renderable); lineZone.MaxX = box.InnerWidth; if (virts != null && virts.Has(ComputedStyle.VerticalScrollPriority)) { lineZone.MaxX -= 14; if (lineZone.MaxX < 0) { lineZone.MaxX = 0; } } bool left = (cs.DrawDirectionX == DirectionMode.RTL); lineZone.GoingLeftwards = left; // H-align: int hAlign = cs.HorizontalAlignX; if (hAlign == HorizontalAlignMode.Auto) { if (left) { hAlign = HorizontalAlignMode.Right; } else { hAlign = HorizontalAlignMode.Left; } } if (hAlign == HorizontalAlignMode.Left) { // Ok how it is (left by default). hAlign = 0; } lineZone.HorizontalAlign = hAlign; } // Apply whitespace mode: lineZone.WhiteSpace = cs.WhiteSpaceX; // Apply line height: lineZone.CssLineHeight = cssLineHeight; // Update vertical-align: Css.Value vAlign = cs.Resolve(Css.Properties.VerticalAlign.GlobalProperty); // Get the complete value: float vAlignValue = vAlign.GetDecimal(renderable, Css.Properties.VerticalAlign.GlobalProperty); // If it's a keyword.. if (vAlign is Css.CssKeyword) { // It's a mode: lineZone.VerticalAlign = (int)vAlignValue; lineZone.VerticalAlignOffset = 0f; } else { // It's a baseline offset: lineZone.VerticalAlign = VerticalAlignMode.Baseline; lineZone.VerticalAlignOffset = vAlignValue; } box.ContentWidth = 0; box.ContentHeight = 0; return(lineZone); }
internal override void Layout(LayoutBox box, Renderman renderer) { if (Corners != null) { Corners.PreLayout(); } ComputedStyle computed = RenderData.computedStyle; // Find the zIndex: // NB: At same depth as BGColour - right at the back. float zIndex = (computed.ZIndex - 0.006f); // Get the co-ord of the top edge: float top = box.Y; float left = box.X; // Get the border widths: BoxStyle width = box.Border; // Move top by the widths: top += width.Top; left += width.Left; // And the dimensions of the lines: float boxWidth = box.PaddedWidth; float boxHeight = box.PaddedHeight; // Get the other dimensions: float topY = top - width.Top; float right = left + boxWidth; float rightX = right + width.Right; float bottom = top + boxHeight; float bottomY = bottom + width.Bottom; float leftX = left - width.Left; int segment = renderer.Segment; Transformation transform = renderer.Transform; BoxRegion screenRegion = new BoxRegion(); // Get the default colour - that's the same as the text colour: Color colour = Color.black; // Is the border multicoloured? bool multiColour = false; // Does this border have a colour? if (BaseColour == null) { // Grab the text colour if there is one: if (RenderData.Text != null) { // It's the same as the font colour: colour = RenderData.Text.BaseColour * renderer.ColorOverlay; } else { // Nope - We need to set alpha: colour.a = renderer.ColorOverlay.a; } } else if (BaseColour.Count == 1) { colour = BaseColour[0].GetColour(RenderData, Css.Properties.BorderColor.GlobalProperty) * renderer.ColorOverlay; } else { multiColour = true; } // Handle border-radius: if (Corners != null) { for (int i = 0; i < 4; i++) { if (multiColour) { colour = BaseColour[i].GetColour(RenderData, Css.Properties.BorderColor.GlobalProperty) * renderer.ColorOverlay; } Corners.Layout(colour, box, renderer, i); } } // Get clipper: BoxRegion clip = renderer.ClippingBoundary; float origLeftX = leftX; float origTopY = topY; float origBottomY = bottomY; float origRightX = rightX; // top and topY: if (top < clip.Y) { top = clip.Y; } else if (top > clip.MaxY) { top = clip.MaxY; } if (topY < clip.Y) { topY = clip.Y; } // bottom and bottomY: if (bottom > clip.MaxY) { bottom = clip.MaxY; } else if (bottom < clip.Y) { bottom = clip.Y; } if (bottomY > clip.MaxY) { bottomY = clip.MaxY; } // right and rightX: if (right < clip.X) { right = clip.X; } else if (right > clip.MaxX) { right = clip.MaxX; } // rightX vs clip.MaxX if (rightX > clip.MaxX) { rightX = clip.MaxX; } // left and leftX: if (left < clip.X) { left = clip.X; } else if (left > clip.MaxX) { left = clip.MaxX; } if (leftX < clip.X) { leftX = clip.X; } float cornerPointA; float cornerPointB; for (int i = 0; i < 4; i++) { // Does this border have multiple colours? if (multiColour) { colour = BaseColour[i].GetColour(RenderData, Css.Properties.BorderColor.GlobalProperty) * renderer.ColorOverlay; } // Add to region: switch (i) { case 0: // Top. screenRegion.SetPoints(leftX, topY, rightX, top); break; case 1: // Right. // We only draw the right border if 'segment' includes 'end' if ((segment & LineBoxSegment.End) == 0) { goto NextLine; } screenRegion.SetPoints(right, topY, rightX, bottomY); break; case 2: // Bottom. screenRegion.SetPoints(leftX, bottom, rightX, bottomY); break; case 3: // Left. // Similarly, we only draw left if segment includes 'start': if ((segment & LineBoxSegment.Start) == 0) { goto NextLine; } screenRegion.SetPoints(leftX, topY, left, bottomY); break; } if (screenRegion.Overlaps(clip)) { // It's visible. // Ensure we have a batch (doesn't change graphics or font textures, thus both null): renderer.SetupBatch(this, null, null); // And get our block ready: MeshBlock block = Add(renderer); // Set the UV to that of the solid block colour pixel: block.SetSolidColourUV(); // Set the border colour: block.SetColour(colour); // Apply verts: switch (i) { case 0: // Top: if (Corners == null) { block.VertexTopLeft = renderer.PixelToWorldUnit(leftX, topY, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(rightX, topY, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(left, top, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(right, top, zIndex); } else { // Top left/right corners: cornerPointA = origLeftX + Corners.TopLeftRadius; cornerPointB = origRightX - Corners.TopRightRadius; if (cornerPointA < clip.X) { cornerPointA = clip.X; } if (cornerPointB > clip.MaxX) { cornerPointB = clip.MaxX; } // Note that we use leftX/rightX for all of them. // That's because the corner has a 'straight' edge. block.VertexTopLeft = renderer.PixelToWorldUnit(cornerPointA, topY, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(cornerPointB, topY, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(cornerPointA, top, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(cornerPointB, top, zIndex); } break; case 1: // Right: if (Corners == null) { block.VertexTopLeft = renderer.PixelToWorldUnit(right, top, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(rightX, topY, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(right, bottom, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(rightX, bottomY, zIndex); } else { // Top right/ bottom right corners: cornerPointA = origTopY + Corners.TopRightRadius; cornerPointB = origBottomY - Corners.BottomRightRadius; if (cornerPointA < clip.Y) { cornerPointA = clip.Y; } if (cornerPointB > clip.MaxY) { cornerPointB = clip.MaxY; } // Note that we use topY/bottomY for all of them. block.VertexTopLeft = renderer.PixelToWorldUnit(right, cornerPointA, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(rightX, cornerPointA, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(right, cornerPointB, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(rightX, cornerPointB, zIndex); } break; case 2: // Bottom: if (Corners == null) { block.VertexTopLeft = renderer.PixelToWorldUnit(left, bottom, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(right, bottom, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(leftX, bottomY, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(rightX, bottomY, zIndex); } else { // Bottom left/ bottom right corners: // Note that we use leftX/rightX for all of them. cornerPointA = origLeftX + Corners.BottomLeftRadius; cornerPointB = origRightX - Corners.BottomRightRadius; if (cornerPointA < clip.X) { cornerPointA = clip.X; } if (cornerPointB > clip.MaxX) { cornerPointB = clip.MaxX; } block.VertexTopLeft = renderer.PixelToWorldUnit(cornerPointA, bottom, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(cornerPointB, bottom, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(cornerPointA, bottomY, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(cornerPointB, bottomY, zIndex); } break; case 3: // Left: if (Corners == null) { block.VertexTopLeft = renderer.PixelToWorldUnit(leftX, topY, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(left, top, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(leftX, bottomY, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(left, bottom, zIndex); } else { // Top right/ bottom right corners: cornerPointA = origTopY + Corners.TopLeftRadius; cornerPointB = origBottomY + width.Bottom - Corners.BottomLeftRadius; if (cornerPointA < clip.Y) { cornerPointA = clip.Y; } if (cornerPointB > clip.MaxY) { cornerPointB = clip.MaxY; } block.VertexTopLeft = renderer.PixelToWorldUnit(leftX, cornerPointA, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(left, cornerPointA, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(leftX, cornerPointB, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(left, cornerPointB, zIndex); } break; } // Done! block.Done(transform); } NextLine: continue; } }
/// <summary>Applies a structurally matched selector to the DOM. /// Occurs shortly after StructureMatch.</summary> public MatchingSelector BakeToTarget(ComputedStyle cs, CssEvent e) { // Get the node: Node node = cs.Element; // First, generate our instance: MatchingSelector ms = new MatchingSelector(); // Update it: ms.Selector = this; ms.MatchedRoots = new MatchingRoot[RootCount]; // For each root, create a MatchingRoot object. // Apply target - this helps track which element we're actually testing: e.CurrentNode = node; e.SelectorTarget = null; // We always start from the tail and work backwards. // If we get a match, then the caller can do whatever it wants to the target. for (int i = RootCount - 1; i >= 0; i--) { // Get the matcher: RootMatcher rm = Roots[i]; // Try matching this root: if (!rm.TryMatch(e.CurrentNode)) { // Failed! If we had a matcher and it has Repeat set true, try again: if (rm.NextMatcher != null && rm.NextMatcher.Repeat) { // Move target: rm.NextMatcher.MoveUpwards(e); // Try matching again: i++; continue; } } else { // Match! e.CurrentNode is the node to add. // Create the instance: MatchingRoot matchedRoot = new MatchingRoot(); matchedRoot.Root = rm; matchedRoot.Selector = ms; matchedRoot.Node = e.CurrentNode; // Get renderable node: IRenderableNode renderable = (e.CurrentNode as IRenderableNode); // Add to selector: ms.MatchedRoots[i] = matchedRoot; // Add: ComputedStyle nodeCs = renderable.ComputedStyle; // Push the match now into the linked list: if (nodeCs.FirstMatch == null) { nodeCs.FirstMatch = matchedRoot; nodeCs.LastMatch = matchedRoot; } else { matchedRoot.PreviousInStyle = nodeCs.LastMatch; nodeCs.LastMatch.NextInStyle = matchedRoot; nodeCs.LastMatch = matchedRoot; } if (rm.IsTarget) { // Update the target now: e.SelectorTarget = renderable.RenderData; } } // If we have a structure matcher, run it now. It'll move CurrentNode for us: if (rm.PreviousMatcher != null) { // Move target: rm.PreviousMatcher.MoveUpwards(e); } } // Final pass - if we have a pseudo-element, apply it now: if (PseudoElement != null) { PseudoElement.Select(e); } // Apply target: ms.Target = e.SelectorTarget; // Finally, refresh all: ms.ResetActive(); return(ms); }
/// <summary>Figures out if this root is active or not. /// That may, in turn, figure out if the whole selector is/ is not.</summary> public void ResetActive() { // Get the local matchers: LocalMatcher[] locals = Root.LocalMatchers; bool active = true; if (locals != null) { // For each one.. for (int i = 0; i < locals.Length; i++) { // Is it active? if (!locals[i].TryMatch(Node)) { active = false; break; } } } if (active == Active) { return; } // Active changed! Active = active; // Tell the selector: int activeCount = Selector.ActiveRoots; if (active) { activeCount++; } else { activeCount--; } Selector.ActiveRoots = activeCount; if (activeCount == Selector.RootCount) { // They're all active! Selector.Active = true; if (Selector.Target != null) { // Apply target: ComputedStyle cs = Selector.Target.computedStyle; // Apply now! cs.MatchChanged(Style, true); } } else if (Selector.Active) { // The selector is no longer active. Selector.Active = false; if (Selector.Target != null) { // Apply target: ComputedStyle cs = Selector.Target.computedStyle; // Apply now! cs.MatchChanged(Style, false); } } }
/// <summary>Creates a new element style for the given element.</summary> /// <param name="element">The element that this will be the style for.</param> public ElementStyle(Dom.Element element) : base(element) { Computed = new ComputedStyle(element); }
/// <summary>Call this if the current property requires a text object. NOTE: This one may be null.</summary> public TextRenderingProperty3D GetText3D(ComputedStyle style) { // Grab it: return(style.RenderData.Text3D); }
/// <summary>Apply this CSS style to the given computed style. /// Note that you can grab the element from the computed style if you need that.</summary> /// <param name="style">The computed style to apply the property to.</param> /// <param name="value">The new value being applied.</param> public virtual void ApplyText(TextRenderingProperty text, RenderableData data, ComputedStyle style, Value value) { }
/// <summary>Apply this CSS style to the given computed style. /// Note that you can grab the element from the computed style if you need that.</summary> /// <param name="style">The computed style to apply the property to.</param> /// <param name="value">The new value being applied.</param> public virtual ApplyState Apply(ComputedStyle style, Value value) { // Ok! return(ApplyState.Ok); }
public SparkInformerNode(InformNodeEvent onStart, InformNodeEvent onEnd) { OnStart = onStart; OnEnd = onEnd; Computed = new ComputedStyle(this); }
public KeyframesAnimationInstance(ComputedStyle style, KeyframesRule anim) { Style = style; RawAnimation = anim; }
/// <summary>Gets or creates the base value for the given property. /// The base value is essentially the value held directly in this style sheet. /// E.g. if the value you're setting is the R channel of color-overlay, this sets up the color-overlay value for you.</summary> /// <returns>The raw value (which may have just been created). Never an 'inherit' or 'initial' keyword.</returns> internal Css.Value GetBaseValue(CssProperty property) { Css.Value propertyValue; // Does it exist already? if (!Properties.TryGetValue(property, out propertyValue)) { // Nope! Create it now. Does the computed style hold a value instead? ComputedStyle computed = GetComputed(); if (computed != null && computed.Properties.TryGetValue(property, out propertyValue) && propertyValue != null) { // Derive from the computed value. if (propertyValue is Css.Keywords.Inherit) { // Must clone the inherited value (using special inherit copy): propertyValue = (propertyValue as Css.Keywords.Inherit).SetCopy(); } else if (propertyValue is Css.Keywords.Initial) { // Clone the initial value: propertyValue = property.InitialValue.Copy(); } else { // Must copy it: propertyValue = propertyValue.Copy(); } // Make sure it has low specif: propertyValue.Specifity = -1; } else { // Needs to be created. Must also copy it. // Copy is used because it'll probably change some internal value. propertyValue = property.InitialValue.Copy(); } Properties[property] = propertyValue; } else if (propertyValue is Css.Keywords.Inherit) { // Must clone the inherited value (using special inherit copy): propertyValue = (propertyValue as Css.Keywords.Inherit).SetCopy(); // Make sure it has low specif: propertyValue.Specifity = -1; Properties[property] = propertyValue; } else if (propertyValue is Css.Keywords.Initial) { // Clone the initial value: propertyValue = property.InitialValue.Copy(); // Make sure it has low specif: propertyValue.Specifity = -1; Properties[property] = propertyValue; } // If it's not currently a set, we need it as one. int size = property.SetSize; if (propertyValue is Css.ValueSet) { if (propertyValue.Count < size) { // Resize it: propertyValue.Count = size; } } else { // Create the set: Css.ValueSet set = new Css.ValueSet(); set.Count = size; // Make sure it has low specif: set.Specifity = -1; for (int i = 0; i < size; i++) { // Must copy each value (e.g. if they get animated): set[i] = propertyValue.Copy(); } Properties[property] = set; propertyValue = set; } return(propertyValue); }
/// <summary>True if this selector matches the structure of the DOM where the given CS is.</summary> public bool StructureMatch(ComputedStyle cs, CssEvent e) { // Get the node: Node node = cs.Element; if (node == null) { return(false); } // Apply target - this helps track which element we're actually testing: e.CurrentNode = node; // We always start from the tail and work backwards. // If we get a match, then the caller can do whatever it wants to the target. for (int i = RootCount - 1; i >= 0; i--) { // Get the matcher: RootMatcher rm = Roots[i]; // Try matching this root: if (!rm.TryMatch(e.CurrentNode)) { // Failed! If we had a matcher and it has Repeat set true, try again: if (rm.NextMatcher != null && rm.NextMatcher.Repeat) { // Move target: rm.NextMatcher.MoveUpwards(e); // Still got a node? if (e.CurrentNode == null) { return(false); } // Try matching again: i++; continue; } return(false); } // If we have a structure matcher, run it now. It'll move CurrentNode for us: if (rm.PreviousMatcher != null) { // Move target: rm.PreviousMatcher.MoveUpwards(e); // Still got a node? if (e.CurrentNode == null) { return(false); } } } // If we have a pseudo element, make sure parents haven't also matched this selector. if (PseudoElement != null) { // Have any parents matched this selector? Node parent = node.parentNode; while (parent != null) { if (parent["spark-virt"] != null) { // Already on a virtual element - quit there. return(false); } parent = parent.parentNode; } } // All clear! return(true); }
public void AddViewBoxTransform(BoxRegion region, AspectRatio aspectRatio, SVGSVGElement frag) { // Get the tags computed style: Css.ComputedStyle tagStyle = (frag == null) ? null : frag.Style.Computed; float x = (tagStyle == null ? 0 : tagStyle.OffsetLeft); float y = (tagStyle == null ? 0 : tagStyle.OffsetTop); if (region.IsEmpty) { PushMatrix(TranslateMatrix(x, y)); return; } float width = (tagStyle == null ? region.Width : tagStyle.PixelWidth); float height = (tagStyle == null ? region.Height : tagStyle.PixelHeight); float fScaleX = width / region.Width; float fScaleY = height / region.Height; //(this.MinY < 0 ? -1 : 1) * float fMinX = -region.X * fScaleX; float fMinY = -region.Y * fScaleY; if (aspectRatio == null) { aspectRatio = new AspectRatio(SVGPreserveAspectRatio.xMidYMid, false); } if (aspectRatio.Align != SVGPreserveAspectRatio.none) { if (aspectRatio.Slice) { fScaleX = (float)Math.Max(fScaleX, fScaleY); fScaleY = (float)Math.Max(fScaleX, fScaleY); } else { fScaleX = (float)Math.Min(fScaleX, fScaleY); fScaleY = (float)Math.Min(fScaleX, fScaleY); } float fViewMidX = (region.Width / 2) * fScaleX; float fViewMidY = (region.Height / 2) * fScaleY; float fMidX = width / 2; float fMidY = height / 2; fMinX = -region.X * fScaleX; fMinY = -region.Y * fScaleY; switch (aspectRatio.Align) { case SVGPreserveAspectRatio.xMinYMin: break; case SVGPreserveAspectRatio.xMidYMin: fMinX += fMidX - fViewMidX; break; case SVGPreserveAspectRatio.xMaxYMin: fMinX += width - region.Width * fScaleX; break; case SVGPreserveAspectRatio.xMinYMid: fMinY += fMidY - fViewMidY; break; case SVGPreserveAspectRatio.xMidYMid: fMinX += fMidX - fViewMidX; fMinY += fMidY - fViewMidY; break; case SVGPreserveAspectRatio.xMaxYMid: fMinX += width - region.Width * fScaleX; fMinY += fMidY - fViewMidY; break; case SVGPreserveAspectRatio.xMinYMax: fMinY += height - region.Height * fScaleY; break; case SVGPreserveAspectRatio.xMidYMax: fMinX += fMidX - fViewMidX; fMinY += height - region.Height * fScaleY; break; case SVGPreserveAspectRatio.xMaxYMax: fMinX += width - region.Width * fScaleX; fMinY += height - region.Height * fScaleY; break; default: break; } } // Clip now: SetClip(new BoxRegion(x, y, width, height), false); Matrix4x4 matrix = ScaleMatrix(fScaleX, fScaleY); matrix *= TranslateMatrix(x, y); matrix *= TranslateMatrix(fMinX, fMinY); // Push it: PushMatrix(matrix); }
/// <summary>Loads the character array (<see cref="Css.TextRenderingProperty.Characters"/>) from the given text string.</summary> internal void LoadCharacters(string text, RenderableData renderable) { if (Characters != null && Visible) { // Make sure each char goes offscreen: NowOffScreen(); // Clear visible: Visible = false; } // Get the computed style: ComputedStyle computedStyle = renderable.computedStyle; // No longer dirty: Dirty = false; char[] characters = text.ToCharArray(); // Get text transform: int textTransform = computedStyle.ResolveInt(Css.Properties.TextTransform.GlobalProperty); if (textTransform != TextTransformMode.None && characters.Length > 0) { switch (textTransform) { case TextTransformMode.Capitalize: // Uppercase the first character: characters[0] = char.ToUpper(characters[0]); break; case TextTransformMode.Lowercase: // Lowercase the whole string: for (int i = 0; i < characters.Length; i++) { characters[i] = char.ToLower(characters[i]); } break; case TextTransformMode.Uppercase: // Uppercase the whole string: for (int i = 0; i < characters.Length; i++) { characters[i] = char.ToUpper(characters[i]); } break; } } // Get the whitespace mode: int whiteSpaceMode = computedStyle.WhiteSpaceX; // Purge characters that we don't want: if ((whiteSpaceMode & WhiteSpaceMode.NormalOrNoWrap) != 0) { // Dump breaks and repeated whitespace. bool dumpWhiteSpace = false; for (int i = 0; i < characters.Length; i++) { char character = characters[i]; if (character == '\t') { // Dump: characters[i] = '\0'; } else if (character == '\r' || character == '\n') { // Dump: characters[i] = '\0'; // Dump whitespaces immediately after these: dumpWhiteSpace = true; continue; } else if (character == '\u00A0') { // NBSP: characters[i] = ' '; } else if (character == ' ') { if (dumpWhiteSpace) { // Dump: characters[i] = '\0'; } else { // Dump any consecutive whitespace (except for ): dumpWhiteSpace = true; } continue; } dumpWhiteSpace = false; } } else if (whiteSpaceMode == WhiteSpaceMode.PreLine) { // Dump repeated whitespace (and \r) only. bool dumpWhiteSpace = false; for (int i = 0; i < characters.Length; i++) { char character = characters[i]; if (character == ' ') { if (dumpWhiteSpace) { // Dump: characters[i] = '\0'; } else { dumpWhiteSpace = true; } continue; } else if (character == '\u00A0') { // NBSP: characters[i] = ' '; } else if (character == '\r') { characters[i] = '\0'; dumpWhiteSpace = true; continue; } else if (character == '\n') { dumpWhiteSpace = true; continue; } dumpWhiteSpace = false; } } else { // \r and nbsp only. for (int i = 0; i < characters.Length; i++) { char character = characters[i]; if (character == '\r') { characters[i] = '\0'; } else if (character == '\u00A0') { // NBSP: characters[i] = ' '; } } } // Create characters if they're needed: if (Characters == null || Characters.Length != characters.Length) { Characters = new Glyph[characters.Length]; Kerning = null; } // Considered all empty until shown otherwise. AllEmpty = true; // Next, for each character, find its dynamic character. // At the same time we want to find out what dimensions this word has so it can be located correctly. Glyph previous = null; for (int i = 0; i < characters.Length; i++) { char rawChar = characters[i]; if (rawChar == '\0') { // It got dumped. continue; } Glyph character = null; // Is it a unicode high/low surrogate pair? if (char.IsHighSurrogate(rawChar) && i != characters.Length - 1) { // Low surrogate follows: char lowChar = characters[i + 1]; // Get the full charcode: int charcode = char.ConvertToUtf32(rawChar, lowChar); // Grab the surrogate pair char: character = FontToDraw.GetGlyphOrEmoji(charcode); // Make sure there is no char in the low surrogate spot: Characters[i + 1] = null; // Update this character: Characters[i] = character; // Skip the low surrogate: i++; } else if (rawChar == '\n') { // Special case for newlines (They don't show up in host fonts). if (NEWLINE_GLYPH == null) { NEWLINE_GLYPH = new Glyph(); NEWLINE_GLYPH.RawCharcode = (int)'\n'; } Characters[i] = NEWLINE_GLYPH; } else { character = FontToDraw.GetGlyphOrEmoji((int)rawChar); Characters[i] = character; } if (character == null) { continue; } if (previous != null) { // Look for a kern pair: if (character.Kerning != null) { float offset; if (character.Kerning.TryGetValue(previous, out offset)) { // Got a kern! if (Kerning == null) { Kerning = new float[characters.Length]; } Kerning[i] = offset; } } } previous = character; AllEmpty = false; } }
/// <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; } }