/// <summary> /// Simplest alignment, just arrange words. /// </summary> /// <param name="g"></param> /// <param name="line"></param> private static void ApplyLeftAlignment(RGraphics g, CssLineBox line) { //No alignment needed. //foreach (LineBoxRectangle r in line.Rectangles) //{ // double curx = r.Left + (r.Index == 0 ? r.OwnerBox.ActualPaddingLeft + r.OwnerBox.ActualBorderLeftWidth / 2 : 0); // if (r.SpaceBefore) curx += r.OwnerBox.ActualWordSpacing; // foreach (BoxWord word in r.Words) // { // word.Left = curx; // word.Top = r.Top;// +r.OwnerBox.ActualPaddingTop + r.OwnerBox.ActualBorderTopWidth / 2; // curx = word.Right + r.OwnerBox.ActualWordSpacing; // } //} }
/// <summary> /// Applies vertical and horizontal alignment to words in lineboxes /// </summary> /// <param name="g"></param> /// <param name="lineBox"></param> private static void ApplyHorizontalAlignment(RGraphics g, CssLineBox lineBox) { switch (lineBox.OwnerBox.TextAlign) { case CssConstants.Right: ApplyRightAlignment(g, lineBox); break; case CssConstants.Center: ApplyCenterAlignment(g, lineBox); break; case CssConstants.Justify: ApplyJustifyAlignment(g, lineBox); break; default: ApplyLeftAlignment(g, lineBox); break; } }
/// <summary> /// Applies centered alignment to the text on the linebox /// </summary> /// <param name="g"></param> /// <param name="lineBox"></param> private static void ApplyJustifyAlignment(RGraphics g, CssLineBox lineBox) { if (lineBox.Equals(lineBox.OwnerBox.LineBoxes[lineBox.OwnerBox.LineBoxes.Count - 1])) { return; } var indent = lineBox.Equals(lineBox.OwnerBox.LineBoxes[0]) ? lineBox.OwnerBox.ActualTextIndent : 0f; double textSum = 0f; double words = 0f; var availWidth = lineBox.OwnerBox.ClientRectangle.Width - indent; // Gather text sum foreach (var w in lineBox.Words) { textSum += w.Width; words += 1f; } if (words <= 0f) { return; //Avoid Zero division } var spacing = (availWidth - textSum) / words; //Spacing that will be used var curx = lineBox.OwnerBox.ClientLeft + indent; foreach (var word in lineBox.Words) { word.Left = curx; curx = word.Right + spacing; if (word == lineBox.Words[lineBox.Words.Count - 1]) { word.Left = lineBox.OwnerBox.ClientRight - word.Width; } } }
/// <summary> /// Recursively creates the rectangles of the blockBox, by bubbling from deep to outside of the boxes /// in the rectangle structure /// </summary> private static void BubbleRectangles(CssBox box, CssLineBox line) { if (box.Words.Count > 0) { double x = Single.MaxValue, y = Single.MaxValue, r = Single.MinValue, b = Single.MinValue; var words = line.WordsOf(box); if (words.Count > 0) { foreach (var word in words) { // handle if line is wrapped for the first text element where parent has left margin\padding var left = word.Left; if (box == box.ParentBox.Boxes[0] && word == box.Words[0] && word == line.Words[0] && line != line.OwnerBox.LineBoxes[0] && !word.IsLineBreak) { left -= box.ParentBox.ActualMarginLeft + box.ParentBox.ActualBorderLeftWidth + box.ParentBox.ActualPaddingLeft; } x = Math.Min(x, left); r = Math.Max(r, word.Right); y = Math.Min(y, word.Top); b = Math.Max(b, word.Bottom); } line.UpdateRectangle(box, x, y, r, b); } } else { foreach (var b in box.Boxes) { BubbleRectangles(b, line); } } }
/// <summary> /// Recursively flows the content of the box using the inline model /// </summary> /// <param name="g">Device Info</param> /// <param name="blockbox">Blockbox that contains the text flow</param> /// <param name="box">Current box to flow its content</param> /// <param name="limitRight">Maximum reached right</param> /// <param name="linespacing">Space to use between rows of text</param> /// <param name="startx">x starting coordinate for when breaking lines of text</param> /// <param name="line">Current linebox being used</param> /// <param name="curx">Current x coordinate that will be the left of the next word</param> /// <param name="cury">Current y coordinate that will be the top of the next word</param> /// <param name="maxRight">Maximum right reached so far</param> /// <param name="maxbottom">Maximum bottom reached so far</param> private static void FlowBox(RGraphics g, CssBox blockbox, CssBox box, double limitRight, double linespacing, double startx, ref CssLineBox line, ref double curx, ref double cury, ref double maxRight, ref double maxbottom) { var startX = curx; var startY = cury; box.FirstHostingLineBox = line; var localCurx = curx; var localMaxRight = maxRight; var localmaxbottom = maxbottom; foreach (var b in box.Boxes) { var leftspacing = (b.Position != CssConstants.Absolute && b.Position != CssConstants.Fixed) ? b.ActualMarginLeft + b.ActualBorderLeftWidth + b.ActualPaddingLeft : 0; var rightspacing = (b.Position != CssConstants.Absolute && b.Position != CssConstants.Fixed) ? b.ActualMarginRight + b.ActualBorderRightWidth + b.ActualPaddingRight : 0; b.RectanglesReset(); b.MeasureWordsSize(g); curx += leftspacing; if (b.Words.Count > 0) { var wrapNoWrapBox = false; if (b.WhiteSpace == CssConstants.NoWrap && curx > startx) { var boxRight = curx; foreach (var word in b.Words) { boxRight += word.FullWidth; } if (boxRight > limitRight) { wrapNoWrapBox = true; } } if (DomUtils.IsBoxHasWhitespace(b)) { curx += box.ActualWordSpacing; } foreach (var word in b.Words) { if (maxbottom - cury < box.ActualLineHeight) { maxbottom += box.ActualLineHeight - (maxbottom - cury); } if ((b.WhiteSpace != CssConstants.NoWrap && b.WhiteSpace != CssConstants.Pre && curx + word.Width + rightspacing > limitRight && (b.WhiteSpace != CssConstants.PreWrap || !word.IsSpaces)) || word.IsLineBreak || wrapNoWrapBox) { wrapNoWrapBox = false; curx = startx; // handle if line is wrapped for the first text element where parent has left margin\padding if (b == box.Boxes[0] && !word.IsLineBreak && (word == b.Words[0] || (box.ParentBox != null && box.ParentBox.IsBlock))) { curx += box.ActualMarginLeft + box.ActualBorderLeftWidth + box.ActualPaddingLeft; } cury = maxbottom + linespacing; line = new CssLineBox(blockbox); if (word.IsImage || word.Equals(b.FirstWord)) { curx += leftspacing; } } line.ReportExistanceOf(word); word.Left = curx; word.Top = cury; if (!box.IsFixed) { word.BreakPage(); } curx = word.Left + word.FullWidth; maxRight = Math.Max(maxRight, word.Right); maxbottom = Math.Max(maxbottom, word.Bottom); if (b.Position == CssConstants.Absolute) { word.Left += box.ActualMarginLeft; word.Top += box.ActualMarginTop; } } } else { FlowBox(g, blockbox, b, limitRight, linespacing, startx, ref line, ref curx, ref cury, ref maxRight, ref maxbottom); } curx += rightspacing; } // handle height setting if (maxbottom - startY < box.ActualHeight) { maxbottom += box.ActualHeight - (maxbottom - startY); } // handle width setting if (box.IsInline && 0 <= curx - startX && curx - startX < box.ActualWidth) { // hack for actual width handling curx += box.ActualWidth - (curx - startX); line.Rectangles.Add(box, new RRect(startX, startY, box.ActualWidth, box.ActualHeight)); } // handle box that is only a whitespace if (box.Text != null && box.Text.IsWhitespace() && !box.IsImage && box.IsInline && box.Boxes.Count == 0 && box.Words.Count == 0) { curx += box.ActualWordSpacing; } // hack to support specific absolute position elements if (box.Position == CssConstants.Absolute) { curx = localCurx; maxRight = localMaxRight; maxbottom = localmaxbottom; AdjustAbsolutePosition(box, 0, 0); } box.LastHostingLineBox = line; }