diff --git a/Source/HtmlRenderer.Demo/Samples/0_acid1_dev/003.2.html b/Source/HtmlRenderer.Demo/Samples/0_acid1_dev/003.2.html index 808ec4e0d..80ad44299 100644 --- a/Source/HtmlRenderer.Demo/Samples/0_acid1_dev/003.2.html +++ b/Source/HtmlRenderer.Demo/Samples/0_acid1_dev/003.2.html @@ -119,7 +119,7 @@ - + abcd

bang @@ -132,5 +132,6 @@

+ 1234 diff --git a/Source/HtmlRenderer.Demo/Samples/0_acid1_dev/003.3.html b/Source/HtmlRenderer.Demo/Samples/0_acid1_dev/003.3.html new file mode 100644 index 000000000..d7a3b221c --- /dev/null +++ b/Source/HtmlRenderer.Demo/Samples/0_acid1_dev/003.3.html @@ -0,0 +1,18 @@ + + + test + + + + xyz +
+ 12345 +
sub1
+
sub2
+ 67890 +
+
+ abcde +
+ + diff --git a/Source/HtmlRenderer.Demo/Samples/0_acid1_dev/003.4.html b/Source/HtmlRenderer.Demo/Samples/0_acid1_dev/003.4.html new file mode 100644 index 000000000..30377e503 --- /dev/null +++ b/Source/HtmlRenderer.Demo/Samples/0_acid1_dev/003.4.html @@ -0,0 +1,22 @@ + + + test + + + + abcd +
+

+ bang + + +

+

+ whimper + + +

+
+ 1234 + + diff --git a/Source/LayoutFarm.Composers/7_Events/HtmlInputEventAdapter.cs b/Source/LayoutFarm.Composers/7_Events/HtmlInputEventAdapter.cs index 6cb62e39c..86f5fa781 100755 --- a/Source/LayoutFarm.Composers/7_Events/HtmlInputEventAdapter.cs +++ b/Source/LayoutFarm.Composers/7_Events/HtmlInputEventAdapter.cs @@ -136,6 +136,7 @@ public void MouseMove(UIMouseEventArgs e, CssBox startAt) //dragging *** , if changed if (this._mousedownX != x || this._mousedownY != y) { + //handle mouse drag CssBoxHitChain hitChain = GetFreeHitChain(); hitChain.SetRootGlobalPosition(x, y); @@ -169,6 +170,7 @@ public void MouseMove(UIMouseEventArgs e, CssBox startAt) BoxHitUtils.HitTest(_mouseDownStartAt, lastRootGlobalX, lastRootGlobalY, _latestMouseDownChain); } + //create selection range var newSelectionRange = new SelectionRange( _latestMouseDownChain, @@ -176,10 +178,12 @@ public void MouseMove(UIMouseEventArgs e, CssBox startAt) this.ifonts); if (newSelectionRange.IsValid) { + this._htmlContainer.SetSelection(newSelectionRange); } else { + this._htmlContainer.SetSelection(null); } } diff --git a/Source/LayoutFarm.CssBase/CssProps.cs b/Source/LayoutFarm.CssBase/CssProps.cs index 7c57101ca..e5482aef5 100755 --- a/Source/LayoutFarm.CssBase/CssProps.cs +++ b/Source/LayoutFarm.CssBase/CssProps.cs @@ -4,8 +4,52 @@ namespace LayoutFarm.Css { + // + // = block | inline | run-in ; + // + // = flow | flow-root | table | flex | grid | ruby ; + // + // = list-item && ? && [ flow | flow-root ]? + // + // = table-row-group | table-header-group | + // table-footer-group | table-row | table-cell | + // table-column-group | table-column | table-caption | + // ruby-base | ruby-text | ruby-base-container | + // ruby-text-container ; + // + // = contents | none ; + // + // = inline-block | inline-list-item | + // inline-table | inline-flex | inline-grid ; + // //-------------------------------------------------- + + public enum CssDisplayOutside : byte + { + Internal, + + Block, + Inline, + RunIn, + + TableCell, + TableCaption, + + } + + public enum CssDisplayInside : byte + { + Internal, + + Flow, + FlowRoot, + Table, + Flex, + Grid, + Ruby, + } + + - //-------------------------------------------------- public enum CssDisplay : byte { [Map(CssConstants.Inline)] @@ -41,13 +85,7 @@ public enum CssDisplay : byte [Map(CssConstants.None)] None, - - //=========================================== - /// - ///following group act as Containing box : Block,Table,TableCell, ListItem - /// - __CONTAINER_BEGIN_HERE, - + [Map(CssConstants.Block)] Block, [Map(CssConstants.Table)] @@ -58,8 +96,7 @@ public enum CssDisplay : byte ListItem, [Map(CssConstants.Flex)] - Flex - //=========================================== + Flex } public enum CssWhiteSpace : byte { diff --git a/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox.cs b/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox.cs index 6ec101eb6..22e047abb 100755 --- a/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox.cs +++ b/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox.cs @@ -59,7 +59,7 @@ public CssBox(BoxSpec spec, IRootGraphics rootgfx) if (!spec.IsFreezed) { //must be freezed - //throw new NotSupportedException(); + throw new NotSupportedException(); } #endif @@ -107,7 +107,22 @@ public CssBox ParentBox { get { return _parentBox; } } - + internal bool HasContainerProperty + { + get + { + switch (this._cssDisplay) + { + case Css.CssDisplay.Block: + case Css.CssDisplay.Table: + case Css.CssDisplay.TableCell: + case Css.CssDisplay.ListItem: + case Css.CssDisplay.Flex: + return true; + } + return false; + } + } public CssBox GetTopRootCssBox() { var topmost = this; @@ -148,13 +163,13 @@ public bool IsCustomCssBox /// /// is the box "Display" is "Inline", is this is an inline box and not block. /// - public bool IsInline + internal bool OutsideDisplayIsInline { get { return (this._boxCompactFlags & BoxFlags.IS_INLINE_BOX) != 0; } - internal set + set { if (value) { @@ -266,10 +281,7 @@ internal string dbugCopyTextContent() #endif internal void AddLineBox(CssLineBox linebox) { - - linebox.linkedNode = this._clientLineBoxes.AddLast(linebox); - } internal int LineBoxCount { @@ -373,8 +385,10 @@ List Runs return this._aa_contentRuns; } } - - internal bool HasRuns + /// + /// box has only runs + /// + internal bool HasOnlyRuns { get { diff --git a/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBoxCollection.cs b/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBoxCollection.cs index 2a02b3ff8..1e82d4e76 100755 --- a/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBoxCollection.cs +++ b/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBoxCollection.cs @@ -1,6 +1,4 @@ -//2014,2015 BSD,WinterDev - - +//2014,2015 BSD, WinterDev using System; using System.Collections.Generic; @@ -12,7 +10,8 @@ class CssBoxCollection LinkedList _boxes = new LinkedList(); public CssBoxCollection() { - } + } + public IEnumerable GetChildBoxIter() { var cNode = _boxes.First; @@ -31,6 +30,7 @@ public IEnumerable GetChildBoxBackwardIter() cNode = cNode.Previous; } } + public void AddChild(CssBox owner, CssBox box) { @@ -92,7 +92,7 @@ public CssBox GetLastChild() public IEnumerator GetEnumerator() { return this._boxes.GetEnumerator(); - } + } #if DEBUG public bool dbugContains(CssBox box) diff --git a/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox_Fields.cs b/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox_Fields.cs index 470da3678..11920bafb 100755 --- a/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox_Fields.cs +++ b/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox_Fields.cs @@ -60,13 +60,10 @@ partial class CssBox //condition 3: valid LinkedList _clientLineBoxes; - /// /// absolute position layer /// CssBoxCollection _absPosLayer; - //List _absLayer2; - CssBlockRun justBlockRun; @@ -177,6 +174,7 @@ public void RemoveChild(CssBox box) } break; } } + public void AppendChild(CssBox box) { switch (box.Position) @@ -349,7 +347,7 @@ public void AppendToAbsoluteLayer(CssBox box) //} } - + //------------------------------------- internal void ResetLineBoxes() { diff --git a/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox_OtherActualValues.cs b/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox_OtherActualValues.cs index 209291522..333ee1a16 100755 --- a/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox_OtherActualValues.cs +++ b/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox_OtherActualValues.cs @@ -22,6 +22,9 @@ partial class CssBox float _actualBorderSpacingHorizontal; float _actualBorderSpacingVertical; + CssDisplayOutside _displayOutside; + CssDisplayInside _displayInside; + /// /// Gets the line height /// @@ -36,9 +39,18 @@ public CssDisplay CssDisplay { get { + //TODO review here return this._cssDisplay; } } + public CssDisplayOutside CssDisplayOutside + { + get { return this._displayOutside; } + } + public CssDisplayInside CssDisplayInside + { + get { return this._displayInside; } + } /// /// Gets the text indentation (on first line only) /// @@ -103,19 +115,117 @@ internal void DirectSetBorderWidth(CssSide side, float w) } } + static void TransplateDisplayOutsideInside(CssDisplay cssDisplay, out CssDisplayOutside outside, out CssDisplayInside inside) + { + //Short display Full display Generated box + //none 〃 subtree omitted from box tree + //contents 〃 element replaced by contents in box tree + + //block 'block flow' block-level block container aka block box + //flow-root 'block flow-root' block-level block container that establishes a new block formatting context (BFC) + //inline 'inline flow' inline box + //inline-block 'inline flow-root' inline-level block container + //run-in 'run-in flow' run-in box (inline box with special box-tree-munging rules) + //list-item 'list-item block flow' block box with additional marker box + //inline list-item 'list-item inline flow' inline box with additional marker box + //flex 'block flex' block-level flex container + //inline-flex 'inline flex' inline-level flex container + //grid 'block grid' block-level grid container + //inline-grid 'inline grid' inline-level grid container + //ruby 'inline ruby' inline-level ruby container + //block ruby 'block ruby' block box containing ruby container + //table 'block table' block-level table wrapper box containing table box + //inline-table 'inline table' inline-level table wrapper box containing table box + //table-cell 'table-cell flow' table cell block container + //table-caption 'table-caption flow' table cell block container + //ruby-base 'ruby-base flow' layout-specific internal box + //ruby-text 'ruby-text flow' layout-specific internal box + //other 〃 layout-specific internal box + switch (cssDisplay) + { + default: + throw new NotSupportedException(); + + case CssDisplay.TableColumn: + case CssDisplay.TableColumnGroup: + case CssDisplay.TableRow: + case CssDisplay.TableRowGroup: + case CssDisplay.TableHeaderGroup: + case CssDisplay.TableFooterGroup: + case CssDisplay.None: + outside = CssDisplayOutside.Internal; + inside = CssDisplayInside.Internal; + break; + + + //outside -> inline + case CssDisplay.Inline: + outside = CssDisplayOutside.Inline; //* + inside = CssDisplayInside.Flow; + break; + case CssDisplay.InlineBlock: + outside = CssDisplayOutside.Inline; //* + inside = CssDisplayInside.FlowRoot; + break; + case CssDisplay.InlineTable: + outside = CssDisplayOutside.Inline; //* + inside = CssDisplayInside.Table; + break; + case CssDisplay.InlineFlex: + outside = CssDisplayOutside.Inline; //* + inside = CssDisplayInside.Flex; + break; + //------- + //outside -> block + case CssDisplay.ListItem: + outside = CssDisplayOutside.Block; + inside = CssDisplayInside.Flow; + break; + case CssDisplay.Flex: + outside = CssDisplayOutside.Block; + inside = CssDisplayInside.Flex; + break; + + case CssDisplay.Block: + outside = CssDisplayOutside.Block; + inside = CssDisplayInside.Flow; + break; + + case CssDisplay.Table: + outside = CssDisplayOutside.Block; + inside = CssDisplayInside.Table; + break; + //----------------- + //special + case CssDisplay.TableCaption: + outside = CssDisplayOutside.TableCaption; + inside = CssDisplayInside.Flow; + break; + case CssDisplay.TableCell: + outside = CssDisplayOutside.TableCell; + inside = CssDisplayInside.Flow; + break; + + } + + } public static void ChangeDisplayType(CssBox box, CssDisplay newdisplay) { + TransplateDisplayOutsideInside(newdisplay, out box._displayOutside, out box._displayInside); + if ((box._boxCompactFlags & BoxFlags.DONT_CHANGE_DISPLAY_TYPE) == 0) - { + { box._cssDisplay = newdisplay; } - box.IsInline = ((newdisplay == CssDisplay.Inline || - newdisplay == CssDisplay.InlineBlock || - newdisplay == CssDisplay.InlineFlex) - && !box.IsBrElement); + //box.OutsideDisplayIsInline = ((newdisplay == CssDisplay.Inline || + // newdisplay == CssDisplay.InlineBlock || + // newdisplay == CssDisplay.InlineFlex) + // && !box.IsBrElement); + box.OutsideDisplayIsInline = box._displayOutside == CssDisplayOutside.Inline && !box.IsBrElement; + //--------------------------- box._isVisible = box._cssDisplay != CssDisplay.None && box._myspec.Visibility == CssVisibility.Visible; @@ -130,11 +240,13 @@ public static void ChangeDisplayType(CssBox box, CssDisplay newdisplay) switch (newdisplay) { case CssDisplay.Block: + case CssDisplay.InlineBlock: + case CssDisplay.ListItem: case CssDisplay.Table: - case CssDisplay.TableCell: - case CssDisplay.InlineBlock: case CssDisplay.InlineTable: + case CssDisplay.TableCell: + case CssDisplay.Flex: case CssDisplay.InlineFlex: box._boxCompactFlags |= BoxFlags.HAS_CONTAINER_PROP; diff --git a/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox_Unsafe.cs b/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox_Unsafe.cs index 0172438fd..56a11994c 100755 --- a/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox_Unsafe.cs +++ b/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssBox_Unsafe.cs @@ -33,6 +33,7 @@ internal static CssBoxCollection UnsafeGetChildren(CssBox box) { return box._aa_boxes; } + public static BoxSpec UnsafeGetBoxSpec(CssBox box) { //this method is for BoxCreator and debug only! @@ -67,17 +68,15 @@ public static void UnsafeSetContentRuns(CssBox box, List runs, bool isAl //** box._boxCompactFlags &= ~BoxFlags.LAY_RUNSIZE_MEASURE; } - //---------- - - - + + //----------- #if DEBUG public override string ToString() { if (this._controller != null) { - if (this.HasRuns) + if (this.HasOnlyRuns) { return this._controller.ToString() + " " + this.CssDisplay + " r=" + this.RunCount; } @@ -88,7 +87,7 @@ public override string ToString() } else { - if (this.HasRuns) + if (this.HasOnlyRuns) { return "!a " + " " + this.CssDisplay + " r=" + this.RunCount; } diff --git a/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssLineBox.cs b/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssLineBox.cs index 149f608c9..2bf0be349 100755 --- a/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssLineBox.cs +++ b/Source/LayoutFarm.HtmlRenderer/2_Boxes/1_CoreBox/CssLineBox.cs @@ -114,7 +114,7 @@ public override string ToString() /// sealed class CssLineBox { - readonly CssBox _ownerBox; + readonly CssBox _ownerBox; //a run may come from another CssBox (not from _ownerBox) readonly List _runs = new List(); @@ -135,9 +135,6 @@ sealed class CssLineBox static int dbugTotalId; public readonly int dbugId = dbugTotalId++; #endif - - - /// /// Creates a new LineBox /// @@ -145,6 +142,7 @@ public CssLineBox(CssBox ownerBox) { _ownerBox = ownerBox; } + internal CssLineBox NextLine { get @@ -188,15 +186,15 @@ internal float CachedExactContentWidth } internal float CalculateLineHeight() { - + float maxBottom = 0; List myruns = this._runs; - int j = myruns.Count; + int j = myruns.Count; for (int i = 0; i < j; ++i) { - CssRun run = myruns[i]; + CssRun run = myruns[i]; //maxRight = run.Right > maxRight ? run.Right : maxRight; - maxBottom = run.Bottom > maxBottom ? run.Bottom : maxBottom; + maxBottom = run.Bottom > maxBottom ? run.Bottom : maxBottom; } return maxBottom; } @@ -277,7 +275,7 @@ internal bool CanDoMoreLeftOffset(float newOffset, float rightLimit) { int j = this._runs.Count; if (j > 0) - { + { //last run right position //1. find current left start if (_runs[j - 1].Right + newOffset > rightLimit) @@ -690,7 +688,7 @@ static int StepUpRegisterStrips(Dictionary dicStrips, //step up var strip = inputList[i]; var upperBox = strip.owner.ParentBox; - if (upperBox != null && upperBox != lineOwnerBox && upperBox.IsInline) + if (upperBox != null && upperBox != lineOwnerBox && upperBox.OutsideDisplayIsInline) { RegisterStripPart(upperBox, strip.Left, strip.Top, strip.Right, strip.Bottom, inputList, dicStrips); } diff --git a/Source/LayoutFarm.HtmlRenderer/2_Boxes/2_Runs/CssRun.cs b/Source/LayoutFarm.HtmlRenderer/2_Boxes/2_Runs/CssRun.cs index 5fd9d517a..164280e29 100755 --- a/Source/LayoutFarm.HtmlRenderer/2_Boxes/2_Runs/CssRun.cs +++ b/Source/LayoutFarm.HtmlRenderer/2_Boxes/2_Runs/CssRun.cs @@ -144,9 +144,7 @@ public float Left set { this._x = value; - if (_x == 55) - { - } + } } diff --git a/Source/LayoutFarm.HtmlRenderer/2_Boxes/4_HitAndSelections/SelectionRange.cs b/Source/LayoutFarm.HtmlRenderer/2_Boxes/4_HitAndSelections/SelectionRange.cs index 6b84be289..af98e8c43 100755 --- a/Source/LayoutFarm.HtmlRenderer/2_Boxes/4_HitAndSelections/SelectionRange.cs +++ b/Source/LayoutFarm.HtmlRenderer/2_Boxes/4_HitAndSelections/SelectionRange.cs @@ -26,9 +26,9 @@ public class SelectionRange public SelectionRange(CssBoxHitChain startChain, CssBoxHitChain endChain, IFonts ifonts) - { + { if (IsOnTheSameLine(startChain, endChain)) - { + { //on the same line if (endChain.RootGlobalX < startChain.RootGlobalX) { @@ -40,6 +40,7 @@ public SelectionRange(CssBoxHitChain startChain, } else { + //across line if (endChain.RootGlobalY < startChain.RootGlobalY) { //swap @@ -48,8 +49,7 @@ public SelectionRange(CssBoxHitChain startChain, startChain = tmp; } } - - + //1. this.SetupStartHitPoint(startChain, ifonts); //2. @@ -59,6 +59,7 @@ public SelectionRange(CssBoxHitChain startChain, return; } + this.SetupEndHitPoint(startChain, endChain, ifonts); this.snapSelectionArea = this.GetSelectionRectArea(); } @@ -328,10 +329,14 @@ void SetupEndHitPoint(CssBoxHitChain startChain, CssBoxHitChain endChain, IFonts } break; } + +#if DEBUG if (xposOnEndLine == 0) { } +#endif + //---------------------------------- this.selectedLines = new List(); if (startHitHostLine == endline) @@ -778,19 +783,19 @@ public static void SelectPartialFromStart(this CssLineBox lineBox, int endAtPx, EndHitRun = endRun, EndHitCharIndex = endRunIndex }; - } + } public static void Select(this CssLineBox lineBox, int startAtPx, int endAt, CssRun startRun, int startRunIndex, CssRun endRun, int endRunIndex) - { + { lineBox.SelectionSegment = new SelectionSegment(startAtPx, endAt - startAtPx) { StartHitRun = startRun, StartHitCharIndex = startRunIndex, EndHitRun = endRun, EndHitCharIndex = endRunIndex - }; + }; } } diff --git a/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/BoxHitUtils.cs b/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/BoxHitUtils.cs index d06f4d574..d4186e59e 100755 --- a/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/BoxHitUtils.cs +++ b/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/BoxHitUtils.cs @@ -29,7 +29,7 @@ public static bool HitTest(CssBox box, float x, float y, CssBoxHitChain hitChain //-------------------------------------- float boxHitLocalX = x - box.LocalX; - float boxHitLocalY = y - box.LocalY; + float boxHitLocalY = y - box.LocalY; bool isPointInArea = box.IsPointInArea(x, y); //---------------------------------------------------------------------- if (isPointInArea) @@ -257,7 +257,7 @@ internal static CssBox SearchUpForContainingBlockBox(CssBox startBox) } var box = startBox.ParentBox; - while (box.CssDisplay < Css.CssDisplay.__CONTAINER_BEGIN_HERE && + while (box.HasContainerProperty && box.ParentBox != null) { box = box.ParentBox; diff --git a/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/CssLayoutEngine.FloatFormattingContext.cs b/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/CssLayoutEngine.FloatFormattingContext.cs index b2b820d78..9eafbfad2 100755 --- a/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/CssLayoutEngine.FloatFormattingContext.cs +++ b/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/CssLayoutEngine.FloatFormattingContext.cs @@ -6,18 +6,13 @@ namespace LayoutFarm.HtmlBoxes { struct FloatFormattingContext { - - //Stack lineFormattingContextStack = new Stack(); - - //Stack lineOffsetLeftStack = new Stack(); - //Stack lineOffsetRightStack = new Stack(); - //Stack floatingOutOfLineStack = new Stack(); public CssBox rightFloatBox; public CssBox leftFloatBox; public float lineLeftOffset; public float lineRightOffset; public float offsetFloatTop; public bool floatingOutOfLine; + } } \ No newline at end of file diff --git a/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/CssLayoutEngine.cs b/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/CssLayoutEngine.cs index b379fa2b7..93b34b33f 100755 --- a/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/CssLayoutEngine.cs +++ b/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/CssLayoutEngine.cs @@ -17,629 +17,595 @@ using System.Collections.Generic; using PixelFarm.Drawing; using LayoutFarm.Css; -using PixelFarm.Drawing; namespace LayoutFarm.HtmlBoxes { - /// - /// Helps on CSS Layout. - /// - static partial class CssLayoutEngine - { + + + static class LinesFormattingEngine + { const float CSS_OFFSET_THRESHOLD = 0.1f; /// - /// Measure image box size by the width\height set on the box and the actual rendered image size.
- /// If no image exists for the box error icon will be set. + /// do layout line formatting context ///
- /// the image word to measure - public static void MeasureImageSize(CssImageRun imgRun, LayoutVisitor lay) + /// + /// + public static void DoLayoutLinesContext(CssBox hostBlock, LayoutVisitor lay) { - var width = imgRun.OwnerBox.Width; - var height = imgRun.OwnerBox.Height; - - bool hasImageTagWidth = width.Number > 0 && width.UnitOrNames == CssUnitOrNames.Pixels; - bool hasImageTagHeight = height.Number > 0 && height.UnitOrNames == CssUnitOrNames.Pixels; - bool scaleImageHeight = false; + //this in line formatting context + //*** hostBlock must confirm that it has all inline children + hostBlock.SetHeightToZero(); + hostBlock.ResetLineBoxes(); - if (hasImageTagWidth) - { - imgRun.Width = width.Number; - } - else if (width.Number > 0 && width.IsPercentage) - { + //---------------------------------------------------------------------------------------- + float limitLocalRight = hostBlock.VisualWidth - (hostBlock.ActualPaddingRight + hostBlock.ActualBorderRightWidth); + float localX = hostBlock.ActualTextIndent + hostBlock.ActualPaddingLeft + hostBlock.ActualBorderLeftWidth; + float localY = hostBlock.ActualPaddingTop + hostBlock.ActualBorderTopWidth; + //---------------------------------------------------------------------------------------- - imgRun.Width = width.Number * lay.LatestContainingBlock.VisualWidth; - scaleImageHeight = true; - } - else if (imgRun.HasUserImageContent) - { - imgRun.Width = imgRun.ImageRectangle == Rectangle.Empty ? imgRun.OriginalImageWidth : imgRun.ImageRectangle.Width; - } - else + if (lay.HasFloatBoxInContext) { - imgRun.Width = hasImageTagHeight ? height.Number / 1.14f : 20; - } + var recentLeftFloatBox = lay.LatestLeftFloatBox; + var recentRightFloatBox = lay.LatestRightFloatBox; + var latestSibling = lay.LatestSiblingBox; - var maxWidth = imgRun.OwnerBox.MaxWidth;// new CssLength(imageWord.OwnerBox.MaxWidth); - if (maxWidth.Number > 0) - { - float maxWidthVal = -1; - switch (maxWidth.UnitOrNames) + if (latestSibling != null) { - case CssUnitOrNames.Percent: + //check latest sibling first + if (hostBlock.Float == CssFloat.None) + { + if (recentLeftFloatBox != null) { - maxWidthVal = maxWidth.Number * lay.LatestContainingBlock.VisualWidth; - } break; - case CssUnitOrNames.Pixels: + if (hostBlock.LocalY < recentLeftFloatBox.LocalVisualBottom) + { + localX = recentLeftFloatBox.LocalVisualRight; + } + } + else { - maxWidthVal = maxWidth.Number; - } break; - } - - if (maxWidthVal > -1 && imgRun.Width > maxWidthVal) + } + } + } + else { - imgRun.Width = maxWidthVal; - scaleImageHeight = !hasImageTagHeight; + if (hostBlock.Float == CssFloat.None) + { + if (recentLeftFloatBox != null) + { + localX = recentLeftFloatBox.LocalVisualRight; + } + if (recentRightFloatBox != null) + { + limitLocalRight = recentRightFloatBox.LocalX; + } + } + //check if need newline or not } + } - if (hasImageTagHeight) + int interlineSpace = 0; + + //First line box + + var line = new CssLineBox(hostBlock); + hostBlock.AddLineBox(line); + //**** + var floatCtx = new FloatFormattingContext(); + FlowBoxContentIntoHostLineFmtContext(lay, hostBlock, hostBlock, + limitLocalRight, localX, + ref line, ref localX, ref floatCtx); + + //**** + // if width is not restricted we need to lower it to the actual width + if (hostBlock.VisualWidth + lay.ContainerBlockGlobalX >= CssBoxConstConfig.BOX_MAX_RIGHT) { - imgRun.Height = height.Number; + float newWidth = localX + hostBlock.ActualPaddingRight + hostBlock.ActualBorderRightWidth;// CssBox.MAX_RIGHT - (args.ContainerBlockGlobalX + blockBox.LocalX); + if (newWidth <= CSS_OFFSET_THRESHOLD) + { + newWidth = CSS_OFFSET_THRESHOLD; + } + hostBlock.SetVisualWidth(newWidth); } - else if (imgRun.HasUserImageContent) + //--------------------- + float maxLineWidth = 0; + if (hostBlock.CssDirection == CssDirection.Rtl) { - imgRun.Height = imgRun.ImageRectangle == Rectangle.Empty ? imgRun.OriginalImageHeight : imgRun.ImageRectangle.Height; + CssTextAlign textAlign = hostBlock.CssTextAlign; + foreach (CssLineBox linebox in hostBlock.GetLineBoxIter()) + { + ApplyAlignment(linebox, textAlign, lay); + ApplyRightToLeft(linebox); //*** + linebox.CloseLine(lay); //*** + linebox.CachedLineTop = localY; + localY += linebox.CacheLineHeight + interlineSpace; // + interline space? + + if (maxLineWidth < linebox.CachedExactContentWidth) + { + maxLineWidth = linebox.CachedExactContentWidth; + } + } } else { - imgRun.Height = imgRun.Width > 0 ? imgRun.Width * 1.14f : 22.8f; - } - if (imgRun.HasUserImageContent) - { - // If only the width was set in the html tag, ratio the height. - if ((hasImageTagWidth && !hasImageTagHeight) || scaleImageHeight) - { - // Divide the given tag width with the actual image width, to get the ratio. - float ratio = imgRun.Width / imgRun.OriginalImageWidth; - imgRun.Height = imgRun.OriginalImageHeight * ratio; - } - // If only the height was set in the html tag, ratio the width. - else if (hasImageTagHeight && !hasImageTagWidth) + CssTextAlign textAlign = hostBlock.CssTextAlign; + foreach (CssLineBox linebox in hostBlock.GetLineBoxIter()) { - // Divide the given tag height with the actual image height, to get the ratio. - float ratio = imgRun.Height / imgRun.OriginalImageHeight; - imgRun.Width = imgRun.OriginalImageWidth * ratio; + ApplyAlignment(linebox, textAlign, lay); + + linebox.CloseLine(lay); //*** + + linebox.CachedLineTop = localY; + localY += linebox.CacheLineHeight + interlineSpace; + + if (maxLineWidth < linebox.CachedExactContentWidth) + { + maxLineWidth = linebox.CachedExactContentWidth; + } } } - //imageWord.Height += imageWord.OwnerBox.ActualBorderBottomWidth + imageWord.OwnerBox.ActualBorderTopWidth + imageWord.OwnerBox.ActualPaddingTop + imageWord.OwnerBox.ActualPaddingBottom; + + hostBlock.SetVisualHeight(localY + hostBlock.ActualPaddingBottom + hostBlock.ActualBorderBottomWidth); + + //final + SetFinalInnerContentSize(hostBlock, maxLineWidth, hostBlock.VisualHeight, lay); } - /// - /// Check if the given box contains only inline child boxes. - /// - /// the box to check - /// true - only inline child boxes, false - otherwise - static bool ContainsInlinesOnly(CssBox box) + + //--------------------------- + static void FlowRunsIntoHost(LayoutVisitor lay, + CssBox hostBox, + CssBox splitableBox, + CssBox b, + int childNumber, //child number of b + float limitRight, + float firstRunStartX, + float leftMostSpace, + float rightMostSpace, + List runs, + ref CssLineBox hostLine, + ref float cx) { - var children = CssBox.UnsafeGetChildren(box); - var linkedNode = children.GetFirstLinkedNode(); - while (linkedNode != null) - { + //flow runs into hostLine, create new line if need + bool wrapNoWrapBox = false; + CssWhiteSpace bWhiteSpace = b.WhiteSpace; + bool hostBoxIsB = hostBox == b; - if (!linkedNode.Value.IsInline) + if (bWhiteSpace == CssWhiteSpace.NoWrap && cx > firstRunStartX) + { + var tmpRight = cx; + for (int i = runs.Count - 1; i >= 0; --i) { - return false; + tmpRight += runs[i].Width; + } + //----------------------------------------- + if (tmpRight > limitRight) + { + wrapNoWrapBox = true; } - linkedNode = linkedNode.Next; } - return true; - } - public static void PerformContentLayout(CssBox box, LayoutVisitor lay) - { - //this box has its own container property - //this box may use... - // 1) line formatting context , or - // 2) block formatting context + //----------------------------------------------------- - var myContainingBlock = lay.LatestContainingBlock; - CssBox prevSibling = lay.LatestSiblingBox; - if (box.CssDisplay != Css.CssDisplay.TableCell) + int lim = runs.Count - 1; + for (int i = 0; i <= lim; ++i) { - //------------------------------------------- - if (box.CssDisplay != Css.CssDisplay.Table) + var run = runs[i]; + //--------------------------------------------------- + //check if need to start new line ? + if ((cx + run.Width + rightMostSpace > limitRight && + bWhiteSpace != CssWhiteSpace.NoWrap && + bWhiteSpace != CssWhiteSpace.Pre && + (bWhiteSpace != CssWhiteSpace.PreWrap || !run.IsSpaces)) + || run.IsLineBreak || wrapNoWrapBox) { - float availableWidth = myContainingBlock.GetClientWidth(); - if (!box.Width.IsEmptyOrAuto) + + wrapNoWrapBox = false; //once! + + //------------------------------- + //create new line *** + hostLine = new CssLineBox(hostBox); + hostBox.AddLineBox(hostLine); + //reset x pos for new line + cx = firstRunStartX; + + + // handle if line is wrapped for the first text element where parent has left margin/padding + if (childNumber == 0 && //b is first child of splitable box ('b' == splitableBox.GetFirstChild()) + !run.IsLineBreak && + (i == 0 || splitableBox.ParentBox.IsBlock))//this run is first run of 'b' (run == b.FirstRun) { - availableWidth = CssValueParser.ConvertToPx(box.Width, availableWidth, box); + cx += splitableBox.ActualMarginLeft + + splitableBox.ActualBorderLeftWidth + + splitableBox.ActualPaddingLeft; } - box.SetCssBoxWidth(availableWidth); + if (run.IsSolidContent || i == 0) + { + cx += leftMostSpace; + } } - //------------------------------------------- + //--------------------------------------------------- - float localLeft = myContainingBlock.GetClientLeft() + box.ActualMarginLeft; - float localTop = 0; + if (run.IsSpaces && hostLine.RunCount == 0) + { + //not add + continue; + } + //--------------------------------------------------- - if (prevSibling == null) + hostLine.AddRun(run); //*** + if (lim == 0) { - //this is first child of parent - if (box.ParentBox != null) + //single one + if (!hostBoxIsB) { - localTop = myContainingBlock.GetClientTop(); + cx += b.ActualPaddingLeft; } + run.SetLocation(cx, 0); + cx += run.Width + b.ActualPaddingRight; } else { - localTop = prevSibling.LocalVisualBottom; - } - localTop += box.UpdateMarginTopCollapse(prevSibling); - box.SetLocation(localLeft, localTop); - box.SetHeightToZero(); - } - //-------------------------------------------------------------------------- - - switch (box.CssDisplay) - { - case Css.CssDisplay.Table: - case Css.CssDisplay.InlineTable: + if (i == 0) { - //If we're talking about a table here.. - - lay.PushContaingBlock(box); - var currentLevelLatestSibling = lay.LatestSiblingBox; - lay.LatestSiblingBox = null;//reset + //first + if (!hostBoxIsB) + { + cx += b.ActualPaddingLeft; + } - CssTableLayoutEngine.PerformLayout(box, myContainingBlock.GetClientWidth(), lay); - - lay.LatestSiblingBox = currentLevelLatestSibling; - lay.PopContainingBlock(); - //TODO: check if this can have absolute layer? - } break; - case CssDisplay.InlineFlex: - case CssDisplay.Flex: + run.SetLocation(cx, 0); + cx = run.Right; + } + else if (i == lim) { - //------------------------------------------------ - //arrange as normal first - if (box.IsCustomCssBox) - { - //has custom layout method - box.ReEvaluateComputedValues(lay.SampleIFonts, lay.LatestContainingBlock); - box.CustomRecomputedValue(lay.LatestContainingBlock, lay.GraphicsPlatform); - } - else - { - if (ContainsInlinesOnly(box)) - { - //This will automatically set the bottom of this block - DoLayoutLinesContext(box, lay); - } - else if (box.ChildCount > 0) - { - DoLayoutBlocksContext(box, lay); - } - - if (box.HasAbsoluteLayer) - { - LayoutContentInAbsoluteLayer(lay, box); - } - } - //------------------------------------------------ - RearrangeWithFlexContext(box, lay); - //------------------------------------------------ - } break; - default: + run.SetLocation(cx, 0); + cx += run.Width + b.ActualPaddingRight; + } + else { - //formatting context for... - //1. line formatting context - //2. block formatting context - if (box.IsCustomCssBox) - { - //has custom layout method - box.ReEvaluateComputedValues(lay.SampleIFonts, lay.LatestContainingBlock); - box.CustomRecomputedValue(lay.LatestContainingBlock, lay.GraphicsPlatform); - } - else - { - if (ContainsInlinesOnly(box)) - { - //This will automatically set the bottom of this block - DoLayoutLinesContext(box, lay); - } - else if (box.ChildCount > 0) - { - DoLayoutBlocksContext(box, lay); - } - if (box.HasAbsoluteLayer) - { - LayoutContentInAbsoluteLayer(lay, box); - } - } - //TODO: review here again - if (box.Float != CssFloat.None) - { - var iw = box.InnerContentWidth; - var ew = box.VisualWidth; - //float to specific position - box.SetVisualSize(iw, box.VisualHeight); - } - } break; + run.SetLocation(cx, 0); + cx = run.Right; + } + } + //--------------------------------------------------- + //move current_line_x to right of run + //cx = run.Right; } + } + /// + /// Recursively flows the content of the box using the inline model + /// + /// + /// + /// + /// + /// + /// + /// + static void FlowBoxContentIntoHostLineFmtContext( + LayoutVisitor lay, + CssBox hostBox, //target host box that contains line formatting context + CssBox srcBox, //src that has runs /splitable content) to flow into hostBox line model + float limitLocalRight, + float firstRunStartX, + ref CssLineBox hostLine, + ref float cx, + ref FloatFormattingContext floatCtx) + { - switch (box.Float) + //recursive *** + //-------------------------------------------------------------------- + var oX = cx; + if (srcBox.HasOnlyRuns) { - case CssFloat.Left: - { - var recentLeftFloatBox = lay.LatestLeftFloatBox; - var recentRightFloatBox = lay.LatestRightFloatBox; - float availableWidth2 = myContainingBlock.GetClientWidth(); - - if (recentRightFloatBox != null) - { - availableWidth2 -= recentRightFloatBox.LocalX; - } - - float sx = myContainingBlock.GetClientLeft(); - float sy = myContainingBlock.GetClientTop(); + //condition 3 - if (recentLeftFloatBox != null) - { - availableWidth2 -= recentLeftFloatBox.LocalVisualRight; - sx = recentLeftFloatBox.LocalVisualRight; - sy = recentLeftFloatBox.LocalY; - } + FlowRunsIntoHost(lay, hostBox, srcBox, srcBox, 0, + limitLocalRight, firstRunStartX, + 0, 0, + CssBox.UnsafeGetRunList(srcBox), + ref hostLine, ref cx + ); + } + else + { - if (box.VisualWidth > availableWidth2) - { - //start newline - sx = myContainingBlock.GetClientLeft(); + int childNumber = 0; + var ifonts = lay.SampleIFonts; - float sy1 = 0; - float sy2 = 0; - sy1 = sy2 = myContainingBlock.GetClientTop(); + CssBoxCollection children = CssBox.UnsafeGetChildren(srcBox); + var cNode = children.GetFirstLinkedNode(); + while (cNode != null) + { + float leftMostSpace = 0, rightMostSpace = 0; + CssBox b = cNode.Value; - if (recentLeftFloatBox != null) - { - sy1 = recentLeftFloatBox.LocalX + recentLeftFloatBox.InnerContentHeight + - recentLeftFloatBox.ActualPaddingBottom + - recentLeftFloatBox.ActualMarginBottom; - } - if (recentRightFloatBox != null) - { - sy2 = recentRightFloatBox.LocalX + recentRightFloatBox.InnerContentHeight + - recentRightFloatBox.ActualPaddingBottom + - recentRightFloatBox.ActualMarginBottom; - } + //if b has absolute pos then it is removed from the flow + if (b.NeedComputedValueEvaluation) + { + b.ReEvaluateComputedValues(ifonts, hostBox); + } + b.MeasureRunsSize(lay); +#if DEBUG + if (b.Position == CssPosition.Absolute) + { + //should not found here! + throw new NotSupportedException(); + } +#endif - sy = (sy1 > sy2) ? sy1 : sy2; - } - box.SetLocation(sx, sy); - lay.LatestLeftFloatBox = box; - lay.AddFloatBox(box); - } break; - case CssFloat.Right: + cx += leftMostSpace; + if (b.CssDisplayInside == CssDisplayInside.FlowRoot)//eg. inline block { + //-------- + // if inside display is FlowRoot *** + //--------- - var recentLeftFloatBox = lay.LatestLeftFloatBox; - var recentRightFloatBox = lay.LatestRightFloatBox; - float availableWidth2 = myContainingBlock.GetClientWidth(); + //outside -> inline + //inside -> flow-root - if (recentLeftFloatBox != null) + //can't split + //create 'block-run' + CssLayoutEngine.PerformContentLayout(b, lay); + CssBlockRun blockRun = b.JustBlockRun; + if (blockRun == null) { - availableWidth2 -= recentLeftFloatBox.LocalX; + blockRun = new CssBlockRun(b); + blockRun.SetOwner(srcBox); + b.JustBlockRun = blockRun; } - float sx = myContainingBlock.GetClientRight() - box.VisualWidth; - float sy = myContainingBlock.GetClientTop(); - if (recentRightFloatBox != null) + if (b.Width.IsEmptyOrAuto) { - availableWidth2 -= recentRightFloatBox.LocalX; - sx = recentRightFloatBox.LocalX - box.VisualWidth; - sy = recentRightFloatBox.LocalY; + blockRun.SetSize(CssBox.GetLatestCachedMinWidth(b), b.VisualHeight); } - - if (box.VisualWidth > availableWidth2) + else { - //start newline - sx = myContainingBlock.GetClientRight() - box.VisualWidth; - - float sy1 = 0; - float sy2 = 0; - sy1 = sy2 = myContainingBlock.GetClientTop(); + blockRun.SetSize(b.VisualWidth, b.VisualHeight); + } - if (recentLeftFloatBox != null) - { - sy1 = recentLeftFloatBox.LocalY + recentLeftFloatBox.InnerContentHeight + - recentLeftFloatBox.ActualPaddingBottom + - recentLeftFloatBox.ActualMarginBottom; - } - if (recentRightFloatBox != null) - { - sy2 = recentRightFloatBox.LocalY + recentRightFloatBox.InnerContentHeight + - recentRightFloatBox.ActualPaddingBottom + - recentRightFloatBox.ActualMarginBottom; - } + b.SetLocation(b.LocalX, 0); //because of inline*** - sy = (sy1 > sy2) ? sy1 : sy2; - } - box.SetLocation(sx, sy); - lay.LatestRightFloatBox = box; - lay.AddFloatBox(box); - } break; - case CssFloat.None: - default: + FlowRunsIntoHost(lay, hostBox, srcBox, b, childNumber, + limitLocalRight, firstRunStartX, + leftMostSpace, rightMostSpace, + new List() { b.JustBlockRun }, + ref hostLine, ref cx); + } + else if (b.CssDisplayOutside == CssDisplayOutside.Block) { - //review here for inherit property + //warning : this code block not follow w3c spec *** - } break; - } - } - /// - /// do layout line formatting context - /// - /// - /// - static void DoLayoutLinesContext(CssBox hostBlock, LayoutVisitor lay) - { - //this in line formatting context - //*** hostBlock must confirm that it has all inline children - hostBlock.SetHeightToZero(); - hostBlock.ResetLineBoxes(); + CssLayoutEngine.PerformContentLayout(b, lay); + CssBlockRun blockRun = b.JustBlockRun; + if (blockRun == null) + { + blockRun = new CssBlockRun(b); + blockRun.SetOwner(srcBox); + b.JustBlockRun = blockRun; + } - //---------------------------------------------------------------------------------------- - float limitLocalRight = hostBlock.VisualWidth - (hostBlock.ActualPaddingRight + hostBlock.ActualBorderRightWidth); - float localX = hostBlock.ActualTextIndent + hostBlock.ActualPaddingLeft + hostBlock.ActualBorderLeftWidth; - float localY = hostBlock.ActualPaddingTop + hostBlock.ActualBorderTopWidth; - //---------------------------------------------------------------------------------------- + //set width to full *** + blockRun.SetSize(hostBox.GetClientWidth(), b.VisualHeight); - if (lay.HasFloatBoxInContext) - { - var recentLeftFloatBox = lay.LatestLeftFloatBox; - var recentRightFloatBox = lay.LatestRightFloatBox; - var latestSibling = lay.LatestSiblingBox; + b.SetLocation(b.LocalX, 0); //because of inline*** - if (latestSibling != null) - { - //check latest sibling first - if (hostBlock.Float == CssFloat.None) - { - if (recentLeftFloatBox != null) - { - if (hostBlock.LocalY < recentLeftFloatBox.LocalVisualBottom) - { - localX = recentLeftFloatBox.LocalVisualRight; - } - } - else - { + FlowRunsIntoHost(lay, hostBox, srcBox, b, childNumber, + limitLocalRight, firstRunStartX, + leftMostSpace, rightMostSpace, + new List() { b.JustBlockRun }, + ref hostLine, ref cx); - } } - } - else - { - if (hostBlock.Float == CssFloat.None) + else if (b.HasOnlyRuns) { - if (recentLeftFloatBox != null) - { - localX = recentLeftFloatBox.LocalVisualRight; - } - if (recentRightFloatBox != null) + switch (b.Float) { - limitLocalRight = recentRightFloatBox.LocalX; - } - } - //check if need newline or not - } - - } + default: + case CssFloat.None: + { + FlowRunsIntoHost(lay, hostBox, srcBox, b, childNumber, + limitLocalRight, firstRunStartX, + leftMostSpace, rightMostSpace, + CssBox.UnsafeGetRunList(b), + ref hostLine, ref cx); - int interlineSpace = 0; + } break; + case CssFloat.Left: + { + //float is out of flow item + //1. current line is shortening + //2. add 'b' to special container *** - //First line box + var newAnonBlock = new CssFloatContainerBox( + CssBox.UnsafeGetBoxSpec(b), + b.RootGfx, CssDisplay.Block); + newAnonBlock.ReEvaluateComputedValues(ifonts, hostBox); - var line = new CssLineBox(hostBlock); - hostBlock.AddLineBox(line); - //**** - var floatCtx = new FloatFormattingContext(); - FlowBoxContentIntoHostLineFmtContext(lay, hostBlock, hostBlock, - limitLocalRight, localX, - ref line, ref localX, ref floatCtx); + //add to abs layer + hostBox.AppendToAbsoluteLayer(newAnonBlock); + newAnonBlock.ResetLineBoxes(); - //**** - // if width is not restricted we need to lower it to the actual width - if (hostBlock.VisualWidth + lay.ContainerBlockGlobalX >= CssBoxConstConfig.BOX_MAX_RIGHT) - { - float newWidth = localX + hostBlock.ActualPaddingRight + hostBlock.ActualBorderRightWidth;// CssBox.MAX_RIGHT - (args.ContainerBlockGlobalX + blockBox.LocalX); - if (newWidth <= CSS_OFFSET_THRESHOLD) - { - newWidth = CSS_OFFSET_THRESHOLD; - } - hostBlock.SetVisualWidth(newWidth); - } - //--------------------- - float maxLineWidth = 0; - if (hostBlock.CssDirection == CssDirection.Rtl) - { - CssTextAlign textAlign = hostBlock.CssTextAlign; - foreach (CssLineBox linebox in hostBlock.GetLineBoxIter()) - { - ApplyAlignment(linebox, textAlign, lay); - ApplyRightToLeft(linebox); //*** - linebox.CloseLine(lay); //*** - linebox.CachedLineTop = localY; - localY += linebox.CacheLineHeight + interlineSpace; // + interline space? + float localX1 = 0; + var line = new CssLineBox(newAnonBlock); + newAnonBlock.AddLineBox(line); - if (maxLineWidth < linebox.CachedExactContentWidth) - { - maxLineWidth = linebox.CachedExactContentWidth; - } - } - } - else - { + var newFloatCtx = new FloatFormattingContext(); + FlowBoxContentIntoHostLineFmtContext(lay, newAnonBlock, b, + limitLocalRight, 0, + ref line, ref localX1, ref newFloatCtx); - CssTextAlign textAlign = hostBlock.CssTextAlign; - foreach (CssLineBox linebox in hostBlock.GetLineBoxIter()) - { - ApplyAlignment(linebox, textAlign, lay); - linebox.CloseLine(lay); //*** + float localY = 0; + int interlineSpace = 0; + float maxLineWidth = 0; + CssTextAlign textAlign = newAnonBlock.CssTextAlign; + foreach (CssLineBox linebox in newAnonBlock.GetLineBoxIter()) + { + ApplyAlignment(linebox, textAlign, lay); + linebox.CloseLine(lay); //*** + linebox.CachedLineTop = localY; + localY += linebox.CacheLineHeight + interlineSpace; + if (maxLineWidth < linebox.CachedExactContentWidth) + { + maxLineWidth = linebox.CachedExactContentWidth; + } + } - linebox.CachedLineTop = localY; - localY += linebox.CacheLineHeight + interlineSpace; + float hostSizeW = hostBox.VisualWidth; + SetFinalInnerContentSize(newAnonBlock, maxLineWidth, localY, lay); + //need to adjust line box - if (maxLineWidth < linebox.CachedExactContentWidth) - { - maxLineWidth = linebox.CachedExactContentWidth; - } - } - } - hostBlock.SetVisualHeight(localY + hostBlock.ActualPaddingBottom + hostBlock.ActualBorderBottomWidth); + //TODO: review here!, + if (hostLine.CanDoMoreLeftOffset(newAnonBlock.InnerContentWidth, limitLocalRight)) + { + hostLine.DoLeftOffset(newAnonBlock.InnerContentWidth); + cx = hostLine.GetRightOfLastRun(); + newAnonBlock.SetLocation(floatCtx.lineLeftOffset, floatCtx.offsetFloatTop); //TODO: review top location again + floatCtx.lineLeftOffset = newAnonBlock.LocalX + newAnonBlock.InnerContentWidth; + } + else + { + //newline + newAnonBlock.SetLocation(hostBox.GetClientLeft(), hostLine.CalculateLineHeight()); + floatCtx.offsetFloatTop = newAnonBlock.LocalY; + } - //final - SetFinalInnerContentSize(hostBlock, maxLineWidth, hostBlock.VisualHeight, lay); - } - static void DoLayoutBlocksContext(CssBox box, LayoutVisitor lay) - { + } break; + case CssFloat.Right: + { + //float is out of flow item + //1. create new block box and then + //flow content in to this new box + var newAnonBlock = new CssFloatContainerBox( + CssBox.UnsafeGetBoxSpec(b), + b.RootGfx, CssDisplay.Block); - //block formatting context.... - lay.PushContaingBlock(box); - var currentLevelLatestSibling = lay.LatestSiblingBox; - lay.LatestSiblingBox = null;//reset - //------------------------------------------ - var children = CssBox.UnsafeGetChildren(box); - var cnode = children.GetFirstLinkedNode(); - while (cnode != null) - { - var childBox = cnode.Value; + newAnonBlock.ReEvaluateComputedValues(ifonts, hostBox); - //---------------------------- - if (childBox.IsBrElement) - { - //br always block - CssBox.ChangeDisplayType(childBox, Css.CssDisplay.Block); - childBox.SetVisualHeight(FontDefaultConfig.DEFAULT_FONT_SIZE * 0.95f); - } - //----------------------------- - if (childBox.IsInline) - { - //inline correction on-the-fly ! - //1. collect consecutive inlinebox - // and move to new anon block box + //add to abs layer + hostBox.AppendToAbsoluteLayer(newAnonBlock); + newAnonBlock.ResetLineBoxes(); + float localX1 = 0; - CssBox anoForInline = CreateAnonBlock(box, childBox); - anoForInline.ReEvaluateComputedValues(lay.SampleIFonts, box); + var line = new CssLineBox(newAnonBlock); + newAnonBlock.AddLineBox(line); - var tmp = cnode.Next; - do - { - children.Remove(childBox); - anoForInline.AppendChild(childBox); + var newFloatCtx = new FloatFormattingContext(); + FlowBoxContentIntoHostLineFmtContext(lay, newAnonBlock, b, + limitLocalRight, 0, + ref line, ref localX1, ref newFloatCtx); - if (tmp != null) - { - childBox = tmp.Value; - if (childBox.IsInline) - { - tmp = tmp.Next; - if (tmp == null) - { - children.Remove(childBox); - anoForInline.AppendChild(childBox); - break; - } - } - else - { - break;//break from do while - } - } - else - { - break; - } - } while (true); + float localY = 0; + int interlineSpace = 0; + float maxLineWidth = 0; + CssTextAlign textAlign = newAnonBlock.CssTextAlign; + foreach (CssLineBox linebox in newAnonBlock.GetLineBoxIter()) + { + ApplyAlignment(linebox, textAlign, lay); + linebox.CloseLine(lay); //*** + linebox.CachedLineTop = localY; + localY += linebox.CacheLineHeight + interlineSpace; + if (maxLineWidth < linebox.CachedExactContentWidth) + { + maxLineWidth = linebox.CachedExactContentWidth; + } + } + SetFinalInnerContentSize(newAnonBlock, maxLineWidth, localY, lay); - childBox = anoForInline; - //------------------------ - //2. move this inline box - //to new anonbox - cnode = tmp; - //------------------------ - childBox.PerformLayout(lay); + //todo: review here + float hostSizeW = hostBox.VisualWidth; + var rightOfLastRun = hostLine.GetRightOfLastRun(); - if (childBox.CanBeReferenceSibling) - { - lay.LatestSiblingBox = childBox; - } - } - else - { - - childBox.PerformLayout(lay); - - switch (childBox.Float) - { - case CssFloat.Left: - { - childBox.IsOutOfFlowBox = true; - lay.LatestLeftFloatBox = childBox; - - } break; - case CssFloat.Right: - { - childBox.IsOutOfFlowBox = true; - //float box is out-of-flow box - //so move it to abs layer - lay.LatestRightFloatBox = childBox; + if (!floatCtx.floatingOutOfLine) + { + if (rightOfLastRun + maxLineWidth < hostSizeW - floatCtx.lineRightOffset) + { + float newX = hostSizeW - (maxLineWidth + floatCtx.lineRightOffset); + newAnonBlock.SetLocation(newX, floatCtx.offsetFloatTop); + floatCtx.lineRightOffset = newX; + floatCtx.rightFloatBox = newAnonBlock; + floatCtx.floatingOutOfLine = true; + } + else + { + //start newline + float newX = hostSizeW - maxLineWidth; + newAnonBlock.SetLocation(newX, floatCtx.offsetFloatTop + hostLine.CalculateLineHeight()); + floatCtx.lineRightOffset = newX; + floatCtx.rightFloatBox = newAnonBlock; + floatCtx.floatingOutOfLine = true; + floatCtx.offsetFloatTop = newAnonBlock.LocalY; + } + } + else + { + //out-of-line mode + if (floatCtx.rightFloatBox != null) + { + float newX = floatCtx.rightFloatBox.LocalX - maxLineWidth; + if (newX > 0) + { + newAnonBlock.SetLocation(newX, floatCtx.offsetFloatTop); + floatCtx.lineRightOffset = newX; + floatCtx.rightFloatBox = newAnonBlock; + floatCtx.offsetFloatTop = newAnonBlock.LocalY; + } + else + { //start new line + newX = hostSizeW - maxLineWidth; + newAnonBlock.SetLocation(newX, floatCtx.rightFloatBox.LocalY + floatCtx.rightFloatBox.InnerContentHeight); + floatCtx.lineRightOffset = newX; + floatCtx.rightFloatBox = newAnonBlock; + floatCtx.offsetFloatTop = newAnonBlock.LocalY + newAnonBlock.InnerContentHeight; + } + } + else + { + throw new NotSupportedException(); + } + } - } break; + } break; + } } - - if (childBox.Float == CssFloat.None && childBox.CanBeReferenceSibling) + else { - lay.LatestSiblingBox = childBox; - } + //go deeper + //recursive *** + //not new lineFormatting context + FlowBoxContentIntoHostLineFmtContext(lay, hostBox, b, + limitLocalRight, firstRunStartX, + ref hostLine, ref cx, ref floatCtx); - cnode = cnode.Next; + } + cx += rightMostSpace; + childNumber++; + //--------------------- + cNode = cNode.Next; } } - - //------------------------------------------ - lay.LatestSiblingBox = currentLevelLatestSibling; - lay.PopContainingBlock(); - //------------------------------------------------ - float boxWidth = CalculateActualWidth(box); - - if (lay.ContainerBlockGlobalX + boxWidth > CssBoxConstConfig.BOX_MAX_RIGHT) - { - } - else + if (srcBox.Position == CssPosition.Relative) { - if (box.CssDisplay != Css.CssDisplay.TableCell) - { - box.SetVisualWidth(boxWidth); - } + //offset content relative to it 'flow' position' + var left = CssValueParser.ConvertToPx(srcBox.Left, hostBox.VisualWidth, srcBox); + var top = CssValueParser.ConvertToPx(srcBox.Top, hostBox.VisualWidth, srcBox); + srcBox.SetLocation(srcBox.LocalX + left, srcBox.LocalY + top); } - float boxHeight = box.GetHeightAfterMarginBottomCollapse(lay.LatestContainingBlock); - box.SetVisualHeight(boxHeight); - //-------------------------------------------------------------------------------- - //final - SetFinalInnerContentSize(box, boxWidth, boxHeight, lay); - } + + static void SetFinalInnerContentSize(CssBox box, float innerContentW, float innerContentH, LayoutVisitor lay) { box.InnerContentWidth = innerContentW; @@ -695,330 +661,620 @@ static void SetFinalInnerContentSize(CssBox box, float innerContentW, float inne } break; } } - static float CalculateActualWidth(CssBox box) + + /// + /// Applies vertical and horizontal alignment to words in lineboxes + /// + /// + /// + static void ApplyAlignment(CssLineBox lineBox, CssTextAlign textAlign, LayoutVisitor lay) { - float maxRight = 0; - var boxes = CssBox.UnsafeGetChildren(box); - var cnode = boxes.GetFirstLinkedNode(); - while (cnode != null) + switch (textAlign) { - var cssbox = cnode.Value; - float nodeRight = cssbox.LocalX + cssbox.InnerContentWidth + - cssbox.ActualPaddingLeft + cssbox.ActualPaddingRight + - cssbox.ActualMarginLeft + - cssbox.ActualMarginRight; - - maxRight = nodeRight > maxRight ? nodeRight : maxRight; - cnode = cnode.Next; + case CssTextAlign.Right: + ApplyRightAlignment(lineBox); + break; + case CssTextAlign.Center: + ApplyCenterAlignment(lineBox); + break; + case CssTextAlign.Justify: + ApplyJustifyAlignment(lineBox); + break; + default: + break; } - return maxRight + (box.ActualBorderLeftWidth + box.ActualPaddingLeft + - box.ActualPaddingRight + box.ActualBorderRightWidth); - } - - static CssBox CreateAnonBlock(CssBox parent, CssBox insertBefore) - { - //auto gen by layout engine *** - var newBox = new CssBox(CssBox.UnsafeGetBoxSpec(parent).GetAnonVersion(), parent.RootGfx); - CssBox.ChangeDisplayType(newBox, Css.CssDisplay.Block); - parent.InsertChild(insertBefore, newBox); - return newBox; + //--------------------------------------------- + // Applies vertical alignment to the linebox + return; + //TODO: review here + lineBox.ApplyBaseline(lineBox.CalculateTotalBoxBaseLine(lay)); + //--------------------------------------------- } /// - /// Recursively flows the content of the box using the inline model + /// Applies right to left direction to words /// - /// - /// - /// - /// - /// - /// - /// - static void FlowBoxContentIntoHostLineFmtContext( - LayoutVisitor lay, - CssBox hostBox, //target host box that contains line formatting context - CssBox srcBox, //src that has runs /splitable content) to flow into hostBox line model - float limitLocalRight, - float firstRunStartX, - ref CssLineBox hostLine, - ref float cx, - ref FloatFormattingContext floatCtx) + /// + /// + static void ApplyRightToLeft(CssLineBox lineBox) { - - //recursive *** - //-------------------------------------------------------------------- - var oX = cx; - if (srcBox.HasRuns) + if (lineBox.RunCount > 0) { - //condition 3 - FlowRunsIntoHost(lay, hostBox, srcBox, srcBox, 0, - limitLocalRight, firstRunStartX, - 0, 0, - CssBox.UnsafeGetRunList(srcBox), - ref hostLine, ref cx - ); + float left = lineBox.GetFirstRun().Left; + float right = lineBox.GetLastRun().Right; + foreach (CssRun run in lineBox.GetRunIter()) + { + float diff = run.Left - left; + float w_right = right - diff; + run.Left = w_right - run.Width; + } } - else - { + } + static void ApplyJustifyAlignment(CssLineBox lineBox) + { - int childNumber = 0; - var ifonts = lay.SampleIFonts; - foreach (CssBox b in srcBox.GetChildBoxIter()) - { - float leftMostSpace = 0, rightMostSpace = 0; - //if b has absolute pos then it is removed from the flow - if (b.NeedComputedValueEvaluation) - { - b.ReEvaluateComputedValues(ifonts, hostBox); - } - b.MeasureRunsSize(lay); -#if DEBUG - if (b.Position == CssPosition.Absolute) - { - //should not found here! - throw new NotSupportedException(); - } -#endif - cx += leftMostSpace; - //------------------------------------------------ - if (b.CssDisplay == CssDisplay.InlineBlock) - { - //can't split - //create 'block-run' - PerformContentLayout(b, lay); + if (lineBox.IsLastLine) return; - CssBlockRun blockRun = b.JustBlockRun; - if (blockRun == null) - { - blockRun = new CssBlockRun(b); - blockRun.SetOwner(srcBox); - b.JustBlockRun = blockRun; - } + float indent = lineBox.IsFirstLine ? lineBox.OwnerBox.ActualTextIndent : 0f; + float runWidthSum = 0f; + int runCount = 0; - if (b.Width.IsEmptyOrAuto) - { - blockRun.SetSize(CssBox.GetLatestCachedMinWidth(b), b.VisualHeight); - } - else - { - blockRun.SetSize(b.VisualWidth, b.VisualHeight); - } + float availableWidth = lineBox.OwnerBox.GetClientWidth() - indent; - b.SetLocation(b.LocalX, 0); //because of inline*** + // Gather text sum + foreach (CssRun w in lineBox.GetRunIter()) + { + runWidthSum += w.Width; + runCount++; + } - FlowRunsIntoHost(lay, hostBox, srcBox, b, childNumber, - limitLocalRight, firstRunStartX, - leftMostSpace, rightMostSpace, - new List() { b.JustBlockRun }, - ref hostLine, ref cx); + if (runCount == 0) return; //Avoid Zero division + + float spaceOfEachRun = (availableWidth - runWidthSum) / runCount; //Spacing that will be used + + float cX = lineBox.OwnerBox.GetClientLeft() + indent; + CssRun lastRun = lineBox.GetLastRun(); + foreach (CssRun run in lineBox.GetRunIter()) + { + run.Left = cX; + cX = run.Right + spaceOfEachRun; + if (run == lastRun) + { + run.Left = lineBox.OwnerBox.GetClientRight() - run.Width; + } + } + } + + /// + /// Applies centered alignment to the text on the linebox + /// + /// + /// + static void ApplyCenterAlignment(CssLineBox line) + { + + if (line.RunCount == 0) return; + CssRun lastRun = line.GetLastRun(); + float diff = (line.OwnerBox.GetClientWidth() - lastRun.Right) / 2; + if (diff > CSS_OFFSET_THRESHOLD) + { + foreach (CssRun word in line.GetRunIter()) + { + word.Left += diff; + } + line.CachedLineContentWidth += diff; + } + } + + /// + /// Applies right alignment to the text on the linebox + /// + /// + /// + static void ApplyRightAlignment(CssLineBox line) + { + if (line.RunCount == 0) + { + return; + } + CssRun lastRun = line.GetLastRun(); + float diff = line.OwnerBox.GetClientWidth() - line.GetLastRun().Right; + if (diff > CSS_OFFSET_THRESHOLD) + { + foreach (CssRun word in line.GetRunIter()) + { + word.Left += diff; + } + } + } + + } + + + + /// + /// Helps on CSS Layout. + /// + static class CssLayoutEngine + { + + + public static void PerformContentLayout(CssBox box, LayoutVisitor lay) + { + //recursive + + //this box has its own container property + //this box may use... + // 1) line formatting context , or + // 2) block formatting context + + var myContainingBlock = lay.LatestContainingBlock; + CssBox prevSibling = lay.LatestSiblingBox; + if (box.CssDisplay != Css.CssDisplay.TableCell) + { + //------------------------------------------- + if (box.CssDisplay != Css.CssDisplay.Table) + { + float availableWidth = myContainingBlock.GetClientWidth(); + if (!box.Width.IsEmptyOrAuto) + { + availableWidth = CssValueParser.ConvertToPx(box.Width, availableWidth, box); } - else if (b.HasRuns) + + box.SetCssBoxWidth(availableWidth); + } + //------------------------------------------- + + float localLeft = myContainingBlock.GetClientLeft() + box.ActualMarginLeft; + float localTop = 0; + + if (prevSibling == null) + { + //this is first child of parent + if (box.ParentBox != null) { - switch (b.Float) + localTop = myContainingBlock.GetClientTop(); + } + } + else + { + localTop = prevSibling.LocalVisualBottom; + } + localTop += box.UpdateMarginTopCollapse(prevSibling); + box.SetLocation(localLeft, localTop); + box.SetHeightToZero(); + } + //-------------------------------------------------------------------------- + + switch (box.CssDisplay) + { + case Css.CssDisplay.Table: + case Css.CssDisplay.InlineTable: + { + //If we're talking about a table here.. + + lay.PushContaingBlock(box); + var currentLevelLatestSibling = lay.LatestSiblingBox; + lay.LatestSiblingBox = null;//reset + + CssTableLayoutEngine.PerformLayout(box, myContainingBlock.GetClientWidth(), lay); + + lay.LatestSiblingBox = currentLevelLatestSibling; + lay.PopContainingBlock(); + //TODO: check if this can have absolute layer? + } break; + default: + { + //formatting context for... + //1. line formatting context + //2. block formatting context + if (box.IsCustomCssBox) { - default: - case CssFloat.None: + //has custom layout method + box.ReEvaluateComputedValues(lay.SampleIFonts, lay.LatestContainingBlock); + box.CustomRecomputedValue(lay.LatestContainingBlock, lay.GraphicsPlatform); + } + else + { + + if (ContainsInlinesOnly(box)) + { + //This will automatically set the bottom of this block + LinesFormattingEngine.DoLayoutLinesContext(box, lay); + } + else if (box.ChildCount > 0) + { + + DoLayoutBlocksContext(box, lay); + } + + if (box.HasAbsoluteLayer) + { + LayoutContentInAbsoluteLayer(lay, box); + } + } + //--------------------- + //again! + switch (box.CssDisplay) + { + case CssDisplay.Flex: + case CssDisplay.InlineFlex: { - FlowRunsIntoHost(lay, hostBox, srcBox, b, childNumber, - limitLocalRight, firstRunStartX, - leftMostSpace, rightMostSpace, - CssBox.UnsafeGetRunList(b), - ref hostLine, ref cx); + //------------------------------------------------ + RearrangeWithFlexContext(box, lay); + //------------------------------------------------ } break; - case CssFloat.Left: - { - //float is out of flow item - //1. current line is shortening - //2. add 'b' to special container *** + default: + { //TODO: review here again + if (box.Float != CssFloat.None) + { + var iw = box.InnerContentWidth; + var ew = box.VisualWidth; + //float to specific position + box.SetVisualSize(iw, box.VisualHeight); + } + } break; + } + //--------------------- + } break; + } + switch (box.Float) + { + case CssFloat.Left: + { + var recentLeftFloatBox = lay.LatestLeftFloatBox; + var recentRightFloatBox = lay.LatestRightFloatBox; + float availableWidth2 = myContainingBlock.GetClientWidth(); - var newAnonBlock = new CssFloatContainerBox( - CssBox.UnsafeGetBoxSpec(b), - b.RootGfx, CssDisplay.Block); - newAnonBlock.ReEvaluateComputedValues(ifonts, hostBox); + if (recentRightFloatBox != null) + { + availableWidth2 -= recentRightFloatBox.LocalX; + } - //add to abs layer - hostBox.AppendToAbsoluteLayer(newAnonBlock); - newAnonBlock.ResetLineBoxes(); + float sx = myContainingBlock.GetClientLeft(); + float sy = myContainingBlock.GetClientTop(); - float localX1 = 0; - var line = new CssLineBox(newAnonBlock); - newAnonBlock.AddLineBox(line); + if (recentLeftFloatBox != null) + { + availableWidth2 -= recentLeftFloatBox.LocalVisualRight; + sx = recentLeftFloatBox.LocalVisualRight; + sy = recentLeftFloatBox.LocalY; + } + + if (box.VisualWidth > availableWidth2) + { + //start newline + sx = myContainingBlock.GetClientLeft(); + + float sy1 = 0; + float sy2 = 0; + sy1 = sy2 = myContainingBlock.GetClientTop(); + + if (recentLeftFloatBox != null) + { + sy1 = recentLeftFloatBox.LocalX + recentLeftFloatBox.InnerContentHeight + + recentLeftFloatBox.ActualPaddingBottom + + recentLeftFloatBox.ActualMarginBottom; + } + if (recentRightFloatBox != null) + { + sy2 = recentRightFloatBox.LocalX + recentRightFloatBox.InnerContentHeight + + recentRightFloatBox.ActualPaddingBottom + + recentRightFloatBox.ActualMarginBottom; + } + + sy = (sy1 > sy2) ? sy1 : sy2; + } + + box.SetLocation(sx, sy); + lay.LatestLeftFloatBox = box; + lay.AddFloatBox(box); + } break; + case CssFloat.Right: + { + + var recentLeftFloatBox = lay.LatestLeftFloatBox; + var recentRightFloatBox = lay.LatestRightFloatBox; + float availableWidth2 = myContainingBlock.GetClientWidth(); + + if (recentLeftFloatBox != null) + { + availableWidth2 -= recentLeftFloatBox.LocalX; + } + + float sx = myContainingBlock.GetClientRight() - box.VisualWidth; + float sy = myContainingBlock.GetClientTop(); + + if (recentRightFloatBox != null) + { + availableWidth2 -= recentRightFloatBox.LocalX; + sx = recentRightFloatBox.LocalX - box.VisualWidth; + sy = recentRightFloatBox.LocalY; + } + + if (box.VisualWidth > availableWidth2) + { + //start newline + sx = myContainingBlock.GetClientRight() - box.VisualWidth; + + float sy1 = 0; + float sy2 = 0; + sy1 = sy2 = myContainingBlock.GetClientTop(); + + if (recentLeftFloatBox != null) + { + sy1 = recentLeftFloatBox.LocalY + recentLeftFloatBox.InnerContentHeight + + recentLeftFloatBox.ActualPaddingBottom + + recentLeftFloatBox.ActualMarginBottom; + } + if (recentRightFloatBox != null) + { + sy2 = recentRightFloatBox.LocalY + recentRightFloatBox.InnerContentHeight + + recentRightFloatBox.ActualPaddingBottom + + recentRightFloatBox.ActualMarginBottom; + } + + sy = (sy1 > sy2) ? sy1 : sy2; + } + box.SetLocation(sx, sy); + lay.LatestRightFloatBox = box; + lay.AddFloatBox(box); + } break; + case CssFloat.None: + default: + { + //review here for inherit property + + } break; + } + + } + + + + /// + /// Check if the given box contains only inline child boxes. + /// + /// the box to check + /// true - only inline child boxes, false - otherwise + static bool ContainsInlinesOnly(CssBox box) + { + var children = CssBox.UnsafeGetChildren(box); + var linkedNode = children.GetFirstLinkedNode(); + while (linkedNode != null) + { + if (!linkedNode.Value.OutsideDisplayIsInline) + { + return false; + } + + linkedNode = linkedNode.Next; + } + return true; + } + + static void DoLayoutBlocksContext(CssBox box, LayoutVisitor lay) + { + + //block formatting context.... + lay.PushContaingBlock(box); + var currentLevelLatestSibling = lay.LatestSiblingBox; + lay.LatestSiblingBox = null;//reset + //------------------------------------------ + var children = CssBox.UnsafeGetChildren(box); + var cnode = children.GetFirstLinkedNode(); + while (cnode != null) + { + var childBox = cnode.Value; + + //---------------------------- + if (childBox.IsBrElement) + { + //br always block + CssBox.ChangeDisplayType(childBox, Css.CssDisplay.Block); + childBox.SetVisualHeight(FontDefaultConfig.DEFAULT_FONT_SIZE * 0.95f); + } + //----------------------------- + if (childBox.OutsideDisplayIsInline) + { + //inline correction on-the-fly ! + //1. collect consecutive inlinebox + // and move to new anon block box + + CssBox anoForInline = CreateAnonBlock(box, childBox); + anoForInline.ReEvaluateComputedValues(lay.SampleIFonts, box); + + var tmp = cnode.Next; + do + { + children.Remove(childBox); + anoForInline.AppendChild(childBox); + + if (tmp != null) + { + childBox = tmp.Value; + if (childBox.OutsideDisplayIsInline) + { + tmp = tmp.Next; + if (tmp == null) + { + children.Remove(childBox); + anoForInline.AppendChild(childBox); + break;//break from do while + } + } + else + { + break;//break from do while + } + } + else + { + break;//break from do while + } + } while (true); + + childBox = anoForInline; + //------------------------ + //2. move this inline box + //to new anonbox + cnode = tmp; + //------------------------ + childBox.PerformLayout(lay); + + if (childBox.CanBeReferenceSibling) + { + lay.LatestSiblingBox = childBox; + } + } + else + { - var newFloatCtx = new FloatFormattingContext(); - FlowBoxContentIntoHostLineFmtContext(lay, newAnonBlock, b, - limitLocalRight, 0, - ref line, ref localX1, ref newFloatCtx); + childBox.PerformLayout(lay); + switch (childBox.Float) + { + case CssFloat.Left: + { + childBox.IsOutOfFlowBox = true; + lay.LatestLeftFloatBox = childBox; - float localY = 0; - int interlineSpace = 0; - float maxLineWidth = 0; - CssTextAlign textAlign = newAnonBlock.CssTextAlign; - foreach (CssLineBox linebox in newAnonBlock.GetLineBoxIter()) - { - ApplyAlignment(linebox, textAlign, lay); - linebox.CloseLine(lay); //*** - linebox.CachedLineTop = localY; - localY += linebox.CacheLineHeight + interlineSpace; - if (maxLineWidth < linebox.CachedExactContentWidth) - { - maxLineWidth = linebox.CachedExactContentWidth; - } - } + } break; + case CssFloat.Right: + { + childBox.IsOutOfFlowBox = true; + //float box is out-of-flow box + //so move it to abs layer + lay.LatestRightFloatBox = childBox; - float hostSizeW = hostBox.VisualWidth; - SetFinalInnerContentSize(newAnonBlock, maxLineWidth, localY, lay); - //need to adjust line box + } break; + } - //TODO: review here!, - if (hostLine.CanDoMoreLeftOffset(newAnonBlock.InnerContentWidth, limitLocalRight)) - { - hostLine.DoLeftOffset(newAnonBlock.InnerContentWidth); - cx = hostLine.GetRightOfLastRun(); - newAnonBlock.SetLocation(floatCtx.lineLeftOffset, floatCtx.offsetFloatTop); //TODO: review top location again - floatCtx.lineLeftOffset = newAnonBlock.LocalX + newAnonBlock.InnerContentWidth; - } - else - { - //newline - newAnonBlock.SetLocation(hostBox.GetClientLeft(), hostLine.CalculateLineHeight()); - floatCtx.offsetFloatTop = newAnonBlock.LocalY; - } + if (childBox.Float == CssFloat.None && childBox.CanBeReferenceSibling) + { + lay.LatestSiblingBox = childBox; + } - } break; - case CssFloat.Right: - { - //float is out of flow item - //1. create new block box and then - //flow content in to this new box - var newAnonBlock = new CssFloatContainerBox( - CssBox.UnsafeGetBoxSpec(b), - b.RootGfx, CssDisplay.Block); + cnode = cnode.Next; - newAnonBlock.ReEvaluateComputedValues(ifonts, hostBox); + } + } - //add to abs layer - hostBox.AppendToAbsoluteLayer(newAnonBlock); - newAnonBlock.ResetLineBoxes(); - float localX1 = 0; + //------------------------------------------ + lay.LatestSiblingBox = currentLevelLatestSibling; + lay.PopContainingBlock(); + //------------------------------------------------ + float boxWidth = CalculateActualWidth(box); - var line = new CssLineBox(newAnonBlock); - newAnonBlock.AddLineBox(line); + if (lay.ContainerBlockGlobalX + boxWidth > CssBoxConstConfig.BOX_MAX_RIGHT) + { + } + else + { + if (box.CssDisplay != Css.CssDisplay.TableCell) + { + box.SetVisualWidth(boxWidth); + } + } - var newFloatCtx = new FloatFormattingContext(); - FlowBoxContentIntoHostLineFmtContext(lay, newAnonBlock, b, - limitLocalRight, 0, - ref line, ref localX1, ref newFloatCtx); + float boxHeight = box.GetHeightAfterMarginBottomCollapse(lay.LatestContainingBlock); + box.SetVisualHeight(boxHeight); + //-------------------------------------------------------------------------------- + //final + SetFinalInnerContentSize(box, boxWidth, boxHeight, lay); - float localY = 0; - int interlineSpace = 0; - float maxLineWidth = 0; - CssTextAlign textAlign = newAnonBlock.CssTextAlign; - foreach (CssLineBox linebox in newAnonBlock.GetLineBoxIter()) - { - ApplyAlignment(linebox, textAlign, lay); - linebox.CloseLine(lay); //*** - linebox.CachedLineTop = localY; - localY += linebox.CacheLineHeight + interlineSpace; - if (maxLineWidth < linebox.CachedExactContentWidth) - { - maxLineWidth = linebox.CachedExactContentWidth; - } - } - SetFinalInnerContentSize(newAnonBlock, maxLineWidth, localY, lay); + } - //todo: review here - float hostSizeW = hostBox.VisualWidth; - var rightOfLastRun = hostLine.GetRightOfLastRun(); + static void SetFinalInnerContentSize(CssBox box, float innerContentW, float innerContentH, LayoutVisitor lay) + { + box.InnerContentWidth = innerContentW; + box.InnerContentHeight = innerContentH; - if (!floatCtx.floatingOutOfLine) - { - if (rightOfLastRun + maxLineWidth < hostSizeW - floatCtx.lineRightOffset) - { - float newX = hostSizeW - (maxLineWidth + floatCtx.lineRightOffset); - newAnonBlock.SetLocation(newX, floatCtx.offsetFloatTop); - floatCtx.lineRightOffset = newX; - floatCtx.rightFloatBox = newAnonBlock; - floatCtx.floatingOutOfLine = true; - } - else - { - //start newline - float newX = hostSizeW - maxLineWidth; - newAnonBlock.SetLocation(newX, floatCtx.offsetFloatTop + hostLine.CalculateLineHeight()); - floatCtx.lineRightOffset = newX; - floatCtx.rightFloatBox = newAnonBlock; - floatCtx.floatingOutOfLine = true; - floatCtx.offsetFloatTop = newAnonBlock.LocalY; - } - } - else - { - //out-of-line mode - if (floatCtx.rightFloatBox != null) - { - float newX = floatCtx.rightFloatBox.LocalX - maxLineWidth; - if (newX > 0) - { - newAnonBlock.SetLocation(newX, floatCtx.offsetFloatTop); - floatCtx.lineRightOffset = newX; - floatCtx.rightFloatBox = newAnonBlock; - floatCtx.offsetFloatTop = newAnonBlock.LocalY; - } - else - { //start new line - newX = hostSizeW - maxLineWidth; - newAnonBlock.SetLocation(newX, floatCtx.rightFloatBox.LocalY + floatCtx.rightFloatBox.InnerContentHeight); - floatCtx.lineRightOffset = newX; - floatCtx.rightFloatBox = newAnonBlock; - floatCtx.offsetFloatTop = newAnonBlock.LocalY + newAnonBlock.InnerContentHeight; - } - } - else - { - throw new NotSupportedException(); - } - } + if (!box.Height.IsEmptyOrAuto) + { + var h = CssValueParser.ConvertToPx(box.Height, lay.LatestContainingBlock.VisualWidth, lay.LatestContainingBlock); + box.SetExpectedSize(box.ExpectedWidth, h); + box.SetVisualHeight(h); + box.SetCssBoxHeight(h); + } + else + { + switch (box.Position) + { + case CssPosition.Fixed: + case CssPosition.Absolute: + box.SetVisualHeight(box.InnerContentHeight); + break; + } - } break; - } + } + if (!box.Width.IsEmptyOrAuto) + { + //find max line width + var w = CssValueParser.ConvertToPx(box.Width, lay.LatestContainingBlock.VisualWidth, lay.LatestContainingBlock); + box.SetExpectedSize(w, box.ExpectedHeight); + box.SetVisualWidth(w); + box.SetCssBoxWidth(w); + } + else + { + switch (box.Position) + { + case CssPosition.Fixed: + case CssPosition.Absolute: + box.SetVisualWidth(box.InnerContentWidth); + break; + } + } - } - else + switch (box.Overflow) + { + case CssOverflow.Scroll: + case CssOverflow.Auto: { -#if DEBUG - if (srcBox.CssDisplay == CssDisplay.InlineBlock) + if ((box.InnerContentHeight > box.VisualHeight) || + (box.InnerContentWidth > box.VisualWidth)) { - //should not found here! - throw new NotSupportedException(); + lay.RequestScrollView(box); } -#endif - //go deeper - //recursive *** - //not new lineFormatting context - FlowBoxContentIntoHostLineFmtContext(lay, hostBox, b, - limitLocalRight, firstRunStartX, - ref hostLine, ref cx, ref floatCtx); - } - - cx += rightMostSpace; - childNumber++; - } + } break; } - if (srcBox.Position == CssPosition.Relative) + } + static float CalculateActualWidth(CssBox box) + { + float maxRight = 0; + var boxes = CssBox.UnsafeGetChildren(box); + var cnode = boxes.GetFirstLinkedNode(); + while (cnode != null) { - //offset content relative to it 'flow' position' - var left = CssValueParser.ConvertToPx(srcBox.Left, hostBox.VisualWidth, srcBox); - var top = CssValueParser.ConvertToPx(srcBox.Top, hostBox.VisualWidth, srcBox); - srcBox.SetLocation(srcBox.LocalX + left, srcBox.LocalY + top); + var cssbox = cnode.Value; + float nodeRight = cssbox.LocalX + cssbox.InnerContentWidth + + cssbox.ActualPaddingLeft + cssbox.ActualPaddingRight + + cssbox.ActualMarginLeft + + cssbox.ActualMarginRight; + + maxRight = nodeRight > maxRight ? nodeRight : maxRight; + cnode = cnode.Next; } + return maxRight + (box.ActualBorderLeftWidth + box.ActualPaddingLeft + + box.ActualPaddingRight + box.ActualBorderRightWidth); + } + static CssBox CreateAnonBlock(CssBox parent, CssBox insertBefore) + { + //auto gen by layout engine *** + var newBox = new CssBox(CssBox.UnsafeGetBoxSpec(parent).GetAnonVersion(), parent.RootGfx); + CssBox.ChangeDisplayType(newBox, Css.CssDisplay.Block); + parent.InsertChild(insertBefore, newBox); + return newBox; } + + static void LayoutContentInAbsoluteLayer(LayoutVisitor lay, CssBox srcBox) { @@ -1072,255 +1328,8 @@ static void LayoutContentInAbsoluteLayer(LayoutVisitor lay, CssBox srcBox) srcBox.InnerContentHeight = i_maxBottom; } - static void FlowRunsIntoHost(LayoutVisitor lay, - CssBox hostBox, - CssBox splitableBox, - CssBox b, - int childNumber, //child number of b - float limitRight, - float firstRunStartX, - float leftMostSpace, - float rightMostSpace, - List runs, - ref CssLineBox hostLine, - ref float cx) - { - //flow runs into hostLine, create new line if need - bool wrapNoWrapBox = false; - CssWhiteSpace bWhiteSpace = b.WhiteSpace; - bool hostBoxIsB = hostBox == b; - - if (bWhiteSpace == CssWhiteSpace.NoWrap && cx > firstRunStartX) - { - var tmpRight = cx; - for (int i = runs.Count - 1; i >= 0; --i) - { - tmpRight += runs[i].Width; - } - //----------------------------------------- - if (tmpRight > limitRight) - { - wrapNoWrapBox = true; - } - } - - //----------------------------------------------------- - - int lim = runs.Count - 1; - for (int i = 0; i <= lim; ++i) - { - var run = runs[i]; - //--------------------------------------------------- - //check if need to start new line ? - if ((cx + run.Width + rightMostSpace > limitRight && - bWhiteSpace != CssWhiteSpace.NoWrap && - bWhiteSpace != CssWhiteSpace.Pre && - (bWhiteSpace != CssWhiteSpace.PreWrap || !run.IsSpaces)) - || run.IsLineBreak || wrapNoWrapBox) - { - - wrapNoWrapBox = false; //once! - - //------------------------------- - //create new line *** - hostLine = new CssLineBox(hostBox); - hostBox.AddLineBox(hostLine); - //reset x pos for new line - cx = firstRunStartX; - - - // handle if line is wrapped for the first text element where parent has left margin/padding - if (childNumber == 0 && //b is first child of splitable box ('b' == splitableBox.GetFirstChild()) - !run.IsLineBreak && - (i == 0 || splitableBox.ParentBox.IsBlock))//this run is first run of 'b' (run == b.FirstRun) - { - cx += splitableBox.ActualMarginLeft + - splitableBox.ActualBorderLeftWidth + - splitableBox.ActualPaddingLeft; - } - - if (run.IsSolidContent || i == 0) - { - cx += leftMostSpace; - } - } - //--------------------------------------------------- - - if (run.IsSpaces && hostLine.RunCount == 0) - { - //not add - continue; - } - //--------------------------------------------------- - - hostLine.AddRun(run); //*** - if (lim == 0) - { - //single one - if (!hostBoxIsB) - { - cx += b.ActualPaddingLeft; - } - run.SetLocation(cx, 0); - cx += run.Width + b.ActualPaddingRight; - } - else - { - if (i == 0) - { - //first - if (!hostBoxIsB) - { - cx += b.ActualPaddingLeft; - } - - run.SetLocation(cx, 0); - cx = run.Right; - } - else if (i == lim) - { - run.SetLocation(cx, 0); - cx += run.Width + b.ActualPaddingRight; - } - else - { - run.SetLocation(cx, 0); - cx = run.Right; - } - } - //--------------------------------------------------- - //move current_line_x to right of run - //cx = run.Right; - } - } - /// - /// Applies vertical and horizontal alignment to words in lineboxes - /// - /// - /// - static void ApplyAlignment(CssLineBox lineBox, CssTextAlign textAlign, LayoutVisitor lay) - { - switch (textAlign) - { - case CssTextAlign.Right: - ApplyRightAlignment(lineBox); - break; - case CssTextAlign.Center: - ApplyCenterAlignment(lineBox); - break; - case CssTextAlign.Justify: - ApplyJustifyAlignment(lineBox); - break; - default: - break; - } - //--------------------------------------------- - // Applies vertical alignment to the linebox - return; - //TODO: review here - lineBox.ApplyBaseline(lineBox.CalculateTotalBoxBaseLine(lay)); - //--------------------------------------------- - } - - /// - /// Applies right to left direction to words - /// - /// - /// - static void ApplyRightToLeft(CssLineBox lineBox) - { - if (lineBox.RunCount > 0) - { - - float left = lineBox.GetFirstRun().Left; - float right = lineBox.GetLastRun().Right; - foreach (CssRun run in lineBox.GetRunIter()) - { - float diff = run.Left - left; - float w_right = right - diff; - run.Left = w_right - run.Width; - } - } - } - static void ApplyJustifyAlignment(CssLineBox lineBox) - { - - - if (lineBox.IsLastLine) return; - - float indent = lineBox.IsFirstLine ? lineBox.OwnerBox.ActualTextIndent : 0f; - - float runWidthSum = 0f; - int runCount = 0; - - float availableWidth = lineBox.OwnerBox.GetClientWidth() - indent; - - // Gather text sum - foreach (CssRun w in lineBox.GetRunIter()) - { - runWidthSum += w.Width; - runCount++; - } - - if (runCount == 0) return; //Avoid Zero division - - float spaceOfEachRun = (availableWidth - runWidthSum) / runCount; //Spacing that will be used - - float cX = lineBox.OwnerBox.GetClientLeft() + indent; - CssRun lastRun = lineBox.GetLastRun(); - foreach (CssRun run in lineBox.GetRunIter()) - { - run.Left = cX; - cX = run.Right + spaceOfEachRun; - if (run == lastRun) - { - run.Left = lineBox.OwnerBox.GetClientRight() - run.Width; - } - } - } - - /// - /// Applies centered alignment to the text on the linebox - /// - /// - /// - private static void ApplyCenterAlignment(CssLineBox line) - { - if (line.RunCount == 0) return; - CssRun lastRun = line.GetLastRun(); - float diff = (line.OwnerBox.GetClientWidth() - lastRun.Right) / 2; - if (diff > CSS_OFFSET_THRESHOLD) - { - foreach (CssRun word in line.GetRunIter()) - { - word.Left += diff; - } - line.CachedLineContentWidth += diff; - } - } - /// - /// Applies right alignment to the text on the linebox - /// - /// - /// - private static void ApplyRightAlignment(CssLineBox line) - { - if (line.RunCount == 0) - { - return; - } - CssRun lastRun = line.GetLastRun(); - float diff = line.OwnerBox.GetClientWidth() - line.GetLastRun().Right; - if (diff > CSS_OFFSET_THRESHOLD) - { - foreach (CssRun word in line.GetRunIter()) - { - word.Left += diff; - } - } - } static void RearrangeWithFlexContext(CssBox box, LayoutVisitor lay) { diff --git a/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/CssTableLayoutEngine.cs b/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/CssTableLayoutEngine.cs index c687cf8f9..67f7ccc12 100755 --- a/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/CssTableLayoutEngine.cs +++ b/Source/LayoutFarm.HtmlRenderer/3_Boxes_Layout/0_General/CssTableLayoutEngine.cs @@ -1292,7 +1292,7 @@ static void CalculateMinMaxSumWords( paddingSum += CssTableLayoutEngine.CalculateTableSpacing(box); } - if (box.HasRuns) + if (box.HasOnlyRuns) { // calculate the min and max sum for all the words in the box foreach (CssRun run in box.GetRunIter()) diff --git a/Source/LayoutFarm.HtmlRenderer/5_Boxes_SpecialBoxes/CssBoxImage.cs b/Source/LayoutFarm.HtmlRenderer/5_Boxes_SpecialBoxes/CssBoxImage.cs index 426f4afc2..e6632ac47 100755 --- a/Source/LayoutFarm.HtmlRenderer/5_Boxes_SpecialBoxes/CssBoxImage.cs +++ b/Source/LayoutFarm.HtmlRenderer/5_Boxes_SpecialBoxes/CssBoxImage.cs @@ -146,9 +146,9 @@ public override void Paint(PaintVisitor p, RectangleF rect) } break; } -//#if DEBUG -// p.FillRectangle(Color.Red, rect.X, rect.Y, rect.Width, rect.Height); -//#endif + //#if DEBUG + // p.FillRectangle(Color.Red, rect.X, rect.Y, rect.Width, rect.Height); + //#endif } @@ -183,8 +183,99 @@ public override void MeasureRunsSize(LayoutVisitor lay) this.RunSizeMeasurePass = true; - CssLayoutEngine.MeasureImageSize(_imgRun, lay); + MeasureImageSize(_imgRun, lay); } + + /// + /// Measure image box size by the width\height set on the box and the actual rendered image size.
+ /// If no image exists for the box error icon will be set. + ///
+ /// the image word to measure + static void MeasureImageSize(CssImageRun imgRun, LayoutVisitor lay) + { + var width = imgRun.OwnerBox.Width; + var height = imgRun.OwnerBox.Height; + + bool hasImageTagWidth = width.Number > 0 && width.UnitOrNames == Css.CssUnitOrNames.Pixels; + bool hasImageTagHeight = height.Number > 0 && height.UnitOrNames == Css.CssUnitOrNames.Pixels; + bool scaleImageHeight = false; + + if (hasImageTagWidth) + { + imgRun.Width = width.Number; + } + else if (width.Number > 0 && width.IsPercentage) + { + + imgRun.Width = width.Number * lay.LatestContainingBlock.VisualWidth; + scaleImageHeight = true; + } + else if (imgRun.HasUserImageContent) + { + imgRun.Width = imgRun.ImageRectangle == Rectangle.Empty ? imgRun.OriginalImageWidth : imgRun.ImageRectangle.Width; + } + else + { + imgRun.Width = hasImageTagHeight ? height.Number / 1.14f : 20; + } + + var maxWidth = imgRun.OwnerBox.MaxWidth;// new CssLength(imageWord.OwnerBox.MaxWidth); + if (maxWidth.Number > 0) + { + float maxWidthVal = -1; + switch (maxWidth.UnitOrNames) + { + case Css.CssUnitOrNames.Percent: + { + maxWidthVal = maxWidth.Number * lay.LatestContainingBlock.VisualWidth; + } break; + case Css.CssUnitOrNames.Pixels: + { + maxWidthVal = maxWidth.Number; + } break; + } + + + if (maxWidthVal > -1 && imgRun.Width > maxWidthVal) + { + imgRun.Width = maxWidthVal; + scaleImageHeight = !hasImageTagHeight; + } + } + + if (hasImageTagHeight) + { + imgRun.Height = height.Number; + } + else if (imgRun.HasUserImageContent) + { + imgRun.Height = imgRun.ImageRectangle == Rectangle.Empty ? imgRun.OriginalImageHeight : imgRun.ImageRectangle.Height; + } + else + { + imgRun.Height = imgRun.Width > 0 ? imgRun.Width * 1.14f : 22.8f; + } + + if (imgRun.HasUserImageContent) + { + // If only the width was set in the html tag, ratio the height. + if ((hasImageTagWidth && !hasImageTagHeight) || scaleImageHeight) + { + // Divide the given tag width with the actual image width, to get the ratio. + float ratio = imgRun.Width / imgRun.OriginalImageWidth; + imgRun.Height = imgRun.OriginalImageHeight * ratio; + } + // If only the height was set in the html tag, ratio the width. + else if (hasImageTagHeight && !hasImageTagWidth) + { + // Divide the given tag height with the actual image height, to get the ratio. + float ratio = imgRun.Height / imgRun.OriginalImageHeight; + imgRun.Width = imgRun.OriginalImageWidth * ratio; + } + } + //imageWord.Height += imageWord.OwnerBox.ActualBorderBottomWidth + imageWord.OwnerBox.ActualBorderTopWidth + imageWord.OwnerBox.ActualPaddingTop + imageWord.OwnerBox.ActualPaddingBottom; + } + }